github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/save/filesystem.go (about) 1 // Copyright © 2021 https://github.com/distribution/distribution 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package save 16 17 import ( 18 "bufio" 19 "bytes" 20 "context" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "os" 25 "path" 26 "time" 27 28 "github.com/alibaba/sealer/logger" 29 30 storagedriver "github.com/distribution/distribution/v3/registry/storage/driver" 31 "github.com/distribution/distribution/v3/registry/storage/driver/base" 32 "github.com/distribution/distribution/v3/registry/storage/driver/factory" 33 ) 34 35 const ( 36 driverName = "filesystem" 37 defaultRootDirectory = "/var/lib/registry" 38 defaultMaxThreads = uint64(100) 39 40 // minThreads is the minimum value for the maxthreads configuration 41 // parameter. If the driver's parameters are less than this we set 42 // the parameters to minThreads 43 minThreads = uint64(25) 44 ) 45 46 // DriverParameters represents all configuration options available for the 47 // filesystem driver 48 type DriverParameters struct { 49 RootDirectory string 50 MaxThreads uint64 51 } 52 53 func init() { 54 factory.Register(driverName, &filesystemDriverFactory{}) 55 } 56 57 // filesystemDriverFactory implements the factory.StorageDriverFactory interface 58 type filesystemDriverFactory struct{} 59 60 func (factory *filesystemDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) { 61 return FromParameters(parameters) 62 } 63 64 type driver struct { 65 rootDirectory string 66 } 67 68 type baseEmbed struct { 69 base.Base 70 } 71 72 // Driver is a storagedriver.StorageDriver implementation backed by a local 73 // filesystem. All provided paths will be subpaths of the RootDirectory. 74 type Driver struct { 75 baseEmbed 76 } 77 78 // FromParameters constructs a new Driver with a given parameters map 79 // Optional Parameters: 80 // - rootdirectory 81 // - maxthreads 82 func FromParameters(parameters map[string]interface{}) (*Driver, error) { 83 params, err := fromParametersImpl(parameters) 84 if err != nil || params == nil { 85 return nil, err 86 } 87 return New(*params), nil 88 } 89 90 func fromParametersImpl(parameters map[string]interface{}) (*DriverParameters, error) { 91 var ( 92 err error 93 maxThreads = defaultMaxThreads 94 rootDirectory = defaultRootDirectory 95 ) 96 97 if parameters != nil { 98 if rootDir, ok := parameters["rootdirectory"]; ok { 99 rootDirectory = fmt.Sprint(rootDir) 100 } 101 102 maxThreads, err = base.GetLimitFromParameter(parameters["maxthreads"], minThreads, defaultMaxThreads) 103 if err != nil { 104 return nil, fmt.Errorf("maxthreads config error: %s", err.Error()) 105 } 106 } 107 108 params := &DriverParameters{ 109 RootDirectory: rootDirectory, 110 MaxThreads: maxThreads, 111 } 112 return params, nil 113 } 114 115 // New constructs a new Driver with a given rootDirectory 116 func New(params DriverParameters) *Driver { 117 fsDriver := &driver{rootDirectory: params.RootDirectory} 118 119 return &Driver{ 120 baseEmbed: baseEmbed{ 121 Base: base.Base{ 122 StorageDriver: base.NewRegulator(fsDriver, params.MaxThreads), 123 }, 124 }, 125 } 126 } 127 128 // Implement the storagedriver.StorageDriver interface 129 130 func (d *driver) Name() string { 131 return driverName 132 } 133 134 // GetContent retrieves the content stored at "path" as a []byte. 135 func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) { 136 rc, err := d.Reader(ctx, path, 0) 137 if err != nil { 138 return nil, err 139 } 140 defer rc.Close() 141 142 p, err := ioutil.ReadAll(rc) 143 if err != nil { 144 return nil, err 145 } 146 147 return p, nil 148 } 149 150 // PutContent stores the []byte content at a location designated by "path". 151 func (d *driver) PutContent(ctx context.Context, subPath string, contents []byte) error { 152 writer, err := d.Writer(ctx, subPath, false) 153 if err != nil { 154 return err 155 } 156 defer func() { 157 if err := writer.Close(); err != nil { 158 logger.Fatal("failed to close file") 159 } 160 }() 161 _, err = io.Copy(writer, bytes.NewReader(contents)) 162 if err != nil { 163 return writer.Cancel() 164 } 165 return writer.Commit() 166 } 167 168 // Reader retrieves an io.ReadCloser for the content stored at "path" with a 169 // given byte offset. 170 func (d *driver) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { 171 file, err := os.OpenFile(d.fullPath(path), os.O_RDONLY, 0600) 172 if err != nil { 173 if os.IsNotExist(err) { 174 return nil, storagedriver.PathNotFoundError{Path: path} 175 } 176 177 return nil, err 178 } 179 180 seekPos, err := file.Seek(offset, io.SeekStart) 181 if err != nil { 182 fierr := file.Close() 183 if fierr != nil { 184 return nil, fierr 185 } 186 return nil, err 187 } else if seekPos < offset { 188 fierr := file.Close() 189 if fierr != nil { 190 return nil, fierr 191 } 192 return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset} 193 } 194 195 return file, nil 196 } 197 198 func (d *driver) Writer(ctx context.Context, subPath string, append bool) (storagedriver.FileWriter, error) { 199 fullPath := d.fullPath(subPath) 200 parentDir := path.Dir(fullPath) 201 if err := os.MkdirAll(parentDir, 0750); err != nil { 202 return nil, err 203 } 204 // #nosec 205 fp, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0600) 206 if err != nil { 207 return nil, err 208 } 209 210 var offset int64 211 212 if !append { 213 err := fp.Truncate(0) 214 if err != nil { 215 return nil, fp.Close() 216 } 217 } else { 218 n, err := fp.Seek(0, io.SeekEnd) 219 if err != nil { 220 return nil, fp.Close() 221 } 222 offset = n 223 } 224 225 return newFileWriter(fp, offset), nil 226 } 227 228 // Stat retrieves the FileInfo for the given path, including the current size 229 // in bytes and the creation time. 230 func (d *driver) Stat(ctx context.Context, subPath string) (storagedriver.FileInfo, error) { 231 fullPath := d.fullPath(subPath) 232 233 fi, err := os.Stat(fullPath) 234 if err != nil { 235 if os.IsNotExist(err) { 236 return nil, storagedriver.PathNotFoundError{Path: subPath} 237 } 238 239 return nil, err 240 } 241 242 return fileInfo{ 243 path: subPath, 244 FileInfo: fi, 245 }, nil 246 } 247 248 // List returns a list of the objects that are direct descendants of the given 249 // path. 250 func (d *driver) List(ctx context.Context, subPath string) ([]string, error) { 251 fullPath := d.fullPath(subPath) 252 // #nosec 253 dir, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE, 0600) 254 if err != nil { 255 if os.IsNotExist(err) { 256 return nil, storagedriver.PathNotFoundError{Path: subPath} 257 } 258 return nil, err 259 } 260 261 defer func() { 262 if err := dir.Close(); err != nil { 263 logger.Fatal("failed to close file") 264 } 265 }() 266 fileNames, err := dir.Readdirnames(0) 267 if err != nil { 268 return nil, err 269 } 270 271 keys := make([]string, 0, len(fileNames)) 272 for _, fileName := range fileNames { 273 keys = append(keys, path.Join(subPath, fileName)) 274 } 275 276 return keys, nil 277 } 278 279 // Move moves an object stored at sourcePath to destPath, removing the original 280 // object. 281 func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error { 282 source := d.fullPath(sourcePath) 283 dest := d.fullPath(destPath) 284 285 if _, err := os.Stat(source); os.IsNotExist(err) { 286 return storagedriver.PathNotFoundError{Path: sourcePath} 287 } 288 289 if err := os.MkdirAll(path.Dir(dest), 0750); err != nil { 290 return err 291 } 292 293 err := os.Rename(source, dest) 294 return err 295 } 296 297 // Delete recursively deletes all objects stored at "path" and its subpaths. 298 func (d *driver) Delete(ctx context.Context, subPath string) error { 299 fullPath := d.fullPath(subPath) 300 301 _, err := os.Stat(fullPath) 302 if err != nil && !os.IsNotExist(err) { 303 return err 304 } else if err != nil { 305 return storagedriver.PathNotFoundError{Path: subPath} 306 } 307 308 err = os.RemoveAll(fullPath) 309 return err 310 } 311 312 // URLFor returns a URL which may be used to retrieve the content stored at the given path. 313 // May return an UnsupportedMethodErr in certain StorageDriver implementations. 314 func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) { 315 return "", storagedriver.ErrUnsupportedMethod{} 316 } 317 318 // Walk traverses a filesystem defined within driver, starting 319 // from the given path, calling f on each file and directory 320 func (d *driver) Walk(ctx context.Context, path string, f storagedriver.WalkFn) error { 321 return storagedriver.WalkFallback(ctx, d, path, f) 322 } 323 324 // fullPath returns the absolute path of a key within the Driver's storage. 325 func (d *driver) fullPath(subPath string) string { 326 return path.Join(d.rootDirectory, subPath) 327 } 328 329 type fileInfo struct { 330 os.FileInfo 331 path string 332 } 333 334 var _ storagedriver.FileInfo = fileInfo{} 335 336 // Path provides the full path of the target of this file info. 337 func (fi fileInfo) Path() string { 338 return fi.path 339 } 340 341 // Size returns current length in bytes of the file. The return value can 342 // be used to write to the end of the file at path. The value is 343 // meaningless if IsDir returns true. 344 func (fi fileInfo) Size() int64 { 345 if fi.IsDir() { 346 return 0 347 } 348 349 return fi.FileInfo.Size() 350 } 351 352 // ModTime returns the modification time for the file. For backends that 353 // don't have a modification time, the creation time should be returned. 354 func (fi fileInfo) ModTime() time.Time { 355 return fi.FileInfo.ModTime() 356 } 357 358 // IsDir returns true if the path is a directory. 359 func (fi fileInfo) IsDir() bool { 360 return fi.FileInfo.IsDir() 361 } 362 363 type fileWriter struct { 364 file *os.File 365 size int64 366 bw *bufio.Writer 367 closed bool 368 committed bool 369 cancelled bool 370 } 371 372 func newFileWriter(file *os.File, size int64) *fileWriter { 373 return &fileWriter{ 374 file: file, 375 size: size, 376 bw: bufio.NewWriter(file), 377 } 378 } 379 380 func (fw *fileWriter) Write(p []byte) (int, error) { 381 if fw.closed { 382 return 0, fmt.Errorf("already closed") 383 } else if fw.committed { 384 return 0, fmt.Errorf("already committed") 385 } else if fw.cancelled { 386 return 0, fmt.Errorf("already cancelled") 387 } 388 n, err := fw.bw.Write(p) 389 fw.size += int64(n) 390 return n, err 391 } 392 393 func (fw *fileWriter) Size() int64 { 394 return fw.size 395 } 396 397 func (fw *fileWriter) Close() error { 398 if fw.closed { 399 return fmt.Errorf("already closed") 400 } 401 402 if err := fw.bw.Flush(); err != nil { 403 return err 404 } 405 406 if err := fw.file.Sync(); err != nil { 407 return err 408 } 409 410 if err := fw.file.Close(); err != nil { 411 return err 412 } 413 fw.closed = true 414 return nil 415 } 416 417 func (fw *fileWriter) Cancel() error { 418 if fw.closed { 419 return fmt.Errorf("already closed") 420 } 421 422 fw.cancelled = true 423 if err := fw.file.Close(); err != nil { 424 return err 425 } 426 return os.Remove(fw.file.Name()) 427 } 428 429 func (fw *fileWriter) Commit() error { 430 if fw.closed { 431 return fmt.Errorf("already closed") 432 } else if fw.committed { 433 return fmt.Errorf("already committed") 434 } else if fw.cancelled { 435 return fmt.Errorf("already cancelled") 436 } 437 438 if err := fw.bw.Flush(); err != nil { 439 return err 440 } 441 442 if err := fw.file.Sync(); err != nil { 443 return err 444 } 445 446 fw.committed = true 447 return nil 448 }