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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package fusefrontend
import (
"sync/atomic"
"syscall"
"github.com/rfjakob/gocryptfs/internal/tlog"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/rfjakob/gocryptfs/internal/nametransform"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
)
// prepareAtSyscall returns a (dirfd, cName) pair that can be used
// with the "___at" family of system calls (openat, fstatat, unlinkat...) to
// access the backing encrypted child file.
func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) {
if child == "" {
tlog.Warn.Printf("BUG: prepareAtSyscall: child=%q, should have called prepareAtSyscallMyself", child)
return n.prepareAtSyscallMyself()
}
rn := n.rootNode()
// All filesystem operations go through here, so this is a good place
// to reset the idle marker.
atomic.StoreUint32(&rn.IsIdle, 0)
if n.IsRoot() && rn.isFiltered(child) {
return -1, "", syscall.EPERM
}
var encryptName func(int, string, []byte) (string, error)
if !rn.args.PlaintextNames {
encryptName = func(dirfd int, child string, iv []byte) (cName string, err error) {
// Badname allowed, try to determine filenames
if rn.nameTransform.HaveBadnamePatterns() {
return rn.nameTransform.EncryptAndHashBadName(child, iv, dirfd)
}
return rn.nameTransform.EncryptAndHashName(child, iv)
}
}
// Cache lookup
var iv []byte
dirfd, iv = rn.dirCache.Lookup(n)
if dirfd > 0 {
if rn.args.PlaintextNames {
return dirfd, child, 0
}
var err error
cName, err = encryptName(dirfd, child, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
return
}
// Slowpath: Open ourselves & read diriv
parentDirfd, myCName, errno := n.prepareAtSyscallMyself()
if errno != 0 {
return
}
defer syscall.Close(parentDirfd)
dirfd, err := syscallcompat.Openat(parentDirfd, myCName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
// Cache store
if !rn.args.PlaintextNames {
var err error
iv, err = nametransform.ReadDirIVAt(dirfd)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
}
rn.dirCache.Store(n, dirfd, iv)
if rn.args.PlaintextNames {
return dirfd, child, 0
}
cName, err = encryptName(dirfd, child, iv)
if err != nil {
syscall.Close(dirfd)
return -1, "", fs.ToErrno(err)
}
return
}
func (n *Node) prepareAtSyscallMyself() (dirfd int, cName string, errno syscall.Errno) {
dirfd = -1
// Handle root node
if n.IsRoot() {
var err error
rn := n.rootNode()
// Open cipherdir (following symlinks)
dirfd, err = syscallcompat.Open(rn.args.Cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0)
if err != nil {
return -1, "", fs.ToErrno(err)
}
return dirfd, ".", 0
}
// Otherwise convert to prepareAtSyscall of parent node
myName, p1 := n.Parent()
if p1 == nil || myName == "" {
errno = syscall.ENOENT
return
}
parent := toNode(p1.Operations())
return parent.prepareAtSyscall(myName)
}
|