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