github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/driver/filesystem/driver.go (about) 1 package filesystem 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path" 10 "time" 11 12 "github.com/docker/distribution/context" 13 storagedriver "github.com/docker/distribution/registry/storage/driver" 14 "github.com/docker/distribution/registry/storage/driver/base" 15 "github.com/docker/distribution/registry/storage/driver/factory" 16 ) 17 18 const driverName = "filesystem" 19 const defaultRootDirectory = "/var/lib/registry" 20 21 func init() { 22 factory.Register(driverName, &filesystemDriverFactory{}) 23 } 24 25 // filesystemDriverFactory implements the factory.StorageDriverFactory interface 26 type filesystemDriverFactory struct{} 27 28 func (factory *filesystemDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) { 29 return FromParameters(parameters), nil 30 } 31 32 type driver struct { 33 rootDirectory string 34 } 35 36 type baseEmbed struct { 37 base.Base 38 } 39 40 // Driver is a storagedriver.StorageDriver implementation backed by a local 41 // filesystem. All provided paths will be subpaths of the RootDirectory. 42 type Driver struct { 43 baseEmbed 44 } 45 46 // FromParameters constructs a new Driver with a given parameters map 47 // Optional Parameters: 48 // - rootdirectory 49 func FromParameters(parameters map[string]interface{}) *Driver { 50 var rootDirectory = defaultRootDirectory 51 if parameters != nil { 52 rootDir, ok := parameters["rootdirectory"] 53 if ok { 54 rootDirectory = fmt.Sprint(rootDir) 55 } 56 } 57 return New(rootDirectory) 58 } 59 60 // New constructs a new Driver with a given rootDirectory 61 func New(rootDirectory string) *Driver { 62 return &Driver{ 63 baseEmbed: baseEmbed{ 64 Base: base.Base{ 65 StorageDriver: &driver{ 66 rootDirectory: rootDirectory, 67 }, 68 }, 69 }, 70 } 71 } 72 73 // Implement the storagedriver.StorageDriver interface 74 75 func (d *driver) Name() string { 76 return driverName 77 } 78 79 // GetContent retrieves the content stored at "path" as a []byte. 80 func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) { 81 rc, err := d.ReadStream(ctx, path, 0) 82 if err != nil { 83 return nil, err 84 } 85 defer rc.Close() 86 87 p, err := ioutil.ReadAll(rc) 88 if err != nil { 89 return nil, err 90 } 91 92 return p, nil 93 } 94 95 // PutContent stores the []byte content at a location designated by "path". 96 func (d *driver) PutContent(ctx context.Context, subPath string, contents []byte) error { 97 if _, err := d.WriteStream(ctx, subPath, 0, bytes.NewReader(contents)); err != nil { 98 return err 99 } 100 101 return os.Truncate(d.fullPath(subPath), int64(len(contents))) 102 } 103 104 // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a 105 // given byte offset. 106 func (d *driver) ReadStream(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { 107 file, err := os.OpenFile(d.fullPath(path), os.O_RDONLY, 0644) 108 if err != nil { 109 if os.IsNotExist(err) { 110 return nil, storagedriver.PathNotFoundError{Path: path} 111 } 112 113 return nil, err 114 } 115 116 seekPos, err := file.Seek(int64(offset), os.SEEK_SET) 117 if err != nil { 118 file.Close() 119 return nil, err 120 } else if seekPos < int64(offset) { 121 file.Close() 122 return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset} 123 } 124 125 return file, nil 126 } 127 128 // WriteStream stores the contents of the provided io.Reader at a location 129 // designated by the given path. 130 func (d *driver) WriteStream(ctx context.Context, subPath string, offset int64, reader io.Reader) (nn int64, err error) { 131 // TODO(stevvooe): This needs to be a requirement. 132 // if !path.IsAbs(subPath) { 133 // return fmt.Errorf("absolute path required: %q", subPath) 134 // } 135 136 fullPath := d.fullPath(subPath) 137 parentDir := path.Dir(fullPath) 138 if err := os.MkdirAll(parentDir, 0777); err != nil { 139 return 0, err 140 } 141 142 fp, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0666) 143 if err != nil { 144 // TODO(stevvooe): A few missing conditions in storage driver: 145 // 1. What if the path is already a directory? 146 // 2. Should number 1 be exposed explicitly in storagedriver? 147 // 2. Can this path not exist, even if we create above? 148 return 0, err 149 } 150 defer fp.Close() 151 152 nn, err = fp.Seek(offset, os.SEEK_SET) 153 if err != nil { 154 return 0, err 155 } 156 157 if nn != offset { 158 return 0, fmt.Errorf("bad seek to %v, expected %v in fp=%v", offset, nn, fp) 159 } 160 161 return io.Copy(fp, reader) 162 } 163 164 // Stat retrieves the FileInfo for the given path, including the current size 165 // in bytes and the creation time. 166 func (d *driver) Stat(ctx context.Context, subPath string) (storagedriver.FileInfo, error) { 167 fullPath := d.fullPath(subPath) 168 169 fi, err := os.Stat(fullPath) 170 if err != nil { 171 if os.IsNotExist(err) { 172 return nil, storagedriver.PathNotFoundError{Path: subPath} 173 } 174 175 return nil, err 176 } 177 178 return fileInfo{ 179 path: subPath, 180 FileInfo: fi, 181 }, nil 182 } 183 184 // List returns a list of the objects that are direct descendants of the given 185 // path. 186 func (d *driver) List(ctx context.Context, subPath string) ([]string, error) { 187 fullPath := d.fullPath(subPath) 188 189 dir, err := os.Open(fullPath) 190 if err != nil { 191 if os.IsNotExist(err) { 192 return nil, storagedriver.PathNotFoundError{Path: subPath} 193 } 194 return nil, err 195 } 196 197 defer dir.Close() 198 199 fileNames, err := dir.Readdirnames(0) 200 if err != nil { 201 return nil, err 202 } 203 204 keys := make([]string, 0, len(fileNames)) 205 for _, fileName := range fileNames { 206 keys = append(keys, path.Join(subPath, fileName)) 207 } 208 209 return keys, nil 210 } 211 212 // Move moves an object stored at sourcePath to destPath, removing the original 213 // object. 214 func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error { 215 source := d.fullPath(sourcePath) 216 dest := d.fullPath(destPath) 217 218 if _, err := os.Stat(source); os.IsNotExist(err) { 219 return storagedriver.PathNotFoundError{Path: sourcePath} 220 } 221 222 if err := os.MkdirAll(path.Dir(dest), 0755); err != nil { 223 return err 224 } 225 226 err := os.Rename(source, dest) 227 return err 228 } 229 230 // Delete recursively deletes all objects stored at "path" and its subpaths. 231 func (d *driver) Delete(ctx context.Context, subPath string) error { 232 fullPath := d.fullPath(subPath) 233 234 _, err := os.Stat(fullPath) 235 if err != nil && !os.IsNotExist(err) { 236 return err 237 } else if err != nil { 238 return storagedriver.PathNotFoundError{Path: subPath} 239 } 240 241 err = os.RemoveAll(fullPath) 242 return err 243 } 244 245 // URLFor returns a URL which may be used to retrieve the content stored at the given path. 246 // May return an UnsupportedMethodErr in certain StorageDriver implementations. 247 func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) { 248 return "", storagedriver.ErrUnsupportedMethod{} 249 } 250 251 // fullPath returns the absolute path of a key within the Driver's storage. 252 func (d *driver) fullPath(subPath string) string { 253 return path.Join(d.rootDirectory, subPath) 254 } 255 256 type fileInfo struct { 257 os.FileInfo 258 path string 259 } 260 261 var _ storagedriver.FileInfo = fileInfo{} 262 263 // Path provides the full path of the target of this file info. 264 func (fi fileInfo) Path() string { 265 return fi.path 266 } 267 268 // Size returns current length in bytes of the file. The return value can 269 // be used to write to the end of the file at path. The value is 270 // meaningless if IsDir returns true. 271 func (fi fileInfo) Size() int64 { 272 if fi.IsDir() { 273 return 0 274 } 275 276 return fi.FileInfo.Size() 277 } 278 279 // ModTime returns the modification time for the file. For backends that 280 // don't have a modification time, the creation time should be returned. 281 func (fi fileInfo) ModTime() time.Time { 282 return fi.FileInfo.ModTime() 283 } 284 285 // IsDir returns true if the path is a directory. 286 func (fi fileInfo) IsDir() bool { 287 return fi.FileInfo.IsDir() 288 }