1package inmemory 2 3import ( 4 "fmt" 5 "io" 6 "path" 7 "sort" 8 "strings" 9 "time" 10) 11 12var ( 13 errExists = fmt.Errorf("exists") 14 errNotExists = fmt.Errorf("notexists") 15 errIsNotDir = fmt.Errorf("notdir") 16 errIsDir = fmt.Errorf("isdir") 17) 18 19type node interface { 20 name() string 21 path() string 22 isdir() bool 23 modtime() time.Time 24} 25 26// dir is the central type for the memory-based storagedriver. All operations 27// are dispatched from a root dir. 28type dir struct { 29 common 30 31 // TODO(stevvooe): Use sorted slice + search. 32 children map[string]node 33} 34 35var _ node = &dir{} 36 37func (d *dir) isdir() bool { 38 return true 39} 40 41// add places the node n into dir d. 42func (d *dir) add(n node) { 43 if d.children == nil { 44 d.children = make(map[string]node) 45 } 46 47 d.children[n.name()] = n 48 d.mod = time.Now() 49} 50 51// find searches for the node, given path q in dir. If the node is found, it 52// will be returned. If the node is not found, the closet existing parent. If 53// the node is found, the returned (node).path() will match q. 54func (d *dir) find(q string) node { 55 q = strings.Trim(q, "/") 56 i := strings.Index(q, "/") 57 58 if q == "" { 59 return d 60 } 61 62 if i == 0 { 63 panic("shouldn't happen, no root paths") 64 } 65 66 var component string 67 if i < 0 { 68 // No more path components 69 component = q 70 } else { 71 component = q[:i] 72 } 73 74 child, ok := d.children[component] 75 if !ok { 76 // Node was not found. Return p and the current node. 77 return d 78 } 79 80 if child.isdir() { 81 // traverse down! 82 q = q[i+1:] 83 return child.(*dir).find(q) 84 } 85 86 return child 87} 88 89func (d *dir) list(p string) ([]string, error) { 90 n := d.find(p) 91 92 if n.path() != p { 93 return nil, errNotExists 94 } 95 96 if !n.isdir() { 97 return nil, errIsNotDir 98 } 99 100 var children []string 101 for _, child := range n.(*dir).children { 102 children = append(children, child.path()) 103 } 104 105 sort.Strings(children) 106 return children, nil 107} 108 109// mkfile or return the existing one. returns an error if it exists and is a 110// directory. Essentially, this is open or create. 111func (d *dir) mkfile(p string) (*file, error) { 112 n := d.find(p) 113 if n.path() == p { 114 if n.isdir() { 115 return nil, errIsDir 116 } 117 118 return n.(*file), nil 119 } 120 121 dirpath, filename := path.Split(p) 122 // Make any non-existent directories 123 n, err := d.mkdirs(dirpath) 124 if err != nil { 125 return nil, err 126 } 127 128 dd := n.(*dir) 129 n = &file{ 130 common: common{ 131 p: path.Join(dd.path(), filename), 132 mod: time.Now(), 133 }, 134 } 135 136 dd.add(n) 137 return n.(*file), nil 138} 139 140// mkdirs creates any missing directory entries in p and returns the result. 141func (d *dir) mkdirs(p string) (*dir, error) { 142 p = normalize(p) 143 144 n := d.find(p) 145 146 if !n.isdir() { 147 // Found something there 148 return nil, errIsNotDir 149 } 150 151 if n.path() == p { 152 return n.(*dir), nil 153 } 154 155 dd := n.(*dir) 156 157 relative := strings.Trim(strings.TrimPrefix(p, n.path()), "/") 158 159 if relative == "" { 160 return dd, nil 161 } 162 163 components := strings.Split(relative, "/") 164 for _, component := range components { 165 d, err := dd.mkdir(component) 166 167 if err != nil { 168 // This should actually never happen, since there are no children. 169 return nil, err 170 } 171 dd = d 172 } 173 174 return dd, nil 175} 176 177// mkdir creates a child directory under d with the given name. 178func (d *dir) mkdir(name string) (*dir, error) { 179 if name == "" { 180 return nil, fmt.Errorf("invalid dirname") 181 } 182 183 _, ok := d.children[name] 184 if ok { 185 return nil, errExists 186 } 187 188 child := &dir{ 189 common: common{ 190 p: path.Join(d.path(), name), 191 mod: time.Now(), 192 }, 193 } 194 d.add(child) 195 d.mod = time.Now() 196 197 return child, nil 198} 199 200func (d *dir) move(src, dst string) error { 201 dstDirname, _ := path.Split(dst) 202 203 dp, err := d.mkdirs(dstDirname) 204 if err != nil { 205 return err 206 } 207 208 srcDirname, srcFilename := path.Split(src) 209 sp := d.find(srcDirname) 210 211 if normalize(srcDirname) != normalize(sp.path()) { 212 return errNotExists 213 } 214 215 spd, ok := sp.(*dir) 216 if !ok { 217 return errIsNotDir // paranoid. 218 } 219 220 s, ok := spd.children[srcFilename] 221 if !ok { 222 return errNotExists 223 } 224 225 delete(spd.children, srcFilename) 226 227 switch n := s.(type) { 228 case *dir: 229 n.p = dst 230 case *file: 231 n.p = dst 232 } 233 234 dp.add(s) 235 236 return nil 237} 238 239func (d *dir) delete(p string) error { 240 dirname, filename := path.Split(p) 241 parent := d.find(dirname) 242 243 if normalize(dirname) != normalize(parent.path()) { 244 return errNotExists 245 } 246 247 if _, ok := parent.(*dir).children[filename]; !ok { 248 return errNotExists 249 } 250 251 delete(parent.(*dir).children, filename) 252 return nil 253} 254 255// dump outputs a primitive directory structure to stdout. 256func (d *dir) dump(indent string) { 257 fmt.Println(indent, d.name()+"/") 258 259 for _, child := range d.children { 260 if child.isdir() { 261 child.(*dir).dump(indent + "\t") 262 } else { 263 fmt.Println(indent, child.name()) 264 } 265 266 } 267} 268 269func (d *dir) String() string { 270 return fmt.Sprintf("&dir{path: %v, children: %v}", d.p, d.children) 271} 272 273// file stores actual data in the fs tree. It acts like an open, seekable file 274// where operations are conducted through ReadAt and WriteAt. Use it with 275// SectionReader for the best effect. 276type file struct { 277 common 278 data []byte 279} 280 281var _ node = &file{} 282 283func (f *file) isdir() bool { 284 return false 285} 286 287func (f *file) truncate() { 288 f.data = f.data[:0] 289} 290 291func (f *file) sectionReader(offset int64) io.Reader { 292 return io.NewSectionReader(f, offset, int64(len(f.data))-offset) 293} 294 295func (f *file) ReadAt(p []byte, offset int64) (n int, err error) { 296 return copy(p, f.data[offset:]), nil 297} 298 299func (f *file) WriteAt(p []byte, offset int64) (n int, err error) { 300 off := int(offset) 301 if cap(f.data) < off+len(p) { 302 data := make([]byte, len(f.data), off+len(p)) 303 copy(data, f.data) 304 f.data = data 305 } 306 307 f.mod = time.Now() 308 f.data = f.data[:off+len(p)] 309 310 return copy(f.data[off:off+len(p)], p), nil 311} 312 313func (f *file) String() string { 314 return fmt.Sprintf("&file{path: %q}", f.p) 315} 316 317// common provides shared fields and methods for node implementations. 318type common struct { 319 p string 320 mod time.Time 321} 322 323func (c *common) name() string { 324 _, name := path.Split(c.p) 325 return name 326} 327 328func (c *common) path() string { 329 return c.p 330} 331 332func (c *common) modtime() time.Time { 333 return c.mod 334} 335 336func normalize(p string) string { 337 return "/" + strings.Trim(p, "/") 338} 339