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)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
dirfd, err := syscallcompat.Openat(parentDirFd, cName,
|
||||
syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
|
||||
if err == syscall.EACCES {
|
||||
// We need permission to read and modify the directory
|
||||
tlog.Debug.Printf("Rmdir: handling EACCESS")
|
||||
// Unless we are running as root, we need read, write and execute permissions
|
||||
// to handle gocryptfs.diriv.
|
||||
permWorkaround := false
|
||||
var origMode uint32
|
||||
if !fs.args.PreserveOwner {
|
||||
var st unix.Stat_t
|
||||
err = syscallcompat.Fstatat(parentDirFd, cName, &st, unix.AT_SYMLINK_NOFOLLOW)
|
||||
if err != nil {
|
||||
tlog.Debug.Printf("Rmdir: Stat: %v", err)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
// This cast is needed on Darwin, where st.Mode is uint16.
|
||||
origMode := uint32(st.Mode)
|
||||
err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700)
|
||||
if err != nil {
|
||||
tlog.Debug.Printf("Rmdir: Fchmodat failed: %v", err)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
// Retry open
|
||||
dirfd, err = syscallcompat.Openat(parentDirFd, cName,
|
||||
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 st.Mode&0700 != 0700 {
|
||||
tlog.Debug.Printf("Rmdir: permWorkaround")
|
||||
permWorkaround = true
|
||||
// This cast is needed on Darwin, where st.Mode is uint16.
|
||||
origMode = uint32(st.Mode)
|
||||
err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700)
|
||||
if err != nil {
|
||||
tlog.Debug.Printf("Rmdir: permWorkaround: chmod failed: %v", err)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
dirfd, err := syscallcompat.Openat(parentDirFd, cName,
|
||||
syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
|
||||
if err != nil {
|
||||
tlog.Debug.Printf("Rmdir: Open: %v", err)
|
||||
return fuse.ToStatus(err)
|
||||
}
|
||||
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:
|
||||
// Check directory contents
|
||||
children, err := syscallcompat.Getdents(dirfd)
|
||||
|
Loading…
Reference in New Issue
Block a user