From 9078a77850dd680bfa938d9ed7c83600a60c0e7b Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 6 Feb 2016 19:27:59 +0100 Subject: Move pathfs_frontend to internal/fusefrontend "git status" for reference: renamed: pathfs_frontend/args.go -> internal/fusefrontend/args.go renamed: pathfs_frontend/compat_darwin.go -> internal/fusefrontend/compat_darwin.go renamed: pathfs_frontend/compat_linux.go -> internal/fusefrontend/compat_linux.go renamed: pathfs_frontend/file.go -> internal/fusefrontend/file.go renamed: pathfs_frontend/file_holes.go -> internal/fusefrontend/file_holes.go renamed: pathfs_frontend/fs.go -> internal/fusefrontend/fs.go renamed: pathfs_frontend/fs_dir.go -> internal/fusefrontend/fs_dir.go renamed: pathfs_frontend/names.go -> internal/fusefrontend/names.go renamed: pathfs_frontend/write_lock.go -> internal/fusefrontend/write_lock.go modified: main.go --- internal/fusefrontend/fs_dir.go | 157 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 internal/fusefrontend/fs_dir.go (limited to 'internal/fusefrontend/fs_dir.go') diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go new file mode 100644 index 0000000..2b1e25d --- /dev/null +++ b/internal/fusefrontend/fs_dir.go @@ -0,0 +1,157 @@ +package fusefrontend + +// Mkdir and Rmdir + +import ( + "fmt" + "os" + "path/filepath" + "syscall" + + "github.com/hanwen/go-fuse/fuse" + + "github.com/rfjakob/gocryptfs/internal/toggledlog" + "github.com/rfjakob/gocryptfs/internal/cryptocore" + "github.com/rfjakob/gocryptfs/internal/nametransform" +) + +func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) { + if fs.isFiltered(relPath) { + return fuse.EPERM + } + encPath, err := fs.getBackingPath(relPath) + if err != nil { + return fuse.ToStatus(err) + } + if !fs.args.DirIV { + return fuse.ToStatus(os.Mkdir(encPath, os.FileMode(mode))) + } + + // We need write and execute permissions to create gocryptfs.diriv + origMode := mode + mode = mode | 0300 + + // The new directory may take the place of an older one that is still in the cache + fs.nameTransform.DirIVCache.Clear() + // Create directory + fs.dirIVLock.Lock() + defer fs.dirIVLock.Unlock() + err = os.Mkdir(encPath, os.FileMode(mode)) + if err != nil { + return fuse.ToStatus(err) + } + // Create gocryptfs.diriv inside + err = nametransform.WriteDirIV(encPath) + if err != nil { + // This should not happen + toggledlog.Warn.Printf("Mkdir: WriteDirIV failed: %v", err) + err2 := syscall.Rmdir(encPath) + if err2 != nil { + toggledlog.Warn.Printf("Mkdir: Rmdir rollback failed: %v", err2) + } + return fuse.ToStatus(err) + } + + // Set permissions back to what the user wanted + if origMode != mode { + err = os.Chmod(encPath, os.FileMode(origMode)) + if err != nil { + toggledlog.Warn.Printf("Mkdir: Chmod failed: %v", err) + } + } + + return fuse.OK +} + +func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) { + encPath, err := fs.getBackingPath(name) + if err != nil { + return fuse.ToStatus(err) + } + if !fs.args.DirIV { + return fuse.ToStatus(syscall.Rmdir(encPath)) + } + + // If the directory is not empty besides gocryptfs.diriv, do not even + // attempt the dance around gocryptfs.diriv. + fd, err := os.Open(encPath) + if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.EACCES { + // We need permission to read and modify the directory + toggledlog.Debug.Printf("Rmdir: handling EACCESS") + fi, err2 := os.Stat(encPath) + if err2 != nil { + toggledlog.Debug.Printf("Rmdir: Stat: %v", err2) + return fuse.ToStatus(err2) + } + origMode := fi.Mode() + newMode := origMode | 0700 + err2 = os.Chmod(encPath, newMode) + if err2 != nil { + toggledlog.Debug.Printf("Rmdir: Chmod failed: %v", err2) + return fuse.ToStatus(err) + } + defer func() { + if code != fuse.OK { + // Undo the chmod if removing the directory failed + err3 := os.Chmod(encPath, origMode) + if err3 != nil { + toggledlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err2) + } + } + }() + // Retry open + fd, err = os.Open(encPath) + } + if err != nil { + toggledlog.Debug.Printf("Rmdir: Open: %v", err) + return fuse.ToStatus(err) + } + list, err := fd.Readdirnames(10) + fd.Close() + if err != nil { + toggledlog.Debug.Printf("Rmdir: Readdirnames: %v", err) + return fuse.ToStatus(err) + } + if len(list) > 1 { + return fuse.ToStatus(syscall.ENOTEMPTY) + } else if len(list) == 0 { + toggledlog.Warn.Printf("Rmdir: gocryptfs.diriv missing, allowing deletion") + return fuse.ToStatus(syscall.Rmdir(encPath)) + } + + // Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ" + dirivPath := filepath.Join(encPath, nametransform.DirIVFilename) + parentDir := filepath.Dir(encPath) + tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64()) + tmpDirivPath := filepath.Join(parentDir, tmpName) + toggledlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpDirivPath) + // The directory is in an inconsistent state between rename and rmdir. Protect against + // concurrent readers. + fs.dirIVLock.Lock() + defer fs.dirIVLock.Unlock() + err = os.Rename(dirivPath, tmpDirivPath) + if err != nil { + toggledlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v", + nametransform.DirIVFilename, tmpDirivPath, err) + return fuse.ToStatus(err) + } + // Actual Rmdir + err = syscall.Rmdir(encPath) + if err != nil { + // This can happen if another file in the directory was created in the + // meantime, undo the rename + err2 := os.Rename(tmpDirivPath, dirivPath) + if err2 != nil { + toggledlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2) + } + return fuse.ToStatus(err) + } + // Delete "gocryptfs.diriv.rmdir.INODENUMBER" + err = syscall.Unlink(tmpDirivPath) + if err != nil { + toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err) + } + // The now-deleted directory may have been in the DirIV cache. Clear it. + fs.nameTransform.DirIVCache.Clear() + return fuse.OK +} -- cgit v1.2.3