overlay.go (6831B)
1 package vfs 2 3 import ( 4 "errors" 5 "path" 6 "sort" 7 "strings" 8 ) 9 10 // Writable layer for the shell. The generated tree (Build) stays read-only and 11 // shared; each shell session keeps an Overlay of what it created, changed or 12 // deleted, and FS replays it over a private clone of the base on every 13 // command. The overlay lives in memory only (an SSH session loses it on 14 // disconnect) and is capped so a public session cannot grow without bound. 15 16 const ( 17 // OverlayQuota caps the bytes (paths + contents) one session may write. 18 OverlayQuota = 1 << 20 19 // OverlayMaxEntries caps how many paths one session may touch. 20 OverlayMaxEntries = 4096 21 ) 22 23 // Errors carry the text a shell prints after "cmd: 'path': ". 24 var ( 25 ErrNotExist = errors.New("No such file or directory") 26 ErrNotDir = errors.New("Not a directory") 27 ErrIsDir = errors.New("Is a directory") 28 ErrExist = errors.New("File exists") 29 ErrNotEmpty = errors.New("Directory not empty") 30 ErrPermission = errors.New("Permission denied") 31 ErrNoSpace = errors.New("No space left on device") 32 ) 33 34 // overlayEntry is one changed path. A nil *overlayEntry in the map is a 35 // whiteout (deleted base node). fresh marks a directory that replaces the 36 // base directory at the same path instead of merging with it. 37 type overlayEntry struct { 38 dir bool 39 fresh bool 40 content string 41 } 42 43 func (e *overlayEntry) size(p string) int { 44 if e == nil { 45 return len(p) 46 } 47 return len(p) + len(e.content) 48 } 49 50 // Overlay is a session's set of changes, keyed by absolute path. 51 type Overlay struct { 52 entries map[string]*overlayEntry 53 used int 54 } 55 56 // NewOverlay returns an empty overlay. 57 func NewOverlay() *Overlay { 58 return &Overlay{entries: map[string]*overlayEntry{}} 59 } 60 61 // Changes is the number of paths the overlay touches. 62 func (o *Overlay) Changes() int { return len(o.entries) } 63 64 // Used is the bytes counted against OverlayQuota. 65 func (o *Overlay) Used() int { return o.used } 66 67 // Reset drops every change. 68 func (o *Overlay) Reset() { 69 o.entries = map[string]*overlayEntry{} 70 o.used = 0 71 } 72 73 func (o *Overlay) set(p string, e *overlayEntry) error { 74 old, had := o.entries[p] 75 next := o.used + e.size(p) 76 if had { 77 next -= old.size(p) 78 } 79 if next > OverlayQuota || (!had && len(o.entries) >= OverlayMaxEntries) { 80 return ErrNoSpace 81 } 82 o.entries[p] = e 83 o.used = next 84 return nil 85 } 86 87 func (o *Overlay) drop(p string) { 88 if e, ok := o.entries[p]; ok { 89 o.used -= e.size(p) 90 delete(o.entries, p) 91 } 92 } 93 94 // Clone deep-copies a node. 95 func (n *Node) Clone() *Node { 96 c := &Node{Name: n.Name, Dir: n.Dir, Content: n.Content} 97 if n.Dir { 98 c.Children = make(map[string]*Node, len(n.Children)) 99 for k, v := range n.Children { 100 c.Children[k] = v.Clone() 101 } 102 } 103 return c 104 } 105 106 // FS is a writable view: a private clone of base with the overlay applied. 107 // Mutations update both the view and the overlay; base is never touched. 108 type FS struct { 109 base *Node 110 root *Node 111 home string 112 ov *Overlay 113 } 114 115 // NewFS clones base and replays ov over it. home (and /tmp) are writable. 116 func NewFS(base *Node, home string, ov *Overlay) *FS { 117 if ov == nil { 118 ov = NewOverlay() 119 } 120 f := &FS{base: base, root: base.Clone(), home: home, ov: ov} 121 f.apply() 122 return f 123 } 124 125 func depth(p string) int { return strings.Count(p, "/") } 126 127 func (f *FS) apply() { 128 keys := make([]string, 0, len(f.ov.entries)) 129 for k := range f.ov.entries { 130 keys = append(keys, k) 131 } 132 sort.Slice(keys, func(i, j int) bool { 133 if di, dj := depth(keys[i]), depth(keys[j]); di != dj { 134 return di < dj 135 } 136 return keys[i] < keys[j] 137 }) 138 for _, k := range keys { 139 parent := f.root.Lookup(path.Dir(k)) 140 if parent == nil || !parent.Dir { 141 continue 142 } 143 name := path.Base(k) 144 e := f.ov.entries[k] 145 switch { 146 case e == nil: 147 delete(parent.Children, name) 148 case e.dir: 149 if cur, ok := parent.Children[name]; e.fresh || !ok || !cur.Dir { 150 parent.Children[name] = NewDir(name) 151 } 152 default: 153 parent.AddFile(name, e.content) 154 } 155 } 156 } 157 158 // Root is the merged tree. Treat it as read-only; mutate through FS. 159 func (f *FS) Root() *Node { return f.root } 160 161 // Get looks up an absolute path in the merged tree. 162 func (f *FS) Get(abs string) *Node { return f.root.Lookup(abs) } 163 164 func inside(abs, base string) bool { 165 return abs == base || strings.HasPrefix(abs, strings.TrimSuffix(base, "/")+"/") 166 } 167 168 // Writable: the session owns its home and /tmp; the rest belongs to root. 169 func (f *FS) Writable(abs string) bool { 170 return inside(abs, f.home) || inside(abs, "/tmp") 171 } 172 173 // CheckCreate reports why abs cannot be created, replaced or removed. 174 func (f *FS) CheckCreate(abs string) error { 175 if abs == "/" { 176 return ErrPermission 177 } 178 parent := f.Get(path.Dir(abs)) 179 switch { 180 case parent == nil: 181 return ErrNotExist 182 case !parent.Dir: 183 return ErrNotDir 184 case !f.Writable(path.Dir(abs)) || !f.Writable(abs): 185 return ErrPermission 186 } 187 return nil 188 } 189 190 func (f *FS) inBase(abs string) bool { return f.base.Lookup(abs) != nil } 191 192 // Mkdir creates (or replaces) a directory at abs. Call CheckCreate first. 193 func (f *FS) Mkdir(abs string) error { 194 parent := f.Get(path.Dir(abs)) 195 if parent == nil || !parent.Dir { 196 return ErrNotExist 197 } 198 if err := f.ov.set(abs, &overlayEntry{dir: true, fresh: f.inBase(abs)}); err != nil { 199 return err 200 } 201 name := path.Base(abs) 202 parent.Children[name] = NewDir(name) 203 return nil 204 } 205 206 // Write creates or replaces the file at abs. Call CheckCreate first. 207 func (f *FS) Write(abs, content string) error { 208 parent := f.Get(path.Dir(abs)) 209 if parent == nil || !parent.Dir { 210 return ErrNotExist 211 } 212 if err := f.ov.set(abs, &overlayEntry{content: content}); err != nil { 213 return err 214 } 215 parent.AddFile(path.Base(abs), content) 216 return nil 217 } 218 219 // Remove deletes abs and everything under it. Call CheckCreate first. 220 func (f *FS) Remove(abs string) error { 221 parent := f.Get(path.Dir(abs)) 222 if parent == nil || !parent.Dir { 223 return ErrNotExist 224 } 225 prefix := abs + "/" 226 for k := range f.ov.entries { 227 if strings.HasPrefix(k, prefix) { 228 f.ov.drop(k) 229 } 230 } 231 if f.inBase(abs) { 232 if err := f.ov.set(abs, nil); err != nil { 233 return err 234 } 235 } else { 236 f.ov.drop(abs) 237 } 238 delete(parent.Children, path.Base(abs)) 239 return nil 240 } 241 242 // Copy materializes n (a whole subtree for a directory) at abs. 243 func (f *FS) Copy(n *Node, abs string) error { 244 n = n.Clone() // n may live under abs's old subtree 245 if f.Get(abs) != nil { 246 if err := f.Remove(abs); err != nil { 247 return err 248 } 249 } 250 if !n.Dir { 251 return f.Write(abs, n.Content) 252 } 253 if err := f.Mkdir(abs); err != nil { 254 return err 255 } 256 for _, c := range n.SortedChildren() { 257 if err := f.Copy(c, path.Join(abs, c.Name)); err != nil { 258 return err 259 } 260 } 261 return nil 262 } 263 264 // Count is the number of nodes in n's subtree, n included. 265 func Count(n *Node) int { 266 if !n.Dir { 267 return 1 268 } 269 total := 1 270 for _, c := range n.Children { 271 total += Count(c) 272 } 273 return total 274 }