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  }