errno to Go errors - c

I'm trying to issue KVM ioctls from Go code. At the moment I have something as follows:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := (*C.struct_kvm_irqfd)(C.calloc(1, C.sizeof_struct_kvm_irqfd))
defer C.free(unsafe.Pointer(irqfd))
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
The Ioctl function is implemented as follows :
func Ioctl(fd uintptr, cmd C.uint, arg uintptr) (uintptr, error) {
ret, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
uintptr(cmd),
uintptr(arg),
)
if int64(ret) == -1 {
return ret, ErrnoToErr(errno)
}
return ret, nil
}
And the ErrnoToErr function is implemented as follows :
func ErrnoToErr(errno syscall.Errno) error {
return fmt.Errorf("%v", errno.Error())
}
Inside GetSupportedCpuid, the argument for the ioctl, cpuid is allocated using C.calloc. If this were C code cpuid could just be allocated on the stack. Is there any way I can get around allocating using calloc and using free? Any alternatives that would be more idiomatic?
Would there be a way to have ErrnoToErr return a go error type corresponding to the errno? Currently it uses the (e Errno) Error() inside syscall/syscall_unix.go; so it just dereferences a list of strings and returns the string. It would be nice to have a type, so that I can test for specific errors in my unit tests.

Replying just to the updated example, we could do this:
func (vm *Vm) RegisterIrqFd(efd *EventFd, gsi uint32) error {
irqfd := C.struct_kvm_irqfd{}
irqfd.fd = C.uint(efd.Fd())
irqfd.gsi = C.uint(gsi)
if _, err := sysutils.Ioctl(vm.Fd(), C.KVM_IRQFD, uintptr(unsafe.Pointer(&irqfd))); err != nil {
return fmt.Errorf("RegisterIrqFd failed: %v", err)
}
return nil
}
I think this is safe, but we might find that irqfd escapes to heap anyway since we take its address here, in which case there's not much savings. Even so, as long as this doesn't violate Go pointer rules (I don't think it does) it's a lot nicer to read.

Read the documentation syscall.Errno
So convertion may be performed like this
func ErrnoToErr(errno syscall.Errno) error {
if errno != 0 {
return errno
}
return nil
}
For equality it only states
Errno values can be tested against error values from the os package
using errors.Is.
The question of comparing errors was already considered here How to compare Go errors

Related

How to pass C.ulong inter process?

In order to reuse CGO pointers (type C.uintptr_t) between multiple applications, I tried to use go rpc to pass the initialized pointer, but the program reported an error: rpc: gob error encoding body: gob: type not registered for interface: main._Ctype_ulong. I think there might be some issues with pointer types.
1. init func
func initApp(configPath *C.char) C.uintptr_t
2. App1, daemon process, call the init func, and pass the pointer to another by go rpc
var globalSDKPtr C.ulong
type HelloService struct{}
func (p *HelloService) Hello(request string, reply *C.ulong) error {
*reply = globalSDKPtr
return nil
}
func startRPS() {
rpc.RegisterName("HelloService", new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error:", err)
}
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error:", err)
}
rpc.ServeConn(conn)
}
3. App2, recevie the pointer reuse it.
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("dialing:", err)
}
var reply C.ulong
err = client.Call("HelloService.Hello", "hello", &reply)
if err != nil {
log.Fatal(err)
}
res := C.query(reply)
I guess the reason for the problem is that my thinking is wrong. The way to reuse cgo pointers may not be the way of go rpc, but shared memory, but in any case, passing cgo-related things is always confusing. . Can anyone help me out.

How to remove the last N bytes from a file

I want to delete thelast N bytes from file in Go,
Actually, this is already implemented is the os.Truncate() function. But this function takes the new size. So to use this, you have to first get the size of the file. For that, you may use os.Stat().
Wrapping it into a function:
func truncateFile(name string, bytesToRemove int64) error {
fi, err := os.Stat(name)
if err != nil {
return err
}
return os.Truncate(name, fi.Size()-bytesToRemove)
}
Using it to remove the last 5000 bytes:
if err := truncateFile("C:\\Test.zip", 5000); err != nil {
fmt.Println("Error:", err)
}
Another alternative is to use the File.Truncate() method for that. If we have an os.File, we may also use File.Stat() to get its size.
This is how it would look like:
func truncateFile(name string, bytesToRemove int64) error {
f, err := os.OpenFile(name, os.O_RDWR, 0644)
if err != nil {
return err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return err
}
return f.Truncate(fi.Size() - bytesToRemove)
}
Using it is the same. This may be preferable if we're working on a file (we have it opened) and we have to truncate it. But in that case you'd want to pass os.File instead of its name to truncateFile().
Note: if you try to remove more bytes than the file currently has, truncateFile() will return an error.

Problem accumulating/appending values in an array using recursion with Go

First of all, this is my first non-dummy program using Go. Any recommendation will be appreciated.
Code description:
I want to retrieve all the information from an API where the information is being paginated. So I want to iterate through all the pages in order to get all the information.
This is what I did so far:
I have the these two functions:
func request(requestData *RequestData) []*ProjectsResponse {
client := &http.Client{
Timeout: time.Second * 10,
}
projects := []*ProjectsResponse{}
innerRequest(client, requestData.URL, projects)
return projects
}
func innerRequest(client *http.Client, URL string, projects []*ProjectsResponse) {
if URL == "" {
return
}
req, err := http.NewRequest("GET", URL, nil)
if err != nil {
log.Printf("Request creation failed with error %s\n", err)
}
req.Header.Add("Private-Token", os.Getenv("API_KEY"))
res, err := client.Do(req)
log.Printf("Executing request: %s", req.URL)
if err != nil {
log.Printf("The HTTP request failed with error %s\n", err)
}
data, _ := ioutil.ReadAll(res.Body)
var response ProjectsResponse
err = json.Unmarshal(data, &response)
if err != nil {
log.Printf("Unmarshalling failed with error %s\n", err)
}
projects = append(projects, &response)
pagingData := getPageInformation(res)
innerRequest(client, pagingData.NextLink, projects)
}
Undesired behavior:
The values in the projects []*ProjectsResponse array are being appended on each iteration of the recursion, but when the recursion ends I get an empty array list. So, somehow after the innerRequests ends, in the project variable inside the request method I get nothing.
Hope somebody and spot my mistake.
Thanks in advance.
I'm guessing that all of your project objects are scoped to the function so they no longer exist when the function ends. I don't think you need your projects to exist before you call innerRequest, so you should probably just have that method return the projects. I think something like this should work...
func innerRequest(client *http.Client, URL string) []*ProjectsResponse {
if URL == "" {
return nil
}
//More code...
pagingData := getPageInformation(res)
return append([]*ProjectsResponse{&response}, innerRequest(client, pagingData.NextLink)...)
}
The confusion lies in the way a slice is handled in Go. Here is the in-depth explanation, but I will abbreviate.
The common misconception is that the slice you pass around is a reference to the slice, which is false. The actual variable you operate on when you handle a slice is known as a slice header. This is a simple struct with a reference to the underlying array under the covers and follows Go's pass by value rules, i.e. it is copied when passed to a function. Thus, if it is not returned, you won't have the updated header.
Returning data from recursion follows a straightforward pattern. Here is a basic example. I'm also including a version that doesn't require a return, but operates on the slice as a reference, which will modify the original. (Note: Passing around slice pointers is generally not considered idiomatic Go)
Playground link: https://play.golang.org/p/v5XeYpH1VlF
// correct way to return from recursion
func addReturn(s []int) []int {
if finalCondition(s) {
return s
}
s = append(s, 1)
return addReturn(s)
}
// using a reference
func addReference(s *[]int) {
if finalCondition(*s) {
return
}
*s = append(*s, 1)
addReference(s)
}
// whatever terminates the recursion
func finalCondition(s []int) bool {
if len(s) >= 10 {
return true
}
return false
}

Go. Writing []byte to file results in zero byte file

I try to serialize a structured data to file. I looked through some examples and made such construction:
func (order Order) Serialize(folder string) {
b := bytes.Buffer{}
e := gob.NewEncoder(&b)
err := e.Encode(order)
if err != nil { panic(err) }
os.MkdirAll(folder, 0777)
file, err := os.Create(folder + order.Id)
if err != nil { panic(err) }
defer file.Close()
writer := bufio.NewWriter(file)
n, err := writer.Write(b.Bytes())
fmt.Println(n)
if err != nil {
panic(err)
}
}
Serialize is a method serializing its object to file called by it's id property. I looked through debugger - byte buffer contains data before writing. I mean object is fully initialized. Even n variable representing quantity of written bytes is more than a thousand - the file shouldn't be empty at all. The file is created but it is totally empty. What's wrong?
bufio.Writer (as the package name hints) uses a buffer to cache writes. If you ever use it, you must call Writer.Flush() when you're done writing to it to ensure the buffered data gets written to the underlying io.Writer.
Also note that you can directly write to an os.File, no need to create a buffered writer "around" it. (*os.File implements io.Writer).
Also note that you can create the gob.Encoder directly directed to the os.File, so even the bytes.Buffer is unnecessary.
Also os.MkdirAll() may fail, check its return value.
Also it's better to "concatenate" parts of a file path using filepath.Join() which takes care of extra / missing slashes at the end of folder names.
And last, it would be better to signal the failure of Serialize(), e.g. with an error return value, so the caller party has the chance to examine if the operation succeeded, and act accordingly.
So Order.Serialize() should look like this:
func (order Order) Serialize(folder string) error {
if err := os.MkdirAll(folder, 0777); err != nil {
return err
}
file, err := os.Create(filepath.Join(folder, order.Id))
if err != nil {
return err
}
defer file.Close()
if err := gob.NewEncoder(file).Encode(order); err != nil {
return err
}
return nil
}

How to check if a file exists in Go?

Go's standard library does not have a function solely intended to check if a file exists or not (like Python's os.path.exists). What is the idiomatic way to do it?
To check if a file doesn't exist, equivalent to Python's if not os.path.exists(filename):
if _, err := os.Stat("/path/to/whatever"); errors.Is(err, os.ErrNotExist) {
// path/to/whatever does not exist
}
To check if a file exists, equivalent to Python's if os.path.exists(filename):
Edited: per recent comments
if _, err := os.Stat("/path/to/whatever"); err == nil {
// path/to/whatever exists
} else if errors.Is(err, os.ErrNotExist) {
// path/to/whatever does *not* exist
} else {
// Schrodinger: file may or may not exist. See err for details.
// Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence
}
Answer by Caleb Spare posted in gonuts mailing list.
[...] It's not actually needed very often and [...] using os.Stat is
easy enough for the cases where it is required.
[...] For instance: if you are going to open the file, there's no reason to check whether it exists first. The file could disappear in between checking and opening, and anyway you'll need to check the os.Open error regardless. So you simply call os.IsNotExist(err) after you try
to open the file, and deal with its non-existence there (if that requires special handling).
[...] You don't need to check for the paths existing at all (and you shouldn't).
os.MkdirAll works whether or not the paths already exist. (Also you need to check the error from that call.)
Instead of using os.Create, you should use os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) . That way you'll get an error if the file already exists. Also this doesn't have a race condition with something else making the file, unlike your version which checks for existence beforehand.
Taken from: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
The first thing to consider is that it is rare that you would only want to check whether or not a file exists. In most situations, you're trying to do something with the file if it exists. In Go, any time you try to perform some operation on a file that doesn't exist, the result should be a specific error (os.ErrNotExist) and the best thing to do is check whether the return err value (e.g. when calling a function like os.OpenFile(...)) is os.ErrNotExist.
The recommended way to do this used to be:
file, err := os.OpenFile(...)
if os.IsNotExist(err) {
// handle the case where the file doesn't exist
}
However, since the addition of errors.Is in Go 1.13 (released in late 2019), the new recommendation is to use errors.Is:
file, err := os.OpenFile(...)
if errors.Is(err, os.ErrNotExist) {
// handle the case where the file doesn't exist
}
It's usually best to avoid using os.Stat to check for the existence of a file before you attempt to do something with it, because it will always be possible for the file to be renamed, deleted, etc. in the window of time before you do something with it.
However, if you're OK with this caveat and you really, truly just want to check whether a file exists without then proceeding to do something useful with it (as a contrived example, let's say that you're writing a pointless CLI tool that tells you whether or not a file exists and then exits ¯\_(ツ)_/¯), then the recommended way to do it would be:
if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
// file does not exist
} else {
// file exists
}
You should use the os.Stat() and os.IsNotExist() functions as in the following example:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if err == nil {
return true, nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}
edit1: fixed issue of returning true when under some circumstances.
edit2: switched to using errors.Is() from os.IsNotExist(), which many say is a best-practice and here
What other answers missed, is that the path given to the function could actually be a directory. Following function makes sure, that the path is really a file.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Another thing to point out: This code could still lead to a race condition, where another thread or process deletes or creates the specified file, while the fileExists function is running.
If you're worried about this, use a lock in your threads, serialize the access to this function or use an inter-process semaphore if multiple applications are involved. If other applications are involved, outside of your control, you're out of luck, I guess.
The example by user11617 is incorrect; it will report that the file exists even in cases where it does not, but there was an error of some other sort.
The signature should be Exists(string) (bool, error). And then, as it happens, the call sites are no better.
The code he wrote would better as:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
But I suggest this instead:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
_, err := os.Stat(file)
if err == nil {
log.Printf("file %s exists", file)
} else if os.IsNotExist(err) {
log.Printf("file %s not exists", file)
} else {
log.Printf("file %s stat error: %v", file, err)
}
basicly
package main
import (
"fmt"
"os"
)
func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func main() {
var file string = "foo.txt"
exist := fileExists(file)
if exist {
fmt.Println("file exist")
} else {
fmt.Println("file not exists")
}
}
run example
other way
with os.Open
package main
import (
"fmt"
"os"
)
func fileExists(path string) bool {
_, err := os.Open(path) // For read access.
return err == nil
}
func main() {
fmt.Println(fileExists("d4d.txt"))
}
run it
Best way to check if file exists:
if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
// your code here if file exists
}
The function example:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Let's look at few aspects first, both the function provided by os package of golang are not utilities but error checkers, what do I mean by that is they are just a wrapper to handle errors on cross platform.
So basically if os.Stat if this function doesn't give any error that means the file is existing if it does you need to check what kind of error it is, here comes the use of these two function os.IsNotExist and os.IsExist.
This can be understood as the Stat of the file throwing error because it doesn't exists or is it throwing error because it exist and there is some problem with it.
The parameter that these functions take is of type error, although you might be able to pass nil to it but it wouldn't make sense.
This also points to the fact that IsExist is not same as !IsNotExist, they are way two different things.
So now if you want to know if a given file exist in go, I would prefer the best way is:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
As mentioned in other answers, it is possible to construct the required behaviour / errors from using different flags with os.OpenFile. In fact, os.Create is just a sensible-defaults shorthand for doing so:
// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
You should combine these flags yourself to get the behaviour you are interested in:
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
Depending on what you pick, you will get different errors.
Below is an example which will either truncate an existing file, or fail when a file exists.
openOpts := os.O_RDWR|os.O_CREATE
if truncateWhenExists {
openOpts |= os.O_TRUNC // file will be truncated
} else {
openOpts |= os.O_EXCL // file must not exist
}
f, err := os.OpenFile(filePath, openOpts, 0644)
// ... do stuff
This is how I check if a file exists in Go 1.16
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main () {
if _, err:= os.Stat("/path/to/file"); errors.Is(err, fs.ErrNotExist){
fmt.Print(err.Error())
} else {
fmt.Print("file exists")
}
}
Here is my take on a file exists method. It also checks that the file is not a directory and in case of an error, returns it as well.
// FileExists checks if a file exists (and it is not a directory).
func FileExists(filePath string) (bool, error) {
info, err := os.Stat(filePath)
if err == nil {
return !info.IsDir(), nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
return false, err
}

Resources