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