Example #1
0
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
}
Example #2
0
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
}
Example #3
0
// 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)
}
Example #4
0
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
}
Example #5
0
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
}
Example #6
0
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 fuse.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
}