v2api: implement Open()
This commit is contained in:
parent
d73e4b3f7c
commit
d2139e18ef
|
@ -147,13 +147,15 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get device number and inode number into `st`
|
// Get device number and inode number into `st`
|
||||||
st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW)
|
var st syscall.Stat_t
|
||||||
|
err = syscall.Fstat(fd, &st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fs.ToErrno(err)
|
errno = fs.ToErrno(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Get unique inode number
|
// Get unique inode number
|
||||||
rn.inoMap.TranslateStat(st)
|
rn.inoMap.TranslateStat(&st)
|
||||||
out.Attr.FromStat(st)
|
out.Attr.FromStat(&st)
|
||||||
// Create child node
|
// Create child node
|
||||||
id := fs.StableAttr{
|
id := fs.StableAttr{
|
||||||
Mode: uint32(st.Mode),
|
Mode: uint32(st.Mode),
|
||||||
|
@ -164,7 +166,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
|
||||||
ch := n.NewInode(ctx, node, id)
|
ch := n.NewInode(ctx, node, id)
|
||||||
|
|
||||||
f := os.NewFile(uintptr(fd), cName)
|
f := os.NewFile(uintptr(fd), cName)
|
||||||
return ch, NewFile2(f, rn, st), 0, 0
|
return ch, NewFile2(f, rn, &st), 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlink - FUSE call. Delete a file.
|
// Unlink - FUSE call. Delete a file.
|
||||||
|
@ -218,3 +220,49 @@ func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
|
||||||
}
|
}
|
||||||
return []byte(target), 0
|
return []byte(target), 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open - FUSE call. Open already-existing file.
|
||||||
|
//
|
||||||
|
// Symlink-safe through Openat().
|
||||||
|
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||||
|
dirfd, cName, errno := n.prepareAtSyscall("")
|
||||||
|
if errno != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer syscall.Close(dirfd)
|
||||||
|
|
||||||
|
rn := n.rootNode()
|
||||||
|
newFlags := rn.mangleOpenFlags(flags)
|
||||||
|
// Taking this lock makes sure we don't race openWriteOnlyFile()
|
||||||
|
rn.openWriteOnlyLock.RLock()
|
||||||
|
defer rn.openWriteOnlyLock.RUnlock()
|
||||||
|
|
||||||
|
// Open backing file
|
||||||
|
fd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
||||||
|
// Handle a few specific errors
|
||||||
|
if err != nil {
|
||||||
|
if err == syscall.EMFILE {
|
||||||
|
var lim syscall.Rlimit
|
||||||
|
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
||||||
|
tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur)
|
||||||
|
}
|
||||||
|
if err == syscall.EACCES && (int(flags)&syscall.O_ACCMODE) == syscall.O_WRONLY {
|
||||||
|
fd, err = rn.openWriteOnlyFile(dirfd, cName, newFlags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Could not handle the error? Bail out
|
||||||
|
if err != nil {
|
||||||
|
errno = fs.ToErrno(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var st syscall.Stat_t
|
||||||
|
err = syscall.Fstat(fd, &st)
|
||||||
|
if err != nil {
|
||||||
|
errno = fs.ToErrno(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f := os.NewFile(uintptr(fd), cName)
|
||||||
|
fh = NewFile2(f, rn, &st)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -13,3 +13,4 @@ var _ = (fs.NodeMkdirer)((*Node)(nil))
|
||||||
var _ = (fs.NodeRmdirer)((*Node)(nil))
|
var _ = (fs.NodeRmdirer)((*Node)(nil))
|
||||||
var _ = (fs.NodeUnlinker)((*Node)(nil))
|
var _ = (fs.NodeUnlinker)((*Node)(nil))
|
||||||
var _ = (fs.NodeReadlinker)((*Node)(nil))
|
var _ = (fs.NodeReadlinker)((*Node)(nil))
|
||||||
|
var _ = (fs.NodeOpener)((*Node)(nil))
|
||||||
|
|
|
@ -28,6 +28,9 @@ type RootNode struct {
|
||||||
nameTransform nametransform.NameTransformer
|
nameTransform nametransform.NameTransformer
|
||||||
// Content encryption helper
|
// Content encryption helper
|
||||||
contentEnc *contentenc.ContentEnc
|
contentEnc *contentenc.ContentEnc
|
||||||
|
// This lock is used by openWriteOnlyFile() to block concurrent opens while
|
||||||
|
// it relaxes the permissions on a file.
|
||||||
|
openWriteOnlyLock sync.RWMutex
|
||||||
// MitigatedCorruptions is used to report data corruption that is internally
|
// MitigatedCorruptions is used to report data corruption that is internally
|
||||||
// mitigated by ignoring the corrupt item. For example, when OpenDir() finds
|
// mitigated by ignoring the corrupt item. For example, when OpenDir() finds
|
||||||
// a corrupt filename, we still return the other valid filenames.
|
// a corrupt filename, we still return the other valid filenames.
|
||||||
|
@ -137,3 +140,48 @@ func (rn *RootNode) decryptSymlinkTarget(cData64 string) (string, error) {
|
||||||
}
|
}
|
||||||
return string(data), nil
|
return string(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Due to RMW, we always need read permissions on the backing file. This is a
|
||||||
|
// problem if the file permissions do not allow reading (i.e. 0200 permissions).
|
||||||
|
// This function works around that problem by chmod'ing the file, obtaining a fd,
|
||||||
|
// and chmod'ing it back.
|
||||||
|
func (rn *RootNode) openWriteOnlyFile(dirfd int, cName string, newFlags int) (rwFd int, err error) {
|
||||||
|
woFd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer syscall.Close(woFd)
|
||||||
|
var st syscall.Stat_t
|
||||||
|
err = syscall.Fstat(woFd, &st)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// The cast to uint32 fixes a build failure on Darwin, where st.Mode is uint16.
|
||||||
|
perms := uint32(st.Mode)
|
||||||
|
// Verify that we don't have read permissions
|
||||||
|
if perms&0400 != 0 {
|
||||||
|
tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms)
|
||||||
|
err = syscall.EPERM
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Upgrade the lock to block other Open()s and downgrade again on return
|
||||||
|
rn.openWriteOnlyLock.RUnlock()
|
||||||
|
rn.openWriteOnlyLock.Lock()
|
||||||
|
defer func() {
|
||||||
|
rn.openWriteOnlyLock.Unlock()
|
||||||
|
rn.openWriteOnlyLock.RLock()
|
||||||
|
}()
|
||||||
|
// Relax permissions and revert on return
|
||||||
|
err = syscall.Fchmod(woFd, perms|0400)
|
||||||
|
if err != nil {
|
||||||
|
tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err2 := syscall.Fchmod(woFd, perms)
|
||||||
|
if err2 != nil {
|
||||||
|
tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue