I have a piece of code which tries to write to a path. If that file turns out to already exist and it's a directory, it writes a file to that directory. The code won't overwrite anything existing anyway but I'm not finding an easy to identify that the path is a directory without hitting obvious potential race conditions. I'm aware that there is a race risk because I will be closing and opening but I don't see a way to avoid that. Here's what I have now
fh, err := os.OpenFile(viper.GetString("output"), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0700)
if err != nil {
// is it a directory?
if errors.Is(err, *WHATHERE*) {
// close and write to a file in that directory
} else {
// some other error (like exists)
return err
}
} else {
// write to the file, we're done
}
I'm happy with all the parts I've replaced with a comment. What I'm trying to work out is if there is a simple way (or standard-library way) to test that the error returned is "is a directory". Have I missed something obvious?
Try without os.O_EXCL
:
fh, err := os.OpenFile(viper.GetString("output"), os.O_CREATE|os.O_WRONLY, 0o700)
if err != nil {
var perr *os.PathError
var errNo syscall.Errno
if errors.As(err, &perr) && errors.As(perr.Err, &errNo) && errNo == syscall.EISDIR {
// close and write to a file in that directory
}
// ...
}
// ...
Also, since PathError.Unwrap returns the underlying error PathError.Err
, you can simplify the above to:
// ...
if errors.Is(err, syscall.EISDIR) {
// close and write to a file in that directory
}
// ...
When you use O_EXCL
, open()
returns EEXIST
if the path is a directory:
Ensure that this call creates the file: if this flag is specified in conjunction with
O_CREAT
, and pathname already exists, thenopen()
fails with the errorEEXIST
.