From 604b0779d47d39fc5390a67167a8090748843cc1 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Tue, 5 Sep 2017 22:47:15 +0200 Subject: [PATCH] macos: automatically remove .DS_Store on Rmdir MacOS sprinkles .DS_Store files everywhere. This is hard to avoid for users, so handle it transparently in Rmdir(). Mitigates https://github.com/rfjakob/gocryptfs/issues/140 --- internal/fusefrontend/fs_dir.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/internal/fusefrontend/fs_dir.go b/internal/fusefrontend/fs_dir.go index c1bf9bf..b144ea4 100644 --- a/internal/fusefrontend/fs_dir.go +++ b/internal/fusefrontend/fs_dir.go @@ -20,6 +20,8 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) +const dsStoreName = ".DS_Store" + func (fs *FS) mkdirWithIv(cPath string, mode uint32) error { // Between the creation of the directory and the creation of gocryptfs.diriv // the directory is inconsistent. Take the lock to prevent other readers @@ -117,6 +119,16 @@ func (fs *FS) Mkdir(newPath string, mode uint32, context *fuse.Context) (code fu return fuse.OK } +// haveDsstore return true if one of the entries in "names" is ".DS_Store". +func haveDsstore(names []string) bool { + for _, n := range names { + if n == dsStoreName { + return true + } + } + return false +} + // Rmdir implements pathfs.FileSystem func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { cPath, err := fs.getBackingPath(path) @@ -175,6 +187,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { } dirfd := os.NewFile(uintptr(dirfdRaw), cName) defer dirfd.Close() +retry: // Check directory contents children, err := dirfd.Readdirnames(10) if err == io.EOF { @@ -189,6 +202,18 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { tlog.Warn.Printf("Rmdir: Readdirnames: %v", err) return fuse.ToStatus(err) } + // MacOS sprinkles .DS_Store files everywhere. This is hard to avoid for + // users, so handle it transparently here. + if runtime.GOOS == "darwin" && len(children) <= 2 && haveDsstore(children) { + ds := filepath.Join(cPath, dsStoreName) + err = syscall.Unlink(ds) + if err != nil { + tlog.Warn.Printf("Rmdir: failed to delete blocking file %q: %v", ds, err) + return fuse.ToStatus(err) + } + tlog.Warn.Printf("Rmdir: had to delete blocking file %q", ds) + goto retry + } // If the directory is not empty besides gocryptfs.diriv, do not even // attempt the dance around gocryptfs.diriv. if len(children) > 1 { @@ -236,6 +261,7 @@ func (fs *FS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { return fuse.OK } +// If syscallcompat.HaveGetdents is false we will warn once about it var haveGetdentsWarnOnce sync.Once // OpenDir implements pathfs.FileSystem @@ -325,7 +351,7 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f if err != nil { tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v", cDirName, cName, err) - if runtime.GOOS == "darwin" && cName == ".DS_Store" { + if runtime.GOOS == "darwin" && cName == dsStoreName { // MacOS creates lots of these files. Log the warning but don't // increment errorCount - does not warrant returning EIO. continue