19088: Export collection/project properties as bucket-level tags.
[arvados.git] / sdk / go / arvados / fs_deferred.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         "log"
9         "os"
10         "sync"
11         "time"
12 )
13
14 func deferredCollectionFS(fs FileSystem, parent inode, coll Collection) inode {
15         modTime := coll.ModifiedAt
16         if modTime.IsZero() {
17                 modTime = time.Now()
18         }
19         placeholder := &treenode{
20                 fs:     fs,
21                 parent: parent,
22                 inodes: nil,
23                 fileinfo: fileinfo{
24                         name:    coll.Name,
25                         modTime: modTime,
26                         mode:    0755 | os.ModeDir,
27                         sys:     func() interface{} { return &coll },
28                 },
29         }
30         return &deferrednode{wrapped: placeholder, create: func() inode {
31                 err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+coll.UUID, nil, nil)
32                 if err != nil {
33                         log.Printf("BUG: unhandled error: %s", err)
34                         return placeholder
35                 }
36                 newfs, err := coll.FileSystem(fs, fs)
37                 if err != nil {
38                         log.Printf("BUG: unhandled error: %s", err)
39                         return placeholder
40                 }
41                 cfs := newfs.(*collectionFileSystem)
42                 cfs.SetParent(parent, coll.Name)
43                 return cfs
44         }}
45 }
46
47 // A deferrednode wraps an inode that's expensive to build. Initially,
48 // it responds to basic directory functions by proxying to the given
49 // placeholder. If a caller uses a read/write/lock operation,
50 // deferrednode calls the create() func to create the real inode, and
51 // proxies to the real inode from then on.
52 //
53 // In practice, this means a deferrednode's parent's directory listing
54 // can be generated using only the placeholder, instead of waiting for
55 // create().
56 type deferrednode struct {
57         wrapped inode
58         create  func() inode
59         mtx     sync.Mutex
60         created bool
61 }
62
63 func (dn *deferrednode) realinode() inode {
64         dn.mtx.Lock()
65         defer dn.mtx.Unlock()
66         if !dn.created {
67                 dn.wrapped = dn.create()
68                 dn.created = true
69         }
70         return dn.wrapped
71 }
72
73 func (dn *deferrednode) currentinode() inode {
74         dn.mtx.Lock()
75         defer dn.mtx.Unlock()
76         return dn.wrapped
77 }
78
79 func (dn *deferrednode) Read(p []byte, pos filenodePtr) (int, filenodePtr, error) {
80         return dn.realinode().Read(p, pos)
81 }
82
83 func (dn *deferrednode) Write(p []byte, pos filenodePtr) (int, filenodePtr, error) {
84         return dn.realinode().Write(p, pos)
85 }
86
87 func (dn *deferrednode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
88         return dn.realinode().Child(name, replace)
89 }
90
91 // Sync is a no-op if the real inode hasn't even been created yet.
92 func (dn *deferrednode) Sync() error {
93         dn.mtx.Lock()
94         defer dn.mtx.Unlock()
95         if !dn.created {
96                 return nil
97         } else if syncer, ok := dn.wrapped.(syncer); ok {
98                 return syncer.Sync()
99         } else {
100                 return ErrInvalidOperation
101         }
102 }
103
104 func (dn *deferrednode) Truncate(size int64) error       { return dn.realinode().Truncate(size) }
105 func (dn *deferrednode) SetParent(p inode, name string)  { dn.realinode().SetParent(p, name) }
106 func (dn *deferrednode) IsDir() bool                     { return dn.currentinode().IsDir() }
107 func (dn *deferrednode) Readdir() ([]os.FileInfo, error) { return dn.realinode().Readdir() }
108 func (dn *deferrednode) Size() int64                     { return dn.currentinode().Size() }
109 func (dn *deferrednode) FileInfo() os.FileInfo           { return dn.currentinode().FileInfo() }
110 func (dn *deferrednode) Lock()                           { dn.realinode().Lock() }
111 func (dn *deferrednode) Unlock()                         { dn.realinode().Unlock() }
112 func (dn *deferrednode) RLock()                          { dn.realinode().RLock() }
113 func (dn *deferrednode) RUnlock()                        { dn.realinode().RUnlock() }
114 func (dn *deferrednode) FS() FileSystem                  { return dn.currentinode().FS() }
115 func (dn *deferrednode) Parent() inode                   { return dn.currentinode().Parent() }
116 func (dn *deferrednode) MemorySize() int64               { return dn.currentinode().MemorySize() }
117 func (dn *deferrednode) Snapshot() (inode, error)        { return dn.realinode().Snapshot() }
118 func (dn *deferrednode) Splice(repl inode) error         { return dn.realinode().Splice(repl) }