github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/cmount/fs.go (about)

     1  //go:build cmount && ((linux && cgo) || (darwin && cgo) || (freebsd && cgo) || windows)
     2  
     3  package cmount
     4  
     5  import (
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/rclone/rclone/cmd/mountlib"
    15  	"github.com/rclone/rclone/fs"
    16  	"github.com/rclone/rclone/fs/fserrors"
    17  	"github.com/rclone/rclone/fs/log"
    18  	"github.com/rclone/rclone/vfs"
    19  	"github.com/winfsp/cgofuse/fuse"
    20  )
    21  
    22  const fhUnset = ^uint64(0)
    23  
    24  // FS represents the top level filing system
    25  type FS struct {
    26  	VFS       *vfs.VFS
    27  	f         fs.Fs
    28  	opt       *mountlib.Options
    29  	ready     chan (struct{})
    30  	mu        sync.Mutex // to protect the below
    31  	handles   []vfs.Handle
    32  	destroyed atomic.Int32
    33  }
    34  
    35  // NewFS makes a new FS
    36  func NewFS(VFS *vfs.VFS, opt *mountlib.Options) *FS {
    37  	fsys := &FS{
    38  		VFS:   VFS,
    39  		f:     VFS.Fs(),
    40  		opt:   opt,
    41  		ready: make(chan (struct{})),
    42  	}
    43  	return fsys
    44  }
    45  
    46  // Open a handle returning an integer file handle
    47  func (fsys *FS) openHandle(handle vfs.Handle) (fh uint64) {
    48  	fsys.mu.Lock()
    49  	defer fsys.mu.Unlock()
    50  	var i int
    51  	var oldHandle vfs.Handle
    52  	for i, oldHandle = range fsys.handles {
    53  		if oldHandle == nil {
    54  			fsys.handles[i] = handle
    55  			goto found
    56  		}
    57  	}
    58  	fsys.handles = append(fsys.handles, handle)
    59  	i = len(fsys.handles) - 1
    60  found:
    61  	return uint64(i)
    62  }
    63  
    64  // get the handle for fh, call with the lock held
    65  func (fsys *FS) _getHandle(fh uint64) (i int, handle vfs.Handle, errc int) {
    66  	if fh > uint64(len(fsys.handles)) {
    67  		fs.Debugf(nil, "Bad file handle: too big: 0x%X", fh)
    68  		return i, nil, -fuse.EBADF
    69  	}
    70  	i = int(fh)
    71  	handle = fsys.handles[i]
    72  	if handle == nil {
    73  		fs.Debugf(nil, "Bad file handle: nil handle: 0x%X", fh)
    74  		return i, nil, -fuse.EBADF
    75  	}
    76  	return i, handle, 0
    77  }
    78  
    79  // Get the handle for the file handle
    80  func (fsys *FS) getHandle(fh uint64) (handle vfs.Handle, errc int) {
    81  	fsys.mu.Lock()
    82  	_, handle, errc = fsys._getHandle(fh)
    83  	fsys.mu.Unlock()
    84  	return
    85  }
    86  
    87  // Close the handle
    88  func (fsys *FS) closeHandle(fh uint64) (errc int) {
    89  	fsys.mu.Lock()
    90  	i, _, errc := fsys._getHandle(fh)
    91  	if errc == 0 {
    92  		fsys.handles[i] = nil
    93  	}
    94  	fsys.mu.Unlock()
    95  	return
    96  }
    97  
    98  // lookup a Node given a path
    99  func (fsys *FS) lookupNode(path string) (node vfs.Node, errc int) {
   100  	node, err := fsys.VFS.Stat(path)
   101  	return node, translateError(err)
   102  }
   103  
   104  // lookup a Dir given a path
   105  func (fsys *FS) lookupDir(path string) (dir *vfs.Dir, errc int) {
   106  	node, errc := fsys.lookupNode(path)
   107  	if errc != 0 {
   108  		return nil, errc
   109  	}
   110  	dir, ok := node.(*vfs.Dir)
   111  	if !ok {
   112  		return nil, -fuse.ENOTDIR
   113  	}
   114  	return dir, 0
   115  }
   116  
   117  // lookup a parent Dir given a path returning the dir and the leaf
   118  func (fsys *FS) lookupParentDir(filePath string) (leaf string, dir *vfs.Dir, errc int) {
   119  	parentDir, leaf := path.Split(filePath)
   120  	dir, errc = fsys.lookupDir(parentDir)
   121  	return leaf, dir, errc
   122  }
   123  
   124  // lookup a File given a path
   125  func (fsys *FS) lookupFile(path string) (file *vfs.File, errc int) {
   126  	node, errc := fsys.lookupNode(path)
   127  	if errc != 0 {
   128  		return nil, errc
   129  	}
   130  	file, ok := node.(*vfs.File)
   131  	if !ok {
   132  		return nil, -fuse.EISDIR
   133  	}
   134  	return file, 0
   135  }
   136  
   137  // get a node and handle from the path or from the fh if not fhUnset
   138  //
   139  // handle may be nil
   140  func (fsys *FS) getNode(path string, fh uint64) (node vfs.Node, handle vfs.Handle, errc int) {
   141  	if fh == fhUnset {
   142  		node, errc = fsys.lookupNode(path)
   143  	} else {
   144  		handle, errc = fsys.getHandle(fh)
   145  		if errc == 0 {
   146  			node = handle.Node()
   147  		}
   148  	}
   149  	return
   150  }
   151  
   152  // stat fills up the stat block for Node
   153  func (fsys *FS) stat(node vfs.Node, stat *fuse.Stat_t) (errc int) {
   154  	Size := uint64(node.Size())
   155  	Blocks := (Size + 511) / 512
   156  	modTime := node.ModTime()
   157  	Mode := node.Mode().Perm()
   158  	if node.IsDir() {
   159  		Mode |= fuse.S_IFDIR
   160  	} else {
   161  		Mode |= fuse.S_IFREG
   162  	}
   163  	//stat.Dev = 1
   164  	stat.Ino = node.Inode() // FIXME do we need to set the inode number?
   165  	stat.Mode = uint32(Mode)
   166  	stat.Nlink = 1
   167  	stat.Uid = fsys.VFS.Opt.UID
   168  	stat.Gid = fsys.VFS.Opt.GID
   169  	//stat.Rdev
   170  	stat.Size = int64(Size)
   171  	t := fuse.NewTimespec(modTime)
   172  	stat.Atim = t
   173  	stat.Mtim = t
   174  	stat.Ctim = t
   175  	stat.Blksize = 512
   176  	stat.Blocks = int64(Blocks)
   177  	stat.Birthtim = t
   178  	// fs.Debugf(nil, "stat = %+v", *stat)
   179  	return 0
   180  }
   181  
   182  // Init is called after the filesystem is ready
   183  func (fsys *FS) Init() {
   184  	defer log.Trace(fsys.f, "")("")
   185  	close(fsys.ready)
   186  }
   187  
   188  // Destroy is called when it is unmounted (note that depending on how
   189  // the file system is terminated the file system may not receive the
   190  // Destroy call).
   191  func (fsys *FS) Destroy() {
   192  	defer log.Trace(fsys.f, "")("")
   193  	fsys.destroyed.Store(1)
   194  }
   195  
   196  // Getattr reads the attributes for path
   197  func (fsys *FS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
   198  	defer log.Trace(path, "fh=0x%X", fh)("errc=%v", &errc)
   199  	node, _, errc := fsys.getNode(path, fh)
   200  	if errc == 0 {
   201  		errc = fsys.stat(node, stat)
   202  	}
   203  	return
   204  }
   205  
   206  // Opendir opens path as a directory
   207  func (fsys *FS) Opendir(path string) (errc int, fh uint64) {
   208  	defer log.Trace(path, "")("errc=%d, fh=0x%X", &errc, &fh)
   209  	handle, err := fsys.VFS.OpenFile(path, os.O_RDONLY, 0777)
   210  	if err != nil {
   211  		return translateError(err), fhUnset
   212  	}
   213  	return 0, fsys.openHandle(handle)
   214  }
   215  
   216  // Readdir reads the directory at dirPath
   217  func (fsys *FS) Readdir(dirPath string,
   218  	fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
   219  	ofst int64,
   220  	fh uint64) (errc int) {
   221  	itemsRead := -1
   222  	defer log.Trace(dirPath, "ofst=%d, fh=0x%X", ofst, fh)("items=%d, errc=%d", &itemsRead, &errc)
   223  
   224  	dir, errc := fsys.lookupDir(dirPath)
   225  	if errc != 0 {
   226  		return errc
   227  	}
   228  
   229  	// We can't seek in directories and FUSE should know that so
   230  	// return an error if ofst is ever set.
   231  	if ofst > 0 {
   232  		return -fuse.ESPIPE
   233  	}
   234  
   235  	nodes, err := dir.ReadDirAll()
   236  	if err != nil {
   237  		return translateError(err)
   238  	}
   239  
   240  	// Optionally, create a struct stat that describes the file as
   241  	// for getattr (but FUSE only looks at st_ino and the
   242  	// file-type bits of st_mode).
   243  	//
   244  	// We have called host.SetCapReaddirPlus() so WinFsp will
   245  	// use the full stat information - a Useful optimization on
   246  	// Windows.
   247  	//
   248  	// NB we are using the first mode for readdir: The readdir
   249  	// implementation ignores the offset parameter, and passes
   250  	// zero to the filler function's offset. The filler function
   251  	// will not return '1' (unless an error happens), so the whole
   252  	// directory is read in a single readdir operation.
   253  	fill(".", nil, 0)
   254  	fill("..", nil, 0)
   255  	for _, node := range nodes {
   256  		name := node.Name()
   257  		if len(name) > mountlib.MaxLeafSize {
   258  			fs.Errorf(dirPath, "Name too long (%d bytes) for FUSE, skipping: %s", len(name), name)
   259  			continue
   260  		}
   261  		// We have called host.SetCapReaddirPlus() so supply the stat information
   262  		// It is very cheap at this point so supply it regardless of OS capabilities
   263  		var stat fuse.Stat_t
   264  		_ = fsys.stat(node, &stat) // not capable of returning an error
   265  		fill(name, &stat, 0)
   266  	}
   267  	itemsRead = len(nodes)
   268  	return 0
   269  }
   270  
   271  // Releasedir finished reading the directory
   272  func (fsys *FS) Releasedir(path string, fh uint64) (errc int) {
   273  	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
   274  	return fsys.closeHandle(fh)
   275  }
   276  
   277  // Statfs reads overall stats on the filesystem
   278  func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) {
   279  	defer log.Trace(path, "")("stat=%+v, errc=%d", stat, &errc)
   280  	const blockSize = 4096
   281  	total, _, free := fsys.VFS.Statfs()
   282  	stat.Blocks = uint64(total) / blockSize // Total data blocks in file system.
   283  	stat.Bfree = uint64(free) / blockSize   // Free blocks in file system.
   284  	stat.Bavail = stat.Bfree                // Free blocks in file system if you're not root.
   285  	stat.Files = 1e9                        // Total files in file system.
   286  	stat.Ffree = 1e9                        // Free files in file system.
   287  	stat.Bsize = blockSize                  // Block size
   288  	stat.Namemax = 255                      // Maximum file name length?
   289  	stat.Frsize = blockSize                 // Fragment size, smallest addressable data size in the file system.
   290  	mountlib.ClipBlocks(&stat.Blocks)
   291  	mountlib.ClipBlocks(&stat.Bfree)
   292  	mountlib.ClipBlocks(&stat.Bavail)
   293  	return 0
   294  }
   295  
   296  // OpenEx opens a file
   297  func (fsys *FS) OpenEx(path string, fi *fuse.FileInfo_t) (errc int) {
   298  	defer log.Trace(path, "flags=0x%X", fi.Flags)("errc=%d, fh=0x%X", &errc, &fi.Fh)
   299  	fi.Fh = fhUnset
   300  
   301  	// translate the fuse flags to os flags
   302  	flags := translateOpenFlags(fi.Flags)
   303  	handle, err := fsys.VFS.OpenFile(path, flags, 0777)
   304  	if err != nil {
   305  		return translateError(err)
   306  	}
   307  
   308  	// If size unknown then use direct io to read
   309  	if entry := handle.Node().DirEntry(); entry != nil && entry.Size() < 0 {
   310  		fi.DirectIo = true
   311  	}
   312  	if fsys.opt.DirectIO {
   313  		fi.DirectIo = true
   314  	}
   315  
   316  	fi.Fh = fsys.openHandle(handle)
   317  	return 0
   318  }
   319  
   320  // Open opens a file
   321  func (fsys *FS) Open(path string, flags int) (errc int, fh uint64) {
   322  	var fi = fuse.FileInfo_t{
   323  		Flags: flags,
   324  	}
   325  	errc = fsys.OpenEx(path, &fi)
   326  	return errc, fi.Fh
   327  }
   328  
   329  // CreateEx creates and opens a file.
   330  func (fsys *FS) CreateEx(filePath string, mode uint32, fi *fuse.FileInfo_t) (errc int) {
   331  	defer log.Trace(filePath, "flags=0x%X, mode=0%o", fi.Flags, mode)("errc=%d, fh=0x%X", &errc, &fi.Fh)
   332  	fi.Fh = fhUnset
   333  	leaf, parentDir, errc := fsys.lookupParentDir(filePath)
   334  	if errc != 0 {
   335  		return errc
   336  	}
   337  	file, err := parentDir.Create(leaf, fi.Flags)
   338  	if err != nil {
   339  		return translateError(err)
   340  	}
   341  	// translate the fuse flags to os flags
   342  	flags := translateOpenFlags(fi.Flags) | os.O_CREATE
   343  	handle, err := file.Open(flags)
   344  	if err != nil {
   345  		return translateError(err)
   346  	}
   347  	fi.Fh = fsys.openHandle(handle)
   348  	return 0
   349  }
   350  
   351  // Create creates and opens a file.
   352  func (fsys *FS) Create(filePath string, flags int, mode uint32) (errc int, fh uint64) {
   353  	var fi = fuse.FileInfo_t{
   354  		Flags: flags,
   355  	}
   356  	errc = fsys.CreateEx(filePath, mode, &fi)
   357  	return errc, fi.Fh
   358  }
   359  
   360  // Truncate truncates a file to size
   361  func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
   362  	defer log.Trace(path, "size=%d, fh=0x%X", size, fh)("errc=%d", &errc)
   363  	node, handle, errc := fsys.getNode(path, fh)
   364  	if errc != 0 {
   365  		return errc
   366  	}
   367  	var err error
   368  	if handle != nil {
   369  		err = handle.Truncate(size)
   370  	} else {
   371  		err = node.Truncate(size)
   372  	}
   373  	if err != nil {
   374  		return translateError(err)
   375  	}
   376  	return 0
   377  }
   378  
   379  // Read data from file handle
   380  func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
   381  	defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
   382  	handle, errc := fsys.getHandle(fh)
   383  	if errc != 0 {
   384  		return errc
   385  	}
   386  	n, err := handle.ReadAt(buff, ofst)
   387  	if err == io.EOF {
   388  	} else if err != nil {
   389  		return translateError(err)
   390  	}
   391  	return n
   392  }
   393  
   394  // Write data to file handle
   395  func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
   396  	defer log.Trace(path, "ofst=%d, fh=0x%X", ofst, fh)("n=%d", &n)
   397  	handle, errc := fsys.getHandle(fh)
   398  	if errc != 0 {
   399  		return errc
   400  	}
   401  	n, err := handle.WriteAt(buff, ofst)
   402  	if err != nil {
   403  		return translateError(err)
   404  	}
   405  	return n
   406  }
   407  
   408  // Flush flushes an open file descriptor or path
   409  func (fsys *FS) Flush(path string, fh uint64) (errc int) {
   410  	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
   411  	handle, errc := fsys.getHandle(fh)
   412  	if errc != 0 {
   413  		return errc
   414  	}
   415  	return translateError(handle.Flush())
   416  }
   417  
   418  // Release closes the file if still open
   419  func (fsys *FS) Release(path string, fh uint64) (errc int) {
   420  	defer log.Trace(path, "fh=0x%X", fh)("errc=%d", &errc)
   421  	handle, errc := fsys.getHandle(fh)
   422  	if errc != 0 {
   423  		return errc
   424  	}
   425  	_ = fsys.closeHandle(fh)
   426  	return translateError(handle.Release())
   427  }
   428  
   429  // Unlink removes a file.
   430  func (fsys *FS) Unlink(filePath string) (errc int) {
   431  	defer log.Trace(filePath, "")("errc=%d", &errc)
   432  	leaf, parentDir, errc := fsys.lookupParentDir(filePath)
   433  	if errc != 0 {
   434  		return errc
   435  	}
   436  	return translateError(parentDir.RemoveName(leaf))
   437  }
   438  
   439  // Mkdir creates a directory.
   440  func (fsys *FS) Mkdir(dirPath string, mode uint32) (errc int) {
   441  	defer log.Trace(dirPath, "mode=0%o", mode)("errc=%d", &errc)
   442  	leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
   443  	if errc != 0 {
   444  		return errc
   445  	}
   446  	_, err := parentDir.Mkdir(leaf)
   447  	return translateError(err)
   448  }
   449  
   450  // Rmdir removes a directory
   451  func (fsys *FS) Rmdir(dirPath string) (errc int) {
   452  	defer log.Trace(dirPath, "")("errc=%d", &errc)
   453  	leaf, parentDir, errc := fsys.lookupParentDir(dirPath)
   454  	if errc != 0 {
   455  		return errc
   456  	}
   457  	return translateError(parentDir.RemoveName(leaf))
   458  }
   459  
   460  // Rename renames a file.
   461  func (fsys *FS) Rename(oldPath string, newPath string) (errc int) {
   462  	defer log.Trace(oldPath, "newPath=%q", newPath)("errc=%d", &errc)
   463  	return translateError(fsys.VFS.Rename(oldPath, newPath))
   464  }
   465  
   466  // Windows sometimes seems to send times that are the epoch which is
   467  // 1601-01-01 +/- timezone so filter out times that are earlier than
   468  // this.
   469  var invalidDateCutoff = time.Date(1601, 1, 2, 0, 0, 0, 0, time.UTC)
   470  
   471  // Utimens changes the access and modification times of a file.
   472  func (fsys *FS) Utimens(path string, tmsp []fuse.Timespec) (errc int) {
   473  	defer log.Trace(path, "tmsp=%+v", tmsp)("errc=%d", &errc)
   474  	node, errc := fsys.lookupNode(path)
   475  	if errc != 0 {
   476  		return errc
   477  	}
   478  	if tmsp == nil || len(tmsp) < 2 {
   479  		fs.Debugf(path, "Utimens: Not setting time as timespec isn't complete: %v", tmsp)
   480  		return 0
   481  	}
   482  	t := tmsp[1].Time()
   483  	if t.Before(invalidDateCutoff) {
   484  		fs.Debugf(path, "Utimens: Not setting out of range time: %v", t)
   485  		return 0
   486  	}
   487  	fs.Debugf(path, "Utimens: SetModTime: %v", t)
   488  	return translateError(node.SetModTime(t))
   489  }
   490  
   491  // Mknod creates a file node.
   492  func (fsys *FS) Mknod(path string, mode uint32, dev uint64) (errc int) {
   493  	defer log.Trace(path, "mode=0x%X, dev=0x%X", mode, dev)("errc=%d", &errc)
   494  	return -fuse.ENOSYS
   495  }
   496  
   497  // Fsync synchronizes file contents.
   498  func (fsys *FS) Fsync(path string, datasync bool, fh uint64) (errc int) {
   499  	defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
   500  	// This is a no-op for rclone
   501  	return 0
   502  }
   503  
   504  // Link creates a hard link to a file.
   505  func (fsys *FS) Link(oldpath string, newpath string) (errc int) {
   506  	defer log.Trace(oldpath, "newpath=%q", newpath)("errc=%d", &errc)
   507  	return -fuse.ENOSYS
   508  }
   509  
   510  // Symlink creates a symbolic link.
   511  func (fsys *FS) Symlink(target string, newpath string) (errc int) {
   512  	defer log.Trace(target, "newpath=%q", newpath)("errc=%d", &errc)
   513  	return -fuse.ENOSYS
   514  }
   515  
   516  // Readlink reads the target of a symbolic link.
   517  func (fsys *FS) Readlink(path string) (errc int, linkPath string) {
   518  	defer log.Trace(path, "")("linkPath=%q, errc=%d", &linkPath, &errc)
   519  	return -fuse.ENOSYS, ""
   520  }
   521  
   522  // Chmod changes the permission bits of a file.
   523  func (fsys *FS) Chmod(path string, mode uint32) (errc int) {
   524  	defer log.Trace(path, "mode=0%o", mode)("errc=%d", &errc)
   525  	// This is a no-op for rclone
   526  	return 0
   527  }
   528  
   529  // Chown changes the owner and group of a file.
   530  func (fsys *FS) Chown(path string, uid uint32, gid uint32) (errc int) {
   531  	defer log.Trace(path, "uid=%d, gid=%d", uid, gid)("errc=%d", &errc)
   532  	// This is a no-op for rclone
   533  	return 0
   534  }
   535  
   536  // Access checks file access permissions.
   537  func (fsys *FS) Access(path string, mask uint32) (errc int) {
   538  	defer log.Trace(path, "mask=0%o", mask)("errc=%d", &errc)
   539  	// This is a no-op for rclone
   540  	return 0
   541  }
   542  
   543  // Fsyncdir synchronizes directory contents.
   544  func (fsys *FS) Fsyncdir(path string, datasync bool, fh uint64) (errc int) {
   545  	defer log.Trace(path, "datasync=%v, fh=0x%X", datasync, fh)("errc=%d", &errc)
   546  	// This is a no-op for rclone
   547  	return 0
   548  }
   549  
   550  // Setxattr sets extended attributes.
   551  func (fsys *FS) Setxattr(path string, name string, value []byte, flags int) (errc int) {
   552  	defer log.Trace(path, "name=%q, value=%q, flags=%d", name, value, flags)("errc=%d", &errc)
   553  	return -fuse.ENOSYS
   554  }
   555  
   556  // Getxattr gets extended attributes.
   557  func (fsys *FS) Getxattr(path string, name string) (errc int, value []byte) {
   558  	defer log.Trace(path, "name=%q", name)("errc=%d, value=%q", &errc, &value)
   559  	return -fuse.ENOSYS, nil
   560  }
   561  
   562  // Removexattr removes extended attributes.
   563  func (fsys *FS) Removexattr(path string, name string) (errc int) {
   564  	defer log.Trace(path, "name=%q", name)("errc=%d", &errc)
   565  	return -fuse.ENOSYS
   566  }
   567  
   568  // Listxattr lists extended attributes.
   569  func (fsys *FS) Listxattr(path string, fill func(name string) bool) (errc int) {
   570  	defer log.Trace(path, "fill=%p", fill)("errc=%d", &errc)
   571  	return -fuse.ENOSYS
   572  }
   573  
   574  // Getpath allows a case-insensitive file system to report the correct case of
   575  // a file path.
   576  func (fsys *FS) Getpath(path string, fh uint64) (errc int, normalisedPath string) {
   577  	defer log.Trace(path, "Getpath fh=%d", fh)("errc=%d, normalisedPath=%q", &errc, &normalisedPath)
   578  	node, _, errc := fsys.getNode(path, fh)
   579  	if errc != 0 {
   580  		return errc, ""
   581  	}
   582  	normalisedPath = node.Path()
   583  	if !strings.HasPrefix("/", normalisedPath) {
   584  		normalisedPath = "/" + normalisedPath
   585  	}
   586  	return 0, normalisedPath
   587  }
   588  
   589  // Translate errors from mountlib
   590  func translateError(err error) (errc int) {
   591  	if err == nil {
   592  		return 0
   593  	}
   594  	_, uErr := fserrors.Cause(err)
   595  	switch uErr {
   596  	case vfs.OK:
   597  		return 0
   598  	case vfs.ENOENT, fs.ErrorDirNotFound, fs.ErrorObjectNotFound:
   599  		return -fuse.ENOENT
   600  	case vfs.EEXIST, fs.ErrorDirExists:
   601  		return -fuse.EEXIST
   602  	case vfs.EPERM, fs.ErrorPermissionDenied:
   603  		return -fuse.EPERM
   604  	case vfs.ECLOSED:
   605  		return -fuse.EBADF
   606  	case vfs.ENOTEMPTY:
   607  		return -fuse.ENOTEMPTY
   608  	case vfs.ESPIPE:
   609  		return -fuse.ESPIPE
   610  	case vfs.EBADF:
   611  		return -fuse.EBADF
   612  	case vfs.EROFS:
   613  		return -fuse.EROFS
   614  	case vfs.ENOSYS, fs.ErrorNotImplemented:
   615  		return -fuse.ENOSYS
   616  	case vfs.EINVAL:
   617  		return -fuse.EINVAL
   618  	}
   619  	fs.Errorf(nil, "IO error: %v", err)
   620  	return -fuse.EIO
   621  }
   622  
   623  // Translate Open Flags from FUSE to os (as used in the vfs layer)
   624  func translateOpenFlags(inFlags int) (outFlags int) {
   625  	switch inFlags & fuse.O_ACCMODE {
   626  	case fuse.O_RDONLY:
   627  		outFlags = os.O_RDONLY
   628  	case fuse.O_WRONLY:
   629  		outFlags = os.O_WRONLY
   630  	case fuse.O_RDWR:
   631  		outFlags = os.O_RDWR
   632  	}
   633  	if inFlags&fuse.O_APPEND != 0 {
   634  		outFlags |= os.O_APPEND
   635  	}
   636  	if inFlags&fuse.O_CREAT != 0 {
   637  		outFlags |= os.O_CREATE
   638  	}
   639  	if inFlags&fuse.O_EXCL != 0 {
   640  		outFlags |= os.O_EXCL
   641  	}
   642  	if inFlags&fuse.O_TRUNC != 0 {
   643  		outFlags |= os.O_TRUNC
   644  	}
   645  	// NB O_SYNC isn't defined by fuse
   646  	return outFlags
   647  }
   648  
   649  // Make sure interfaces are satisfied
   650  var (
   651  	_ fuse.FileSystemInterface = (*FS)(nil)
   652  	_ fuse.FileSystemOpenEx    = (*FS)(nil)
   653  	_ fuse.FileSystemGetpath   = (*FS)(nil)
   654  	//_ fuse.FileSystemChflags    = (*FS)(nil)
   655  	//_ fuse.FileSystemSetcrtime  = (*FS)(nil)
   656  	//_ fuse.FileSystemSetchgtime = (*FS)(nil)
   657  )