aboutsummaryrefslogtreecommitdiff
path: root/internal/fusefrontend_reverse/node_xattr.go
blob: f4e3bda6f774c2b61d93515cf431e8181b2f9af8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Package fusefrontend_reverse interfaces directly with the go-fuse library.
package fusefrontend_reverse

import (
	"bytes"
	"context"
	"syscall"

	"github.com/rfjakob/gocryptfs/v2/internal/pathiv"
)

// We store encrypted xattrs under this prefix plus the base64-encoded
// encrypted original name.
var xattrStorePrefix = "user.gocryptfs."

// isAcl returns true if the attribute name is for storing ACLs
//
// ACLs are passed through without encryption
func isAcl(attr string) bool {
	return attr == "system.posix_acl_access" || attr == "system.posix_acl_default"
}

// GetXAttr - FUSE call. Reads the value of extended attribute "attr".
//
// This function is symlink-safe through Fgetxattr.
func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
	rn := n.rootNode()
	var data []byte
	// ACLs are passed through without encryption
	if isAcl(attr) {
		var errno syscall.Errno
		data, errno = n.getXAttr(attr)
		if errno != 0 {
			return 0, errno
		}
	} else {
		pAttr, err := rn.decryptXattrName(attr)
		if err != nil {
			return 0, syscall.EINVAL
		}
		pData, errno := n.getXAttr(pAttr)
		if errno != 0 {
			return 0, errno
		}
		nonce := pathiv.Derive(n.Path()+"\000"+attr, pathiv.PurposeXattrIV)
		data = rn.encryptXattrValue(pData, nonce)
	}
	if len(dest) < len(data) {
		return uint32(len(data)), syscall.ERANGE
	}
	l := copy(dest, data)
	return uint32(l), 0
}

// ListXAttr - FUSE call. Lists extended attributes on the file at "relPath".
//
// This function is symlink-safe through Flistxattr.
func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
	pNames, errno := n.listXAttr()
	if errno != 0 {
		return 0, errno
	}
	rn := n.rootNode()
	var buf bytes.Buffer
	for _, pName := range pNames {
		// ACLs are passed through without encryption
		if isAcl(pName) {
			buf.WriteString(pName + "\000")
			continue
		}
		cName, err := rn.encryptXattrName(pName)
		if err != nil {
			continue
		}
		buf.WriteString(cName + "\000")
	}
	if buf.Len() > len(dest) {
		return uint32(buf.Len()), syscall.ERANGE
	}
	return uint32(copy(dest, buf.Bytes())), 0
}