init_js
init_js

Reputation: 4631

Proper way to get errno value out of PathError

I'm trying to determine whether an os.PathError is due to an EINVAL or ENOENT. How do I correctly make that determination?

    res, err := os.Readlink(fpath)

    if err, ok := err.(*os.PathError); ok {
        if err.Err == os.ErrInvalid {
            // This path here. What's the correct check?
            return fpath
        }
        log.Printf("ResolveLinks error: %s", err)
        return ""
    }
    log.Printf("Resolved: %s to %s", fpath, res)
    return res

If fpath points to a regular file instead of a symlink, readlink should produce EINVAL, but my err.Err == os.ErrInvalid check fails and the following is logged:

2019/03/28 12:04:42 ResolveLinks error: readlink foo: invalid argument

I'm supposed to unpack the PathError, but then what? Compare the error string?

I notice that the os module has certain functions to match error types, like os.IsNotExist, but I don't see one for all possible error codes.

Upvotes: 2

Views: 1183

Answers (2)

init_js
init_js

Reputation: 4631

I've found that the error's string will allow to match on the error type, but I'm not confident (at all) this works across platforms or even locales.

if err, ok := err.(*os.PathError); ok {

    //EINVAL
    if err.Err.Error() == "invalid argument" {
        …

    // - OR - 

    //ENOENT
    if err.Err.Error() == "no such file or directory" {
       …
}

Upvotes: 0

Vadim Ashikhman
Vadim Ashikhman

Reputation: 10136

The err.Err is of type syscall.Errno and this is integer type that you can convert to int. Running this code on Ubuntu produces error code 22:

    if serr, ok := err.Err.(syscall.Errno); ok {
        fmt.Println(int(serr))
    }

Bear in mind I do not know if this kind of check is cross platform or not.

if you just want to check file is symlink you can use Lstat to get FileInfo struct and do the check:

fi.Mode() & os.ModeSymlink != 0

Upvotes: 1

Related Questions