@@ -2,7 +2,9 @@ package fusefrontend_reverse
2
2
3
3
import (
4
4
"fmt"
5
+ "os"
5
6
"path/filepath"
7
+ "strings"
6
8
"syscall"
7
9
8
10
"golang.org/x/sys/unix"
@@ -14,6 +16,8 @@ import (
14
16
"github.com/rfjakob/gocryptfs/internal/configfile"
15
17
"github.com/rfjakob/gocryptfs/internal/contentenc"
16
18
"github.com/rfjakob/gocryptfs/internal/cryptocore"
19
+ "github.com/rfjakob/gocryptfs/internal/ctlsock"
20
+ "github.com/rfjakob/gocryptfs/internal/exitcodes"
17
21
"github.com/rfjakob/gocryptfs/internal/fusefrontend"
18
22
"github.com/rfjakob/gocryptfs/internal/nametransform"
19
23
"github.com/rfjakob/gocryptfs/internal/pathiv"
@@ -34,6 +38,8 @@ type ReverseFS struct {
34
38
nameTransform * nametransform.NameTransform
35
39
// Content encryption helper
36
40
contentEnc * contentenc.ContentEnc
41
+ // Relative ciphertext paths to exclude (hide) from the user. Used by -exclude.
42
+ cExclude []string
37
43
}
38
44
39
45
var _ pathfs.FileSystem = & ReverseFS {}
@@ -43,14 +49,30 @@ var _ pathfs.FileSystem = &ReverseFS{}
43
49
// ReverseFS provides an encrypted view.
44
50
func NewFS (args fusefrontend.Args , c * contentenc.ContentEnc , n * nametransform.NameTransform ) * ReverseFS {
45
51
initLongnameCache ()
46
- return & ReverseFS {
52
+ fs := & ReverseFS {
47
53
// pathfs.defaultFileSystem returns ENOSYS for all operations
48
54
FileSystem : pathfs .NewDefaultFileSystem (),
49
55
loopbackfs : pathfs .NewLoopbackFileSystem (args .Cipherdir ),
50
56
args : args ,
51
57
nameTransform : n ,
52
58
contentEnc : c ,
53
59
}
60
+ if len (args .Exclude ) > 0 {
61
+ for _ , dirty := range args .Exclude {
62
+ clean := ctlsock .SanitizePath (dirty )
63
+ if clean != dirty {
64
+ tlog .Warn .Printf ("-exclude: non-canonical path %q has been interpreted as %q" , dirty , clean )
65
+ }
66
+ cPath , err := fs .EncryptPath (clean )
67
+ if err != nil {
68
+ tlog .Fatal .Printf ("-exclude: EncryptPath %q failed: %v" , clean , err )
69
+ os .Exit (exitcodes .ExcludeError )
70
+ }
71
+ fs .cExclude = append (fs .cExclude , cPath )
72
+ }
73
+ tlog .Debug .Printf ("-exclude: %v -> %v" , fs .args .Exclude , fs .cExclude )
74
+ }
75
+ return fs
54
76
}
55
77
56
78
// relDir is identical to filepath.Dir excepts that it returns "" when
@@ -64,6 +86,21 @@ func relDir(path string) string {
64
86
return dir
65
87
}
66
88
89
+ // isExcluded finds out if relative ciphertext path "relPath" is excluded
90
+ // (used when -exclude is passed by the user)
91
+ func (rfs * ReverseFS ) isExcluded (relPath string ) bool {
92
+ for _ , e := range rfs .cExclude {
93
+ if e == relPath {
94
+ return true
95
+ }
96
+ // Files inside an excluded directory are also excluded
97
+ if strings .HasPrefix (relPath , e + "/" ) {
98
+ return true
99
+ }
100
+ }
101
+ return false
102
+ }
103
+
67
104
// isDirIV determines if the path points to a gocryptfs.diriv file
68
105
func (rfs * ReverseFS ) isDirIV (relPath string ) bool {
69
106
if rfs .args .PlaintextNames {
@@ -99,6 +136,9 @@ func (rfs *ReverseFS) isTranslatedConfig(relPath string) bool {
99
136
// GetAttr - FUSE call
100
137
// "relPath" is the relative ciphertext path
101
138
func (rfs * ReverseFS ) GetAttr (relPath string , context * fuse.Context ) (* fuse.Attr , fuse.Status ) {
139
+ if rfs .isExcluded (relPath ) {
140
+ return nil , fuse .ENOENT
141
+ }
102
142
// Handle "gocryptfs.conf"
103
143
if rfs .isTranslatedConfig (relPath ) {
104
144
absConfPath , _ := rfs .abs (configfile .ConfReverseName , nil )
@@ -180,6 +220,9 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr
180
220
181
221
// Access - FUSE call
182
222
func (rfs * ReverseFS ) Access (relPath string , mode uint32 , context * fuse.Context ) fuse.Status {
223
+ if rfs .isExcluded (relPath ) {
224
+ return fuse .ENOENT
225
+ }
183
226
if rfs .isTranslatedConfig (relPath ) || rfs .isDirIV (relPath ) || rfs .isNameFile (relPath ) {
184
227
// access(2) R_OK flag for checking if the file is readable, always 4 as defined in POSIX.
185
228
ROK := uint32 (0x4 )
@@ -203,6 +246,9 @@ func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context)
203
246
204
247
// Open - FUSE call
205
248
func (rfs * ReverseFS ) Open (relPath string , flags uint32 , context * fuse.Context ) (fuseFile nodefs.File , status fuse.Status ) {
249
+ if rfs .isExcluded (relPath ) {
250
+ return nil , fuse .ENOENT
251
+ }
206
252
if rfs .isTranslatedConfig (relPath ) {
207
253
return rfs .loopbackfs .Open (configfile .ConfReverseName , flags , context )
208
254
}
@@ -242,6 +288,9 @@ func (rfs *ReverseFS) openDirPlaintextnames(relPath string, entries []fuse.DirEn
242
288
243
289
// OpenDir - FUSE readdir call
244
290
func (rfs * ReverseFS ) OpenDir (cipherPath string , context * fuse.Context ) ([]fuse.DirEntry , fuse.Status ) {
291
+ if rfs .isExcluded (cipherPath ) {
292
+ return nil , fuse .ENOENT
293
+ }
245
294
relPath , err := rfs .decryptPath (cipherPath )
246
295
if err != nil {
247
296
return nil , fuse .ToStatus (err )
@@ -292,6 +341,21 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
292
341
}
293
342
entries [i ].Name = cName
294
343
}
344
+ // Filter out excluded entries
345
+ if rfs .cExclude != nil {
346
+ filtered := make ([]fuse.DirEntry , 0 , len (entries ))
347
+ for _ , entry := range entries {
348
+ // filepath.Join handles the case of cipherPath="" correctly:
349
+ // Join("", "foo") -> "foo". This does not: cipherPath + "/" + name"
350
+ p := filepath .Join (cipherPath , entry .Name )
351
+ if rfs .isExcluded (p ) {
352
+ // Skip file
353
+ continue
354
+ }
355
+ filtered = append (filtered , entry )
356
+ }
357
+ entries = filtered
358
+ }
295
359
entries = append (entries , virtualFiles [:nVirtual ]... )
296
360
return entries , fuse .OK
297
361
}
@@ -301,7 +365,10 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
301
365
// Securing statfs against symlink races seems to be more trouble than
302
366
// it's worth, so we just ignore the path and always return info about the
303
367
// backing storage root dir.
304
- func (rfs * ReverseFS ) StatFs (path string ) * fuse.StatfsOut {
368
+ func (rfs * ReverseFS ) StatFs (relPath string ) * fuse.StatfsOut {
369
+ if rfs .isExcluded (relPath ) {
370
+ return nil
371
+ }
305
372
var s syscall.Statfs_t
306
373
err := syscall .Statfs (rfs .args .Cipherdir , & s )
307
374
if err != nil {
@@ -314,6 +381,9 @@ func (rfs *ReverseFS) StatFs(path string) *fuse.StatfsOut {
314
381
315
382
// Readlink - FUSE call
316
383
func (rfs * ReverseFS ) Readlink (relPath string , context * fuse.Context ) (string , fuse.Status ) {
384
+ if rfs .isExcluded (relPath ) {
385
+ return "" , fuse .ENOENT
386
+ }
317
387
dirfd , name , err := rfs .openBackingDir (relPath )
318
388
if err != nil {
319
389
return "" , fuse .ToStatus (err )
0 commit comments