func (fs *unionFS) Rmdir(path string, context *fuse.Context) (code fuse.Status) { r := fs.getBranch(path) if r.code != fuse.OK { return r.code } if !r.attr.IsDir() { return fuse.Status(syscall.ENOTDIR) } stream, code := fs.OpenDir(path, context) found := false for _ = range stream { found = true } if found { return fuse.Status(syscall.ENOTEMPTY) } if r.branch > 0 { code = fs.putDeletion(path) return code } code = fs.fileSystems[0].Rmdir(path, context) if code != fuse.OK { return code } r = fs.branchCache.GetFresh(path).(branchResult) if r.branch > 0 { code = fs.putDeletion(path) } return code }
func (f *flipNode) GetAttr(out *fuse.Attr, file nodefs.File, c *fuse.Context) fuse.Status { select { case <-f.ok: // use a status that is easily recognizable. return fuse.Status(syscall.EXDEV) default: } return f.Node.GetAttr(out, file, c) }
func (fs *unionFS) createDeletionStore() (code fuse.Status) { writable := fs.fileSystems[0] fi, code := writable.GetAttr(fs.options.DeletionDirName, nil) if code == fuse.ENOENT { code = writable.Mkdir(fs.options.DeletionDirName, 0755, nil) if code.Ok() { fi, code = writable.GetAttr(fs.options.DeletionDirName, nil) } } if !code.Ok() || !fi.IsDir() { code = fuse.Status(syscall.EROFS) } return code }
// The isDeleted() method tells us if a path has a marker in the deletion store. // It may return an error code if the store could not be accessed. func (fs *unionFS) isDeleted(name string) (deleted bool, code fuse.Status) { marker := fs.deletionPath(name) haveCache, found := fs.deletionCache.HasEntry(filepath.Base(marker)) if haveCache { return found, fuse.OK } _, code = fs.fileSystems[0].GetAttr(marker, nil) if code == fuse.OK { return true, code } if code == fuse.ENOENT { return false, fuse.OK } log.Println("error accessing deletion marker:", marker) return false, fuse.Status(syscall.EROFS) }
func (fs *autoUnionFs) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) { comps := strings.Split(linkName, "/") if len(comps) != 2 { return fuse.EPERM } if comps[0] == _STATUS && comps[1] == _DEBUG_SETTING { fs.SetDebug(true) return fuse.OK } if comps[0] == _CONFIG { roots := fs.getRoots(pointedTo) if roots == nil { return fuse.Status(syscall.ENOTDIR) } name := comps[1] return fs.addFs(name, roots) } return fuse.EPERM }
func (fs *unionFS) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) { deleted, code := fs.isDeleted(path) if !code.Ok() { return code } if !deleted { r := fs.getBranch(path) if r.code != fuse.ENOENT { return fuse.Status(syscall.EEXIST) } } code = fs.promoteDirsTo(path) if code.Ok() { code = fs.fileSystems[0].Mkdir(path, mode, context) } if code.Ok() { fs.removeDeletion(path) attr := &fuse.Attr{ Mode: fuse.S_IFDIR | mode, } fs.branchCache.Set(path, branchResult{attr, fuse.OK, 0}) } var stream []fuse.DirEntry stream, code = fs.OpenDir(path, context) if code.Ok() { // This shouldn't happen, but let's be safe. for _, entry := range stream { fs.putDeletion(filepath.Join(path, entry.Name)) } } return code }
func (fs *unionFS) OpenDir(directory string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) { dirBranch := fs.getBranch(directory) if dirBranch.branch < 0 { return nil, fuse.ENOENT } // We could try to use the cache, but we have a delay, so // might as well get the fresh results async. var wg sync.WaitGroup var deletions map[string]bool wg.Add(1) go func() { deletions = newDirnameMap(fs.fileSystems[0], fs.options.DeletionDirName) wg.Done() }() entries := make([]map[string]uint32, len(fs.fileSystems)) for i := range fs.fileSystems { entries[i] = make(map[string]uint32) } statuses := make([]fuse.Status, len(fs.fileSystems)) for i, l := range fs.fileSystems { if i >= dirBranch.branch { wg.Add(1) go func(j int, pfs pathfs.FileSystem) { ch, s := pfs.OpenDir(directory, context) statuses[j] = s for _, v := range ch { entries[j][v.Name] = v.Mode } wg.Done() }(i, l) } } wg.Wait() if deletions == nil { _, code := fs.fileSystems[0].GetAttr(fs.options.DeletionDirName, context) if code == fuse.ENOENT { deletions = map[string]bool{} } else { return nil, fuse.Status(syscall.EROFS) } } results := entries[0] // TODO(hanwen): should we do anything with the return // statuses? for i, m := range entries { if statuses[i] != fuse.OK { continue } if i == 0 { // We don't need to further process the first // branch: it has no deleted files. continue } for k, v := range m { _, ok := results[k] if ok { continue } deleted := deletions[filePathHash(filepath.Join(directory, k))] if !deleted { results[k] = v } } } if directory == "" { delete(results, fs.options.DeletionDirName) for name, _ := range fs.hiddenFiles { delete(results, name) } } stream = make([]fuse.DirEntry, 0, len(results)) for k, v := range results { stream = append(stream, fuse.DirEntry{ Name: k, Mode: v, }) } return stream, fuse.OK }