summaryrefslogtreecommitdiff
path: root/internal/fusefrontend
diff options
context:
space:
mode:
authorJakob Unterwurzacher2017-09-05 22:47:15 +0200
committerJakob Unterwurzacher2017-09-05 22:47:15 +0200
commit604b0779d47d39fc5390a67167a8090748843cc1 (patch)
tree8e5fbac917a8de6cdb6a185fc49608b07b3af4da /internal/fusefrontend
parent6f3b65d924d8ab0827516ee274935b2c18979e10 (diff)
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
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r--internal/fusefrontend/fs_dir.go28
1 files changed, 27 insertions, 1 deletions
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