2012-12-26 12:23:58 +00:00
|
|
|
// File system interface
|
|
|
|
|
2013-06-27 19:13:07 +00:00
|
|
|
package fs
|
2012-12-26 12:23:58 +00:00
|
|
|
|
|
|
|
import (
|
2012-12-31 16:40:34 +00:00
|
|
|
"fmt"
|
2012-12-26 12:23:58 +00:00
|
|
|
"io"
|
2012-12-31 16:40:34 +00:00
|
|
|
"log"
|
2013-06-27 19:13:07 +00:00
|
|
|
"regexp"
|
2012-12-26 12:23:58 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2013-06-27 19:13:07 +00:00
|
|
|
// Globals
|
|
|
|
var (
|
|
|
|
// Filesystem registry
|
2014-03-15 16:06:11 +00:00
|
|
|
fsRegistry []*FsInfo
|
2013-06-27 19:13:07 +00:00
|
|
|
)
|
|
|
|
|
2014-03-15 16:06:11 +00:00
|
|
|
// Filesystem info
|
|
|
|
type FsInfo struct {
|
|
|
|
Name string // name of this fs
|
|
|
|
NewFs func(string, string) (Fs, error) // create a new file system
|
2014-03-16 13:54:43 +00:00
|
|
|
Config func(string) // function to call to help with config
|
2014-03-15 16:06:11 +00:00
|
|
|
Options []Option
|
2013-06-27 19:13:07 +00:00
|
|
|
}
|
|
|
|
|
2014-03-15 16:06:11 +00:00
|
|
|
// An options for a Fs
|
|
|
|
type Option struct {
|
|
|
|
Name string
|
|
|
|
Help string
|
|
|
|
Optional bool
|
|
|
|
Examples []OptionExample
|
|
|
|
}
|
|
|
|
|
|
|
|
// An example for an option
|
|
|
|
type OptionExample struct {
|
|
|
|
Value string
|
|
|
|
Help string
|
|
|
|
}
|
|
|
|
|
2013-06-27 19:13:07 +00:00
|
|
|
// Register a filesystem
|
|
|
|
//
|
|
|
|
// Fs modules should use this in an init() function
|
2014-03-15 16:06:11 +00:00
|
|
|
func Register(info *FsInfo) {
|
|
|
|
fsRegistry = append(fsRegistry, info)
|
2013-06-27 19:13:07 +00:00
|
|
|
}
|
|
|
|
|
2012-12-26 12:23:58 +00:00
|
|
|
// A Filesystem, describes the local filesystem and the remote object store
|
|
|
|
type Fs interface {
|
2013-01-18 18:54:19 +00:00
|
|
|
// String returns a description of the FS
|
2012-12-31 16:40:34 +00:00
|
|
|
String() string
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// List the Fs into a channel
|
2013-06-28 07:57:32 +00:00
|
|
|
List() ObjectsChan
|
2013-01-18 18:54:19 +00:00
|
|
|
|
2013-01-23 22:43:20 +00:00
|
|
|
// List the Fs directories/buckets/containers into a channel
|
2013-06-28 07:57:32 +00:00
|
|
|
ListDir() DirChan
|
2013-01-23 22:43:20 +00:00
|
|
|
|
2013-06-28 07:57:32 +00:00
|
|
|
// Find the Object at remote. Returns nil if can't be found
|
|
|
|
NewFsObject(remote string) Object
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Put in to the remote path with the modTime given of the given size
|
|
|
|
//
|
|
|
|
// May create the object even if it returns an error - if so
|
|
|
|
// will return the object and the error, otherwise will return
|
|
|
|
// nil and the error
|
2013-06-28 07:57:32 +00:00
|
|
|
Put(in io.Reader, remote string, modTime time.Time, size int64) (Object, error)
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Make the directory (container, bucket)
|
2012-12-26 12:23:58 +00:00
|
|
|
Mkdir() error
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Remove the directory (container, bucket) if empty
|
2012-12-26 12:23:58 +00:00
|
|
|
Rmdir() error
|
2013-01-18 23:21:02 +00:00
|
|
|
|
|
|
|
// Precision of the ModTimes in this Fs
|
|
|
|
Precision() time.Duration
|
2012-12-26 12:23:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A filesystem like object which can either be a remote object or a
|
|
|
|
// local file/directory
|
2013-06-28 07:57:32 +00:00
|
|
|
type Object interface {
|
2014-03-28 17:56:04 +00:00
|
|
|
// String returns a description of the Object
|
|
|
|
String() string
|
|
|
|
|
|
|
|
// Fs returns the Fs that this object is part of
|
|
|
|
Fs() Fs
|
|
|
|
|
2013-01-18 18:54:19 +00:00
|
|
|
// Remote returns the remote path
|
2012-12-26 12:23:58 +00:00
|
|
|
Remote() string
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Md5sum returns the md5 checksum of the file
|
2012-12-26 12:23:58 +00:00
|
|
|
Md5sum() (string, error)
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// ModTime returns the modification date of the file
|
2013-01-02 15:21:55 +00:00
|
|
|
ModTime() time.Time
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// SetModTime sets the metadata on the object to set the modification date
|
2012-12-26 12:23:58 +00:00
|
|
|
SetModTime(time.Time)
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Size returns the size of the file
|
2012-12-26 12:23:58 +00:00
|
|
|
Size() int64
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Open opens the file for read. Call Close() on the returned io.ReadCloser
|
2012-12-26 12:23:58 +00:00
|
|
|
Open() (io.ReadCloser, error)
|
2013-01-18 18:54:19 +00:00
|
|
|
|
2014-04-18 16:04:21 +00:00
|
|
|
// Update in to the object with the modTime given of the given size
|
|
|
|
Update(in io.Reader, modTime time.Time, size int64) error
|
|
|
|
|
2013-01-18 18:54:19 +00:00
|
|
|
// Storable says whether this object can be stored
|
2012-12-26 12:23:58 +00:00
|
|
|
Storable() bool
|
2013-01-18 18:54:19 +00:00
|
|
|
|
|
|
|
// Removes this object
|
2012-12-26 12:23:58 +00:00
|
|
|
Remove() error
|
|
|
|
}
|
|
|
|
|
2013-01-18 17:01:47 +00:00
|
|
|
// Optional interfaces
|
|
|
|
type Purger interface {
|
|
|
|
// Purge all files in the root and the root directory
|
|
|
|
//
|
|
|
|
// Implement this if you have a way of deleting all the files
|
|
|
|
// quicker than just running Remove() on the result of List()
|
|
|
|
Purge() error
|
|
|
|
}
|
|
|
|
|
2013-06-28 07:57:32 +00:00
|
|
|
// A channel of Objects
|
|
|
|
type ObjectsChan chan Object
|
2012-12-26 12:23:58 +00:00
|
|
|
|
2013-06-28 07:57:32 +00:00
|
|
|
// A slice of Objects
|
|
|
|
type Objects []Object
|
2012-12-26 12:23:58 +00:00
|
|
|
|
2014-03-28 17:56:04 +00:00
|
|
|
// A pair of Objects
|
|
|
|
type ObjectPair struct {
|
|
|
|
src, dst Object
|
|
|
|
}
|
|
|
|
|
|
|
|
// A channel of ObjectPair
|
|
|
|
type ObjectPairChan chan ObjectPair
|
|
|
|
|
2013-01-23 22:43:20 +00:00
|
|
|
// A structure of directory/container/bucket lists
|
2013-06-28 07:57:32 +00:00
|
|
|
type Dir struct {
|
2013-01-23 22:43:20 +00:00
|
|
|
Name string // name of the directory
|
|
|
|
When time.Time // modification or creation time - IsZero for unknown
|
|
|
|
Bytes int64 // size of directory and contents -1 for unknown
|
|
|
|
Count int64 // number of objects -1 for unknown
|
|
|
|
}
|
|
|
|
|
2013-06-28 07:57:32 +00:00
|
|
|
// A channel of Dir objects
|
|
|
|
type DirChan chan *Dir
|
2013-01-23 22:43:20 +00:00
|
|
|
|
2014-03-15 16:06:11 +00:00
|
|
|
// Pattern to match a url
|
2014-03-27 17:31:57 +00:00
|
|
|
var matcher = regexp.MustCompile(`^([\w_-]+):(.*)$`)
|
2014-03-15 16:06:11 +00:00
|
|
|
|
|
|
|
// Finds a FsInfo object for the name passed in
|
|
|
|
//
|
|
|
|
// Services are looked up in the config file
|
|
|
|
func Find(name string) (*FsInfo, error) {
|
|
|
|
for _, item := range fsRegistry {
|
|
|
|
if item.Name == name {
|
|
|
|
return item, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Didn't find filing system for %q", name)
|
|
|
|
}
|
|
|
|
|
2012-12-29 11:35:41 +00:00
|
|
|
// NewFs makes a new Fs object from the path
|
|
|
|
//
|
2014-03-15 16:06:11 +00:00
|
|
|
// The path is of the form service://path
|
|
|
|
//
|
|
|
|
// Services are looked up in the config file
|
2012-12-29 11:35:41 +00:00
|
|
|
func NewFs(path string) (Fs, error) {
|
2014-03-15 16:06:11 +00:00
|
|
|
parts := matcher.FindStringSubmatch(path)
|
|
|
|
fsName, configName, fsPath := "local", "local", path
|
|
|
|
if parts != nil {
|
|
|
|
configName, fsPath = parts[1], parts[2]
|
|
|
|
var err error
|
|
|
|
fsName, err = ConfigFile.GetValue(configName, "type")
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Didn't find section in config file for %q", configName)
|
2013-06-27 19:13:07 +00:00
|
|
|
}
|
2013-01-14 23:38:18 +00:00
|
|
|
}
|
2014-03-15 16:06:11 +00:00
|
|
|
fs, err := Find(fsName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return fs.NewFs(configName, fsPath)
|
2012-12-29 11:35:41 +00:00
|
|
|
}
|
|
|
|
|
2014-03-28 17:56:04 +00:00
|
|
|
// Outputs log for object
|
|
|
|
func OutputLog(o interface{}, text string, args ...interface{}) {
|
|
|
|
description := ""
|
|
|
|
if x, ok := o.(fmt.Stringer); ok {
|
|
|
|
description = x.String() + ": "
|
|
|
|
}
|
|
|
|
out := fmt.Sprintf(text, args...)
|
|
|
|
log.Print(description + out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write debuging output for this Object or Fs
|
|
|
|
func Debug(o interface{}, text string, args ...interface{}) {
|
2013-06-27 19:13:07 +00:00
|
|
|
if Config.Verbose {
|
2014-03-28 17:56:04 +00:00
|
|
|
OutputLog(o, text, args...)
|
2012-12-31 16:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-28 17:56:04 +00:00
|
|
|
// Write log output for this Object or Fs
|
|
|
|
func Log(o interface{}, text string, args ...interface{}) {
|
2013-06-27 19:13:07 +00:00
|
|
|
if !Config.Quiet {
|
2014-03-28 17:56:04 +00:00
|
|
|
OutputLog(o, text, args...)
|
2012-12-31 16:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-26 12:23:58 +00:00
|
|
|
// checkClose is a utility function used to check the return from
|
|
|
|
// Close in a defer statement.
|
|
|
|
func checkClose(c io.Closer, err *error) {
|
|
|
|
cerr := c.Close()
|
|
|
|
if *err == nil {
|
|
|
|
*err = cerr
|
|
|
|
}
|
|
|
|
}
|