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  }