diff options
| author | Jakob Unterwurzacher | 2020-07-11 19:56:45 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-07-11 19:56:45 +0200 | 
| commit | 7de3330d70971c95562e8fa6db889cac2f17da36 (patch) | |
| tree | cea084ca98a7577639197155c358f2dfd443dc62 /internal | |
| parent | 84344834c4fb49c2eb484bcc43d24a2522b1b5c1 (diff) | |
v2api: implement Rename
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/fusefrontend/node.go | 63 | ||||
| -rw-r--r-- | internal/fusefrontend/node_api_check.go | 2 | 
2 files changed, 64 insertions, 1 deletions
| diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index 5b42ddc..6f85ddb 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -451,3 +451,66 @@ func (n *Node) Symlink(ctx context.Context, target, name string, out *fuse.Entry  	inode = n.newChild(ctx, st, out)  	return inode, 0  } + +// Rename - FUSE call. +// +// Symlink-safe through Renameat(). +func (n *Node) Rename(ctx context.Context, name string, newParent fs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) { +	dirfd, cName, errno := n.prepareAtSyscall(name) +	if errno != 0 { +		return +	} +	defer syscall.Close(dirfd) + +	n2 := newParent.(*Node) +	dirfd2, cName2, errno := n2.prepareAtSyscall("") +	if errno != 0 { +		return +	} +	defer syscall.Close(dirfd2) + +	// Easy case. +	rn := n.rootNode() +	if rn.args.PlaintextNames { +		return fs.ToErrno(unix.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags))) +	} +	// Long destination file name: create .name file +	nameFileAlreadyThere := false +	var err error +	if nametransform.IsLongContent(cName2) { +		err = rn.nameTransform.WriteLongNameAt(dirfd2, cName2, newName) +		// 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 +		// .name file in this case, and we ignore the error. +		if err == syscall.EEXIST { +			nameFileAlreadyThere = true +		} else if err != nil { +			return fs.ToErrno(err) +		} +	} +	// Actual rename +	tlog.Debug.Printf("Renameat %d/%s -> %d/%s\n", dirfd, cName, dirfd2, cName2) +	err = unix.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags)) +	if err == syscall.ENOTEMPTY || err == syscall.EEXIST { +		// If an empty directory is overwritten we will always get an error as +		// the "empty" directory will still contain gocryptfs.diriv. +		// Interestingly, ext4 returns ENOTEMPTY while xfs returns EEXIST. +		// We handle that by trying to fs.Rmdir() the target directory and trying +		// again. +		tlog.Debug.Printf("Rename: Handling ENOTEMPTY") +		if n2.Rmdir(ctx, newName) == 0 { +			err = unix.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags)) +		} +	} +	if err != nil { +		if nametransform.IsLongContent(cName2) && nameFileAlreadyThere == false { +			// Roll back .name creation unless the .name file was already there +			nametransform.DeleteLongNameAt(dirfd2, cName2) +		} +		return fs.ToErrno(err) +	} +	if nametransform.IsLongContent(cName) { +		nametransform.DeleteLongNameAt(dirfd, cName) +	} +	return 0 +} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go index b798332..ac48341 100644 --- a/internal/fusefrontend/node_api_check.go +++ b/internal/fusefrontend/node_api_check.go @@ -20,6 +20,7 @@ var _ = (fs.NodeStatfser)((*Node)(nil))  var _ = (fs.NodeMknoder)((*Node)(nil))  var _ = (fs.NodeLinker)((*Node)(nil))  var _ = (fs.NodeSymlinker)((*Node)(nil)) +var _ = (fs.NodeRenamer)((*Node)(nil))  /* TODO  var _ = (fs.NodeGetxattrer)((*Node)(nil)) @@ -27,5 +28,4 @@ var _ = (fs.NodeSetxattrer)((*Node)(nil))  var _ = (fs.NodeRemovexattrer)((*Node)(nil))  var _ = (fs.NodeListxattrer)((*Node)(nil))  var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) -var _ = (fs.NodeRenamer)((*Node)(nil))  */ | 
