github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/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 "sync" 38 39 "camlistore.org/pkg/blob" 40 "camlistore.org/pkg/blobserver" 41 "camlistore.org/pkg/blobserver/local" 42 "camlistore.org/pkg/jsonconfig" 43 "camlistore.org/pkg/types" 44 ) 45 46 // DiskStorage implements the blobserver.Storage interface using the 47 // local filesystem. 48 type DiskStorage struct { 49 root string 50 51 // dirLockMu must be held for writing when deleting an empty directory 52 // and for read when receiving blobs. 53 dirLockMu *sync.RWMutex 54 55 // gen will be nil if partition != "" 56 gen *local.Generationer 57 } 58 59 // New returns a new local disk storage implementation at the provided 60 // root directory, which must already exist. 61 func New(root string) (*DiskStorage, error) { 62 // Local disk. 63 fi, err := os.Stat(root) 64 if os.IsNotExist(err) { 65 return nil, fmt.Errorf("Storage root %q doesn't exist", root) 66 } 67 if err != nil { 68 return nil, fmt.Errorf("Failed to stat directory %q: %v", root, err) 69 } 70 if !fi.IsDir() { 71 return nil, fmt.Errorf("Storage root %q exists but is not a directory.", root) 72 } 73 ds := &DiskStorage{ 74 root: root, 75 dirLockMu: new(sync.RWMutex), 76 gen: local.NewGenerationer(root), 77 } 78 if err := ds.migrate3to2(); err != nil { 79 return nil, fmt.Errorf("Error updating localdisk format: %v", err) 80 } 81 if _, _, err := ds.StorageGeneration(); err != nil { 82 return nil, fmt.Errorf("Error initialization generation for %q: %v", root, err) 83 } 84 return ds, nil 85 } 86 87 func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (storage blobserver.Storage, err error) { 88 path := config.RequiredString("path") 89 if err := config.Validate(); err != nil { 90 return nil, err 91 } 92 return New(path) 93 } 94 95 func init() { 96 blobserver.RegisterStorageConstructor("filesystem", blobserver.StorageConstructor(newFromConfig)) 97 } 98 99 func (ds *DiskStorage) tryRemoveDir(dir string) { 100 ds.dirLockMu.Lock() 101 defer ds.dirLockMu.Unlock() 102 os.Remove(dir) // ignore error 103 } 104 105 func (ds *DiskStorage) FetchStreaming(blob blob.Ref) (io.ReadCloser, int64, error) { 106 return ds.Fetch(blob) 107 } 108 109 func (ds *DiskStorage) Fetch(blob blob.Ref) (types.ReadSeekCloser, int64, error) { 110 fileName := ds.blobPath(blob) 111 stat, err := os.Stat(fileName) 112 if os.IsNotExist(err) { 113 return nil, 0, os.ErrNotExist 114 } 115 file, err := os.Open(fileName) 116 if err != nil { 117 if os.IsNotExist(err) { 118 err = os.ErrNotExist 119 } 120 return nil, 0, err 121 } 122 return file, stat.Size(), nil 123 } 124 125 func (ds *DiskStorage) RemoveBlobs(blobs []blob.Ref) error { 126 for _, blob := range blobs { 127 fileName := ds.blobPath(blob) 128 err := os.Remove(fileName) 129 switch { 130 case err == nil: 131 continue 132 case os.IsNotExist(err): 133 // deleting already-deleted file; harmless. 134 continue 135 default: 136 return err 137 } 138 } 139 return nil 140 }