github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/localdisk/localdisk.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 Package localdisk registers the "filesystem" blobserver storage type, 19 storing blobs in a forest of sharded directories at the specified root. 20 21 Example low-level config: 22 23 "/storage/": { 24 "handler": "storage-filesystem", 25 "handlerArgs": { 26 "path": "/var/camlistore/blobs" 27 } 28 }, 29 30 */ 31 package localdisk 32 33 import ( 34 "fmt" 35 "io" 36 "os" 37 "path/filepath" 38 "sync" 39 40 "camlistore.org/pkg/blob" 41 "camlistore.org/pkg/blobserver" 42 "camlistore.org/pkg/blobserver/local" 43 "camlistore.org/pkg/jsonconfig" 44 "camlistore.org/pkg/osutil" 45 "camlistore.org/pkg/types" 46 ) 47 48 // DiskStorage implements the blobserver.Storage interface using the 49 // local filesystem. 50 type DiskStorage struct { 51 root string 52 53 // dirLockMu must be held for writing when deleting an empty directory 54 // and for read when receiving blobs. 55 dirLockMu *sync.RWMutex 56 57 // gen will be nil if partition != "" 58 gen *local.Generationer 59 } 60 61 func (ds *DiskStorage) String() string { 62 return fmt.Sprintf("\"filesystem\" file-per-blob at %s", ds.root) 63 } 64 65 // IsDir reports whether root is a localdisk (file-per-blob) storage directory. 66 func IsDir(root string) (bool, error) { 67 if osutil.DirExists(filepath.Join(root, blob.RefFromString("").HashName())) { 68 return true, nil 69 } 70 return false, nil 71 } 72 73 // New returns a new local disk storage implementation at the provided 74 // root directory, which must already exist. 75 func New(root string) (*DiskStorage, error) { 76 // Local disk. 77 fi, err := os.Stat(root) 78 if os.IsNotExist(err) { 79 return nil, fmt.Errorf("Storage root %q doesn't exist", root) 80 } 81 if err != nil { 82 return nil, fmt.Errorf("Failed to stat directory %q: %v", root, err) 83 } 84 if !fi.IsDir() { 85 return nil, fmt.Errorf("Storage root %q exists but is not a directory.", root) 86 } 87 ds := &DiskStorage{ 88 root: root, 89 dirLockMu: new(sync.RWMutex), 90 gen: local.NewGenerationer(root), 91 } 92 if err := ds.migrate3to2(); err != nil { 93 return nil, fmt.Errorf("Error updating localdisk format: %v", err) 94 } 95 if _, _, err := ds.StorageGeneration(); err != nil { 96 return nil, fmt.Errorf("Error initialization generation for %q: %v", root, err) 97 } 98 return ds, nil 99 } 100 101 func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (storage blobserver.Storage, err error) { 102 path := config.RequiredString("path") 103 if err := config.Validate(); err != nil { 104 return nil, err 105 } 106 return New(path) 107 } 108 109 func init() { 110 blobserver.RegisterStorageConstructor("filesystem", blobserver.StorageConstructor(newFromConfig)) 111 } 112 113 func (ds *DiskStorage) tryRemoveDir(dir string) { 114 ds.dirLockMu.Lock() 115 defer ds.dirLockMu.Unlock() 116 os.Remove(dir) // ignore error 117 } 118 119 func (ds *DiskStorage) Fetch(blob blob.Ref) (io.ReadCloser, uint32, error) { 120 fileName := ds.blobPath(blob) 121 stat, err := os.Stat(fileName) 122 if os.IsNotExist(err) { 123 return nil, 0, os.ErrNotExist 124 } 125 size := types.U32(stat.Size()) 126 file, err := os.Open(fileName) 127 if err != nil { 128 if os.IsNotExist(err) { 129 err = os.ErrNotExist 130 } 131 return nil, 0, err 132 } 133 return file, size, nil 134 } 135 136 func (ds *DiskStorage) RemoveBlobs(blobs []blob.Ref) error { 137 for _, blob := range blobs { 138 fileName := ds.blobPath(blob) 139 err := os.Remove(fileName) 140 switch { 141 case err == nil: 142 continue 143 case os.IsNotExist(err): 144 // deleting already-deleted file; harmless. 145 continue 146 default: 147 return err 148 } 149 } 150 return nil 151 }