1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
13 type CustomFileSystem interface {
15 MountByID(mount string)
16 MountProject(mount, uuid string)
17 MountUsers(mount string)
20 type customFileSystem struct {
24 staleThreshold time.Time
28 func (c *Client) CustomFileSystem(kc keepClient) CustomFileSystem {
30 fs := &customFileSystem{
32 fileSystem: fileSystem{
33 fsBackend: keepBackend{apiClient: c, keepClient: kc},
37 root.inode = &treenode{
42 mode: os.ModeDir | 0755,
45 inodes: make(map[string]inode),
50 func (fs *customFileSystem) MountByID(mount string) {
51 fs.root.inode.Child(mount, func(inode) (inode, error) {
56 inodes: make(map[string]inode),
60 mode: 0755 | os.ModeDir,
63 create: fs.mountCollection,
68 func (fs *customFileSystem) MountProject(mount, uuid string) {
69 fs.root.inode.Child(mount, func(inode) (inode, error) {
70 return fs.newProjectNode(fs.root, mount, uuid), nil
74 func (fs *customFileSystem) MountUsers(mount string) {
75 fs.root.inode.Child(mount, func(inode) (inode, error) {
80 inodes: make(map[string]inode),
84 mode: 0755 | os.ModeDir,
91 // SiteFileSystem returns a FileSystem that maps collections and other
92 // Arvados objects onto a filesystem layout.
94 // This is experimental: the filesystem layout is not stable, and
95 // there are significant known bugs and shortcomings. For example,
96 // writes are not persisted until Sync() is called.
97 func (c *Client) SiteFileSystem(kc keepClient) CustomFileSystem {
98 fs := c.CustomFileSystem(kc)
100 fs.MountUsers("users")
104 func (fs *customFileSystem) Sync() error {
106 defer fs.staleLock.Unlock()
107 fs.staleThreshold = time.Now()
111 // Stale returns true if information obtained at time t should be
113 func (fs *customFileSystem) Stale(t time.Time) bool {
115 defer fs.staleLock.Unlock()
116 return !fs.staleThreshold.Before(t)
119 func (fs *customFileSystem) newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error) {
120 return nil, ErrInvalidOperation
123 func (fs *customFileSystem) mountCollection(parent inode, id string) inode {
125 err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+id, nil, nil)
129 cfs, err := coll.FileSystem(fs, fs)
133 root := cfs.rootnode()
134 root.SetParent(parent, id)
138 func (fs *customFileSystem) newProjectNode(root inode, name, uuid string) inode {
144 inodes: make(map[string]inode),
148 mode: 0755 | os.ModeDir,
154 func (fs *customFileSystem) newUserNode(root inode, name, uuid string) inode {
160 inodes: make(map[string]inode),
164 mode: 0755 | os.ModeDir,
170 // vdirnode wraps an inode by ignoring any requests to add/replace
171 // children, and calling a create() func when a non-existing child is
174 // create() can return either a new node, which will be added to the
175 // treenode, or nil for ENOENT.
176 type vdirnode struct {
178 create func(parent inode, name string) inode
181 func (vn *vdirnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
182 return vn.inode.Child(name, func(existing inode) (inode, error) {
183 if existing == nil && vn.create != nil {
184 existing = vn.create(vn, name)
186 existing.SetParent(vn, name)
187 vn.inode.(*treenode).fileinfo.modTime = time.Now()
192 } else if tryRepl, err := replace(existing); err != nil {
194 } else if tryRepl != existing {
195 return existing, ErrInvalidArgument