230 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
package filesystem
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"path"
 | 
						|
 | 
						|
	"github.com/docker/docker-registry/storagedriver"
 | 
						|
	"github.com/docker/docker-registry/storagedriver/factory"
 | 
						|
)
 | 
						|
 | 
						|
const driverName = "filesystem"
 | 
						|
const defaultRootDirectory = "/tmp/registry/storage"
 | 
						|
 | 
						|
func init() {
 | 
						|
	factory.Register(driverName, &filesystemDriverFactory{})
 | 
						|
}
 | 
						|
 | 
						|
// filesystemDriverFactory implements the factory.StorageDriverFactory interface
 | 
						|
type filesystemDriverFactory struct{}
 | 
						|
 | 
						|
func (factory *filesystemDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
 | 
						|
	return FromParameters(parameters), nil
 | 
						|
}
 | 
						|
 | 
						|
// Driver is a storagedriver.StorageDriver implementation backed by a local
 | 
						|
// filesystem. All provided paths will be subpaths of the RootDirectory
 | 
						|
type Driver struct {
 | 
						|
	rootDirectory string
 | 
						|
}
 | 
						|
 | 
						|
// FromParameters constructs a new Driver with a given parameters map
 | 
						|
// Optional Parameters:
 | 
						|
// - rootdirectory
 | 
						|
func FromParameters(parameters map[string]string) *Driver {
 | 
						|
	var rootDirectory = defaultRootDirectory
 | 
						|
	if parameters != nil {
 | 
						|
		rootDir, ok := parameters["rootdirectory"]
 | 
						|
		if ok {
 | 
						|
			rootDirectory = rootDir
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return New(rootDirectory)
 | 
						|
}
 | 
						|
 | 
						|
// New constructs a new Driver with a given rootDirectory
 | 
						|
func New(rootDirectory string) *Driver {
 | 
						|
	return &Driver{rootDirectory}
 | 
						|
}
 | 
						|
 | 
						|
// subPath returns the absolute path of a key within the Driver's storage
 | 
						|
func (d *Driver) subPath(subPath string) string {
 | 
						|
	return path.Join(d.rootDirectory, subPath)
 | 
						|
}
 | 
						|
 | 
						|
// Implement the storagedriver.StorageDriver interface
 | 
						|
 | 
						|
// GetContent retrieves the content stored at "path" as a []byte.
 | 
						|
func (d *Driver) GetContent(path string) ([]byte, error) {
 | 
						|
	contents, err := ioutil.ReadFile(d.subPath(path))
 | 
						|
	if err != nil {
 | 
						|
		return nil, storagedriver.PathNotFoundError{Path: path}
 | 
						|
	}
 | 
						|
	return contents, nil
 | 
						|
}
 | 
						|
 | 
						|
// PutContent stores the []byte content at a location designated by "path".
 | 
						|
func (d *Driver) PutContent(subPath string, contents []byte) error {
 | 
						|
	fullPath := d.subPath(subPath)
 | 
						|
	parentDir := path.Dir(fullPath)
 | 
						|
	err := os.MkdirAll(parentDir, 0755)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	err = ioutil.WriteFile(fullPath, contents, 0644)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
 | 
						|
// given byte offset.
 | 
						|
func (d *Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
 | 
						|
	file, err := os.OpenFile(d.subPath(path), os.O_RDONLY, 0644)
 | 
						|
	if err != nil {
 | 
						|
		return nil, storagedriver.PathNotFoundError{Path: path}
 | 
						|
	}
 | 
						|
 | 
						|
	seekPos, err := file.Seek(int64(offset), os.SEEK_SET)
 | 
						|
	if err != nil {
 | 
						|
		file.Close()
 | 
						|
		return nil, err
 | 
						|
	} else if seekPos < int64(offset) {
 | 
						|
		file.Close()
 | 
						|
		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
 | 
						|
	}
 | 
						|
 | 
						|
	return file, nil
 | 
						|
}
 | 
						|
 | 
						|
// WriteStream stores the contents of the provided io.ReadCloser at a location
 | 
						|
// designated by the given path.
 | 
						|
func (d *Driver) WriteStream(subPath string, offset, size uint64, reader io.ReadCloser) error {
 | 
						|
	defer reader.Close()
 | 
						|
 | 
						|
	resumableOffset, err := d.CurrentSize(subPath)
 | 
						|
	if _, pathNotFound := err.(storagedriver.PathNotFoundError); err != nil && !pathNotFound {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if offset > resumableOffset {
 | 
						|
		return storagedriver.InvalidOffsetError{Path: subPath, Offset: offset}
 | 
						|
	}
 | 
						|
 | 
						|
	fullPath := d.subPath(subPath)
 | 
						|
	parentDir := path.Dir(fullPath)
 | 
						|
	err = os.MkdirAll(parentDir, 0755)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var file *os.File
 | 
						|
	if offset == 0 {
 | 
						|
		file, err = os.Create(fullPath)
 | 
						|
	} else {
 | 
						|
		file, err = os.OpenFile(fullPath, os.O_WRONLY|os.O_APPEND, 0)
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	defer file.Close()
 | 
						|
 | 
						|
	buf := make([]byte, 32*1024)
 | 
						|
	for {
 | 
						|
		bytesRead, er := reader.Read(buf)
 | 
						|
		if bytesRead > 0 {
 | 
						|
			bytesWritten, ew := file.WriteAt(buf[0:bytesRead], int64(offset))
 | 
						|
			if bytesWritten > 0 {
 | 
						|
				offset += uint64(bytesWritten)
 | 
						|
			}
 | 
						|
			if ew != nil {
 | 
						|
				err = ew
 | 
						|
				break
 | 
						|
			}
 | 
						|
			if bytesRead != bytesWritten {
 | 
						|
				err = io.ErrShortWrite
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if er == io.EOF {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		if er != nil {
 | 
						|
			err = er
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// CurrentSize retrieves the curernt size in bytes of the object at the given
 | 
						|
// path.
 | 
						|
func (d *Driver) CurrentSize(subPath string) (uint64, error) {
 | 
						|
	fullPath := d.subPath(subPath)
 | 
						|
 | 
						|
	fileInfo, err := os.Stat(fullPath)
 | 
						|
	if err != nil && !os.IsNotExist(err) {
 | 
						|
		return 0, err
 | 
						|
	} else if err != nil {
 | 
						|
		return 0, storagedriver.PathNotFoundError{Path: subPath}
 | 
						|
	}
 | 
						|
	return uint64(fileInfo.Size()), nil
 | 
						|
}
 | 
						|
 | 
						|
// List returns a list of the objects that are direct descendants of the given
 | 
						|
// path.
 | 
						|
func (d *Driver) List(subPath string) ([]string, error) {
 | 
						|
	if subPath[len(subPath)-1] != '/' {
 | 
						|
		subPath += "/"
 | 
						|
	}
 | 
						|
	fullPath := d.subPath(subPath)
 | 
						|
 | 
						|
	dir, err := os.Open(fullPath)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	fileNames, err := dir.Readdirnames(0)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	keys := make([]string, 0, len(fileNames))
 | 
						|
	for _, fileName := range fileNames {
 | 
						|
		keys = append(keys, path.Join(subPath, fileName))
 | 
						|
	}
 | 
						|
 | 
						|
	return keys, nil
 | 
						|
}
 | 
						|
 | 
						|
// Move moves an object stored at sourcePath to destPath, removing the original
 | 
						|
// object.
 | 
						|
func (d *Driver) Move(sourcePath string, destPath string) error {
 | 
						|
	source := d.subPath(sourcePath)
 | 
						|
	dest := d.subPath(destPath)
 | 
						|
 | 
						|
	if _, err := os.Stat(source); os.IsNotExist(err) {
 | 
						|
		return storagedriver.PathNotFoundError{Path: sourcePath}
 | 
						|
	}
 | 
						|
 | 
						|
	err := os.Rename(source, dest)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// Delete recursively deletes all objects stored at "path" and its subpaths.
 | 
						|
func (d *Driver) Delete(subPath string) error {
 | 
						|
	fullPath := d.subPath(subPath)
 | 
						|
 | 
						|
	_, err := os.Stat(fullPath)
 | 
						|
	if err != nil && !os.IsNotExist(err) {
 | 
						|
		return err
 | 
						|
	} else if err != nil {
 | 
						|
		return storagedriver.PathNotFoundError{Path: subPath}
 | 
						|
	}
 | 
						|
 | 
						|
	err = os.RemoveAll(fullPath)
 | 
						|
	return err
 | 
						|
}
 |