// node returns an fs.Node corresponding to the given key. It distinguishes // between files and directories and correctly translates etcd errors into // appropriate syscall errors. func (f *etcdFS) node(ctx context.Context, key string) (fs.Node, error) { resp, err := f.etcd.Get(ctx, key, &client.GetOptions{ Sort: true, Quorum: true, }) if err != nil { log.Printf("Error fetching node %q: %v", key, err) if _, ok := err.(client.Error); !ok { return nil, err } e := err.(client.Error) switch e.Code { case client.ErrorCodeKeyNotFound: return nil, fuse.Errno(syscall.ENOENT) case client.ErrorCodeNotDir: return nil, fuse.Errno(syscall.ENOTDIR) case client.ErrorCodeUnauthorized: return nil, fuse.Errno(syscall.EPERM) default: return nil, err } } n := resp.Node if n.Dir { return &etcdDir{f, n}, nil } return &etcdFile{f, n}, nil }
func (file *File) ReadAll(intr fs.Intr) ([]byte, fuse.Error) { bytes, err := ioutil.ReadFile(file.Path) if err != nil { return nil, fuse.Errno(syscall.ENOSYS) } return bytes, nil }
func (e fuseFile) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fusefs.Handle, error) { if !req.Flags.IsReadOnly() { return nil, fuse.Errno(syscall.EACCES) } return e, nil }
func (e fuseFile) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fusefs.Intr) (fusefs.Handle, fuse.Error) { if req.Flags&syscall.O_ACCMODE != syscall.O_RDONLY { return nil, fuse.Errno(syscall.EACCES) } return e, nil }
func (e fuseFile) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fusefs.Intr) (fusefs.Handle, fuse.Error) { if !req.Flags.IsReadOnly() { return nil, fuse.Errno(syscall.EACCES) } return e, nil }
func (h *handle) Read(ctx context.Context, request *fuse.ReadRequest, response *fuse.ReadResponse) (retErr error) { defer func() { if retErr == nil { protolion.Debug(&FileRead{&h.f.Node, string(response.Data), errorToString(retErr)}) } else { protolion.Error(&FileRead{&h.f.Node, string(response.Data), errorToString(retErr)}) } }() var buffer bytes.Buffer if err := h.f.fs.apiClient.GetFileUnsafe( h.f.File.Commit.Repo.Name, h.f.File.Commit.ID, h.f.File.Path, request.Offset, int64(request.Size), h.f.fs.getFromCommitID(h.f.getRepoOrAliasName()), h.f.Shard, h.f.fs.handleID, &buffer, ); err != nil { if grpc.Code(err) == codes.NotFound { // ENOENT from read(2) is weird, let's call this EINVAL // instead. return fuse.Errno(syscall.EINVAL) } return err } response.Data = buffer.Bytes() return nil }
func (d *dir) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) { d.mu.Lock() defer d.mu.Unlock() // TODO handle req.Mode var child node err := d.fs.db.Update(func(tx *bolt.Tx) error { bucket := d.fs.bucket(tx).Bucket(bucketInode) if bucket == nil { return errors.New("inode bucket is missing") } inode, err := inodes.Allocate(bucket) if err != nil { return err } child = &dir{ inode: inode, name: req.Name, parent: d, fs: d.fs, active: make(map[string]node), } d.active[req.Name] = child return d.saveInternal(tx, req.Name, child) // TODO clean up active on error }) if err != nil { if err == inodes.OutOfInodes { return nil, fuse.Errno(syscall.ENOSPC) } return nil, err } return child, nil }
func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { if !req.Flags.IsReadOnly() { return nil, fuse.Errno(syscall.EACCES) } resp.Flags |= fuse.OpenKeepCache return f, nil }
func (file *UpperCaseFile) ReadAll(intr fs.Intr) ([]byte, fuse.Error) { bytes, err := ioutil.ReadFile(file.Path) if err != nil { return nil, fuse.Errno(syscall.ENOSYS) } return []byte(strings.ToUpper(string(bytes))), nil }
// Rename implements the fs.NodeRenamer interface for Dir. func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) (err error) { d.folder.fs.log.CDebugf(ctx, "Dir Rename %s -> %s", req.OldName, req.NewName) defer func() { d.folder.reportErr(ctx, libkbfs.WriteMode, err) }() var realNewDir *Dir switch newDir := newDir.(type) { case *Dir: realNewDir = newDir case *TLF: var err error realNewDir, err = newDir.loadDir(ctx) if err != nil { return err } default: // The destination is not a TLF instance, probably // because it's Root (or some other node type added // later). The kernel won't let a rename newDir point // to a non-directory. // // We have no cheap atomic rename across folders, so // we can't serve this. EXDEV makes `mv` do a // copy+delete, and the Lookup on the destination path // will decide whether it's legal. return fuse.Errno(syscall.EXDEV) } if d.folder != realNewDir.folder { // Check this explicitly, not just trusting KBFSOps.Rename to // return an error, because we rely on it for locking // correctness. return fuse.Errno(syscall.EXDEV) } // overwritten node, if any, will be removed from Folder.nodes, if // it is there in the first place, by its Forget if err := d.folder.fs.config.KBFSOps().Rename( ctx, d.node, req.OldName, realNewDir.node, req.NewName); err != nil { return err } return nil }
func (n *Node) Access(ctx context.Context, req *fuse.AccessRequest) error { isDir, err := isDir(n.path) defer trace(NewAccessOp(req, n.path, isDir)) if err != nil { return err } if access(n.path, req.Mask) { return nil } return fuse.Errno(syscall.EACCES) }
// checkIsEmpty returns nil if 'id' has no children. func checkIsEmpty(e sqlExecutor, id uint64) error { var count uint64 const countSQL = ` SELECT COUNT(parentID) FROM fs.namespace WHERE parentID = $1` if err := e.QueryRow(countSQL, id).Scan(&count); err != nil { return err } if count != 0 { return fuse.Errno(syscall.ENOTEMPTY) } return nil }
func (f *file) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { // allow kernel to use buffer cache resp.Flags &^= fuse.OpenDirectIO f.mu.Lock() defer f.mu.Unlock() tmp := f.handles + 1 if tmp == 0 { return nil, fuse.Errno(syscall.ENFILE) } f.handles = tmp return f, nil }
// validateRename takes a source and destination node and verifies that // a rename can be performed from source to destination. // source must not be nil. destination can be. func validateRename(tx *sql.Tx, source, destination *Node) error { if destination == nil { // No object at destination: good. return nil } if source.Mode.IsDir() { if destination.Mode.IsDir() { // Both are directories: destination must be empty return checkIsEmpty(tx, destination.ID) } // directory -> file: not allowed. return fuse.Errno(syscall.ENOTDIR) } // Source is a file. if destination.Mode.IsDir() { // file -> directory: not allowed. return fuse.Errno(syscall.EISDIR) } return nil }
// Readlink implements the fs.NodeReadlinker interface for Symlink func (s *Symlink) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (link string, err error) { s.parent.folder.fs.log.CDebugf(ctx, "Symlink Readlink") defer func() { s.parent.folder.reportErr(ctx, libkbfs.ReadMode, err) }() _, de, err := s.parent.folder.fs.config.KBFSOps().Lookup(ctx, s.parent.node, s.name) if err != nil { return "", err } if de.Type != libkbfs.Sym { return "", fuse.Errno(syscall.EINVAL) } return de.SymPath, nil }
// osErrorToFuseError converts an os.PathError, os.LinkError or // syscall.Errno into an error func osErrorToFuseError(err error) error { if err == nil { return nil } errno := syscall.EIO if patherr, ok := err.(*os.PathError); ok { errno = patherr.Err.(syscall.Errno) } else if linkerr, ok := err.(*os.LinkError); ok { errno = linkerr.Err.(syscall.Errno) } else if _, ok := err.(*syscall.Errno); ok { errno = err.(syscall.Errno) } return fuse.Errno(errno) }
// Rename implements the fs.NodeRenamer interface for Dir. func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) (err error) { ctx = NewContextWithOpID(ctx, d.folder.fs.log) d.folder.fs.log.CDebugf(ctx, "Dir Rename %s -> %s", req.OldName, req.NewName) defer func() { d.folder.fs.reportErr(ctx, err) }() newDir2, ok := newDir.(*Dir) if !ok { // The destination is not a Dir instance, probably because // it's Root (or some other node type added later). The kernel // won't let a rename newDir point to a non-directory. // // We have no cheap atomic rename across folders, so we can't // serve this. EXDEV makes `mv` do a copy+delete, and the // Lookup on the destination path will decide whether it's // legal. return fuse.Errno(syscall.EXDEV) } if d.folder != newDir2.folder { // Check this explicitly, not just trusting KBFSOps.Rename to // return an error, because we rely on it for locking // correctness. return fuse.Errno(syscall.EXDEV) } // overwritten node, if any, will be removed from Folder.nodes, if // it is there in the first place, by its Forget if err := d.folder.fs.config.KBFSOps().Rename( ctx, d.node, req.OldName, newDir2.node, req.NewName); err != nil { return err } return nil }
// HandleWriter func (f *File) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error { f.mu.Lock() defer f.mu.Unlock() newLen := req.Offset + int64(len(req.Data)) if newLen > int64(maxInt) { return fuse.Errno(syscall.EFBIG) } n := copy(f.data[req.Offset:], req.Data) if n < len(req.Data) { f.data = append(f.data, req.Data[n:]...) } resp.Size = len(req.Data) return nil }
func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { f.mu.Lock() defer f.mu.Unlock() // expand the buffer if necessary newLen := req.Offset + int64(len(req.Data)) if newLen > int64(maxInt) { return fuse.Errno(syscall.EFBIG) } if newLen := int(newLen); newLen > len(f.data) { f.data = append(f.data, make([]byte, newLen-len(f.data))...) } n := copy(f.data[req.Offset:], req.Data) resp.Size = n return nil }
func (v *Volume) SyncReceive(ctx context.Context, dirPath string, peers map[uint32][]byte, dirClockBuf []byte, recv func() ([]*wirepeer.Dirent, error)) error { n, drop, err := v.lookupPath(dirPath) if err != nil { return err } defer drop() d, ok := n.(*dir) if !ok { return fuse.Errno(syscall.ENOTDIR) } if err := d.syncReceive(ctx, peers, dirClockBuf, recv); err != nil { return err } return nil }
func (file *File) Getxattr(req *fuse.GetxattrRequest, res *fuse.GetxattrResponse, intr fs.Intr) fuse.Error { // return fuse.EPERM // fmt.Printf("Get attr: %s\n", req.Name) buf := make([]byte, 8192) size, err := syscallx.Getxattr(file.Path, req.Name, buf) if err != nil { // fmt.Printf("Get xattr error: %s - %s: \n", file.Path, req.Name, err) // return err // On osx, we need to return NOATTR, but this isn't built into go or bazil.org/fuse, so we need to do this: return fuse.Errno(93) } res.Xattr = buf[:size] return nil }
func (f *File) Setattr(c context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error { log.Debugln("file_setattr:", f.dir.path, f.name, req) f.mu.Lock() defer f.mu.Unlock() if req.Valid.Size() { if req.Size > uint64(maxInt) { return fuse.Errno(syscall.EFBIG) } newLen := int(req.Size) switch { case newLen > len(f.data): f.data = append(f.data, make([]byte, newLen-len(f.data))...) case newLen < len(f.data): f.data = f.data[:newLen] } } return nil }
func (d Dir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) { var out []fuse.Dirent files, err := ioutil.ReadDir(d.Path) if err != nil { log.Print(err) return nil, fuse.Errno(err.(syscall.Errno)) } for _, node := range files { de := fuse.Dirent{Name: node.Name()} if node.IsDir() { de.Type = fuse.DT_Dir } if node.Mode().IsRegular() { de.Type = fuse.DT_File } out = append(out, de) } return out, nil }
// Moves a file from dir to newDir (potentially the same as dir) and changes its name from req.OldName // to req.NewName func (dir *Directory) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error { filesystem.Lock(dir) defer filesystem.Unlock(dir) util.P_out(req.String()) if d, newDirOk := newDir.(*Directory); newDirOk { if v, oldNameInDir := dir.children[req.OldName]; oldNameInDir { v.setName(req.NewName) d.setChild(v) if file, ok := v.(*File); ok { file.dirty = true file.parent = d } dir.removeChild(req.OldName) d.dirty = true dir.dirty = true return nil } return fuse.ENOENT } return fuse.Errno(syscall.ENOTDIR) }
func (d Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error { lock.Lock() defer lock.Unlock() log.Debug("Remove(%s,%s)", d.GetPath(), req.Name) p := path.Join(d.GetPath(), req.Name) iter := d.Fs.Session.Query("SELECT entry FROM fuse.filesystem WHERE filename=? AND block=0 AND entry > '!';", p).Iter() var entry string for iter.Scan(&entry) { return fuse.Errno(syscall.ENOTEMPTY) } if err := d.Fs.Session.Query("DELETE FROM fuse.filesystem WHERE filename=? AND entry=? AND block=0;", d.GetPath(), req.Name).Exec(); err != nil { log.Error(err.Error()) return err } if err := d.Fs.Session.Query("DELETE FROM fuse.filesystem WHERE filename=?;", p).Exec(); err != nil { log.Error(err.Error()) return err } d.Fs.DropPath(p) return nil }
// RemoveDir is called to remove a directory func (dir *consulDir) RemoveDir(ctx context.Context, req *fuse.RemoveRequest) error { // Look in the cache to find the child directory being removed. dir.mux.Lock() childDir, ok := dir.dirs[req.Name] if !ok { dir.mux.Unlock() return fuse.ENOENT } dir.mux.Unlock() // Don't delete a directory with files in it. To do that, we have to refresh the // file listing of the directory. err := childDir.refresh(ctx) if err != nil { return err } dir.mux.Lock() defer dir.mux.Unlock() childDir.mux.Lock() defer childDir.mux.Unlock() // State could have changed while the refresh was happening childDir2, ok := dir.dirs[req.Name] if !ok { return fuse.ENOENT } if childDir != childDir2 { // Many concurrent removes? Just give up. return fuse.EIO } // Only delete an empty directory if len(childDir.dirs) > 0 || len(childDir.files) > 0 { return fuse.Errno(syscall.ENOTEMPTY) } delete(dir.dirs, req.Name) return nil }
func (createrDir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) { // pick a really distinct error, to identify it later return nil, nil, fuse.Errno(syscall.ENAMETOOLONG) }
// Errno implements the fuse.ErrorNumber interface for NoCurrentSessionError. func (e NoCurrentSessionError) Errno() fuse.Errno { return fuse.Errno(syscall.EACCES) }
// Errno implements the fuse.ErrorNumber interface for DirTooBigError. func (e DirTooBigError) Errno() fuse.Errno { return fuse.Errno(syscall.EFBIG) }
// Errno implements the fuse.ErrorNumber interface for NameTooLongError. func (e NameTooLongError) Errno() fuse.Errno { return fuse.Errno(syscall.ENAMETOOLONG) }