diff options
| author | Jakob Unterwurzacher | 2020-07-11 19:43:07 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-07-11 19:43:07 +0200 | 
| commit | 250dbc64362265beace368b62f5a6656908a2e84 (patch) | |
| tree | 2eed3ec591ee4597f0173afd9d97e555c470b51f | |
| parent | c35b575d5f5aa29c4f39e9f0df15385d9f379caf (diff) | |
v2api: implement Symlink
| -rw-r--r-- | internal/fusefrontend/node.go | 49 | ||||
| -rw-r--r-- | internal/fusefrontend/node_api_check.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend/root_node.go | 14 | 
3 files changed, 64 insertions, 1 deletions
| diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index 2f5f3ec..aa9bd51 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -400,3 +400,52 @@ func (n *Node) Link(ctx context.Context, target fs.InodeEmbedder, name string, o  	inode = n.newChild(ctx, st, out)  	return inode, 0  } + +// Symlink - FUSE call. Create a symlink. +// +// Symlink-safe through use of Symlinkat. +func (n *Node) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (inode *fs.Inode, errno syscall.Errno) { +	dirfd, cName, errno := n.prepareAtSyscall(name) +	if errno != 0 { +		return +	} +	defer syscall.Close(dirfd) + +	// Make sure context is nil if we don't want to preserve the owner +	rn := n.rootNode() +	if !rn.args.PreserveOwner { +		ctx = nil +	} + +	cTarget := target +	if !rn.args.PlaintextNames { +		// Symlinks are encrypted like file contents (GCM) and base64-encoded +		cTarget = rn.encryptSymlinkTarget(target) +	} +	// Create ".name" file to store long file name (except in PlaintextNames mode) +	var err error +	ctx2 := toFuseCtx(ctx) +	if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { +		err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) +		if err != nil { +			errno = fs.ToErrno(err) +			return +		} +		// Create "gocryptfs.longfile." symlink +		err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, ctx2) +		if err != nil { +			nametransform.DeleteLongNameAt(dirfd, cName) +		} +	} else { +		// Create symlink +		err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, ctx2) +	} + +	st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) +	if err != nil { +		errno = fs.ToErrno(err) +		return +	} +	inode = n.newChild(ctx, st, out) +	return inode, 0 +} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go index a59971f..b798332 100644 --- a/internal/fusefrontend/node_api_check.go +++ b/internal/fusefrontend/node_api_check.go @@ -19,6 +19,7 @@ var _ = (fs.NodeSetattrer)((*Node)(nil))  var _ = (fs.NodeStatfser)((*Node)(nil))  var _ = (fs.NodeMknoder)((*Node)(nil))  var _ = (fs.NodeLinker)((*Node)(nil)) +var _ = (fs.NodeSymlinker)((*Node)(nil))  /* TODO  var _ = (fs.NodeGetxattrer)((*Node)(nil)) @@ -26,6 +27,5 @@ var _ = (fs.NodeSetxattrer)((*Node)(nil))  var _ = (fs.NodeRemovexattrer)((*Node)(nil))  var _ = (fs.NodeListxattrer)((*Node)(nil))  var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) -var _ = (fs.NodeSymlinker)((*Node)(nil))  var _ = (fs.NodeRenamer)((*Node)(nil))  */ diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go index cf23169..7ceeb06 100644 --- a/internal/fusefrontend/root_node.go +++ b/internal/fusefrontend/root_node.go @@ -245,3 +245,17 @@ func (rn *RootNode) openBackingDir(relPath string) (dirfd int, cName string, err  	}  	return dirfd, cName, nil  } + +// encryptSymlinkTarget: "data" is encrypted like file contents (GCM) +// and base64-encoded. +// The empty string encrypts to the empty string. +// +// Symlink-safe because it does not do any I/O. +func (rn *RootNode) encryptSymlinkTarget(data string) (cData64 string) { +	if data == "" { +		return "" +	} +	cData := rn.contentEnc.EncryptBlock([]byte(data), 0, nil) +	cData64 = rn.nameTransform.B64EncodeToString(cData) +	return cData64 +} | 
