13111: Fix nil pointer dereference at sitefs root.
[arvados.git] / sdk / go / arvados / fs_site.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "os"
9         "time"
10 )
11
12 type siteFileSystem struct {
13         fileSystem
14 }
15
16 // SiteFileSystem returns a FileSystem that maps collections and other
17 // Arvados objects onto a filesystem layout.
18 //
19 // This is experimental: the filesystem layout is not stable, and
20 // there are significant known bugs and shortcomings. For example,
21 // writes are not persisted until Sync() is called.
22 func (c *Client) SiteFileSystem(kc keepClient) FileSystem {
23         root := &vdirnode{}
24         fs := &siteFileSystem{
25                 fileSystem: fileSystem{
26                         fsBackend: keepBackend{apiClient: c, keepClient: kc},
27                         root:      root,
28                 },
29         }
30         root.inode = &treenode{
31                 fs:     fs,
32                 parent: root,
33                 fileinfo: fileinfo{
34                         name:    "/",
35                         mode:    os.ModeDir | 0755,
36                         modTime: time.Now(),
37                 },
38                 inodes: make(map[string]inode),
39         }
40         root.inode.Child("by_id", func(inode) inode {
41                 var vn inode
42                 vn = &vdirnode{
43                         inode: &treenode{
44                                 fs:     fs,
45                                 parent: fs.root,
46                                 inodes: make(map[string]inode),
47                                 fileinfo: fileinfo{
48                                         name:    "by_id",
49                                         modTime: time.Now(),
50                                         mode:    0755 | os.ModeDir,
51                                 },
52                         },
53                         create: fs.mountCollection,
54                 }
55                 return vn
56         })
57         return fs
58 }
59
60 func (fs *siteFileSystem) newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error) {
61         return nil, ErrInvalidOperation
62 }
63
64 func (fs *siteFileSystem) mountCollection(parent inode, id string) inode {
65         var coll Collection
66         err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+id, nil, nil)
67         if err != nil {
68                 return nil
69         }
70         cfs, err := coll.FileSystem(fs, fs)
71         if err != nil {
72                 return nil
73         }
74         root := cfs.rootnode()
75         root.SetParent(parent, id)
76         return root
77 }
78
79 // vdirnode wraps an inode by ignoring any requests to add/replace
80 // children, and calling a create() func when a non-existing child is
81 // looked up.
82 //
83 // create() can return either a new node, which will be added to the
84 // treenode, or nil for ENOENT.
85 type vdirnode struct {
86         inode
87         create func(parent inode, name string) inode
88 }
89
90 func (vn *vdirnode) Child(name string, _ func(inode) inode) inode {
91         return vn.inode.Child(name, func(existing inode) inode {
92                 if existing != nil {
93                         return existing
94                 } else if vn.create == nil {
95                         return nil
96                 }
97                 n := vn.create(vn, name)
98                 if n != nil {
99                         n.SetParent(vn, name)
100                         vn.inode.(*treenode).fileinfo.modTime = time.Now()
101                 }
102                 return n
103         })
104 }