fusefrontend: Fix longname handling for renames with existing target

Fixes https://github.com/rfjakob/gocryptfs/issues/170

Steps to reproduce the problem:

* Create a regular forward mount point
* Create a file with a shortname and one with a long filename
* Try to run 'mv <shortname> <longname>'

This should actually work and replace the existing file, but instead it
fails with:

    mv: cannot move '<shortname>' to '<longname>': File exists

The problem is the creation of the .name file. If the target already exists
we can safely ignore the EEXIST error and just keep the existing .name file.
This commit is contained in:
Sebastian Lackner 2017-11-25 01:56:56 +01:00 committed by rfjakob
parent d257bb34c1
commit 9f56b33e0c
3 changed files with 54 additions and 5 deletions

View File

@ -517,7 +517,13 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod
finalNewPath = cNewName
// Create destination .name file
err = fs.nameTransform.WriteLongName(newDirFd, cNewName, newPath)
if err != nil {
// Failure to write the .name file is expected when the target path already
// exists. Since hashes are pretty unique, there is no need to modify the
// file anyway. We still set newDirFd to nil to ensure that we do not delete
// the file on error.
if err == syscall.EEXIST {
newDirFd = nil
} else if err != nil {
return fuse.ToStatus(err)
}
}

View File

@ -114,7 +114,11 @@ func (n *NameTransform) WriteLongName(dirfd *os.File, hashName string, plainName
fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), hashName+LongNameSuffix,
syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL, 0600)
if err != nil {
tlog.Warn.Printf("WriteLongName: Openat: %v", err)
// Don't warn if the file already exists - this is allowed for renames
// and should be handled by the caller.
if err != syscall.EEXIST {
tlog.Warn.Printf("WriteLongName: Openat: %v", err)
}
return err
}
fd := os.NewFile(uintptr(fdRaw), hashName+LongNameSuffix)

View File

@ -544,7 +544,7 @@ func TestLongNames(t *testing.T) {
if !test_helpers.VerifyExistence(wd + n255x) {
t.Errorf("n255x is not in directory listing")
}
// Rename long to long
// Rename long to long (target does not exist)
n255y := string(bytes.Repeat([]byte("y"), 255))
err = os.Rename(wd+n255x, wd+n255y)
if err != nil {
@ -553,7 +553,20 @@ func TestLongNames(t *testing.T) {
if !test_helpers.VerifyExistence(wd + n255y) {
t.Errorf("n255y is not in directory listing")
}
// Rename long to short
// Rename long to long (target exists)
f, err = os.Create(wd + n255x)
if err != nil {
t.Fatalf("Could not create n255x: %v", err)
}
f.Close()
err = os.Rename(wd+n255x, wd+n255y)
if err != nil {
t.Fatalf("Could not rename n255x to n255y: %v", err)
}
if !test_helpers.VerifyExistence(wd + n255y) {
t.Errorf("n255y is not in directory listing")
}
// Rename long to short (target does not exist)
err = os.Rename(wd+n255y, wd+"short")
if err != nil {
t.Fatalf("Could not rename n255y to short: %v", err)
@ -561,7 +574,20 @@ func TestLongNames(t *testing.T) {
if !test_helpers.VerifyExistence(wd + "short") {
t.Errorf("short is not in directory listing")
}
// Rename short to long
// Rename long to short (target exists)
f, err = os.Create(wd + n255y)
if err != nil {
t.Fatalf("Could not create n255y: %v", err)
}
f.Close()
err = os.Rename(wd+n255y, wd+"short")
if err != nil {
t.Fatalf("Could not rename n255y to short: %v", err)
}
if !test_helpers.VerifyExistence(wd + "short") {
t.Errorf("short is not in directory listing")
}
// Rename short to long (target does not exist)
err = os.Rename(wd+"short", wd+n255x)
if err != nil {
t.Fatalf("Could not rename short to n255x: %v", err)
@ -569,6 +595,19 @@ func TestLongNames(t *testing.T) {
if !test_helpers.VerifyExistence(wd + n255x) {
t.Errorf("255x is not in directory listing II")
}
// Rename short to long (target exists)
f, err = os.Create(wd + "short")
if err != nil {
t.Fatalf("Could not create short: %v", err)
}
f.Close()
err = os.Rename(wd+"short", wd+n255x)
if err != nil {
t.Fatalf("Could not rename short to n255x: %v", err)
}
if !test_helpers.VerifyExistence(wd + n255x) {
t.Errorf("n255x is not in directory listing")
}
// Unlink
err = syscall.Unlink(wd + n255x)
if err != nil {