func TestDefaultNodeMount(t *testing.T) { dir, err := ioutil.TempDir("", "go-fuse") if err != nil { t.Fatalf("TempDir: %v", err) } defer os.RemoveAll(dir) root := nodefs.NewDefaultNode() s, conn, err := nodefs.MountRoot(dir, root, nil) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() defer s.Unmount() if err := conn.Mount(root.Inode(), "sub", nodefs.NewDefaultNode(), nil); !err.Ok() { t.Fatalf("Mount: %v", err) } if entries, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } else if len(entries) != 1 { t.Fatalf("got %d entries", len(entries)) } else if entries[0].Name() != "sub" { t.Fatalf("got %q, want %q", entries[0].Name(), "sub") } }
func TestDefaultNodeGetAttr(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) opts := &nodefs.Options{ // Note: defaultNode.GetAttr() calling file.GetAttr() is only useful if // AttrTimeout is zero. // See https://github.com/JonathonReinhart/gitlab-fuse/issues/2 Owner: fuse.CurrentOwner(), } root := nodefs.NewDefaultNode() s, _, err := nodefs.MountRoot(dir, root, opts) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() // Attach another custom node type root.Inode().NewChild("foo", false, &myNode{ Node: nodefs.NewDefaultNode(), content: []byte("success"), }) filepath := path.Join(dir, "foo") // NewDefaultNode() should provide for stat that indicates 0-byte regular file fi, err := os.Stat(filepath) if err != nil { t.Fatalf("Stat: %v", err) } if mode := (fi.Mode() & os.ModeType); mode != 0 { // Mode() & ModeType should be zero for regular files t.Fatalf("Unexpected mode: %#o", mode) } if size := fi.Size(); size != 0 { t.Fatalf("Unexpected size: %d", size) } // But when we open the file, we should get the content content, err := ioutil.ReadFile(filepath) if err != nil { t.Fatalf("ReadFile: %v", err) } if string(content) != "success" { t.Fatalf("Unexpected content: %v", content) } }
func (b *DirNode) Mkdir(name string, mode uint32, context *fuse.Context) (newNode *nodefs.Inode, code fuse.Status) { d, f, e := b.Dir.Create(name, true) if e != nil { return nil, fuse.ToStatus(e) } if d != nil { dn := &DirNode{nodefs.NewDefaultNode(), d} return b.Inode().NewChild(name, true, dn), fuse.OK } else if f != nil { fn := &FileNode{nodefs.NewDefaultNode(), f} return b.Inode().NewChild(name, false, fn), fuse.OK } return nil, fuse.ToStatus(syscall.EIO) }
func (fs *multiGitFS) newConfigNode(corresponding nodefs.Node) *configNode { return &configNode{ fs: fs, Node: nodefs.NewDefaultNode(), corresponding: corresponding, } }
func TestDefaultXAttr(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := &xattrNode{ Node: nodefs.NewDefaultNode(), } opts := nodefs.NewOptions() opts.Debug = testutil.VerboseTest() s, _, err := nodefs.MountRoot(dir, root, opts) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() var data [1024]byte sz, err := syscall.Getxattr(filepath.Join(dir, "child"), "attr", data[:]) if err != nil { t.Fatalf("Getxattr: %v", err) } else if val := string(data[:sz]); val != "value" { t.Fatalf("got %v, want 'value'", val) } }
func TestMountOnExisting(t *testing.T) { ts := NewTestCase(t) defer ts.Cleanup() err := os.Mkdir(ts.mnt+"/mnt", 0777) if err != nil { t.Fatalf("Mkdir failed: %v", err) } nfs := nodefs.NewDefaultNode() code := ts.connector.Mount(ts.rootNode(), "mnt", nfs, nil) if code != fuse.EBUSY { t.Fatal("expect EBUSY:", code) } err = os.Remove(ts.mnt + "/mnt") if err != nil { t.Fatalf("Remove failed: %v", err) } code = ts.connector.Mount(ts.rootNode(), "mnt", nfs, nil) if !code.Ok() { t.Fatal("expect OK:", code) } code = ts.pathFs.Unmount("mnt") if !code.Ok() { t.Errorf("Unmount failed: %v", code) } }
func TestLiveness(t *testing.T) { dir := testutil.TempDir() defer os.RemoveAll(dir) root := nodefs.NewDefaultNode() s, _, err := nodefs.MountRoot(dir, root, nil) if err != nil { t.Fatalf("MountRoot: %v", err) } go s.Serve() if err := s.WaitMount(); err != nil { t.Fatal("WaitMount", err) } defer s.Unmount() if _, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } // We previously encountered a sitation where a finalizer would close our fd out from under us. Try to force both finalizers to run and object destruction to complete. runtime.GC() runtime.GC() if _, err := ioutil.ReadDir(dir); err != nil { t.Fatalf("ReadDir: %v", err) } }
// DeviceFs is a simple filesystem interface to an MTP device. It // should be wrapped in a Locking(Raw)FileSystem to make sure it is // threadsafe. The file system assumes the device does not touch the // storage. Arguments are the opened mtp device and a directory for the // backing store. func NewDeviceFSRoot(d *mtp.Device, storages []uint32, options DeviceFsOptions) (nodefs.Node, error) { root := rootNode{Node: nodefs.NewDefaultNode()} fs := &deviceFS{ root: &root, dev: d, options: &options, } root.fs = fs fs.storages = storages if err := d.GetDeviceInfo(&fs.devInfo); err != nil { return nil, err } if !strings.Contains(fs.devInfo.MTPExtension, "android.com") { fs.options.Android = false } if !options.Android { if err := fs.setupClassic(); err != nil { return nil, err } } fs.mungeVfat = make(map[uint32]bool) for _, sid := range fs.storages { var info mtp.StorageInfo if err := fs.dev.GetStorageInfo(sid, &info); err != nil { return nil, err } fs.mungeVfat[sid] = info.IsRemovable() && fs.options.RemovableVFat } return fs.Root(), nil }
func (fs *P4Fs) newFolder(path string, change int) *p4Folder { return &p4Folder{ Node: nodefs.NewDefaultNode(), fs: fs, path: path, change: change, } }
func NewMemTreeFs(files map[string]MemFile) *MemTreeFs { fs := &MemTreeFs{ root: &memNode{Node: nodefs.NewDefaultNode()}, files: files, } fs.root.fs = fs return fs }
func (t *treeFS) newDirNode(id *git.Oid) nodefs.Node { n := &dirNode{ gitNode: gitNode{ fs: t, id: id.Copy(), Node: nodefs.NewDefaultNode(), }, } return n }
func (me *MemUnionFs) newNode(isdir bool) *memNode { n := &memNode{ Node: nodefs.NewDefaultNode(), fs: me, mutex: &me.mutex, } now := time.Now() n.info.SetTimes(&now, &now, &now) return n }
func (bn *BoardNode) NodeFromName(name string) (nodefs.Node, error) { t := bn.LookupByTitle(name) if t == nil { log.Println("could not open thrnode", name) return nil, errors.New("could not open threads") } return &ThreadNode{ Node: nodefs.NewDefaultNode(), thread: t}, nil }
func (n *folderNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, *nodefs.Inode, fuse.Status) { if !n.fetch() { return nil, nil, fuse.EIO } obj := mtp.ObjectInfo{ StorageID: n.StorageID(), Filename: name, ObjectFormat: mtp.OFC_Undefined, ModificationDate: time.Now(), ParentObject: n.Handle(), CompressedSize: 0, } var file nodefs.File var fsNode nodefs.Node if n.fs.options.Android { _, _, handle, err := n.fs.dev.SendObjectInfo(n.StorageID(), n.Handle(), &obj) if err != nil { log.Println("SendObjectInfo failed", err) return nil, nil, fuse.EIO } err = n.fs.dev.SendObject(&bytes.Buffer{}, 0) if err != nil { log.Println("SendObject failed:", err) return nil, nil, fuse.EIO } aNode := &androidNode{ mtpNodeImpl: mtpNodeImpl{ Node: nodefs.NewDefaultNode(), obj: &obj, fs: n.fs, handle: handle, }, } if !aNode.startEdit() { return nil, nil, fuse.EIO } file = &androidFile{ File: nodefs.NewDefaultFile(), node: aNode, } fsNode = aNode } else { var err error file, fsNode, err = n.fs.createClassicFile(obj) if err != nil { return nil, nil, fuse.ToStatus(err) } } return file, n.Inode().NewChild(name, false, fsNode), fuse.OK }
func (fs *DeviceFs) newFolder(obj mtp.ObjectInfo, h uint32) *folderNode { obj.AssociationType = mtp.OFC_Association return &folderNode{ mtpNodeImpl: mtpNodeImpl{ Node: nodefs.NewDefaultNode(), handle: h, obj: &obj, fs: fs, }, } }
// Creates a new P4FS func NewP4FSRoot(conn *p4.Conn, backingDir string) nodefs.Node { fs := &P4Fs{ p4: conn, } fs.backingDir = backingDir fs.root = &p4Root{ Node: nodefs.NewDefaultNode(), fs: fs, } return fs.root }
func (b *DirNode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) { { c := b.Inode().GetChild(name) if c != nil { return c, fuse.OK } } d, f, e := b.Dir.Lookup(name) if e != nil { return nil, fuse.ToStatus(e) } if d != nil { dn := &DirNode{nodefs.NewDefaultNode(), d} dn.GetAttr(out, nil, context) return b.Inode().NewChild(name, true, dn), fuse.OK } else if f != nil { fn := &FileNode{nodefs.NewDefaultNode(), f} fn.GetAttr(out, nil, context) return b.Inode().NewChild(name, false, fn), fuse.OK } return nil, fuse.ToStatus(syscall.EIO) }
func newDirNode(o *fuse.Owner, t *time.Time, nm NodeMaker) (dn *DirNode) { if nm == nil { return nil } if o == nil { o = getDefaultOwner() } if t == nil { now := time.Now() t = &now } return &DirNode{ Node: nodefs.NewDefaultNode(), NodeMaker: nm, time: *t, owner: *o} }
func (t *treeFS) newLinkNode(id *git.Oid) (nodefs.Node, error) { n := &linkNode{ gitNode: gitNode{ fs: t, id: id.Copy(), Node: nodefs.NewDefaultNode(), }, } blob, err := t.repo.LookupBlob(id) if err != nil { return nil, err } defer blob.Free() n.target = append([]byte{}, blob.Contents()...) return n, nil }
func (b *DirNode) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, child *nodefs.Inode, code fuse.Status) { d, f, e := b.Dir.Lookup(name) if e != nil { d, f, e = b.Dir.Create(name, false) } if e != nil { return nil, nil, fuse.ToStatus(e) } if d != nil { d.Dispose() } else if f != nil { fn := &FileNode{nodefs.NewDefaultNode(), f} fobj, _ := fn.Open(flags, context) return fobj, b.Inode().NewChild(name, false, fn), fuse.OK } return nil, nil, fuse.ToStatus(syscall.EIO) panic("") }
func (n *MemTreeFs) addFile(name string, f MemFile) { comps := strings.Split(name, "/") node := n.root.Inode() for i, c := range comps { child := node.GetChild(c) if child == nil { fsnode := &memNode{Node: nodefs.NewDefaultNode()} if i == len(comps)-1 { fsnode.file = f } child = node.New(fsnode.file == nil, fsnode) node.AddChild(c, child) } node = child } }
func (fs *deviceFS) createClassicFile(obj mtp.ObjectInfo) (file nodefs.File, node nodefs.Node, err error) { backingFile, err := ioutil.TempFile(fs.options.Dir, "") cl := &classicNode{ mtpNodeImpl: mtpNodeImpl{ Node: nodefs.NewDefaultNode(), obj: &obj, fs: fs, }, dirty: true, backing: backingFile.Name(), } file = &pendingFile{ loopback: nodefs.NewLoopbackFile(backingFile), node: cl, File: nodefs.NewDefaultFile(), } node = cl return }
func (m *MNode) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) { m.obj.Lock() defer m.obj.Unlock() { c := m.Inode().GetChild(name) if c!=nil { return c,fuse.OK } } d,ok := m.obj.Obj.(*joinf.Directory) if !ok { return nil,fuse.ENOTDIR } id,e := d.Find(name) if e!=nil { return nil,fuse.ToStatus(e) } co := m.lm.Load(id) nin := &MNode{nodefs.NewDefaultNode(),m.lm,co} code := nin.GetAttr(out,nil,context) if code!=fuse.OK { return nil,code } _,isDir := co.Obj.(*joinf.Directory) return m.Inode().NewChild(name,isDir,nin),fuse.OK }
func (r *manifestFSRoot) addRepo(project *manifest.Project) { node, components := r.fsConn.Node(r.Inode(), project.Path) if len(components) == 0 { log.Fatalf("huh %v", *project) } last := len(components) - 1 for _, c := range components[:last] { node = node.NewChild(c, true, nodefs.NewDefaultNode()) } rootNode := r.repoMap[project.Name] if rootNode == nil { panic(project.Name) } if code := r.fsConn.Mount(node, components[last], rootNode, nil); !code.Ok() { // TODO - this cannot happen if the manifest // is well formed, but should check that in a // place where we can return error. log.Printf("Mount: %v - %v", project, code) } }
func (m *MNode) makeKnot(name string,isDir bool) (*nodefs.Inode,*MNode,fuse.Status) { m.obj.Lock() defer m.obj.Unlock() d,ok := m.obj.Obj.(*joinf.Directory) if !ok { return nil,nil,fuse.ENOTDIR } { c := m.Inode().GetChild(name) if c!=nil { return nil,nil,fuse.Status(syscall.EEXIST) } id,_ := d.Find(name) if id!="" { return nil,nil,fuse.Status(syscall.EEXIST) } } id := uuid.NewV4().String() if isDir { r := m.lm.Dirs(id) e := joinf.CreateDir(r) if e!=nil { return nil,nil,fuse.ToStatus(e) } e = d.Insert(name,id) if e!=nil { r.Delete() return nil,nil,fuse.ToStatus(e) } r.Dispose() }else{ r := m.lm.Files(id) e := joinf.CreateFile(r) if e!=nil { return nil,nil,fuse.ToStatus(e) } e = d.Insert(name,id) if e!=nil { r.Delete() return nil,nil,fuse.ToStatus(e) } r.Dispose() } co := m.lm.Load(id) nin := &MNode{nodefs.NewDefaultNode(),m.lm,co} return m.Inode().NewChild(name,isDir,nin),nin,fuse.OK }
func (t *treeFS) newBlobNode(id *git.Oid, mode git.Filemode) (nodefs.Node, error) { n := &blobNode{ gitNode: gitNode{ fs: t, id: id.Copy(), Node: nodefs.NewDefaultNode(), }, } odb, err := t.repo.Odb() if err != nil { return nil, err } defer odb.Free() sz, _, err := odb.ReadHeader(id) if err != nil { return nil, err } n.size = sz n.mode = mode return n, nil }
func (fs *DeviceFs) newFile(obj mtp.ObjectInfo, size int64, id uint32) (node nodefs.Node) { if obj.CompressedSize != 0xFFFFFFFF { size = int64(obj.CompressedSize) } mNode := mtpNodeImpl{ Node: nodefs.NewDefaultNode(), obj: &obj, handle: id, fs: fs, Size: size, } if fs.options.Android { node = &androidNode{ mtpNodeImpl: mNode, } } else { node = &classicNode{ mtpNodeImpl: mNode, } } return node }
func NewMNode(lm *FSMan,id string) *MNode{ return &MNode{nodefs.NewDefaultNode(),lm,lm.Load(id)} }
func (fs *P4Fs) newP4Link() *p4Link { return &p4Link{ Node: nodefs.NewDefaultNode(), fs: fs, } }
func (fs *P4Fs) newFile(st *p4.Stat) *p4File { f := &p4File{Node: nodefs.NewDefaultNode(), fs: fs, stat: *st} return f }