syscallcompat: OSX: add Renamat wrapper
Adds a poor man's renameat implementation for OSX.
This commit is contained in:
parent
9b725c15cf
commit
79851bf6cc
|
@ -4,6 +4,7 @@ package fusefrontend
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -17,6 +18,7 @@ import (
|
||||||
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
"github.com/rfjakob/gocryptfs/internal/contentenc"
|
||||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -385,6 +387,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
||||||
}
|
}
|
||||||
defer oldDirFd.Close()
|
defer oldDirFd.Close()
|
||||||
finalOldDirFd = int(oldDirFd.Fd())
|
finalOldDirFd = int(oldDirFd.Fd())
|
||||||
|
// Use relative path
|
||||||
finalOldPath = cOldName
|
finalOldPath = cOldName
|
||||||
}
|
}
|
||||||
// Handle long destination file name
|
// Handle long destination file name
|
||||||
|
@ -399,6 +402,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
||||||
}
|
}
|
||||||
defer newDirFd.Close()
|
defer newDirFd.Close()
|
||||||
finalNewDirFd = int(newDirFd.Fd())
|
finalNewDirFd = int(newDirFd.Fd())
|
||||||
|
// Use relative path
|
||||||
finalNewPath = cNewName
|
finalNewPath = cNewName
|
||||||
// Create destination .name file
|
// Create destination .name file
|
||||||
err = fs.nameTransform.WriteLongName(newDirFd, cNewName, newPath)
|
err = fs.nameTransform.WriteLongName(newDirFd, cNewName, newPath)
|
||||||
|
@ -407,7 +411,8 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Actual rename
|
// Actual rename
|
||||||
err = syscall.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
|
fmt.Printf("Renameat oldfd=%d oldpath=%s newfd=%d newpath=%s\n", finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
|
||||||
|
err = syscallcompat.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
|
||||||
if err == syscall.ENOTEMPTY || err == syscall.EEXIST {
|
if err == syscall.ENOTEMPTY || err == syscall.EEXIST {
|
||||||
// If an empty directory is overwritten we will always get an error as
|
// If an empty directory is overwritten we will always get an error as
|
||||||
// the "empty" directory will still contain gocryptfs.diriv.
|
// the "empty" directory will still contain gocryptfs.diriv.
|
||||||
|
@ -416,7 +421,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
|
||||||
// again.
|
// again.
|
||||||
tlog.Debug.Printf("Rename: Handling ENOTEMPTY")
|
tlog.Debug.Printf("Rename: Handling ENOTEMPTY")
|
||||||
if fs.Rmdir(newPath, context) == fuse.OK {
|
if fs.Rmdir(newPath, context) == fuse.OK {
|
||||||
err = syscall.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
|
err = syscallcompat.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/rfjakob/gocryptfs/internal/configfile"
|
"github.com/rfjakob/gocryptfs/internal/configfile"
|
||||||
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
"github.com/rfjakob/gocryptfs/internal/cryptocore"
|
||||||
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
"github.com/rfjakob/gocryptfs/internal/nametransform"
|
||||||
|
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
|
||||||
"github.com/rfjakob/gocryptfs/internal/tlog"
|
"github.com/rfjakob/gocryptfs/internal/tlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -168,7 +169,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
|
||||||
// Protect against concurrent readers.
|
// Protect against concurrent readers.
|
||||||
fs.dirIVLock.Lock()
|
fs.dirIVLock.Lock()
|
||||||
defer fs.dirIVLock.Unlock()
|
defer fs.dirIVLock.Unlock()
|
||||||
err = syscall.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
|
err = syscallcompat.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
|
||||||
int(parentDirFd.Fd()), tmpName)
|
int(parentDirFd.Fd()), tmpName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
|
tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
|
||||||
|
@ -182,7 +183,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This can happen if another file in the directory was created in the
|
// This can happen if another file in the directory was created in the
|
||||||
// meantime, undo the rename
|
// meantime, undo the rename
|
||||||
err2 := syscall.Renameat(int(parentDirFd.Fd()), tmpName,
|
err2 := syscallcompat.Renameat(int(parentDirFd.Fd()), tmpName,
|
||||||
int(dirfd.Fd()), nametransform.DirIVFilename)
|
int(dirfd.Fd()), nametransform.DirIVFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
|
tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
|
||||||
|
|
|
@ -2,44 +2,87 @@ package syscallcompat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// prealloc - preallocate space without changing the file size. This prevents
|
// Sorry, fallocate is not available on OSX at all and
|
||||||
// us from running out of space in the middle of an operation.
|
// fcntl F_PREALLOCATE is not accessible from Go.
|
||||||
func Prealloc(fd int, off int64, len int64) (err error) {
|
// See https://github.com/rfjakob/gocryptfs/issues/18 if you want to help.
|
||||||
//
|
func EnospcPrealloc(fd int, off int64, len int64) (err error) {
|
||||||
// Sorry, fallocate is not available on OSX at all and
|
|
||||||
// fcntl F_PREALLOCATE is not accessible from Go.
|
|
||||||
//
|
|
||||||
// See https://github.com/rfjakob/gocryptfs/issues/18 if you want to help.
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var openatLock sync.Mutex
|
// See above.
|
||||||
|
|
||||||
// Poor man's Openat:
|
|
||||||
// 1) fchdir to the dirfd
|
|
||||||
// 2) open the file
|
|
||||||
// 3) chdir back.
|
|
||||||
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
|
||||||
openatLock.Lock()
|
|
||||||
defer openatLock.Unlock()
|
|
||||||
|
|
||||||
oldWd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
err = syscall.Fchdir(dirfd)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
defer os.Chdir(oldWd)
|
|
||||||
|
|
||||||
return syscall.Open(path, flags, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
||||||
return syscall.EOPNOTSUPP
|
return syscall.EOPNOTSUPP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var chdirMutex sync.Mutex
|
||||||
|
|
||||||
|
// Poor man's Openat
|
||||||
|
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
||||||
|
chdirMutex.Lock()
|
||||||
|
defer chdirMutex.Unlock()
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
// Save the old working directory
|
||||||
|
oldWd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
// Chdir to target directory
|
||||||
|
err = syscall.Fchdir(dirfd)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
// Chdir back at the end
|
||||||
|
defer os.Chdir(oldWd)
|
||||||
|
}
|
||||||
|
return syscall.Open(path, flags, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poor man's Renameat
|
||||||
|
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
|
||||||
|
chdirMutex.Lock()
|
||||||
|
defer chdirMutex.Unlock()
|
||||||
|
// Unless both paths are absolute we have to save the old working dir and
|
||||||
|
// Chdir(oldWd) back to it in the end. If we error out before the first
|
||||||
|
// chdir, Chdir(oldWd) is unneccassary but does no harm.
|
||||||
|
if !filepath.IsAbs(oldpath) || !filepath.IsAbs(newpath) {
|
||||||
|
oldWd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.Chdir(oldWd)
|
||||||
|
}
|
||||||
|
// Make oldpath absolute
|
||||||
|
oldpath, err = dirfdAbs(olddirfd, oldpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make newpath absolute
|
||||||
|
newpath, err = dirfdAbs(newdirfd, newpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.Rename(oldpath, newpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dirfdAbs transforms the dirfd-relative "path" to an absolute one. If the
|
||||||
|
// path is not already absolute, this function will change the working
|
||||||
|
// directory. The caller has to chdir back.
|
||||||
|
func dirfdAbs(dirfd int, path string) (absPath string, err error) {
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
err = syscall.Fchdir(dirfd)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(wd, path), nil
|
||||||
|
}
|
||||||
|
|
|
@ -37,10 +37,14 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
||||||
|
return syscall.Fallocate(fd, mode, off, len)
|
||||||
|
}
|
||||||
|
|
||||||
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
|
||||||
return syscall.Openat(dirfd, path, flags, mode)
|
return syscall.Openat(dirfd, path, flags, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fallocate(fd int, mode uint32, off int64, len int64) (err error) {
|
func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) {
|
||||||
return syscall.Fallocate(fd, mode, off, len)
|
return syscall.Renameat(olddirfd, oldpath, newdirfd, newpath)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue