aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorJakob Unterwurzacher2018-09-22 20:56:01 +0200
committerJakob Unterwurzacher2018-09-23 12:17:59 +0200
commita1fb4566186ec38ec2b21b936f29df669efbfe4f (patch)
treee0d525c07c0e51af2110164ed185f18e68d23cff /internal
parent897bb8924fe23be5c989b519b62fbf7841a31a4c (diff)
fusefrontend: make Rename() symlink-safe
Use Openat() and the openBackingDir() helper so we never follow symlinks.
Diffstat (limited to 'internal')
-rw-r--r--internal/fusefrontend/fs.go66
1 files changed, 20 insertions, 46 deletions
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go
index a1ccbba..557393b 100644
--- a/internal/fusefrontend/fs.go
+++ b/internal/fusefrontend/fs.go
@@ -485,65 +485,39 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
if fs.isFiltered(newPath) {
return fuse.EPERM
}
- cOldPath, err := fs.getBackingPath(oldPath)
+ oldDirfd, oldCName, err := fs.openBackingDir(oldPath)
if err != nil {
return fuse.ToStatus(err)
}
- cNewPath, err := fs.getBackingPath(newPath)
+ defer syscall.Close(oldDirfd)
+ newDirfd, newCName, err := fs.openBackingDir(newPath)
if err != nil {
return fuse.ToStatus(err)
}
+ defer syscall.Close(newDirfd)
// The Rename may cause a directory to take the place of another directory.
// That directory may still be in the DirIV cache, clear it.
fs.nameTransform.DirIVCache.Clear()
// Easy case.
if fs.args.PlaintextNames {
- return fuse.ToStatus(syscall.Rename(cOldPath, cNewPath))
- }
- // Handle long source file name
- oldDirFd := -1
- finalOldDirFd := -1
- var finalOldPath = cOldPath
- cOldName := filepath.Base(cOldPath)
- if nametransform.IsLongContent(cOldName) {
- oldDirFd, err = syscall.Open(filepath.Dir(cOldPath), syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
- if err != nil {
- return fuse.ToStatus(err)
- }
- defer syscall.Close(oldDirFd)
- finalOldDirFd = oldDirFd
- // Use relative path
- finalOldPath = cOldName
- }
- // Handle long destination file name
- newDirFd := -1
- finalNewDirFd := -1
- finalNewPath := cNewPath
- cNewName := filepath.Base(cNewPath)
- if nametransform.IsLongContent(cNewName) {
- newDirFd, err = syscall.Open(filepath.Dir(cNewPath), syscall.O_RDONLY|syscall.O_DIRECTORY, 0)
- if err != nil {
- return fuse.ToStatus(err)
- }
- defer syscall.Close(newDirFd)
- finalNewDirFd = newDirFd
- // Use relative path
- finalNewPath = cNewName
- // Create destination .name file
- err = fs.nameTransform.WriteLongName(newDirFd, cNewName, newPath)
+ return fuse.ToStatus(syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName))
+ }
+ // Long destination file name: create .name file
+ nameFileAlreadyThere := false
+ if nametransform.IsLongContent(newCName) {
+ err = fs.nameTransform.WriteLongName(newDirfd, newCName, newPath)
// Failure to write the .name file is expected when the target path already
// exists. Since hashes are pretty unique, there is no need to modify the
- // file anyway. We still set newDirFd to nil to ensure that we do not delete
- // the file on error.
+ // .name file in this case, and we ignore the error.
if err == syscall.EEXIST {
- newDirFd = -1
+ nameFileAlreadyThere = true
} else if err != nil {
return fuse.ToStatus(err)
}
}
// Actual rename
- tlog.Debug.Printf("Renameat oldfd=%d oldpath=%s newfd=%d newpath=%s\n", finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
- err = syscallcompat.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
+ tlog.Debug.Printf("Renameat %d/%s -> %d/%s\n", oldDirfd, oldCName, newDirfd, newCName)
+ err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName)
if err == syscall.ENOTEMPTY || err == syscall.EEXIST {
// If an empty directory is overwritten we will always get an error as
// the "empty" directory will still contain gocryptfs.diriv.
@@ -552,18 +526,18 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
// again.
tlog.Debug.Printf("Rename: Handling ENOTEMPTY")
if fs.Rmdir(newPath, context) == fuse.OK {
- err = syscallcompat.Renameat(finalOldDirFd, finalOldPath, finalNewDirFd, finalNewPath)
+ err = syscallcompat.Renameat(oldDirfd, oldCName, newDirfd, newCName)
}
}
if err != nil {
- if newDirFd >= 0 {
- // Roll back .name creation
- nametransform.DeleteLongName(newDirFd, cNewName)
+ if nametransform.IsLongContent(newCName) && nameFileAlreadyThere == false {
+ // Roll back .name creation unless the .name file was already there
+ nametransform.DeleteLongName(newDirfd, newCName)
}
return fuse.ToStatus(err)
}
- if oldDirFd >= 0 {
- nametransform.DeleteLongName(oldDirFd, cOldName)
+ if nametransform.IsLongContent(oldCName) {
+ nametransform.DeleteLongName(oldDirfd, oldCName)
}
return fuse.OK
}