fusefrontend: fix PlaintextNames versions of Mkdir, Rmdir

The "!fs.args.DirIV" special case was removed by b17f0465c7
but that, by accident, also removed the handling for
PlaintextNames.

Re-add it as an explicit PlaintextNames special case.

Also adds support for removing directories that miss their
gocryptfs.diriv file for some reason.
This commit is contained in:
Jakob Unterwurzacher 2016-06-27 00:25:56 +02:00
parent 547ddf4264
commit 3288d98703
1 changed files with 57 additions and 41 deletions

View File

@ -4,6 +4,7 @@ package fusefrontend
import (
"fmt"
"io"
"os"
"path/filepath"
"syscall"
@ -46,6 +47,11 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu
if err != nil {
return fuse.ToStatus(err)
}
if fs.args.PlaintextNames {
err = os.Mkdir(cPath, os.FileMode(mode))
return fuse.ToStatus(err)
}
// We need write and execute permissions to create gocryptfs.diriv
origMode := mode
mode = mode | 0300
@ -95,7 +101,10 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
if err != nil {
return fuse.ToStatus(err)
}
if fs.args.PlaintextNames {
err = syscall.Rmdir(cPath)
return fuse.ToStatus(err)
}
parentDir := filepath.Dir(cPath)
parentDirFd, err := os.Open(parentDir)
if err != nil {
@ -146,49 +155,56 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) {
defer dirfd.Close()
children, err := dirfd.Readdirnames(10)
if err != nil {
if err == nil {
// If the directory is not empty besides gocryptfs.diriv, do not even
// attempt the dance around gocryptfs.diriv.
if len(children) > 1 {
return fuse.ToStatus(syscall.ENOTEMPTY)
}
// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
tlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName)
// The directory is in an inconsistent state between rename and rmdir.
// Protect against concurrent readers.
fs.dirIVLock.Lock()
defer fs.dirIVLock.Unlock()
err = syscall.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
int(parentDirFd.Fd()), tmpName)
if err != nil {
tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
nametransform.DirIVFilename, tmpName, err)
return fuse.ToStatus(err)
}
// Actual Rmdir
// TODO Use syscall.Unlinkat with the AT_REMOVEDIR flag once it is available
// in Go
err = syscall.Rmdir(cPath)
if err != nil {
// This can happen if another file in the directory was created in the
// meantime, undo the rename
err2 := syscall.Renameat(int(parentDirFd.Fd()), tmpName,
int(dirfd.Fd()), nametransform.DirIVFilename)
if err != nil {
tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
}
return fuse.ToStatus(err)
}
// Delete "gocryptfs.diriv.rmdir.XYZ"
err = syscall.Unlinkat(int(parentDirFd.Fd()), tmpName)
if err != nil {
tlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
}
} else if err == io.EOF {
// The directory is empty
tlog.Warn.Printf("Rmdir: %q: gocryptfs.diriv is missing", cPath)
err = syscall.Rmdir(cPath)
if err != nil {
return fuse.ToStatus(err)
}
} else {
tlog.Warn.Printf("Rmdir: Readdirnames: %v", err)
return fuse.ToStatus(err)
}
// If the directory is not empty besides gocryptfs.diriv, do not even
// attempt the dance around gocryptfs.diriv.
if len(children) > 1 {
return fuse.ToStatus(syscall.ENOTEMPTY)
}
// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ"
tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())
tlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName)
// The directory is in an inconsistent state between rename and rmdir.
// Protect against concurrent readers.
fs.dirIVLock.Lock()
defer fs.dirIVLock.Unlock()
err = syscall.Renameat(int(dirfd.Fd()), nametransform.DirIVFilename,
int(parentDirFd.Fd()), tmpName)
if err != nil {
tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v",
nametransform.DirIVFilename, tmpName, err)
return fuse.ToStatus(err)
}
// Actual Rmdir
// TODO Use syscall.Unlinkat with the AT_REMOVEDIR flag once it is available
// in Go
err = syscall.Rmdir(cPath)
if err != nil {
// This can happen if another file in the directory was created in the
// meantime, undo the rename
err2 := syscall.Renameat(int(parentDirFd.Fd()), tmpName,
int(dirfd.Fd()), nametransform.DirIVFilename)
if err != nil {
tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)
}
return fuse.ToStatus(err)
}
// Delete "gocryptfs.diriv.rmdir.XYZ"
err = syscall.Unlinkat(int(parentDirFd.Fd()), tmpName)
if err != nil {
tlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)
}
// Delete .name file
if nametransform.IsLongContent(cName) {
nametransform.DeleteLongName(parentDirFd, cName)