syscallcompat: use O_PATH in OpenDirNofollow

This fixes the "0100 directory" problem in reverse mode,
and should be slightly faster.
This commit is contained in:
Jakob Unterwurzacher 2018-09-08 18:06:33 +02:00
parent 9ec9d0c49c
commit 930c37e03d
6 changed files with 62 additions and 11 deletions

View File

@ -63,9 +63,15 @@ func (rfs *ReverseFS) findLongnameParent(dir string, dirIV []byte, longname stri
if hit != "" { if hit != "" {
return hit, nil return hit, nil
} }
fd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, dir) dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(dir))
if err != nil { if err != nil {
tlog.Warn.Printf("findLongnameParent: opendir failed: %v\n", err) tlog.Warn.Printf("findLongnameParent: OpenDirNofollow failed: %v\n", err)
return "", err
}
fd, err := syscallcompat.Openat(dirfd, filepath.Base(dir), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
syscall.Close(dirfd)
if err != nil {
tlog.Warn.Printf("findLongnameParent: Openat failed: %v\n", err)
return "", err return "", err
} }
dirEntries, err := syscallcompat.Getdents(fd) dirEntries, err := syscallcompat.Getdents(fd)

View File

@ -305,7 +305,12 @@ func (rfs *ReverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }
// Read plaintext dir // Read plaintext dir
fd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, relPath) dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(relPath))
if err != nil {
return nil, fuse.ToStatus(err)
}
fd, err := syscallcompat.Openat(dirfd, filepath.Base(relPath), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0)
syscall.Close(dirfd)
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }

View File

@ -36,7 +36,7 @@ func OpenDirNofollow(baseDir string, relPath string) (fd int, err error) {
// Walk the directory tree // Walk the directory tree
var dirfd2 int var dirfd2 int
for _, name := range parts { for _, name := range parts {
dirfd2, err = Openat(dirfd, name, syscall.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_DIRECTORY, 0) dirfd2, err = Openat(dirfd, name, syscall.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_DIRECTORY|O_PATH, 0)
syscall.Close(dirfd) syscall.Close(dirfd)
if err != nil { if err != nil {
return -1, err return -1, err

View File

@ -9,9 +9,14 @@ import (
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
) )
const (
// O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined // O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined
// to zero there. // to zero there.
const O_DIRECT = 0 O_DIRECT = 0
// O_PATH is only defined on Linux
O_PATH = 0
)
// Sorry, fallocate is not available on OSX at all and // Sorry, fallocate is not available on OSX at all and
// fcntl F_PREALLOCATE is not accessible from Go. // fcntl F_PREALLOCATE is not accessible from Go.

View File

@ -12,11 +12,16 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "github.com/rfjakob/gocryptfs/internal/tlog"
) )
const _FALLOC_FL_KEEP_SIZE = 0x01 const (
_FALLOC_FL_KEEP_SIZE = 0x01
// O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined // O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined
// to zero there. // to zero there.
const O_DIRECT = syscall.O_DIRECT O_DIRECT = syscall.O_DIRECT
// O_PATH is only defined on Linux
O_PATH = unix.O_PATH
)
var preallocWarn sync.Once var preallocWarn sync.Once

View File

@ -207,3 +207,33 @@ func TestTooLongSymlink(t *testing.T) {
err2.Err) err2.Err)
} }
} }
// Test that we can traverse a directory with 0100 permissions
// (execute but no read). This used to be a problem as OpenDirNofollow opened
// all directory in the path with O_RDONLY. Now it uses O_PATH, which only needs
// the executable bit.
func Test0100Dir(t *testing.T) {
dir := dirA + "/" + t.Name()
err := os.Mkdir(dir, 0700)
if err != nil {
t.Fatal(err)
}
file := dir + "/hello"
err = ioutil.WriteFile(file, []byte("hello"), 0600)
if err != nil {
t.Fatal(err)
}
err = os.Chmod(dir, 0100)
if err != nil {
t.Fatal(err)
}
fileReverse := dirC + "/" + t.Name() + "/hello"
fd, err := os.Open(fileReverse)
// Make sure the dir can be removed after the test is done
os.Chmod(dir, 0700)
if err != nil {
t.Fatal(err)
}
fd.Close()
}