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