// Copyright 2016 the Go-FUSE Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package nodefs // This file contains FileSystemConnector's implementation of // RawFileSystem import ( "bytes" "fmt" "log" "strings" "time" "github.com/hanwen/go-fuse/fuse" ) // Returns the RawFileSystem so it can be mounted. func (c *FileSystemConnector) RawFS() fuse.RawFileSystem { return (*rawBridge)(c) } type rawBridge FileSystemConnector func (c *rawBridge) Fsync(input *fuse.FsyncIn) fuse.Status { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) if opened != nil { return opened.WithFlags.File.Fsync(int(input.FsyncFlags)) } return fuse.ENOSYS } func (c *rawBridge) SetDebug(debug bool) { c.fsConn().SetDebug(debug) } func (c *rawBridge) FsyncDir(input *fuse.FsyncIn) fuse.Status { return fuse.ENOSYS } func (c *rawBridge) fsConn() *FileSystemConnector { return (*FileSystemConnector)(c) } func (c *rawBridge) String() string { if c.rootNode == nil || c.rootNode.mount == nil { return "go-fuse:unmounted" } name := fmt.Sprintf("%T", c.rootNode.Node()) name = strings.TrimLeft(name, "*") return name } func (c *rawBridge) Init(s *fuse.Server) { c.server = s c.rootNode.Node().OnMount((*FileSystemConnector)(c)) } func (c *FileSystemConnector) lookupMountUpdate(out *fuse.Attr, mount *fileSystemMount) (node *Inode, code fuse.Status) { code = mount.mountInode.Node().GetAttr(out, nil, nil) if !code.Ok() { log.Println("Root getattr should not return error", code) out.Mode = fuse.S_IFDIR | 0755 return mount.mountInode, fuse.OK } return mount.mountInode, fuse.OK } // internalLookup executes a lookup without affecting NodeId reference counts. func (c *FileSystemConnector) internalLookup(out *fuse.Attr, parent *Inode, name string, header *fuse.InHeader) (node *Inode, code fuse.Status) { // We may already know the child because it was created using Create or Mkdir, // from an earlier lookup, or because the nodes were created in advance // (in-memory filesystems). child := parent.GetChild(name) if child != nil && child.mountPoint != nil { return c.lookupMountUpdate(out, child.mountPoint) } if child != nil { parent = nil } if child != nil { code = child.fsInode.GetAttr(out, nil, &header.Context) } else { child, code = parent.fsInode.Lookup(out, name, &header.Context) } return child, code } func (c *rawBridge) Lookup(header *fuse.InHeader, name string, out *fuse.EntryOut) (code fuse.Status) { parent := c.toInode(header.NodeId) if !parent.IsDir() { log.Printf("Lookup %q called on non-Directory node %d", name, header.NodeId) return fuse.ENOTDIR } outAttr := (*fuse.Attr)(&out.Attr) child, code := c.fsConn().internalLookup(outAttr, parent, name, header) if code == fuse.ENOENT && parent.mount.negativeEntry(out) { return fuse.OK } if !code.Ok() { return code } if child == nil { log.Println("Lookup returned fuse.OK with nil child", name) } child.mount.fillEntry(out) out.NodeId, out.Generation = c.fsConn().lookupUpdate(child) if out.Ino == 0 { out.Ino = out.NodeId } return fuse.OK } func (c *rawBridge) Forget(nodeID, nlookup uint64) { c.fsConn().forgetUpdate(nodeID, int(nlookup)) } func (c *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) (code fuse.Status) { node := c.toInode(input.NodeId) var f File if input.Flags()&fuse.FUSE_GETATTR_FH != 0 { if opened := node.mount.getOpenedFile(input.Fh()); opened != nil { f = opened.WithFlags.File } } dest := (*fuse.Attr)(&out.Attr) code = node.fsInode.GetAttr(dest, f, &input.Context) if !code.Ok() { return code } if out.Nlink == 0 { // With Nlink == 0, newer kernels will refuse link // operations. out.Nlink = 1 } node.mount.fillAttr(out, input.NodeId) return fuse.OK } func (c *rawBridge) OpenDir(input *fuse.OpenIn, out *fuse.OpenOut) (code fuse.Status) { node := c.toInode(input.NodeId) stream, err := node.fsInode.OpenDir(&input.Context) if err != fuse.OK { return err } stream = append(stream, node.getMountDirEntries()...) de := &connectorDir{ node: node.Node(), stream: append(stream, fuse.DirEntry{Mode: fuse.S_IFDIR, Name: "."}, fuse.DirEntry{Mode: fuse.S_IFDIR, Name: ".."}), rawFS: c, } h, opened := node.mount.registerFileHandle(node, de, nil, input.Flags) out.OpenFlags = opened.FuseFlags out.Fh = h return fuse.OK } func (c *rawBridge) ReadDir(input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) return opened.dir.ReadDir(input, out) } func (c *rawBridge) ReadDirPlus(input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) return opened.dir.ReadDirPlus(input, out) } func (c *rawBridge) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) { node := c.toInode(input.NodeId) f, code := node.fsInode.Open(input.Flags, &input.Context) if !code.Ok() || f == nil { return code } h, opened := node.mount.registerFileHandle(node, nil, f, input.Flags) out.OpenFlags = opened.FuseFlags out.Fh = h return fuse.OK } func (c *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse.Status) { node := c.toInode(input.NodeId) var f File if input.Valid&fuse.FATTR_FH != 0 { opened := node.mount.getOpenedFile(input.Fh) f = opened.WithFlags.File } if code.Ok() && input.Valid&fuse.FATTR_MODE != 0 { permissions := uint32(07777) & input.Mode code = node.fsInode.Chmod(f, permissions, &input.Context) } if code.Ok() && (input.Valid&(fuse.FATTR_UID|fuse.FATTR_GID) != 0) { var uid uint32 = ^uint32(0) // means "do not change" in chown(2) var gid uint32 = ^uint32(0) if input.Valid&fuse.FATTR_UID != 0 { uid = input.Uid } if input.Valid&fuse.FATTR_GID != 0 { gid = input.Gid } code = node.fsInode.Chown(f, uid, gid, &input.Context) } if code.Ok() && input.Valid&fuse.FATTR_SIZE != 0 { code = node.fsInode.Truncate(f, input.Size, &input.Context) } if code.Ok() && (input.Valid&(fuse.FATTR_ATIME|fuse.FATTR_MTIME|fuse.FATTR_ATIME_NOW|fuse.FATTR_MTIME_NOW) != 0) { now := time.Now() var atime *time.Time var mtime *time.Time if input.Valid&fuse.FATTR_ATIME != 0 { if input.Valid&fuse.FATTR_ATIME_NOW != 0 { atime = &now } else { t := time.Unix(int64(input.Atime), int64(input.Atimensec)) atime = &t } } if input.Valid&fuse.FATTR_MTIME != 0 { if input.Valid&fuse.FATTR_MTIME_NOW != 0 { mtime = &now } else { t := time.Unix(int64(input.Mtime), int64(input.Mtimensec)) mtime = &t } } code = node.fsInode.Utimens(f, atime, mtime, &input.Context) } if !code.Ok() { return code } // Must call GetAttr(); the filesystem may override some of // the changes we effect here. attr := (*fuse.Attr)(&out.Attr) code = node.fsInode.GetAttr(attr, nil, &input.Context) if code.Ok() { node.mount.fillAttr(out, input.NodeId) } return code } func (c *rawBridge) Fallocate(input *fuse.FallocateIn) (code fuse.Status) { n := c.toInode(input.NodeId) opened := n.mount.getOpenedFile(input.Fh) return n.fsInode.Fallocate(opened, input.Offset, input.Length, input.Mode, &input.Context) } func (c *rawBridge) Readlink(header *fuse.InHeader) (out []byte, code fuse.Status) { n := c.toInode(header.NodeId) return n.fsInode.Readlink(&header.Context) } func (c *rawBridge) Mknod(input *fuse.MknodIn, name string, out *fuse.EntryOut) (code fuse.Status) { parent := c.toInode(input.NodeId) child, code := parent.fsInode.Mknod(name, input.Mode, uint32(input.Rdev), &input.Context) if code.Ok() { c.childLookup(out, child, &input.Context) code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context) } return code } func (c *rawBridge) Mkdir(input *fuse.MkdirIn, name string, out *fuse.EntryOut) (code fuse.Status) { parent := c.toInode(input.NodeId) child, code := parent.fsInode.Mkdir(name, input.Mode, &input.Context) if code.Ok() { c.childLookup(out, child, &input.Context) code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context) } return code } func (c *rawBridge) Unlink(header *fuse.InHeader, name string) (code fuse.Status) { parent := c.toInode(header.NodeId) return parent.fsInode.Unlink(name, &header.Context) } func (c *rawBridge) Rmdir(header *fuse.InHeader, name string) (code fuse.Status) { parent := c.toInode(header.NodeId) return parent.fsInode.Rmdir(name, &header.Context) } func (c *rawBridge) Symlink(header *fuse.InHeader, pointedTo string, linkName string, out *fuse.EntryOut) (code fuse.Status) { parent := c.toInode(header.NodeId) child, code := parent.fsInode.Symlink(linkName, pointedTo, &header.Context) if code.Ok() { c.childLookup(out, child, &header.Context) code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &header.Context) } return code } func (c *rawBridge) Rename(input *fuse.RenameIn, oldName string, newName string) (code fuse.Status) { oldParent := c.toInode(input.NodeId) child := oldParent.GetChild(oldName) if child == nil { return fuse.ENOENT } if child.mountPoint != nil { return fuse.EBUSY } newParent := c.toInode(input.Newdir) if oldParent.mount != newParent.mount { return fuse.EXDEV } return oldParent.fsInode.Rename(oldName, newParent.fsInode, newName, &input.Context) } func (c *rawBridge) Link(input *fuse.LinkIn, name string, out *fuse.EntryOut) (code fuse.Status) { existing := c.toInode(input.Oldnodeid) parent := c.toInode(input.NodeId) if existing.mount != parent.mount { return fuse.EXDEV } child, code := parent.fsInode.Link(name, existing.fsInode, &input.Context) if code.Ok() { c.childLookup(out, child, &input.Context) code = child.fsInode.GetAttr((*fuse.Attr)(&out.Attr), nil, &input.Context) } return code } func (c *rawBridge) Access(input *fuse.AccessIn) (code fuse.Status) { n := c.toInode(input.NodeId) return n.fsInode.Access(input.Mask, &input.Context) } func (c *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) { parent := c.toInode(input.NodeId) f, child, code := parent.fsInode.Create(name, uint32(input.Flags), input.Mode, &input.Context) if !code.Ok() { return code } c.childLookup(&out.EntryOut, child, &input.Context) handle, opened := parent.mount.registerFileHandle(child, nil, f, input.Flags) out.OpenOut.OpenFlags = opened.FuseFlags out.OpenOut.Fh = handle return code } func (c *rawBridge) Release(input *fuse.ReleaseIn) { if input.Fh != 0 { node := c.toInode(input.NodeId) opened := node.mount.unregisterFileHandle(input.Fh, node) opened.WithFlags.File.Release() } } func (c *rawBridge) ReleaseDir(input *fuse.ReleaseIn) { if input.Fh != 0 { node := c.toInode(input.NodeId) node.mount.unregisterFileHandle(input.Fh, node) } } func (c *rawBridge) GetXAttrSize(header *fuse.InHeader, attribute string) (sz int, code fuse.Status) { node := c.toInode(header.NodeId) data, errno := node.fsInode.GetXAttr(attribute, &header.Context) return len(data), errno } func (c *rawBridge) GetXAttrData(header *fuse.InHeader, attribute string) (data []byte, code fuse.Status) { node := c.toInode(header.NodeId) return node.fsInode.GetXAttr(attribute, &header.Context) } func (c *rawBridge) RemoveXAttr(header *fuse.InHeader, attr string) fuse.Status { node := c.toInode(header.NodeId) return node.fsInode.RemoveXAttr(attr, &header.Context) } func (c *rawBridge) SetXAttr(input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status { node := c.toInode(input.NodeId) return node.fsInode.SetXAttr(attr, data, int(input.Flags), &input.Context) } func (c *rawBridge) ListXAttr(header *fuse.InHeader) (data []byte, code fuse.Status) { node := c.toInode(header.NodeId) attrs, code := node.fsInode.ListXAttr(&header.Context) if code != fuse.OK { return nil, code } b := bytes.NewBuffer([]byte{}) for _, v := range attrs { b.Write([]byte(v)) b.WriteByte(0) } return b.Bytes(), code } //////////////// // files. func (c *rawBridge) Write(input *fuse.WriteIn, data []byte) (written uint32, code fuse.Status) { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) var f File if opened != nil { f = opened.WithFlags.File } return node.Node().Write(f, data, int64(input.Offset), &input.Context) } func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) var f File if opened != nil { f = opened.WithFlags.File } return node.Node().Read(f, buf, int64(input.Offset), &input.Context) } func (c *rawBridge) Flock(input *fuse.FlockIn, flags int) fuse.Status { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) if opened != nil { return opened.WithFlags.File.Flock(flags) } return fuse.EBADF } func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status { node := c.toInode(header.NodeId) s := node.Node().StatFs() if s == nil { return fuse.ENOSYS } *out = *s return fuse.OK } func (c *rawBridge) Flush(input *fuse.FlushIn) fuse.Status { node := c.toInode(input.NodeId) opened := node.mount.getOpenedFile(input.Fh) if opened != nil { return opened.WithFlags.File.Flush() } return fuse.OK }