cmount,vfs: unify Read and Write handles and File and Dir where possible

This commit is contained in:
Nick Craig-Wood 2017-11-03 09:32:18 +00:00
parent 5634659ea3
commit 22ee839d05
2 changed files with 77 additions and 186 deletions

View file

@ -25,10 +25,9 @@ const fhUnset = ^uint64(0)
type FS struct { type FS struct {
VFS *vfs.VFS VFS *vfs.VFS
f fs.Fs f fs.Fs
openDirs *openFiles
openFilesWr *openFiles
openFilesRd *openFiles
ready chan (struct{}) ready chan (struct{})
mu sync.Mutex // to protect the below
handles []vfs.Handle
} }
// NewFS makes a new FS // NewFS makes a new FS
@ -36,86 +35,60 @@ func NewFS(f fs.Fs) *FS {
fsys := &FS{ fsys := &FS{
VFS: vfs.New(f, &vfsflags.Opt), VFS: vfs.New(f, &vfsflags.Opt),
f: f, f: f,
openDirs: newOpenFiles(0x01),
openFilesWr: newOpenFiles(0x02),
openFilesRd: newOpenFiles(0x03),
ready: make(chan (struct{})), ready: make(chan (struct{})),
} }
return fsys return fsys
} }
type openFiles struct { // Open a handle returning an integer file handle
mu sync.Mutex func (fsys *FS) openHandle(handle vfs.Handle) (fh uint64) {
mark uint8 fsys.mu.Lock()
nodes []vfs.Noder defer fsys.mu.Unlock()
}
func newOpenFiles(mark uint8) *openFiles {
return &openFiles{
mark: mark,
}
}
// Open a node returning a file handle
func (of *openFiles) Open(node vfs.Noder) (fh uint64) {
of.mu.Lock()
defer of.mu.Unlock()
var i int var i int
var oldNode vfs.Noder var oldHandle vfs.Handle
for i, oldNode = range of.nodes { for i, oldHandle = range fsys.handles {
if oldNode == nil { if oldHandle == nil {
of.nodes[i] = node fsys.handles[i] = handle
goto found goto found
} }
} }
of.nodes = append(of.nodes, node) fsys.handles = append(fsys.handles, handle)
i = len(of.nodes) - 1 i = len(fsys.handles) - 1
found: found:
return uint64((i << 8) | int(of.mark)) return uint64(i)
} }
// InRange to see if this fh could be one of ours // get the handle for fh, call with the lock held
func (of *openFiles) InRange(fh uint64) bool { func (fsys *FS) _getHandle(fh uint64) (i int, handle vfs.Handle, errc int) {
return uint8(fh) == of.mark if fh > uint64(len(fsys.handles)) {
}
// get the node for fh, call with the lock held
func (of *openFiles) get(fh uint64) (i int, node vfs.Noder, errc int) {
receivedMark := uint8(fh)
if receivedMark != of.mark {
fs.Debugf(nil, "Bad file handle: bad mark 0x%X != 0x%X: 0x%X", receivedMark, of.mark, fh)
return i, nil, -fuse.EBADF
}
i64 := fh >> 8
if i64 > uint64(len(of.nodes)) {
fs.Debugf(nil, "Bad file handle: too big: 0x%X", fh) fs.Debugf(nil, "Bad file handle: too big: 0x%X", fh)
return i, nil, -fuse.EBADF return i, nil, -fuse.EBADF
} }
i = int(i64) i = int(fh)
node = of.nodes[i] handle = fsys.handles[i]
if node == nil { if handle == nil {
fs.Debugf(nil, "Bad file handle: nil node: 0x%X", fh) fs.Debugf(nil, "Bad file handle: nil handle: 0x%X", fh)
return i, nil, -fuse.EBADF return i, nil, -fuse.EBADF
} }
return i, node, 0 return i, handle, 0
} }
// Get the node for the file handle // Get the handle for the file handle
func (of *openFiles) Get(fh uint64) (node vfs.Noder, errc int) { func (fsys *FS) getHandle(fh uint64) (handle vfs.Handle, errc int) {
of.mu.Lock() fsys.mu.Lock()
_, node, errc = of.get(fh) _, handle, errc = fsys._getHandle(fh)
of.mu.Unlock() fsys.mu.Unlock()
return return
} }
// Close the node // Close the handle
func (of *openFiles) Close(fh uint64) (errc int) { func (fsys *FS) closeHandle(fh uint64) (errc int) {
of.mu.Lock() fsys.mu.Lock()
i, _, errc := of.get(fh) i, _, errc := fsys._getHandle(fh)
if errc == 0 { if errc == 0 {
of.nodes[i] = nil fsys.handles[i] = nil
} }
of.mu.Unlock() fsys.mu.Unlock()
return return
} }
@ -158,35 +131,13 @@ func (fsys *FS) lookupFile(path string) (file *vfs.File, errc int) {
return file, 0 return file, 0
} }
// Get the underlying openFile handle from the file handle
func (fsys *FS) getOpenFilesFromFh(fh uint64) (of *openFiles, errc int) {
switch {
case fsys.openFilesRd.InRange(fh):
return fsys.openFilesRd, 0
case fsys.openFilesWr.InRange(fh):
return fsys.openFilesWr, 0
case fsys.openDirs.InRange(fh):
return fsys.openDirs, 0
}
return nil, -fuse.EBADF
}
// Get the underlying handle from the file handle
func (fsys *FS) getHandleFromFh(fh uint64) (handle vfs.Noder, errc int) {
of, errc := fsys.getOpenFilesFromFh(fh)
if errc != 0 {
return nil, errc
}
return of.Get(fh)
}
// get a node from the path or from the fh if not fhUnset // get a node from the path or from the fh if not fhUnset
func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, errc int) { func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, errc int) {
if fh == fhUnset { if fh == fhUnset {
node, errc = fsys.lookupNode(path) node, errc = fsys.lookupNode(path)
} else { } else {
var n vfs.Noder var n vfs.Handle
n, errc = fsys.getHandleFromFh(fh) n, errc = fsys.getHandle(fh)
if errc == 0 { if errc == 0 {
node = n.Node() node = n.Node()
} }
@ -196,19 +147,14 @@ func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, errc int) {
// stat fills up the stat block for Node // stat fills up the stat block for Node
func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) { func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) {
var Size uint64 Size := uint64(node.Size())
var Blocks uint64 Blocks := (Size + 511) / 512
var modTime time.Time modTime := node.ModTime()
var Mode os.FileMode Mode := node.Mode().Perm()
switch x := node.(type) { if node.IsDir() {
case *vfs.Dir: Mode |= fuse.S_IFDIR
modTime = x.ModTime() } else {
Mode = fsys.VFS.Opt.DirPerms | fuse.S_IFDIR Mode |= fuse.S_IFREG
case *vfs.File:
modTime = x.ModTime()
Size = uint64(x.Size())
Blocks = (Size + 511) / 512
Mode = fsys.VFS.Opt.FilePerms | fuse.S_IFREG
} }
//stat.Dev = 1 //stat.Dev = 1
stat.Ino = node.Inode() // FIXME do we need to set the inode number? stat.Ino = node.Inode() // FIXME do we need to set the inode number?
@ -255,13 +201,11 @@ func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
// Opendir opens path as a directory // Opendir opens path as a directory
func (fsys *FS) Opendir(path string) (errc int, fh uint64) { func (fsys *FS) Opendir(path string) (errc int, fh uint64) {
defer fs.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh) defer fs.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh)
dir, errc := fsys.lookupDir(path) handle, err := fsys.VFS.OpenFile(path, os.O_RDONLY, 0777)
if errc == 0 { if errc != 0 {
fh = fsys.openDirs.Open(dir) return translateError(err), fhUnset
} else {
fh = fhUnset
} }
return return 0, fsys.openHandle(handle)
} }
// Readdir reads the directory at dirPath // Readdir reads the directory at dirPath
@ -272,17 +216,12 @@ func (fsys *FS) Readdir(dirPath string,
itemsRead := -1 itemsRead := -1
defer fs.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc) defer fs.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc)
node, errc := fsys.openDirs.Get(fh) node, errc := fsys.getHandle(fh)
if errc != 0 { if errc != 0 {
return errc return errc
} }
dir, ok := node.(*vfs.Dir) items, err := node.Readdir(-1)
if !ok {
return -fuse.ENOTDIR
}
items, err := dir.ReadDirAll()
if err != nil { if err != nil {
return translateError(err) return translateError(err)
} }
@ -303,9 +242,12 @@ func (fsys *FS) Readdir(dirPath string,
fill(".", nil, 0) fill(".", nil, 0)
fill("..", nil, 0) fill("..", nil, 0)
for _, item := range items { for _, item := range items {
name := path.Base(item.DirEntry().Remote()) node, ok := item.(vfs.Node)
if ok {
name := path.Base(node.DirEntry().Remote())
fill(name, nil, 0) fill(name, nil, 0)
} }
}
itemsRead = len(items) itemsRead = len(items)
return 0 return 0
} }
@ -313,7 +255,7 @@ func (fsys *FS) Readdir(dirPath string,
// Releasedir finished reading the directory // Releasedir finished reading the directory
func (fsys *FS) Releasedir(path string, fh uint64) (errc int) { func (fsys *FS) Releasedir(path string, fh uint64) (errc int) {
defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
return fsys.openDirs.Close(fh) return fsys.closeHandle(fh)
} }
// Statfs reads overall stats on the filessystem // Statfs reads overall stats on the filessystem
@ -346,14 +288,7 @@ func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) {
return translateError(err), fhUnset return translateError(err), fhUnset
} }
switch fh := handle.(type) { return 0, fsys.openHandle(handle)
case *vfs.WriteFileHandle:
return 0, fsys.openFilesWr.Open(fh)
case *vfs.ReadFileHandle:
return 0, fsys.openFilesRd.Open(fh)
}
return -fuse.EPERM, fhUnset
} }
// Create creates and opens a file. // Create creates and opens a file.
@ -367,7 +302,7 @@ func (fsys *FS) Create(filePath string, flags int, mode uint32) (errc int, fh ui
if err != nil { if err != nil {
return translateError(err), fhUnset return translateError(err), fhUnset
} }
return 0, fsys.openFilesWr.Open(handle) return 0, fsys.openHandle(handle)
} }
// Truncate truncates a file to size // Truncate truncates a file to size
@ -377,12 +312,8 @@ func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
if errc != 0 { if errc != 0 {
return errc return errc
} }
file, ok := node.(*vfs.File)
if !ok {
return -fuse.EIO
}
// Read the size so far // Read the size so far
currentSize := file.Size() currentSize := node.Size()
fs.Debugf(path, "truncate to %d, currentSize %d", size, currentSize) fs.Debugf(path, "truncate to %d, currentSize %d", size, currentSize)
if int64(currentSize) != size { if int64(currentSize) != size {
fs.Errorf(path, "Can't truncate files") fs.Errorf(path, "Can't truncate files")
@ -391,10 +322,10 @@ func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
return 0 return 0
} }
// Read data from file handle
func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) { func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
defer fs.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n) defer fs.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
// FIXME detect seek handle, errc := fsys.getHandle(fh)
handle, errc := fsys.openFilesRd.Get(fh)
if errc != 0 { if errc != 0 {
return errc return errc
} }
@ -407,19 +338,14 @@ func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
return n return n
} }
// Write data to file handle
func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) { func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
defer fs.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n) defer fs.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
// FIXME detect seek handle, errc := fsys.getHandle(fh)
handle, errc := fsys.openFilesWr.Get(fh)
if errc != 0 { if errc != 0 {
return errc return errc
} }
wfh, ok := handle.(*vfs.WriteFileHandle) n, err := handle.WriteAt(buff, ofst)
if !ok {
// Can only write to write file handle
return -fuse.EIO
}
n, err := wfh.WriteAt(buff, ofst)
if err != nil { if err != nil {
return translateError(err) return translateError(err)
} }
@ -429,44 +355,22 @@ func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
// Flush flushes an open file descriptor or path // Flush flushes an open file descriptor or path
func (fsys *FS) Flush(path string, fh uint64) (errc int) { func (fsys *FS) Flush(path string, fh uint64) (errc int) {
defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
handle, errc := fsys.getHandleFromFh(fh) handle, errc := fsys.getHandle(fh)
if errc != 0 { if errc != 0 {
return errc return errc
} }
var err error return translateError(handle.Flush())
switch x := handle.(type) {
case *vfs.ReadFileHandle:
err = x.Flush()
case *vfs.WriteFileHandle:
err = x.Flush()
default:
return -fuse.EIO
}
return translateError(err)
} }
// Release closes the file if still open // Release closes the file if still open
func (fsys *FS) Release(path string, fh uint64) (errc int) { func (fsys *FS) Release(path string, fh uint64) (errc int) {
defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc) defer fs.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
of, errc := fsys.getOpenFilesFromFh(fh) handle, errc := fsys.getHandle(fh)
if errc != 0 { if errc != 0 {
return errc return errc
} }
handle, errc := of.Get(fh) _ = fsys.closeHandle(fh)
if errc != 0 { return translateError(handle.Release())
return errc
}
_ = of.Close(fh)
var err error
switch x := handle.(type) {
case *vfs.ReadFileHandle:
err = x.Release()
case *vfs.WriteFileHandle:
err = x.Release()
default:
return -fuse.EIO
}
return translateError(err)
} }
// Unlink removes a file. // Unlink removes a file.
@ -503,15 +407,7 @@ func (fsys *FS) Rmdir(dirPath string) (errc int) {
// Rename renames a file. // Rename renames a file.
func (fsys *FS) Rename(oldPath string, newPath string) (errc int) { func (fsys *FS) Rename(oldPath string, newPath string) (errc int) {
defer fs.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc) defer fs.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc)
oldLeaf, oldParentDir, errc := fsys.lookupParentDir(oldPath) return translateError(fsys.VFS.Rename(oldPath, newPath))
if errc != 0 {
return errc
}
newLeaf, newParentDir, errc := fsys.lookupParentDir(newPath)
if errc != 0 {
return errc
}
return translateError(oldParentDir.Rename(oldLeaf, newLeaf, newParentDir))
} }
// Utimens changes the access and modification times of a file. // Utimens changes the access and modification times of a file.
@ -527,14 +423,7 @@ func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
} else { } else {
t = tmsp[1].Time() t = tmsp[1].Time()
} }
var err error return translateError(node.SetModTime(t))
switch x := node.(type) {
case *vfs.Dir:
err = x.SetModTime(t)
case *vfs.File:
err = x.SetModTime(t)
}
return translateError(err)
} }
// Mknod creates a file node. // Mknod creates a file node.

View file

@ -105,6 +105,7 @@ type Handle interface {
// Additional methods useful for FUSE filesystems // Additional methods useful for FUSE filesystems
Flush() error Flush() error
Release() error Release() error
Node() Node
} }
// baseHandle implements all the missing methods // baseHandle implements all the missing methods
@ -129,6 +130,7 @@ func (h baseHandle) WriteAt(b []byte, off int64) (n int, err error) { retu
func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS } func (h baseHandle) WriteString(s string) (n int, err error) { return 0, ENOSYS }
func (h baseHandle) Flush() (err error) { return ENOSYS } func (h baseHandle) Flush() (err error) { return ENOSYS }
func (h baseHandle) Release() (err error) { return ENOSYS } func (h baseHandle) Release() (err error) { return ENOSYS }
func (h baseHandle) Node() Node { return nil }
// Check interfaces // Check interfaces
var ( var (