fusefrontend: ensure directories without W or X perms can be deleted
This fixed the "Permission denied" bug, but still has the problem that the directory may be replaced behind our back. Mitigated by the fact that we skip the workaround when running as root with -allow_other. https://github.com/rfjakob/gocryptfs/issues/354
This commit is contained in:
parent
fab585ec01
commit
962c523644
|
@ -151,42 +151,47 @@ func (fs *FS) Rmdir(relPath string, context *fuse.Context) (code fuse.Status) {
|
||||||
err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR)
|
err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
dirfd, err := syscallcompat.Openat(parentDirFd, cName,
|
// Unless we are running as root, we need read, write and execute permissions
|
||||||
syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
|
// to handle gocryptfs.diriv.
|
||||||
if err == syscall.EACCES {
|
permWorkaround := false
|
||||||
// We need permission to read and modify the directory
|
var origMode uint32
|
||||||
tlog.Debug.Printf("Rmdir: handling EACCESS")
|
if !fs.args.PreserveOwner {
|
||||||
var st unix.Stat_t
|
var st unix.Stat_t
|
||||||
err = syscallcompat.Fstatat(parentDirFd, cName, &st, unix.AT_SYMLINK_NOFOLLOW)
|
err = syscallcompat.Fstatat(parentDirFd, cName, &st, unix.AT_SYMLINK_NOFOLLOW)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Debug.Printf("Rmdir: Stat: %v", err)
|
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
|
if st.Mode&0700 != 0700 {
|
||||||
|
tlog.Debug.Printf("Rmdir: permWorkaround")
|
||||||
|
permWorkaround = true
|
||||||
// This cast is needed on Darwin, where st.Mode is uint16.
|
// This cast is needed on Darwin, where st.Mode is uint16.
|
||||||
origMode := uint32(st.Mode)
|
origMode = uint32(st.Mode)
|
||||||
err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700)
|
err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Debug.Printf("Rmdir: Fchmodat failed: %v", err)
|
tlog.Debug.Printf("Rmdir: permWorkaround: chmod failed: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
// Retry open
|
}
|
||||||
dirfd, err = syscallcompat.Openat(parentDirFd, cName,
|
}
|
||||||
|
dirfd, err := syscallcompat.Openat(parentDirFd, cName,
|
||||||
syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
|
syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
|
||||||
// Undo the chmod if removing the directory failed
|
|
||||||
defer func() {
|
|
||||||
if code != fuse.OK {
|
|
||||||
err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode)
|
|
||||||
if err != nil {
|
|
||||||
tlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tlog.Debug.Printf("Rmdir: Open: %v", err)
|
tlog.Debug.Printf("Rmdir: Open: %v", err)
|
||||||
return fuse.ToStatus(err)
|
return fuse.ToStatus(err)
|
||||||
}
|
}
|
||||||
defer syscall.Close(dirfd)
|
defer syscall.Close(dirfd)
|
||||||
|
// Undo the chmod if removing the directory failed. This must run before
|
||||||
|
// closing dirfd, so defer it after (defer is LIFO).
|
||||||
|
if permWorkaround {
|
||||||
|
defer func() {
|
||||||
|
if code != fuse.OK {
|
||||||
|
err = unix.Fchmod(dirfd, origMode)
|
||||||
|
if err != nil {
|
||||||
|
tlog.Warn.Printf("Rmdir: permWorkaround: rollback failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
retry:
|
retry:
|
||||||
// Check directory contents
|
// Check directory contents
|
||||||
children, err := syscallcompat.Getdents(dirfd)
|
children, err := syscallcompat.Getdents(dirfd)
|
||||||
|
|
Loading…
Reference in New Issue