//go:build !windows package fs import ( "errors" "os" "syscall" "golang.org/x/sys/unix" ) // SyncDir flushes any file renames to the filesystem. func SyncDir(dirName string) error { // fsync the dir to flush the rename dir, err := os.OpenFile(dirName, os.O_RDONLY, os.ModeDir) if err != nil { return err } defer dir.Close() // While we're on unix, we may be running in a Docker container that is // pointed at a Windows volume over samba. That doesn't support fsyncs // on directories. This shows itself as an EINVAL, so we ignore that // error. err = dir.Sync() if pe, ok := err.(*os.PathError); ok && pe.Err == syscall.EINVAL { err = nil } else if err != nil { return err } return dir.Close() } // RenameFileWithReplacement will replace any existing file at newpath with the contents // of oldpath. It works also if it the rename spans over several file systems. // // If no file already exists at newpath, newpath will be created using the contents // of oldpath. If this function returns successfully, the contents of newpath will // be identical to oldpath, and oldpath will be removed. func RenameFileWithReplacement(oldpath, newpath string) error { if err := os.Rename(oldpath, newpath); !errors.Is(err, syscall.EXDEV) { // note: also includes err == nil return err } // move over filesystem boundaries, we have to copy. // (if there was another error, it will likely fail a second time) return MoveFileWithReplacement(oldpath, newpath) } // RenameFile renames oldpath to newpath, returning an error if newpath already // exists. If this function returns successfully, the contents of newpath will // be identical to oldpath, and oldpath will be removed. func RenameFile(oldpath, newpath string) error { if _, err := os.Stat(newpath); err == nil { return newFileExistsError(newpath) } return RenameFileWithReplacement(oldpath, newpath) } // CreateFile creates a new file at newpath, returning an error if newpath already // exists func CreateFile(newpath string) (*os.File, error) { if _, err := os.Stat(newpath); err == nil { return nil, newFileExistsError(newpath) } return os.Create(newpath) } // DiskUsage returns disk usage of disk of path func DiskUsage(path string) (*DiskStatus, error) { fs := unix.Statfs_t{} if err := unix.Statfs(path, &fs); err != nil { return nil, err } var disk DiskStatus disk.All = fs.Blocks * uint64(fs.Bsize) disk.Avail = uint64(fs.Bavail) * uint64(fs.Bsize) disk.Free = fs.Bfree * uint64(fs.Bsize) disk.Used = disk.All - disk.Free return &disk, nil }