github.com/pbberlin/tools@v0.0.0-20160910141205-7aa5421c2169/os/fsi/dsfs/30_fs_impl.go (about) 1 package dsfs 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "strings" 8 "sync/atomic" 9 "time" 10 11 "google.golang.org/appengine/datastore" 12 13 "github.com/pbberlin/tools/os/fsi" 14 "github.com/pbberlin/tools/os/fsi/common" 15 16 aelog "google.golang.org/appengine/log" 17 ) 18 19 func (fs dsFileSys) Name() string { return "dsfs" } 20 21 func (fs dsFileSys) String() string { return fs.mount } 22 23 //--------------------------------------- 24 25 func (fs *dsFileSys) Chmod(name string, mode os.FileMode) error { 26 27 f, err := fs.fileByPath(name) 28 if err == nil { 29 f.MMode = mode 30 31 err := f.Sync() 32 if err != nil { 33 return err 34 } 35 return nil 36 } else { 37 dir, err := fs.dirByPath(name) 38 if err != nil { 39 return err 40 } 41 dir.MMode = mode 42 _, err = fs.saveDirByPathExt(dir, dir.Dir+dir.BName) 43 if err != nil { 44 return err 45 } 46 47 } 48 49 return nil 50 } 51 52 func (fs *dsFileSys) Chtimes(name string, atime time.Time, mtime time.Time) error { 53 54 f, err := fs.fileByPath(name) 55 if err == nil { 56 f.MModTime = atime 57 err := f.Sync() 58 if err != nil { 59 return err 60 } 61 return nil 62 } else { 63 dir, err := fs.dirByPath(name) 64 if err != nil { 65 return err 66 } 67 dir.MModTime = atime 68 _, err = fs.saveDirByPathExt(dir, dir.Dir+dir.BName) 69 if err != nil { 70 return err 71 } 72 } 73 74 return nil 75 76 } 77 78 // Create opens for read-write. 79 // Open opens for readonly access. 80 func (fs *dsFileSys) Create(name string) (fsi.File, error) { 81 82 // WriteFile & Create 83 dir, bname := fs.SplitX(name) 84 85 f := DsFile{} 86 f.fSys = fs 87 f.BName = common.Filify(bname) 88 f.Dir = dir 89 f.MModTime = time.Now() 90 f.MMode = 0644 91 92 // let all the properties by set by fs.saveFileByPath 93 err := f.Sync() 94 if err != nil { 95 return nil, err 96 } 97 98 // return &f, nil 99 ff := fsi.File(&f) 100 return ff, err 101 102 } 103 104 // No distinction between Stat (links are followed) 105 // and LStat (links go unresolved) 106 // We don't support links yet, anyway 107 func (fs *dsFileSys) Lstat(path string) (os.FileInfo, error) { 108 fi, err := fs.Stat(path) 109 return fi, err 110 } 111 112 // Strangely, neither MkdirAll nor Mkdir seem to have 113 // any concept of current working directory. 114 // They seem to operate relative to root. 115 func (fs *dsFileSys) Mkdir(name string, perm os.FileMode) error { 116 _, err := fs.saveDirByPath(name) 117 return err 118 } 119 120 func (fs *dsFileSys) MkdirAll(path string, perm os.FileMode) error { 121 _, err := fs.saveDirByPath(path) 122 return err 123 } 124 125 // Open() open existing file for readonly access. 126 // Create() should be used for read-write. 127 128 // We could make provisions to ensure exclusive access; 129 130 // complies with os.Open() 131 // conflicts with vfs.Open() signature 132 // conflicts with file.Open() interface of Afero 133 func (fs *dsFileSys) Open(name string) (fsi.File, error) { 134 135 // explicitly requesting directory? 136 _, bname := fs.SplitX(name) 137 if strings.HasSuffix(bname, "/") { 138 dir, err := fs.dirByPath(name) 139 if err == nil { 140 ff := fsi.File(&dir) 141 return ff, nil 142 } 143 } 144 145 // otherwise: try file, then directory 146 f, err := fs.fileByPath(name) 147 148 if err != nil && err != datastore.ErrNoSuchEntity && err != fsi.ErrRootDirNoFile { 149 return nil, err 150 } 151 if err == datastore.ErrNoSuchEntity || err == fsi.ErrRootDirNoFile { 152 // http.FileServer requires, that 153 // we return a directory here. 154 // It's also compliant to os.Open(), 155 // where "os.File" means directories too. 156 dir, err2 := fs.dirByPath(name) 157 if err2 != nil { 158 return nil, err 159 } 160 ff := fsi.File(&dir) 161 return ff, nil 162 } 163 164 atomic.StoreInt64(&f.at, 0) // why is this not nested into f.Lock()-f.Unlock()? 165 166 if f.closed == false { // already open 167 // return ErrFileInUse // instead of waiting for lock? 168 } 169 170 f.Lock() 171 f.closed = false 172 f.Unlock() 173 174 // return &f, nil 175 ff := fsi.File(&f) 176 return ff, nil 177 } 178 179 func (fs *dsFileSys) OpenFile(name string, flag int, perm os.FileMode) (fsi.File, error) { 180 return fs.Open(name) 181 } 182 183 // See fsi.FileSystem interface. 184 185 // 186 // ReadDir might not find recently added directories. 187 func (fs *dsFileSys) ReadDir(name string) ([]os.FileInfo, error) { 188 189 dirs, err := fs.dirsByPath(name) 190 // fs.Ctx().Infof("dsfs readdir %-20v dirs %v", name, len(dirs)) 191 if err != nil && err != fsi.EmptyQueryResult { 192 return nil, err 193 } 194 fs.dirsorter(dirs) 195 196 files, err := fs.filesByPath(name) 197 // fs.Ctx().Infof("dsfs readdir %-20v fils %v %v", name, len(files), err) 198 if err != nil { 199 return nil, err 200 } 201 fs.filesorter(files) 202 203 for _, v := range files { 204 dirs = append(dirs, os.FileInfo(v)) 205 } 206 return dirs, nil 207 } 208 209 func (fs *dsFileSys) Remove(name string) error { 210 211 // log.Printf("trying to remove %-20v", name) 212 f, err := fs.fileByPath(name) 213 if err == nil { 214 // log.Printf(" found file %v", f.Dir+f.BName) 215 // log.Printf(" fkey %-26v", f.Key) 216 err = datastore.Delete(fs.Ctx(), f.Key) 217 if err != nil { 218 return fmt.Errorf("error removing file %v", err) 219 } 220 } 221 222 d, err := fs.dirByPath(name) 223 if err == nil { 224 // log.Printf(" dkey %v", d.Key) 225 err = datastore.Delete(fs.Ctx(), d.Key) 226 d.MemCacheDelete() 227 if err != nil { 228 return fmt.Errorf("error removing dir %v", err) 229 } 230 } 231 232 return nil 233 234 } 235 236 func (fs *dsFileSys) RemoveAll(path string) error { 237 238 paths := []string{} 239 walkRemove := func(path string, f os.FileInfo, err error) error { 240 if err != nil { 241 // do nothing; don't break the walk 242 aelog.Errorf(fs.Ctx(), "Error walking %v => %v", path, err) 243 } else { 244 if f != nil { // && f.IsDir() to constrain 245 paths = append(paths, path) 246 } 247 } 248 return nil 249 } 250 251 err := common.Walk(fs, path, walkRemove) 252 if err != nil { 253 aelog.Errorf(fs.Ctx(), "Error removing %v => %v", path, err) 254 } 255 256 // Walk crawls directories first, files second. 257 // Intuitively removal in reverse order should always work. Or does it not? 258 for i := 0; i < len(paths); i++ { 259 iRev := len(paths) - 1 - i 260 err := fs.Remove(paths[iRev]) 261 if err != nil { 262 aelog.Errorf(fs.Ctx(), "Error removing %v => %v", paths[iRev], err) 263 return err 264 } 265 aelog.Infof(fs.Ctx(), "removed path %v", paths[iRev]) 266 } 267 268 return nil 269 } 270 271 func (fs *dsFileSys) Rename(oldname, newname string) error { 272 // we could use a walk similar to remove all 273 return fsi.NotImplemented 274 } 275 276 func (fs *dsFileSys) Stat(path string) (os.FileInfo, error) { 277 278 f, err := fs.fileByPath(path) 279 if err != nil && err != datastore.ErrNoSuchEntity && err != fsi.ErrRootDirNoFile { 280 log.Fatalf("OTHER ERROR %v", err) 281 282 return nil, err 283 } 284 if err == datastore.ErrNoSuchEntity || err == fsi.ErrRootDirNoFile { 285 // log.Printf("isno file err %-24q => %v", path, err) 286 dir, err := fs.dirByPath(path) 287 if err != nil { 288 return nil, err 289 } 290 fiDir := os.FileInfo(dir) 291 // log.Printf("Stat for dire %-24q => %-24v, %v", path, fiDir.Name(), err) 292 return fiDir, nil 293 } 294 295 fiFi := os.FileInfo(f) 296 // log.Printf("Stat for file %-24q => %-24v, %v", path, fiFi.Name(), err) 297 return fiFi, nil 298 } 299 300 func (fs *dsFileSys) ReadFile(path string) ([]byte, error) { 301 302 file, err := fs.fileByPath(path) 303 if err != nil { 304 return []byte{}, err 305 } 306 return file.Data, err 307 } 308 309 // Only one save operation required 310 func (fs *dsFileSys) WriteFile(name string, data []byte, perm os.FileMode) error { 311 312 // WriteFile & Create 313 dir, bname := fs.SplitX(name) 314 f := DsFile{} 315 f.Dir = dir 316 f.BName = common.Filify(bname) 317 f.fSys = fs 318 f.MModTime = time.Now() 319 320 var err error 321 _, err = f.Write(data) 322 if err != nil { 323 return err 324 } 325 326 err = f.Sync() 327 if err != nil { 328 return err 329 } 330 331 return nil 332 }