reverse mode: add --exclude option · rfjakob/gocryptfs@ec2fdc1

@@ -2,7 +2,9 @@ package fusefrontend_reverse

2233

import (

44

"fmt"

5+

"os"

56

"path/filepath"

7+

"strings"

68

"syscall"

79810

"golang.org/x/sys/unix"

@@ -14,6 +16,8 @@ import (

1416

"github.com/rfjakob/gocryptfs/internal/configfile"

1517

"github.com/rfjakob/gocryptfs/internal/contentenc"

1618

"github.com/rfjakob/gocryptfs/internal/cryptocore"

19+

"github.com/rfjakob/gocryptfs/internal/ctlsock"

20+

"github.com/rfjakob/gocryptfs/internal/exitcodes"

1721

"github.com/rfjakob/gocryptfs/internal/fusefrontend"

1822

"github.com/rfjakob/gocryptfs/internal/nametransform"

1923

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

@@ -34,6 +38,8 @@ type ReverseFS struct {

3438

nameTransform *nametransform.NameTransform

3539

// Content encryption helper

3640

contentEnc *contentenc.ContentEnc

41+

// Relative ciphertext paths to exclude (hide) from the user. Used by -exclude.

42+

cExclude []string

3743

}

38443945

var _ pathfs.FileSystem = &ReverseFS{}

@@ -43,14 +49,30 @@ var _ pathfs.FileSystem = &ReverseFS{}

4349

// ReverseFS provides an encrypted view.

4450

func NewFS(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *ReverseFS {

4551

initLongnameCache()

46-

return &ReverseFS{

52+

fs := &ReverseFS{

4753

// pathfs.defaultFileSystem returns ENOSYS for all operations

4854

FileSystem: pathfs.NewDefaultFileSystem(),

4955

loopbackfs: pathfs.NewLoopbackFileSystem(args.Cipherdir),

5056

args: args,

5157

nameTransform: n,

5258

contentEnc: c,

5359

}

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

5476

}

55775678

// relDir is identical to filepath.Dir excepts that it returns "" when

@@ -64,6 +86,21 @@ func relDir(path string) string {

6486

return dir

6587

}

668889+

// 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+67104

// isDirIV determines if the path points to a gocryptfs.diriv file

68105

func (rfs *ReverseFS) isDirIV(relPath string) bool {

69106

if rfs.args.PlaintextNames {

@@ -99,6 +136,9 @@ func (rfs *ReverseFS) isTranslatedConfig(relPath string) bool {

99136

// GetAttr - FUSE call

100137

// "relPath" is the relative ciphertext path

101138

func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr, fuse.Status) {

139+

if rfs.isExcluded(relPath) {

140+

return nil, fuse.ENOENT

141+

}

102142

// Handle "gocryptfs.conf"

103143

if rfs.isTranslatedConfig(relPath) {

104144

absConfPath, _ := rfs.abs(configfile.ConfReverseName, nil)

@@ -180,6 +220,9 @@ func (rfs *ReverseFS) GetAttr(relPath string, context *fuse.Context) (*fuse.Attr

180220181221

// Access - FUSE call

182222

func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context) fuse.Status {

223+

if rfs.isExcluded(relPath) {

224+

return fuse.ENOENT

225+

}

183226

if rfs.isTranslatedConfig(relPath) || rfs.isDirIV(relPath) || rfs.isNameFile(relPath) {

184227

// access(2) R_OK flag for checking if the file is readable, always 4 as defined in POSIX.

185228

ROK := uint32(0x4)

@@ -203,6 +246,9 @@ func (rfs *ReverseFS) Access(relPath string, mode uint32, context *fuse.Context)

203246204247

// Open - FUSE call

205248

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+

}

206252

if rfs.isTranslatedConfig(relPath) {

207253

return rfs.loopbackfs.Open(configfile.ConfReverseName, flags, context)

208254

}

@@ -242,6 +288,9 @@ func (rfs *ReverseFS) openDirPlaintextnames(relPath string, entries []fuse.DirEn

242288243289

// OpenDir - FUSE readdir call

244290

func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {

291+

if rfs.isExcluded(cipherPath) {

292+

return nil, fuse.ENOENT

293+

}

245294

relPath, err := rfs.decryptPath(cipherPath)

246295

if err != nil {

247296

return nil, fuse.ToStatus(err)

@@ -292,6 +341,21 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.

292341

}

293342

entries[i].Name = cName

294343

}

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+

}

295359

entries = append(entries, virtualFiles[:nVirtual]...)

296360

return entries, fuse.OK

297361

}

@@ -301,7 +365,10 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.

301365

// Securing statfs against symlink races seems to be more trouble than

302366

// it's worth, so we just ignore the path and always return info about the

303367

// 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+

}

305372

var s syscall.Statfs_t

306373

err := syscall.Statfs(rfs.args.Cipherdir, &s)

307374

if err != nil {

@@ -314,6 +381,9 @@ func (rfs *ReverseFS) StatFs(path string) *fuse.StatfsOut {

314381315382

// Readlink - FUSE call

316383

func (rfs *ReverseFS) Readlink(relPath string, context *fuse.Context) (string, fuse.Status) {

384+

if rfs.isExcluded(relPath) {

385+

return "", fuse.ENOENT

386+

}

317387

dirfd, name, err := rfs.openBackingDir(relPath)

318388

if err != nil {

319389

return "", fuse.ToStatus(err)