diff options
| author | Jakob Unterwurzacher | 2016-06-27 00:25:56 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-06-27 00:27:36 +0200 | 
| commit | 3288d987032fde33ff3cd837b1a0feb612b9710b (patch) | |
| tree | 04ab9bee8dfed4e072f68eb70dc60be4fc139a75 /internal | |
| parent | 547ddf42648e55b3235343ac7d4eae27931362f8 (diff) | |
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.
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend/fs_dir.go | 96 | 
1 files changed, 56 insertions, 40 deletions
| diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index 80a66ca..9547ce6 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -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 { -		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 { +		// 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: Rename rollback failed: %v", err2) +			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)  	} -	// 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) | 
