mirror of
https://github.com/rclone/rclone.git
synced 2024-11-21 16:03:29 +01:00
Fix golint warnings
This commit is contained in:
parent
2ed158aba3
commit
e9c915e6fe
7
Makefile
7
Makefile
@ -10,6 +10,11 @@ test: rclone
|
||||
go test ./...
|
||||
cd fs && ./test_all.sh
|
||||
|
||||
check: rclone
|
||||
go vet ./...
|
||||
errcheck ./...
|
||||
golint ./...
|
||||
|
||||
doc: rclone.1 MANUAL.html MANUAL.txt
|
||||
|
||||
rclone.1: MANUAL.md
|
||||
@ -52,7 +57,7 @@ serve:
|
||||
tag:
|
||||
@echo "Old tag is $(LAST_TAG)"
|
||||
@echo "New tag is $(NEW_TAG)"
|
||||
echo -e "package fs\n const Version = \"$(NEW_TAG)\"\n" | gofmt > fs/version.go
|
||||
echo -e "package fs\n\n// Version of rclone\nconst Version = \"$(NEW_TAG)\"\n" | gofmt > fs/version.go
|
||||
perl -lpe 's/VERSION/${NEW_TAG}/g; s/DATE/'`date -I`'/g;' docs/content/downloads.md.in > docs/content/downloads.md
|
||||
git tag $(NEW_TAG)
|
||||
@echo "Add this to changelog in docs/content/changelog.md"
|
||||
|
@ -4,9 +4,12 @@ Required software for making a release
|
||||
* Run `gox -build-toolchain`
|
||||
* This assumes you have your own source checkout
|
||||
* pandoc for making the html and man pages
|
||||
* errcheck - go get github.com/kisielk/errcheck
|
||||
* golint - go get github.com/golang/lint
|
||||
|
||||
Making a release
|
||||
* go get -u -f -v ./...
|
||||
* make check
|
||||
* make test
|
||||
* make tag
|
||||
* edit docs/content/changelog.md
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Amazon Cloud Drive interface
|
||||
// Package amazonclouddrive provides an interface to the Amazon Cloud
|
||||
// Drive object storage system.
|
||||
package amazonclouddrive
|
||||
|
||||
/*
|
||||
@ -59,7 +60,7 @@ var (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "amazon cloud drive",
|
||||
NewFs: NewFs,
|
||||
Config: func(name string) {
|
||||
@ -98,12 +99,12 @@ type FsObjectAcd struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsAcd) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsAcd) Root() string {
|
||||
return f.root
|
||||
}
|
||||
@ -242,17 +243,17 @@ func (f *FsAcd) newFsObjectWithInfo(remote string, info *acd.Node) fs.Object {
|
||||
return o
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsAcd) NewFsObject(remote string) fs.Object {
|
||||
return f.newFsObjectWithInfo(remote, nil)
|
||||
}
|
||||
|
||||
// FindLeaf finds a directory of name leaf in the folder with ID pathId
|
||||
func (f *FsAcd) FindLeaf(pathId, leaf string) (pathIdOut string, found bool, err error) {
|
||||
//fs.Debug(f, "FindLeaf(%q, %q)", pathId, leaf)
|
||||
folder := acd.FolderFromId(pathId, f.c.Nodes)
|
||||
// FindLeaf finds a directory of name leaf in the folder with ID pathID
|
||||
func (f *FsAcd) FindLeaf(pathID, leaf string) (pathIDOut string, found bool, err error) {
|
||||
//fs.Debug(f, "FindLeaf(%q, %q)", pathID, leaf)
|
||||
folder := acd.FolderFromId(pathID, f.c.Nodes)
|
||||
var resp *http.Response
|
||||
var subFolder *acd.Folder
|
||||
err = f.pacer.Call(func() (bool, error) {
|
||||
@ -276,10 +277,10 @@ func (f *FsAcd) FindLeaf(pathId, leaf string) (pathIdOut string, found bool, err
|
||||
return *subFolder.Id, true, nil
|
||||
}
|
||||
|
||||
// CreateDir makes a directory with pathId as parent and name leaf
|
||||
func (f *FsAcd) CreateDir(pathId, leaf string) (newId string, err error) {
|
||||
//fmt.Printf("CreateDir(%q, %q)\n", pathId, leaf)
|
||||
folder := acd.FolderFromId(pathId, f.c.Nodes)
|
||||
// CreateDir makes a directory with pathID as parent and name leaf
|
||||
func (f *FsAcd) CreateDir(pathID, leaf string) (newID string, err error) {
|
||||
//fmt.Printf("CreateDir(%q, %q)\n", pathID, leaf)
|
||||
folder := acd.FolderFromId(pathID, f.c.Nodes)
|
||||
var resp *http.Response
|
||||
var info *acd.Folder
|
||||
err = f.pacer.Call(func() (bool, error) {
|
||||
@ -305,8 +306,8 @@ type listAllFn func(*acd.Node) bool
|
||||
// Lists the directory required calling the user function on each item found
|
||||
//
|
||||
// If the user fn ever returns true then it early exits with found = true
|
||||
func (f *FsAcd) listAll(dirId string, title string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
|
||||
query := "parents:" + dirId
|
||||
func (f *FsAcd) listAll(dirID string, title string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
|
||||
query := "parents:" + dirID
|
||||
if directoriesOnly {
|
||||
query += " AND kind:" + folderKind
|
||||
} else if filesOnly {
|
||||
@ -358,11 +359,11 @@ OUTER:
|
||||
//
|
||||
// This fetches the minimum amount of stuff but does more API calls
|
||||
// which makes it slow
|
||||
func (f *FsAcd) listDirRecursive(dirId string, path string, out fs.ObjectsChan) error {
|
||||
func (f *FsAcd) listDirRecursive(dirID string, path string, out fs.ObjectsChan) error {
|
||||
var subError error
|
||||
// Make the API request
|
||||
var wg sync.WaitGroup
|
||||
_, err := f.listAll(dirId, "", false, false, func(node *acd.Node) bool {
|
||||
_, err := f.listAll(dirID, "", false, false, func(node *acd.Node) bool {
|
||||
// Recurse on directories
|
||||
switch *node.Kind {
|
||||
case folderKind:
|
||||
@ -398,7 +399,7 @@ func (f *FsAcd) listDirRecursive(dirId string, path string, out fs.ObjectsChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsAcd) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -418,7 +419,7 @@ func (f *FsAcd) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Lists the containers
|
||||
// ListDir lists the directories
|
||||
func (f *FsAcd) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -546,7 +547,7 @@ func (f *FsAcd) Rmdir() error {
|
||||
return f.purgeCheck(true)
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
// Precision return the precision of this Fs
|
||||
func (f *FsAcd) Precision() time.Duration {
|
||||
return fs.ModTimeNotSupported
|
||||
}
|
||||
@ -585,7 +586,7 @@ func (f *FsAcd) Purge() error {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectAcd) Fs() fs.Fs {
|
||||
return o.acd
|
||||
}
|
||||
@ -598,7 +599,7 @@ func (o *FsObjectAcd) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectAcd) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -661,13 +662,13 @@ func (o *FsObjectAcd) ModTime() time.Time {
|
||||
return modTime
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
func (o *FsObjectAcd) SetModTime(modTime time.Time) {
|
||||
// FIXME not implemented
|
||||
return
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns a boolean showing whether this object storable
|
||||
func (o *FsObjectAcd) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// dircache provides a simple cache for caching directory to path lookups
|
||||
// Package dircache provides a simple cache for caching directory to path lookups
|
||||
package dircache
|
||||
|
||||
// _methods are called without the lock
|
||||
@ -23,13 +23,13 @@ type DirCache struct {
|
||||
foundRoot bool // Whether we have found the root or not
|
||||
}
|
||||
|
||||
// DirCache describes an interface for doing the low level directory work
|
||||
// DirCacher describes an interface for doing the low level directory work
|
||||
type DirCacher interface {
|
||||
FindLeaf(pathID, leaf string) (pathIDOut string, found bool, err error)
|
||||
CreateDir(pathID, leaf string) (newID string, err error)
|
||||
}
|
||||
|
||||
// Make a new DirCache
|
||||
// New makes a DirCache
|
||||
//
|
||||
// The cache is safe for concurrent use
|
||||
func New(root string, trueRootID string, fs DirCacher) *DirCache {
|
||||
@ -49,7 +49,7 @@ func (dc *DirCache) _get(path string) (id string, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Gets an ID given a path
|
||||
// Get an ID given a path
|
||||
func (dc *DirCache) Get(path string) (id string, ok bool) {
|
||||
dc.mu.RLock()
|
||||
id, ok = dc._get(path)
|
||||
@ -91,7 +91,7 @@ func (dc *DirCache) Flush() {
|
||||
dc.mu.Unlock()
|
||||
}
|
||||
|
||||
// Splits a path into directory, leaf
|
||||
// SplitPath splits a path into directory, leaf
|
||||
//
|
||||
// Path shouldn't start or end with a /
|
||||
//
|
||||
@ -108,7 +108,8 @@ func SplitPath(path string) (directory, leaf string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Finds the directory passed in returning the directory ID starting from pathID
|
||||
// FindDir finds the directory passed in returning the directory ID
|
||||
// starting from pathID
|
||||
//
|
||||
// Path shouldn't start or end with a /
|
||||
//
|
||||
@ -207,7 +208,7 @@ func (dc *DirCache) FindPath(path string, create bool) (leaf, directoryID string
|
||||
return
|
||||
}
|
||||
|
||||
// Finds the root directory if not already found
|
||||
// FindRoot finds the root directory if not already found
|
||||
//
|
||||
// Resets the root directory
|
||||
//
|
||||
@ -268,7 +269,8 @@ func (dc *DirCache) RootParentID() (string, error) {
|
||||
return dc.rootParentID, nil
|
||||
}
|
||||
|
||||
// Resets the root directory to the absolute root and clears the DirCache
|
||||
// ResetRoot resets the root directory to the absolute root and clears
|
||||
// the DirCache
|
||||
func (dc *DirCache) ResetRoot() {
|
||||
dc.mu.Lock()
|
||||
defer dc.mu.Unlock()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Drive interface
|
||||
// Package drive interfaces with the Google Drive object storage system
|
||||
package drive
|
||||
|
||||
// FIXME need to deal with some corner cases
|
||||
@ -61,7 +61,7 @@ var (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "drive",
|
||||
NewFs: NewFs,
|
||||
Config: func(name string) {
|
||||
@ -106,12 +106,12 @@ type FsObjectDrive struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsDrive) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsDrive) Root() string {
|
||||
return f.root
|
||||
}
|
||||
@ -170,10 +170,10 @@ type listAllFn func(*drive.File) bool
|
||||
// If the user fn ever returns true then it early exits with found = true
|
||||
//
|
||||
// Search params: https://developers.google.com/drive/search-parameters
|
||||
func (f *FsDrive) listAll(dirId string, title string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
|
||||
func (f *FsDrive) listAll(dirID string, title string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
|
||||
query := fmt.Sprintf("trashed=false")
|
||||
if dirId != "" {
|
||||
query += fmt.Sprintf(" and '%s' in parents", dirId)
|
||||
if dirID != "" {
|
||||
query += fmt.Sprintf(" and '%s' in parents", dirID)
|
||||
}
|
||||
if title != "" {
|
||||
// Escaping the backslash isn't documented but seems to work
|
||||
@ -321,35 +321,35 @@ func (f *FsDrive) newFsObjectWithInfo(remote string, info *drive.File) fs.Object
|
||||
return fs
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsDrive) NewFsObject(remote string) fs.Object {
|
||||
return f.newFsObjectWithInfo(remote, nil)
|
||||
}
|
||||
|
||||
// FindLeaf finds a directory of name leaf in the folder with ID pathId
|
||||
func (f *FsDrive) FindLeaf(pathId, leaf string) (pathIdOut string, found bool, err error) {
|
||||
// Find the leaf in pathId
|
||||
found, err = f.listAll(pathId, leaf, true, false, func(item *drive.File) bool {
|
||||
// FindLeaf finds a directory of name leaf in the folder with ID pathID
|
||||
func (f *FsDrive) FindLeaf(pathID, leaf string) (pathIDOut string, found bool, err error) {
|
||||
// Find the leaf in pathID
|
||||
found, err = f.listAll(pathID, leaf, true, false, func(item *drive.File) bool {
|
||||
if item.Title == leaf {
|
||||
pathIdOut = item.Id
|
||||
pathIDOut = item.Id
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return pathIdOut, found, err
|
||||
return pathIDOut, found, err
|
||||
}
|
||||
|
||||
// CreateDir makes a directory with pathId as parent and name leaf
|
||||
func (f *FsDrive) CreateDir(pathId, leaf string) (newId string, err error) {
|
||||
// CreateDir makes a directory with pathID as parent and name leaf
|
||||
func (f *FsDrive) CreateDir(pathID, leaf string) (newID string, err error) {
|
||||
// fmt.Println("Making", path)
|
||||
// Define the metadata for the directory we are going to create.
|
||||
createInfo := &drive.File{
|
||||
Title: leaf,
|
||||
Description: leaf,
|
||||
MimeType: driveFolderType,
|
||||
Parents: []*drive.ParentReference{{Id: pathId}},
|
||||
Parents: []*drive.ParentReference{{Id: pathID}},
|
||||
}
|
||||
var info *drive.File
|
||||
err = f.pacer.Call(func() (bool, error) {
|
||||
@ -368,11 +368,11 @@ func (f *FsDrive) CreateDir(pathId, leaf string) (newId string, err error) {
|
||||
//
|
||||
// This fetches the minimum amount of stuff but does more API calls
|
||||
// which makes it slow
|
||||
func (f *FsDrive) listDirRecursive(dirId string, path string, out fs.ObjectsChan) error {
|
||||
func (f *FsDrive) listDirRecursive(dirID string, path string, out fs.ObjectsChan) error {
|
||||
var subError error
|
||||
// Make the API request
|
||||
var wg sync.WaitGroup
|
||||
_, err := f.listAll(dirId, "", false, false, func(item *drive.File) bool {
|
||||
_, err := f.listAll(dirID, "", false, false, func(item *drive.File) bool {
|
||||
// Recurse on directories
|
||||
if item.MimeType == driveFolderType {
|
||||
wg.Add(1)
|
||||
@ -388,7 +388,6 @@ func (f *FsDrive) listDirRecursive(dirId string, path string, out fs.ObjectsChan
|
||||
}
|
||||
|
||||
}()
|
||||
return false
|
||||
} else {
|
||||
// If item has no MD5 sum it isn't stored on drive, so ignore it
|
||||
if item.Md5Checksum != "" {
|
||||
@ -417,7 +416,7 @@ func (f *FsDrive) listDirRecursive(dirId string, path string, out fs.ObjectsChan
|
||||
//
|
||||
// This is fast in terms of number of API calls, but slow in terms of
|
||||
// fetching more data than it needs
|
||||
func (f *FsDrive) listDirFull(dirId string, path string, out fs.ObjectsChan) error {
|
||||
func (f *FsDrive) listDirFull(dirID string, path string, out fs.ObjectsChan) error {
|
||||
// Orphans waiting for their parent
|
||||
orphans := make(map[string][]*drive.File)
|
||||
|
||||
@ -457,12 +456,12 @@ func (f *FsDrive) listDirFull(dirId string, path string, out fs.ObjectsChan) err
|
||||
// fmt.Printf("no parents %s %s: %#v\n", item.Title, item.Id, item)
|
||||
return false
|
||||
}
|
||||
parentId := item.Parents[0].Id
|
||||
directory, ok := f.dirCache.GetInv(parentId)
|
||||
parentID := item.Parents[0].Id
|
||||
directory, ok := f.dirCache.GetInv(parentID)
|
||||
if !ok {
|
||||
// Haven't found the parent yet so add to orphans
|
||||
// fmt.Printf("orphan[%s] %s %s\n", parentId, item.Title, item.Id)
|
||||
orphans[parentId] = append(orphans[parentId], item)
|
||||
// fmt.Printf("orphan[%s] %s %s\n", parentID, item.Title, item.Id)
|
||||
orphans[parentID] = append(orphans[parentID], item)
|
||||
} else {
|
||||
outputItem(item, directory)
|
||||
}
|
||||
@ -478,7 +477,7 @@ func (f *FsDrive) listDirFull(dirId string, path string, out fs.ObjectsChan) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsDrive) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -502,7 +501,7 @@ func (f *FsDrive) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// ListDir walks the path returning a channel of directories
|
||||
func (f *FsDrive) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -543,7 +542,7 @@ func (f *FsDrive) createFileInfo(remote string, modTime time.Time, size int64) (
|
||||
bytes: size,
|
||||
}
|
||||
|
||||
leaf, directoryId, err := f.dirCache.FindPath(remote, true)
|
||||
leaf, directoryID, err := f.dirCache.FindPath(remote, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -552,7 +551,7 @@ func (f *FsDrive) createFileInfo(remote string, modTime time.Time, size int64) (
|
||||
createInfo := &drive.File{
|
||||
Title: leaf,
|
||||
Description: leaf,
|
||||
Parents: []*drive.ParentReference{{Id: directoryId}},
|
||||
Parents: []*drive.ParentReference{{Id: directoryID}},
|
||||
MimeType: fs.MimeType(o),
|
||||
ModifiedDate: modTime.Format(timeFormatOut),
|
||||
}
|
||||
@ -638,8 +637,8 @@ func (f *FsDrive) Rmdir() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
func (fs *FsDrive) Precision() time.Duration {
|
||||
// Precision of the object storage system
|
||||
func (f *FsDrive) Precision() time.Duration {
|
||||
return time.Millisecond
|
||||
}
|
||||
|
||||
@ -714,7 +713,7 @@ func (f *FsDrive) Purge() error {
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantMove
|
||||
func (dstFs *FsDrive) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
func (f *FsDrive) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
srcObj, ok := src.(*FsObjectDrive)
|
||||
if !ok {
|
||||
fs.Debug(src, "Can't move - not same remote type")
|
||||
@ -722,13 +721,13 @@ func (dstFs *FsDrive) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
}
|
||||
|
||||
// Temporary FsObject under construction
|
||||
dstObj, dstInfo, err := dstFs.createFileInfo(remote, srcObj.ModTime(), srcObj.bytes)
|
||||
dstObj, dstInfo, err := f.createFileInfo(remote, srcObj.ModTime(), srcObj.bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Do the move
|
||||
info, err := dstFs.svc.Files.Patch(srcObj.id, dstInfo).SetModifiedDate(true).Do()
|
||||
info, err := f.svc.Files.Patch(srcObj.id, dstInfo).SetModifiedDate(true).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -737,14 +736,15 @@ func (dstFs *FsDrive) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
return dstObj, nil
|
||||
}
|
||||
|
||||
// Move src to this remote using server side move operations.
|
||||
// DirMove moves src directory to this remote using server side move
|
||||
// operations.
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantDirMove
|
||||
//
|
||||
// If destination exists then return fs.ErrorDirExists
|
||||
func (dstFs *FsDrive) DirMove(src fs.Fs) error {
|
||||
func (f *FsDrive) DirMove(src fs.Fs) error {
|
||||
srcFs, ok := src.(*FsDrive)
|
||||
if !ok {
|
||||
fs.Debug(srcFs, "Can't move directory - not same remote type")
|
||||
@ -752,14 +752,14 @@ func (dstFs *FsDrive) DirMove(src fs.Fs) error {
|
||||
}
|
||||
|
||||
// Check if destination exists
|
||||
dstFs.dirCache.ResetRoot()
|
||||
err := dstFs.dirCache.FindRoot(false)
|
||||
f.dirCache.ResetRoot()
|
||||
err := f.dirCache.FindRoot(false)
|
||||
if err == nil {
|
||||
return fs.ErrorDirExists
|
||||
}
|
||||
|
||||
// Find ID of parent
|
||||
leaf, directoryId, err := dstFs.dirCache.FindPath(dstFs.root, true)
|
||||
leaf, directoryID, err := f.dirCache.FindPath(f.root, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -767,9 +767,9 @@ func (dstFs *FsDrive) DirMove(src fs.Fs) error {
|
||||
// Do the move
|
||||
patch := drive.File{
|
||||
Title: leaf,
|
||||
Parents: []*drive.ParentReference{{Id: directoryId}},
|
||||
Parents: []*drive.ParentReference{{Id: directoryID}},
|
||||
}
|
||||
_, err = dstFs.svc.Files.Patch(srcFs.dirCache.RootID(), &patch).Do()
|
||||
_, err = f.svc.Files.Patch(srcFs.dirCache.RootID(), &patch).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -779,7 +779,7 @@ func (dstFs *FsDrive) DirMove(src fs.Fs) error {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectDrive) Fs() fs.Fs {
|
||||
return o.drive
|
||||
}
|
||||
@ -792,7 +792,7 @@ func (o *FsObjectDrive) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectDrive) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -822,12 +822,12 @@ func (o *FsObjectDrive) readMetaData() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
leaf, directoryId, err := o.drive.dirCache.FindPath(o.remote, false)
|
||||
leaf, directoryID, err := o.drive.dirCache.FindPath(o.remote, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
found, err := o.drive.listAll(directoryId, leaf, false, true, func(item *drive.File) bool {
|
||||
found, err := o.drive.listAll(directoryID, leaf, false, true, func(item *drive.File) bool {
|
||||
if item.Title == leaf {
|
||||
o.setMetaData(item)
|
||||
return true
|
||||
@ -863,7 +863,7 @@ func (o *FsObjectDrive) ModTime() time.Time {
|
||||
return modTime
|
||||
}
|
||||
|
||||
// Sets the modification time of the drive fs object
|
||||
// SetModTime sets the modification time of the drive fs object
|
||||
func (o *FsObjectDrive) SetModTime(modTime time.Time) {
|
||||
err := o.readMetaData()
|
||||
if err != nil {
|
||||
@ -890,7 +890,7 @@ func (o *FsObjectDrive) SetModTime(modTime time.Time) {
|
||||
o.setMetaData(info)
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns a boolean as to whether this object is storable
|
||||
func (o *FsObjectDrive) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ type resumableUpload struct {
|
||||
|
||||
// Upload the io.Reader in of size bytes with contentType and info
|
||||
func (f *FsDrive) Upload(in io.Reader, size int64, contentType string, info *drive.File, remote string) (*drive.File, error) {
|
||||
fileId := info.Id
|
||||
var body io.Reader = nil
|
||||
fileID := info.Id
|
||||
var body io.Reader
|
||||
body, err := googleapi.WithoutDataWrapper.JSONReader(info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -63,7 +63,7 @@ func (f *FsDrive) Upload(in io.Reader, size int64, contentType string, info *dri
|
||||
params.Set("uploadType", "resumable")
|
||||
urls := "https://www.googleapis.com/upload/drive/v2/files"
|
||||
method := "POST"
|
||||
if fileId != "" {
|
||||
if fileID != "" {
|
||||
params.Set("setModifiedDate", "true")
|
||||
urls += "/{fileId}"
|
||||
method = "PUT"
|
||||
@ -71,7 +71,7 @@ func (f *FsDrive) Upload(in io.Reader, size int64, contentType string, info *dri
|
||||
urls += "?" + params.Encode()
|
||||
req, _ := http.NewRequest(method, urls, body)
|
||||
googleapi.Expand(req.URL, map[string]string{
|
||||
"fileId": fileId,
|
||||
"fileId": fileID,
|
||||
})
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
req.Header.Set("X-Upload-Content-Type", contentType)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Dropbox interface
|
||||
// Package dropbox provides an interface to Dropbox object storage
|
||||
package dropbox
|
||||
|
||||
/*
|
||||
@ -44,7 +44,7 @@ var (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "dropbox",
|
||||
NewFs: NewFs,
|
||||
Config: configHelper,
|
||||
@ -112,12 +112,12 @@ type FsObjectDropbox struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsDropbox) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsDropbox) Root() string {
|
||||
return f.root
|
||||
}
|
||||
@ -217,7 +217,7 @@ func (f *FsDropbox) newFsObjectWithInfo(remote string, info *dropbox.Entry) fs.O
|
||||
return o
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsDropbox) NewFsObject(remote string) fs.Object {
|
||||
@ -243,7 +243,7 @@ func (f *FsDropbox) list(out fs.ObjectsChan) {
|
||||
// Track path component case, it could be different for entries coming from DropBox API
|
||||
// See https://www.dropboxforum.com/hc/communities/public/questions/201665409-Wrong-character-case-of-folder-name-when-calling-listFolder-using-Sync-API?locale=en-us
|
||||
// and https://github.com/ncw/rclone/issues/53
|
||||
nameTree := NewNameTree()
|
||||
nameTree := newNameTree()
|
||||
cursor := ""
|
||||
for {
|
||||
deltaPage, err := f.db.Delta(cursor, f.slashRoot)
|
||||
@ -317,7 +317,7 @@ func (f *FsDropbox) list(out fs.ObjectsChan) {
|
||||
nameTree.WalkFiles(f.root, walkFunc)
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsDropbox) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -327,7 +327,7 @@ func (f *FsDropbox) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// ListDir walks the path returning a channel of FsObjects
|
||||
func (f *FsDropbox) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -412,7 +412,7 @@ func (f *FsDropbox) Rmdir() error {
|
||||
return f.Purge()
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
// Precision returns the precision
|
||||
func (f *FsDropbox) Precision() time.Duration {
|
||||
return fs.ModTimeNotSupported
|
||||
}
|
||||
@ -466,7 +466,7 @@ func (f *FsDropbox) Purge() error {
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantMove
|
||||
func (dstFs *FsDropbox) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
func (f *FsDropbox) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
srcObj, ok := src.(*FsObjectDropbox)
|
||||
if !ok {
|
||||
fs.Debug(src, "Can't move - not same remote type")
|
||||
@ -474,11 +474,11 @@ func (dstFs *FsDropbox) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
}
|
||||
|
||||
// Temporary FsObject under construction
|
||||
dstObj := &FsObjectDropbox{dropbox: dstFs, remote: remote}
|
||||
dstObj := &FsObjectDropbox{dropbox: f, remote: remote}
|
||||
|
||||
srcPath := srcObj.remotePath()
|
||||
dstPath := dstObj.remotePath()
|
||||
entry, err := dstFs.db.Move(srcPath, dstPath)
|
||||
entry, err := f.db.Move(srcPath, dstPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Move failed: %s", err)
|
||||
}
|
||||
@ -486,14 +486,14 @@ func (dstFs *FsDropbox) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
return dstObj, nil
|
||||
}
|
||||
|
||||
// Move src to this remote using server side move operations.
|
||||
// DirMove moves src to this remote using server side move operations.
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantDirMove
|
||||
//
|
||||
// If destination exists then return fs.ErrorDirExists
|
||||
func (dstFs *FsDropbox) DirMove(src fs.Fs) error {
|
||||
func (f *FsDropbox) DirMove(src fs.Fs) error {
|
||||
srcFs, ok := src.(*FsDropbox)
|
||||
if !ok {
|
||||
fs.Debug(srcFs, "Can't move directory - not same remote type")
|
||||
@ -501,13 +501,13 @@ func (dstFs *FsDropbox) DirMove(src fs.Fs) error {
|
||||
}
|
||||
|
||||
// Check if destination exists
|
||||
entry, err := dstFs.db.Metadata(dstFs.slashRoot, false, false, "", "", metadataLimit)
|
||||
entry, err := f.db.Metadata(f.slashRoot, false, false, "", "", metadataLimit)
|
||||
if err == nil && !entry.IsDeleted {
|
||||
return fs.ErrorDirExists
|
||||
}
|
||||
|
||||
// Do the move
|
||||
_, err = dstFs.db.Move(srcFs.slashRoot, dstFs.slashRoot)
|
||||
_, err = f.db.Move(srcFs.slashRoot, f.slashRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("MoveDir failed: %v", err)
|
||||
}
|
||||
@ -516,7 +516,7 @@ func (dstFs *FsDropbox) DirMove(src fs.Fs) error {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectDropbox) Fs() fs.Fs {
|
||||
return o.dropbox
|
||||
}
|
||||
@ -529,7 +529,7 @@ func (o *FsObjectDropbox) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectDropbox) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -618,7 +618,7 @@ func (o *FsObjectDropbox) ModTime() time.Time {
|
||||
return o.modTime
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
//
|
||||
// Commits the datastore
|
||||
func (o *FsObjectDropbox) SetModTime(modTime time.Time) {
|
||||
@ -626,7 +626,7 @@ func (o *FsObjectDropbox) SetModTime(modTime time.Time) {
|
||||
return
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns whether this object is storable
|
||||
func (o *FsObjectDropbox) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ import (
|
||||
"github.com/stacktic/dropbox"
|
||||
)
|
||||
|
||||
type NameTreeNode struct {
|
||||
type nameTreeNode struct {
|
||||
// Map from lowercase directory name to tree node
|
||||
Directories map[string]*NameTreeNode
|
||||
Directories map[string]*nameTreeNode
|
||||
|
||||
// Map from file name (case sensitive) to dropbox entry
|
||||
Files map[string]*dropbox.Entry
|
||||
@ -22,27 +22,26 @@ type NameTreeNode struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
func newNameTreeNode(caseCorrectName string) *NameTreeNode {
|
||||
return &NameTreeNode{
|
||||
func newNameTreeNode(caseCorrectName string) *nameTreeNode {
|
||||
return &nameTreeNode{
|
||||
CaseCorrectName: caseCorrectName,
|
||||
Directories: make(map[string]*NameTreeNode),
|
||||
Directories: make(map[string]*nameTreeNode),
|
||||
Files: make(map[string]*dropbox.Entry),
|
||||
}
|
||||
}
|
||||
|
||||
func NewNameTree() *NameTreeNode {
|
||||
func newNameTree() *nameTreeNode {
|
||||
return newNameTreeNode("")
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) String() string {
|
||||
func (tree *nameTreeNode) String() string {
|
||||
if len(tree.CaseCorrectName) == 0 {
|
||||
return "NameTreeNode/<root>"
|
||||
} else {
|
||||
return fmt.Sprintf("NameTreeNode/%q", tree.CaseCorrectName)
|
||||
return "nameTreeNode/<root>"
|
||||
}
|
||||
return fmt.Sprintf("nameTreeNode/%q", tree.CaseCorrectName)
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) getTreeNode(path string) *NameTreeNode {
|
||||
func (tree *nameTreeNode) getTreeNode(path string) *nameTreeNode {
|
||||
if len(path) == 0 {
|
||||
// no lookup required, just return root
|
||||
return tree
|
||||
@ -70,7 +69,7 @@ func (tree *NameTreeNode) getTreeNode(path string) *NameTreeNode {
|
||||
return current
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) PutCaseCorrectDirectoryName(parentPath string, caseCorrectDirectoryName string) {
|
||||
func (tree *nameTreeNode) PutCaseCorrectDirectoryName(parentPath string, caseCorrectDirectoryName string) {
|
||||
if len(caseCorrectDirectoryName) == 0 {
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(tree, "PutCaseCorrectDirectoryName: empty caseCorrectDirectoryName is not allowed (parentPath: %q)", parentPath)
|
||||
@ -98,7 +97,7 @@ func (tree *NameTreeNode) PutCaseCorrectDirectoryName(parentPath string, caseCor
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) PutFile(parentPath string, caseCorrectFileName string, dropboxEntry *dropbox.Entry) {
|
||||
func (tree *nameTreeNode) PutFile(parentPath string, caseCorrectFileName string, dropboxEntry *dropbox.Entry) {
|
||||
node := tree.getTreeNode(parentPath)
|
||||
if node == nil {
|
||||
return
|
||||
@ -113,7 +112,7 @@ func (tree *NameTreeNode) PutFile(parentPath string, caseCorrectFileName string,
|
||||
node.Files[caseCorrectFileName] = dropboxEntry
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) GetPathWithCorrectCase(path string) *string {
|
||||
func (tree *nameTreeNode) GetPathWithCorrectCase(path string) *string {
|
||||
if path == "" {
|
||||
empty := ""
|
||||
return &empty
|
||||
@ -144,9 +143,9 @@ func (tree *NameTreeNode) GetPathWithCorrectCase(path string) *string {
|
||||
return &resultString
|
||||
}
|
||||
|
||||
type NameTreeFileWalkFunc func(caseCorrectFilePath string, entry *dropbox.Entry)
|
||||
type nameTreeFileWalkFunc func(caseCorrectFilePath string, entry *dropbox.Entry)
|
||||
|
||||
func (tree *NameTreeNode) walkFilesRec(currentPath string, walkFunc NameTreeFileWalkFunc) {
|
||||
func (tree *nameTreeNode) walkFilesRec(currentPath string, walkFunc nameTreeFileWalkFunc) {
|
||||
var prefix string
|
||||
if currentPath == "" {
|
||||
prefix = ""
|
||||
@ -170,7 +169,7 @@ func (tree *NameTreeNode) walkFilesRec(currentPath string, walkFunc NameTreeFile
|
||||
}
|
||||
}
|
||||
|
||||
func (tree *NameTreeNode) WalkFiles(rootPath string, walkFunc NameTreeFileWalkFunc) {
|
||||
func (tree *nameTreeNode) WalkFiles(rootPath string, walkFunc nameTreeFileWalkFunc) {
|
||||
node := tree.getTreeNode(rootPath)
|
||||
if node == nil {
|
||||
return
|
||||
|
@ -1,10 +1,10 @@
|
||||
package dropbox_test
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"github.com/ncw/rclone/dropbox"
|
||||
"testing"
|
||||
|
||||
"github.com/ncw/rclone/fs"
|
||||
dropboxapi "github.com/stacktic/dropbox"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assert(t *testing.T, shouldBeTrue bool, failMessage string) {
|
||||
@ -16,7 +16,7 @@ func assert(t *testing.T, shouldBeTrue bool, failMessage string) {
|
||||
func TestPutCaseCorrectDirectoryName(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutCaseCorrectDirectoryName("a/b", "C")
|
||||
|
||||
assert(t, tree.CaseCorrectName == "", "Root CaseCorrectName should be empty")
|
||||
@ -36,7 +36,7 @@ func TestPutCaseCorrectDirectoryName(t *testing.T) {
|
||||
func TestPutCaseCorrectDirectoryNameEmptyComponent(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutCaseCorrectDirectoryName("/a", "C")
|
||||
tree.PutCaseCorrectDirectoryName("b/", "C")
|
||||
tree.PutCaseCorrectDirectoryName("a//b", "C")
|
||||
@ -47,7 +47,7 @@ func TestPutCaseCorrectDirectoryNameEmptyComponent(t *testing.T) {
|
||||
func TestPutCaseCorrectDirectoryNameEmptyParent(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutCaseCorrectDirectoryName("", "C")
|
||||
|
||||
c := tree.Directories["c"]
|
||||
@ -59,7 +59,7 @@ func TestPutCaseCorrectDirectoryNameEmptyParent(t *testing.T) {
|
||||
func TestGetPathWithCorrectCase(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutCaseCorrectDirectoryName("a", "C")
|
||||
assert(t, tree.GetPathWithCorrectCase("a/c") == nil, "Path for 'a' should not be available")
|
||||
|
||||
@ -72,7 +72,7 @@ func TestGetPathWithCorrectCase(t *testing.T) {
|
||||
func TestPutAndWalk(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutFile("a", "F", &dropboxapi.Entry{Path: "xxx"})
|
||||
tree.PutCaseCorrectDirectoryName("", "A")
|
||||
|
||||
@ -92,7 +92,7 @@ func TestPutAndWalk(t *testing.T) {
|
||||
func TestPutAndWalkWithPrefix(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutFile("a", "F", &dropboxapi.Entry{Path: "xxx"})
|
||||
tree.PutCaseCorrectDirectoryName("", "A")
|
||||
|
||||
@ -112,7 +112,7 @@ func TestPutAndWalkWithPrefix(t *testing.T) {
|
||||
func TestPutAndWalkIncompleteTree(t *testing.T) {
|
||||
errors := fs.Stats.GetErrors()
|
||||
|
||||
tree := dropbox.NewNameTree()
|
||||
tree := newNameTree()
|
||||
tree.PutFile("a", "F", &dropboxapi.Entry{Path: "xxx"})
|
||||
|
||||
walkFunc := func(caseCorrectFilePath string, entry *dropboxapi.Entry) {
|
||||
|
@ -70,7 +70,7 @@ func (ip *inProgress) get(name string) *Account {
|
||||
// Strings returns all the strings in the stringSet
|
||||
func (ss stringSet) Strings() []string {
|
||||
strings := make([]string, 0, len(ss))
|
||||
for name, _ := range ss {
|
||||
for name := range ss {
|
||||
var out string
|
||||
if acc := Stats.inProgress.get(name); acc != nil {
|
||||
out = acc.String()
|
||||
@ -89,7 +89,7 @@ func (ss stringSet) String() string {
|
||||
return strings.Join(ss.Strings(), "\n")
|
||||
}
|
||||
|
||||
// Stats limits and accounts all transfers
|
||||
// StatsInfo limits and accounts all transfers
|
||||
type StatsInfo struct {
|
||||
lock sync.RWMutex
|
||||
bytes int64
|
||||
@ -117,12 +117,12 @@ func (s *StatsInfo) String() string {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
dt := time.Now().Sub(s.start)
|
||||
dt_seconds := dt.Seconds()
|
||||
dtSeconds := dt.Seconds()
|
||||
speed := 0.0
|
||||
if dt > 0 {
|
||||
speed = float64(s.bytes) / 1024 / dt_seconds
|
||||
speed = float64(s.bytes) / 1024 / dtSeconds
|
||||
}
|
||||
dt_rounded := dt - (dt % (time.Second / 10))
|
||||
dtRounded := dt - (dt % (time.Second / 10))
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprintf(buf, `
|
||||
Transferred: %10d Bytes (%7.2f kByte/s)
|
||||
@ -135,7 +135,7 @@ Elapsed time: %10v
|
||||
s.errors,
|
||||
s.checks,
|
||||
s.transfers,
|
||||
dt_rounded)
|
||||
dtRounded)
|
||||
if len(s.checking) > 0 {
|
||||
fmt.Fprintf(buf, "Checking:\n%s\n", s.checking)
|
||||
}
|
||||
@ -199,7 +199,7 @@ func (s *StatsInfo) Errored() bool {
|
||||
func (s *StatsInfo) Error() {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.errors += 1
|
||||
s.errors++
|
||||
}
|
||||
|
||||
// Checking adds a check into the stats
|
||||
@ -214,7 +214,7 @@ func (s *StatsInfo) DoneChecking(o Object) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
delete(s.checking, o.Remote())
|
||||
s.checks += 1
|
||||
s.checks++
|
||||
}
|
||||
|
||||
// GetTransfers reads the number of transfers
|
||||
@ -236,7 +236,7 @@ func (s *StatsInfo) DoneTransferring(o Object) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
delete(s.transferring, o.Remote())
|
||||
s.transfers += 1
|
||||
s.transfers++
|
||||
}
|
||||
|
||||
// Account limits and accounts for one transfer
|
||||
@ -324,7 +324,7 @@ func (file *Account) Read(p []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns bytes read as well as the size.
|
||||
// Progress returns bytes read as well as the size.
|
||||
// Size can be <= 0 if the size is unknown.
|
||||
func (file *Account) Progress() (bytes, size int64) {
|
||||
if file == nil {
|
||||
|
39
fs/config.go
39
fs/config.go
@ -26,17 +26,18 @@ const (
|
||||
configFileName = ".rclone.conf"
|
||||
)
|
||||
|
||||
// SizeSuffix is parsed by flag with k/M/G suffixes
|
||||
type SizeSuffix int64
|
||||
|
||||
// Global
|
||||
var (
|
||||
// Config file
|
||||
// ConfigFile is the config file data structure
|
||||
ConfigFile *goconfig.ConfigFile
|
||||
// Home directory
|
||||
// HomeDir is the home directory of the user
|
||||
HomeDir = configHome()
|
||||
// Config file path
|
||||
// ConfigPath points to the config file
|
||||
ConfigPath = path.Join(HomeDir, configFileName)
|
||||
// Global config
|
||||
// Config is the global config
|
||||
Config = &ConfigInfo{}
|
||||
// Flags
|
||||
verbose = pflag.BoolP("verbose", "v", false, "Print lots more stuff")
|
||||
@ -145,7 +146,7 @@ func Reveal(y string) string {
|
||||
return string(x)
|
||||
}
|
||||
|
||||
// Filesystem config options
|
||||
// ConfigInfo is filesystem config options
|
||||
type ConfigInfo struct {
|
||||
Verbose bool
|
||||
Quiet bool
|
||||
@ -192,7 +193,7 @@ func (ci *ConfigInfo) Transport() http.RoundTripper {
|
||||
return t
|
||||
}
|
||||
|
||||
// Transport returns an http.Client with the correct timeouts
|
||||
// Client returns an http.Client with the correct timeouts
|
||||
func (ci *ConfigInfo) Client() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: ci.Transport(),
|
||||
@ -220,7 +221,7 @@ func configHome() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Loads the config file
|
||||
// LoadConfig loads the config file
|
||||
func LoadConfig() {
|
||||
// Read some flags if set
|
||||
//
|
||||
@ -255,7 +256,7 @@ func LoadConfig() {
|
||||
startTokenBucket()
|
||||
}
|
||||
|
||||
// Save configuration file.
|
||||
// SaveConfig saves configuration file.
|
||||
func SaveConfig() {
|
||||
err := goconfig.SaveConfigFile(ConfigFile, ConfigPath)
|
||||
if err != nil {
|
||||
@ -267,7 +268,7 @@ func SaveConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
// Show an overview of the config file
|
||||
// ShowRemotes shows an overview of the config file
|
||||
func ShowRemotes() {
|
||||
remotes := ConfigFile.GetSectionList()
|
||||
if len(remotes) == 0 {
|
||||
@ -288,7 +289,7 @@ func ChooseRemote() string {
|
||||
return Choose("remote", remotes, nil, false)
|
||||
}
|
||||
|
||||
// Read some input
|
||||
// ReadLine reads some input
|
||||
func ReadLine() string {
|
||||
buf := bufio.NewReader(os.Stdin)
|
||||
line, err := buf.ReadString('\n')
|
||||
@ -320,7 +321,7 @@ func Command(commands []string) byte {
|
||||
}
|
||||
}
|
||||
|
||||
// Asks the user for Yes or No and returns true or false
|
||||
// Confirm asks the user for Yes or No and returns true or false
|
||||
func Confirm() bool {
|
||||
return Command([]string{"yYes", "nNo"}) == 'y'
|
||||
}
|
||||
@ -357,7 +358,7 @@ func Choose(what string, defaults, help []string, newOk bool) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Show the contents of the remote
|
||||
// ShowRemote shows the contents of the remote
|
||||
func ShowRemote(name string) {
|
||||
fmt.Printf("--------------------\n")
|
||||
fmt.Printf("[%s]\n", name)
|
||||
@ -367,7 +368,7 @@ func ShowRemote(name string) {
|
||||
fmt.Printf("--------------------\n")
|
||||
}
|
||||
|
||||
// Print the contents of the remote and ask if it is OK
|
||||
// OkRemote prints the contents of the remote and ask if it is OK
|
||||
func OkRemote(name string) bool {
|
||||
ShowRemote(name)
|
||||
switch i := Command([]string{"yYes this is OK", "eEdit this remote", "dDelete this remote"}); i {
|
||||
@ -384,7 +385,7 @@ func OkRemote(name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Runs the config helper for the remote if needed
|
||||
// RemoteConfig runs the config helper for the remote if needed
|
||||
func RemoteConfig(name string) {
|
||||
fmt.Printf("Remote config\n")
|
||||
fsName := ConfigFile.MustValue(name, "type")
|
||||
@ -400,7 +401,7 @@ func RemoteConfig(name string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Choose an option
|
||||
// ChooseOption asks the user to choose an option
|
||||
func ChooseOption(o *Option) string {
|
||||
fmt.Println(o.Help)
|
||||
if len(o.Examples) > 0 {
|
||||
@ -416,7 +417,7 @@ func ChooseOption(o *Option) string {
|
||||
return ReadLine()
|
||||
}
|
||||
|
||||
// Make a new remote
|
||||
// NewRemote make a new remote from its name
|
||||
func NewRemote(name string) {
|
||||
fmt.Printf("What type of source is it?\n")
|
||||
types := []string{}
|
||||
@ -440,7 +441,7 @@ func NewRemote(name string) {
|
||||
EditRemote(name)
|
||||
}
|
||||
|
||||
// Edit a remote
|
||||
// EditRemote gets the user to edit a remote
|
||||
func EditRemote(name string) {
|
||||
ShowRemote(name)
|
||||
fmt.Printf("Edit remote\n")
|
||||
@ -462,13 +463,13 @@ func EditRemote(name string) {
|
||||
SaveConfig()
|
||||
}
|
||||
|
||||
// Delete a remote
|
||||
// DeleteRemote gets the user to delete a remote
|
||||
func DeleteRemote(name string) {
|
||||
ConfigFile.DeleteSection(name)
|
||||
SaveConfig()
|
||||
}
|
||||
|
||||
// Edit the config file interactively
|
||||
// EditConfig edits the config file interactively
|
||||
func EditConfig() {
|
||||
for {
|
||||
haveRemotes := len(ConfigFile.GetSectionList()) != 0
|
||||
|
93
fs/fs.go
93
fs/fs.go
@ -1,4 +1,4 @@
|
||||
// Generic file system interface for rclone object storage systems
|
||||
// Package fs is a generic file system interface for rclone object storage systems
|
||||
package fs
|
||||
|
||||
import (
|
||||
@ -12,26 +12,27 @@ import (
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// User agent for Fs which can set it
|
||||
// UserAgent for Fs which can set it
|
||||
UserAgent = "rclone/" + Version
|
||||
// Very large precision value to show mod time isn't supported
|
||||
// ModTimeNotSupported is a very large precision value to show
|
||||
// mod time isn't supported on this Fs
|
||||
ModTimeNotSupported = 100 * 365 * 24 * time.Hour
|
||||
)
|
||||
|
||||
// Globals
|
||||
var (
|
||||
// Filesystem registry
|
||||
fsRegistry []*FsInfo
|
||||
// Error returned by NewFs if not found in config file
|
||||
NotFoundInConfigFile = fmt.Errorf("Didn't find section in config file")
|
||||
ErrorCantCopy = fmt.Errorf("Can't copy object - incompatible remotes")
|
||||
ErrorCantMove = fmt.Errorf("Can't copy object - incompatible remotes")
|
||||
ErrorCantDirMove = fmt.Errorf("Can't copy directory - incompatible remotes")
|
||||
ErrorDirExists = fmt.Errorf("Can't copy directory - destination already exists")
|
||||
fsRegistry []*Info
|
||||
// ErrorNotFoundInConfigFile is returned by NewFs if not found in config file
|
||||
ErrorNotFoundInConfigFile = fmt.Errorf("Didn't find section in config file")
|
||||
ErrorCantCopy = fmt.Errorf("Can't copy object - incompatible remotes")
|
||||
ErrorCantMove = fmt.Errorf("Can't copy object - incompatible remotes")
|
||||
ErrorCantDirMove = fmt.Errorf("Can't copy directory - incompatible remotes")
|
||||
ErrorDirExists = fmt.Errorf("Can't copy directory - destination already exists")
|
||||
)
|
||||
|
||||
// Filesystem info
|
||||
type FsInfo struct {
|
||||
// Info information about a filesystem
|
||||
type Info struct {
|
||||
// Name of this fs
|
||||
Name string
|
||||
// Create a new file system. If root refers to an existing
|
||||
@ -44,7 +45,7 @@ type FsInfo struct {
|
||||
Options []Option
|
||||
}
|
||||
|
||||
// An options for a Fs
|
||||
// Option is describes an option for the config wizard
|
||||
type Option struct {
|
||||
Name string
|
||||
Help string
|
||||
@ -52,7 +53,7 @@ type Option struct {
|
||||
Examples []OptionExample
|
||||
}
|
||||
|
||||
// An example for an option
|
||||
// OptionExample describes an example for an Option
|
||||
type OptionExample struct {
|
||||
Value string
|
||||
Help string
|
||||
@ -61,16 +62,16 @@ type OptionExample struct {
|
||||
// Register a filesystem
|
||||
//
|
||||
// Fs modules should use this in an init() function
|
||||
func Register(info *FsInfo) {
|
||||
func Register(info *Info) {
|
||||
fsRegistry = append(fsRegistry, info)
|
||||
}
|
||||
|
||||
// A Filesystem, describes the local filesystem and the remote object store
|
||||
// Fs is the interface a cloud storage system must provide
|
||||
type Fs interface {
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
Name() string
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
Root() string
|
||||
|
||||
// String returns a description of the FS
|
||||
@ -79,10 +80,10 @@ type Fs interface {
|
||||
// List the Fs into a channel
|
||||
List() ObjectsChan
|
||||
|
||||
// List the Fs directories/buckets/containers into a channel
|
||||
// ListDir lists the Fs directories/buckets/containers into a channel
|
||||
ListDir() DirChan
|
||||
|
||||
// Find the Object at remote. Returns nil if can't be found
|
||||
// NewFsObject finds the Object at remote. Returns nil if can't be found
|
||||
NewFsObject(remote string) Object
|
||||
|
||||
// Put in to the remote path with the modTime given of the given size
|
||||
@ -92,12 +93,12 @@ type Fs interface {
|
||||
// nil and the error
|
||||
Put(in io.Reader, remote string, modTime time.Time, size int64) (Object, error)
|
||||
|
||||
// Make the directory (container, bucket)
|
||||
// Mkdir makes the directory (container, bucket)
|
||||
//
|
||||
// Shouldn't return an error if it already exists
|
||||
Mkdir() error
|
||||
|
||||
// Remove the directory (container, bucket) if empty
|
||||
// Rmdir removes the directory (container, bucket) if empty
|
||||
//
|
||||
// Return an error if it doesn't exist or isn't empty
|
||||
Rmdir() error
|
||||
@ -106,8 +107,7 @@ type Fs interface {
|
||||
Precision() time.Duration
|
||||
}
|
||||
|
||||
// A filesystem like object which can either be a remote object or a
|
||||
// local file/directory
|
||||
// Object is a filesystem like object provided by an Fs
|
||||
type Object interface {
|
||||
// String returns a description of the Object
|
||||
String() string
|
||||
@ -145,7 +145,7 @@ type Object interface {
|
||||
Remove() error
|
||||
}
|
||||
|
||||
// Optional interfaces
|
||||
// Purger is an optional interfaces for Fs
|
||||
type Purger interface {
|
||||
// Purge all files in the root and the root directory
|
||||
//
|
||||
@ -156,6 +156,7 @@ type Purger interface {
|
||||
Purge() error
|
||||
}
|
||||
|
||||
// Copier is an optional interface for Fs
|
||||
type Copier interface {
|
||||
// Copy src to this remote using server side copy operations.
|
||||
//
|
||||
@ -169,6 +170,7 @@ type Copier interface {
|
||||
Copy(src Object, remote string) (Object, error)
|
||||
}
|
||||
|
||||
// Mover is an optional interface for Fs
|
||||
type Mover interface {
|
||||
// Move src to this remote using server side move operations.
|
||||
//
|
||||
@ -182,8 +184,10 @@ type Mover interface {
|
||||
Move(src Object, remote string) (Object, error)
|
||||
}
|
||||
|
||||
// DirMover is an optional interface for Fs
|
||||
type DirMover interface {
|
||||
// Move src to this remote using server side move operations.
|
||||
// DirMove moves src to this remote using server side move
|
||||
// operations.
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
@ -193,7 +197,8 @@ type DirMover interface {
|
||||
DirMove(src Fs) error
|
||||
}
|
||||
|
||||
// An optional interface for error as to whether the operation should be retried
|
||||
// Retry is optional interface for error as to whether the operation
|
||||
// should be retried at a high level.
|
||||
//
|
||||
// This should be returned from Update or Put methods as required
|
||||
type Retry interface {
|
||||
@ -201,7 +206,7 @@ type Retry interface {
|
||||
Retry() bool
|
||||
}
|
||||
|
||||
// A type of error
|
||||
// retryError is a type of error
|
||||
type retryError string
|
||||
|
||||
// Error interface
|
||||
@ -228,7 +233,7 @@ type plainRetryError struct {
|
||||
}
|
||||
|
||||
// Retry interface
|
||||
func (_ plainRetryError) Retry() bool {
|
||||
func (err plainRetryError) Retry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -240,21 +245,22 @@ func RetryError(err error) error {
|
||||
return plainRetryError{err}
|
||||
}
|
||||
|
||||
// A channel of Objects
|
||||
// ObjectsChan is a channel of Objects
|
||||
type ObjectsChan chan Object
|
||||
|
||||
// A slice of Objects
|
||||
// Objects is a slice of Object~s
|
||||
type Objects []Object
|
||||
|
||||
// A pair of Objects
|
||||
// ObjectPair is a pair of Objects used to describe a potential copy
|
||||
// operation.
|
||||
type ObjectPair struct {
|
||||
src, dst Object
|
||||
}
|
||||
|
||||
// A channel of ObjectPair
|
||||
// ObjectPairChan is a channel of ObjectPair
|
||||
type ObjectPairChan chan ObjectPair
|
||||
|
||||
// A structure of directory/container/bucket lists
|
||||
// Dir describes a directory for directory/container/bucket lists
|
||||
type Dir struct {
|
||||
Name string // name of the directory
|
||||
When time.Time // modification or creation time - IsZero for unknown
|
||||
@ -262,13 +268,13 @@ type Dir struct {
|
||||
Count int64 // number of objects -1 for unknown
|
||||
}
|
||||
|
||||
// A channel of Dir objects
|
||||
// DirChan is a channel of Dir objects
|
||||
type DirChan chan *Dir
|
||||
|
||||
// Finds a FsInfo object for the name passed in
|
||||
// Find looks for an Info object for the name passed in
|
||||
//
|
||||
// Services are looked up in the config file
|
||||
func Find(name string) (*FsInfo, error) {
|
||||
func Find(name string) (*Info, error) {
|
||||
for _, item := range fsRegistry {
|
||||
if item.Name == name {
|
||||
return item, nil
|
||||
@ -297,7 +303,7 @@ func NewFs(path string) (Fs, error) {
|
||||
var err error
|
||||
fsName, err = ConfigFile.GetValue(configName, "type")
|
||||
if err != nil {
|
||||
return nil, NotFoundInConfigFile
|
||||
return nil, ErrorNotFoundInConfigFile
|
||||
}
|
||||
}
|
||||
fs, err := Find(fsName)
|
||||
@ -309,7 +315,7 @@ func NewFs(path string) (Fs, error) {
|
||||
return fs.NewFs(configName, fsPath)
|
||||
}
|
||||
|
||||
// Outputs log for object
|
||||
// OutputLog logs for an object
|
||||
func OutputLog(o interface{}, text string, args ...interface{}) {
|
||||
description := ""
|
||||
if o != nil {
|
||||
@ -319,22 +325,23 @@ func OutputLog(o interface{}, text string, args ...interface{}) {
|
||||
log.Print(description + out)
|
||||
}
|
||||
|
||||
// Write debuging output for this Object or Fs
|
||||
// Debug writes debuging output for this Object or Fs
|
||||
func Debug(o interface{}, text string, args ...interface{}) {
|
||||
if Config.Verbose {
|
||||
OutputLog(o, text, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Write log output for this Object or Fs
|
||||
// Log writes log output for this Object or Fs
|
||||
func Log(o interface{}, text string, args ...interface{}) {
|
||||
if !Config.Quiet {
|
||||
OutputLog(o, text, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Write error log output for this Object or Fs
|
||||
// Unconditionally logs a message regardless of Config.Quiet or Config.Verbose
|
||||
// ErrorLog writes error log output for this Object or Fs. It
|
||||
// unconditionally logs a message regardless of Config.Quiet or
|
||||
// Config.Verbose.
|
||||
func ErrorLog(o interface{}, text string, args ...interface{}) {
|
||||
OutputLog(o, text, args...)
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// This defines a Limited Fs which can only return the Objects passed in from the Fs passed in
|
||||
// Limited defines a Fs which can only return the Objects passed in
|
||||
// from the Fs passed in
|
||||
type Limited struct {
|
||||
objects []Object
|
||||
fs Fs
|
||||
@ -21,12 +22,12 @@ func NewLimited(fs Fs, objects ...Object) Fs {
|
||||
return f
|
||||
}
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name is name of the remote (as passed into NewFs)
|
||||
func (f *Limited) Name() string {
|
||||
return f.fs.Name() // return name of underlying remote
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root is the root of the remote (as passed into NewFs)
|
||||
func (f *Limited) Root() string {
|
||||
return f.fs.Root() // return root of underlying remote
|
||||
}
|
||||
@ -48,14 +49,14 @@ func (f *Limited) List() ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// List the Fs directories/buckets/containers into a channel
|
||||
// ListDir lists the Fs directories/buckets/containers into a channel
|
||||
func (f *Limited) ListDir() DirChan {
|
||||
out := make(DirChan, Config.Checkers)
|
||||
close(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// Find the Object at remote. Returns nil if can't be found
|
||||
// NewFsObject finds the Object at remote. Returns nil if can't be found
|
||||
func (f *Limited) NewFsObject(remote string) Object {
|
||||
for _, obj := range f.objects {
|
||||
if obj.Remote() == remote {
|
||||
@ -78,13 +79,13 @@ func (f *Limited) Put(in io.Reader, remote string, modTime time.Time, size int64
|
||||
return obj, obj.Update(in, modTime, size)
|
||||
}
|
||||
|
||||
// Make the directory (container, bucket)
|
||||
// Mkdir make the directory (container, bucket)
|
||||
func (f *Limited) Mkdir() error {
|
||||
// All directories are already made - just ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the directory (container, bucket) if empty
|
||||
// Rmdir removes the directory (container, bucket) if empty
|
||||
func (f *Limited) Rmdir() error {
|
||||
// Ignore this in a limited fs
|
||||
return nil
|
||||
|
@ -10,16 +10,16 @@ import (
|
||||
|
||||
const separator = "------------------------------------------------------------"
|
||||
|
||||
// An http transport which logs the traffic
|
||||
type loggedTransport struct {
|
||||
// LoggedTransport is an http transport which logs the traffic
|
||||
type LoggedTransport struct {
|
||||
wrapped http.RoundTripper
|
||||
logBody bool
|
||||
}
|
||||
|
||||
// NewLoggedTransport wraps the transport passed in and logs all roundtrips
|
||||
// including the body if logBody is set.
|
||||
func NewLoggedTransport(transport http.RoundTripper, logBody bool) *loggedTransport {
|
||||
return &loggedTransport{
|
||||
func NewLoggedTransport(transport http.RoundTripper, logBody bool) *LoggedTransport {
|
||||
return &LoggedTransport{
|
||||
wrapped: transport,
|
||||
logBody: logBody,
|
||||
}
|
||||
@ -28,7 +28,7 @@ func NewLoggedTransport(transport http.RoundTripper, logBody bool) *loggedTransp
|
||||
// CancelRequest cancels an in-flight request by closing its
|
||||
// connection. CancelRequest should only be called after RoundTrip has
|
||||
// returned.
|
||||
func (t *loggedTransport) CancelRequest(req *http.Request) {
|
||||
func (t *LoggedTransport) CancelRequest(req *http.Request) {
|
||||
if wrapped, ok := t.wrapped.(interface {
|
||||
CancelRequest(*http.Request)
|
||||
}); ok {
|
||||
@ -38,7 +38,7 @@ func (t *loggedTransport) CancelRequest(req *http.Request) {
|
||||
}
|
||||
|
||||
// RoundTrip implements the RoundTripper interface.
|
||||
func (t *loggedTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
func (t *LoggedTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
buf, _ := httputil.DumpRequest(req, t.logBody)
|
||||
log.Println(separator)
|
||||
log.Println("HTTP REQUEST")
|
||||
|
@ -11,7 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Work out modify window for fses passed in - sets Config.ModifyWindow
|
||||
// CalculateModifyWindow works out modify window for Fses passed in -
|
||||
// sets Config.ModifyWindow
|
||||
//
|
||||
// This is the largest modify window of all the fses in use, and the
|
||||
// user configured value
|
||||
@ -39,7 +40,7 @@ func Md5sumsEqual(src, dst string) bool {
|
||||
return src == dst
|
||||
}
|
||||
|
||||
// Check the two files to see if the MD5sums are the same
|
||||
// CheckMd5sums checks the two files to see if the MD5sums are the same
|
||||
//
|
||||
// Returns two bools, the first of which is equality and the second of
|
||||
// which is true if either of the MD5SUMs were unset.
|
||||
@ -71,7 +72,7 @@ func CheckMd5sums(src, dst Object) (equal bool, unset bool, err error) {
|
||||
return Md5sumsEqual(srcMd5, dstMd5), false, nil
|
||||
}
|
||||
|
||||
// Checks to see if the src and dst objects are equal by looking at
|
||||
// Equal checks to see if the src and dst objects are equal by looking at
|
||||
// size, mtime and MD5SUM
|
||||
//
|
||||
// If the src and dst size are different then it is considered to be
|
||||
@ -139,7 +140,7 @@ func Equal(src, dst Object) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a guess at the mime type from the extension
|
||||
// MimeType returns a guess at the mime type from the extension
|
||||
func MimeType(o Object) string {
|
||||
mimeType := mime.TypeByExtension(path.Ext(o.Remote()))
|
||||
if mimeType == "" {
|
||||
@ -281,7 +282,7 @@ func checkOne(pair ObjectPair, out ObjectPairChan) {
|
||||
out <- pair
|
||||
}
|
||||
|
||||
// Read Objects~s on in send to out if they need uploading
|
||||
// PairChecker reads Objects~s on in send to out if they need transferring.
|
||||
//
|
||||
// FIXME potentially doing lots of MD5SUMS at once
|
||||
func PairChecker(in ObjectPairChan, out ObjectPairChan, wg *sync.WaitGroup) {
|
||||
@ -294,7 +295,7 @@ func PairChecker(in ObjectPairChan, out ObjectPairChan, wg *sync.WaitGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read Objects on in and copy them
|
||||
// PairCopier reads Objects on in and copies them.
|
||||
func PairCopier(in ObjectPairChan, fdst Fs, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
for pair := range in {
|
||||
@ -309,7 +310,8 @@ func PairCopier(in ObjectPairChan, fdst Fs, wg *sync.WaitGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read Objects on in and move them if possible, or copy them if not
|
||||
// PairMover reads Objects on in and moves them if possible, or copies
|
||||
// them if not
|
||||
func PairMover(in ObjectPairChan, fdst Fs, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
// See if we have Move available
|
||||
@ -343,14 +345,14 @@ func PairMover(in ObjectPairChan, fdst Fs, wg *sync.WaitGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all the files passed in the channel
|
||||
func DeleteFiles(to_be_deleted ObjectsChan) {
|
||||
// DeleteFiles removes all the files passed in the channel
|
||||
func DeleteFiles(toBeDeleted ObjectsChan) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(Config.Transfers)
|
||||
for i := 0; i < Config.Transfers; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for dst := range to_be_deleted {
|
||||
for dst := range toBeDeleted {
|
||||
if Config.DryRun {
|
||||
Debug(dst, "Not deleting as --dry-run")
|
||||
} else {
|
||||
@ -385,8 +387,8 @@ func readFilesMap(fs Fs) map[string]Object {
|
||||
return files
|
||||
}
|
||||
|
||||
// Returns true if fdst and fsrc point to the same underlying Fs
|
||||
func FsSame(fdst, fsrc Fs) bool {
|
||||
// Same returns true if fdst and fsrc point to the same underlying Fs
|
||||
func Same(fdst, fsrc Fs) bool {
|
||||
return fdst.Name() == fsrc.Name() && fdst.Root() == fsrc.Root()
|
||||
}
|
||||
|
||||
@ -396,7 +398,7 @@ func FsSame(fdst, fsrc Fs) bool {
|
||||
//
|
||||
// If DoMove is true then files will be moved instead of copied
|
||||
func syncCopyMove(fdst, fsrc Fs, Delete bool, DoMove bool) error {
|
||||
if FsSame(fdst, fsrc) {
|
||||
if Same(fdst, fsrc) {
|
||||
ErrorLog(fdst, "Nothing to do as source and destination are the same")
|
||||
return nil
|
||||
}
|
||||
@ -414,22 +416,22 @@ func syncCopyMove(fdst, fsrc Fs, Delete bool, DoMove bool) error {
|
||||
delFiles := readFilesMap(fdst)
|
||||
|
||||
// Read source files checking them off against dest files
|
||||
to_be_checked := make(ObjectPairChan, Config.Transfers)
|
||||
to_be_uploaded := make(ObjectPairChan, Config.Transfers)
|
||||
toBeChecked := make(ObjectPairChan, Config.Transfers)
|
||||
toBeUploaded := make(ObjectPairChan, Config.Transfers)
|
||||
|
||||
var checkerWg sync.WaitGroup
|
||||
checkerWg.Add(Config.Checkers)
|
||||
for i := 0; i < Config.Checkers; i++ {
|
||||
go PairChecker(to_be_checked, to_be_uploaded, &checkerWg)
|
||||
go PairChecker(toBeChecked, toBeUploaded, &checkerWg)
|
||||
}
|
||||
|
||||
var copierWg sync.WaitGroup
|
||||
copierWg.Add(Config.Transfers)
|
||||
for i := 0; i < Config.Transfers; i++ {
|
||||
if DoMove {
|
||||
go PairMover(to_be_uploaded, fdst, &copierWg)
|
||||
go PairMover(toBeUploaded, fdst, &copierWg)
|
||||
} else {
|
||||
go PairCopier(to_be_uploaded, fdst, &copierWg)
|
||||
go PairCopier(toBeUploaded, fdst, &copierWg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,18 +441,18 @@ func syncCopyMove(fdst, fsrc Fs, Delete bool, DoMove bool) error {
|
||||
dst, found := delFiles[remote]
|
||||
if found {
|
||||
delete(delFiles, remote)
|
||||
to_be_checked <- ObjectPair{src, dst}
|
||||
toBeChecked <- ObjectPair{src, dst}
|
||||
} else {
|
||||
// No need to check since doesn't exist
|
||||
to_be_uploaded <- ObjectPair{src, nil}
|
||||
toBeUploaded <- ObjectPair{src, nil}
|
||||
}
|
||||
}
|
||||
close(to_be_checked)
|
||||
close(toBeChecked)
|
||||
}()
|
||||
|
||||
Log(fdst, "Waiting for checks to finish")
|
||||
checkerWg.Wait()
|
||||
close(to_be_uploaded)
|
||||
close(toBeUploaded)
|
||||
Log(fdst, "Waiting for transfers to finish")
|
||||
copierWg.Wait()
|
||||
|
||||
@ -474,19 +476,19 @@ func syncCopyMove(fdst, fsrc Fs, Delete bool, DoMove bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Syncs fsrc into fdst
|
||||
// Sync fsrc into fdst
|
||||
func Sync(fdst, fsrc Fs) error {
|
||||
return syncCopyMove(fdst, fsrc, true, false)
|
||||
}
|
||||
|
||||
// Copies fsrc into fdst
|
||||
// CopyDir copies fsrc into fdst
|
||||
func CopyDir(fdst, fsrc Fs) error {
|
||||
return syncCopyMove(fdst, fsrc, false, false)
|
||||
}
|
||||
|
||||
// Moves fsrc into fdst
|
||||
// MoveDir moves fsrc into fdst
|
||||
func MoveDir(fdst, fsrc Fs) error {
|
||||
if FsSame(fdst, fsrc) {
|
||||
if Same(fdst, fsrc) {
|
||||
ErrorLog(fdst, "Nothing to do as source and destination are the same")
|
||||
return nil
|
||||
}
|
||||
@ -517,7 +519,7 @@ func MoveDir(fdst, fsrc Fs) error {
|
||||
return Purge(fsrc)
|
||||
}
|
||||
|
||||
// Checks the files in fsrc and fdst according to Size and MD5SUM
|
||||
// Check the files in fsrc and fdst according to Size and MD5SUM
|
||||
func Check(fdst, fsrc Fs) error {
|
||||
Log(fdst, "Building file list")
|
||||
|
||||
@ -597,7 +599,7 @@ func Check(fdst, fsrc Fs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List the Fs to the supplied function
|
||||
// ListFn lists the Fs to the supplied function
|
||||
//
|
||||
// Lists in parallel which may get them out of order
|
||||
func ListFn(f Fs, fn func(Object)) error {
|
||||
@ -639,7 +641,7 @@ func List(f Fs, w io.Writer) error {
|
||||
})
|
||||
}
|
||||
|
||||
// List the Fs to the supplied writer
|
||||
// ListLong lists the Fs to the supplied writer
|
||||
//
|
||||
// Shows size, mod time and path
|
||||
//
|
||||
@ -653,7 +655,7 @@ func ListLong(f Fs, w io.Writer) error {
|
||||
})
|
||||
}
|
||||
|
||||
// List the Fs to the supplied writer
|
||||
// Md5sum list the Fs to the supplied writer
|
||||
//
|
||||
// Produces the same output as the md5sum command
|
||||
//
|
||||
@ -671,7 +673,7 @@ func Md5sum(f Fs, w io.Writer) error {
|
||||
})
|
||||
}
|
||||
|
||||
// List the directories/buckets/containers in the Fs to the supplied writer
|
||||
// ListDir lists the directories/buckets/containers in the Fs to the supplied writer
|
||||
func ListDir(f Fs, w io.Writer) error {
|
||||
for dir := range f.ListDir() {
|
||||
syncFprintf(w, "%12d %13s %9d %s\n", dir.Bytes, dir.When.Format("2006-01-02 15:04:05"), dir.Count, dir.Name)
|
||||
@ -679,7 +681,7 @@ func ListDir(f Fs, w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Makes a destination directory or container
|
||||
// Mkdir makes a destination directory or container
|
||||
func Mkdir(f Fs) error {
|
||||
err := f.Mkdir()
|
||||
if err != nil {
|
||||
@ -689,7 +691,7 @@ func Mkdir(f Fs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes a container but not if not empty
|
||||
// Rmdir removes a container but not if not empty
|
||||
func Rmdir(f Fs) error {
|
||||
if Config.DryRun {
|
||||
Log(f, "Not deleting as dry run is set")
|
||||
@ -703,7 +705,7 @@ func Rmdir(f Fs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes a container and all of its contents
|
||||
// Purge removes a container and all of its contents
|
||||
//
|
||||
// FIXME doesn't delete local directories
|
||||
func Purge(f Fs) error {
|
||||
|
@ -1,3 +1,4 @@
|
||||
package fs
|
||||
|
||||
// Version of rclone
|
||||
const Version = "v1.20"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Utilities for testing the Fs
|
||||
// Package fstest provides utilities for testing the Fs
|
||||
package fstest
|
||||
|
||||
// FIXME put name of test FS in Fs structure
|
||||
@ -22,7 +22,7 @@ func init() {
|
||||
|
||||
}
|
||||
|
||||
// Represents an item for checking
|
||||
// Item represents an item for checking
|
||||
type Item struct {
|
||||
Path string
|
||||
Md5sum string
|
||||
@ -31,7 +31,8 @@ type Item struct {
|
||||
WinPath string
|
||||
}
|
||||
|
||||
// Checks the times are equal within the precision, returns the delta and a flag
|
||||
// CheckTimeEqualWithPrecision checks the times are equal within the
|
||||
// precision, returns the delta and a flag
|
||||
func CheckTimeEqualWithPrecision(t0, t1 time.Time, precision time.Duration) (time.Duration, bool) {
|
||||
dt := t0.Sub(t1)
|
||||
if dt >= precision || dt <= -precision {
|
||||
@ -40,7 +41,7 @@ func CheckTimeEqualWithPrecision(t0, t1 time.Time, precision time.Duration) (tim
|
||||
return dt, true
|
||||
}
|
||||
|
||||
// check the mod time to the given precision
|
||||
// CheckModTime checks the mod time to the given precision
|
||||
func (i *Item) CheckModTime(t *testing.T, obj fs.Object, modTime time.Time, precision time.Duration) {
|
||||
dt, ok := CheckTimeEqualWithPrecision(modTime, i.ModTime, precision)
|
||||
if !ok {
|
||||
@ -48,6 +49,7 @@ func (i *Item) CheckModTime(t *testing.T, obj fs.Object, modTime time.Time, prec
|
||||
}
|
||||
}
|
||||
|
||||
// Check checks all the attributes of the object are correct
|
||||
func (i *Item) Check(t *testing.T, obj fs.Object, precision time.Duration) {
|
||||
if obj == nil {
|
||||
t.Fatalf("Object is nil")
|
||||
@ -66,14 +68,14 @@ func (i *Item) Check(t *testing.T, obj fs.Object, precision time.Duration) {
|
||||
i.CheckModTime(t, obj, obj.ModTime(), precision)
|
||||
}
|
||||
|
||||
// Represents all items for checking
|
||||
// Items represents all items for checking
|
||||
type Items struct {
|
||||
byName map[string]*Item
|
||||
byNameAlt map[string]*Item
|
||||
items []Item
|
||||
}
|
||||
|
||||
// Make an Items
|
||||
// NewItems makes an Items
|
||||
func NewItems(items []Item) *Items {
|
||||
is := &Items{
|
||||
byName: make(map[string]*Item),
|
||||
@ -88,7 +90,7 @@ func NewItems(items []Item) *Items {
|
||||
return is
|
||||
}
|
||||
|
||||
// Check off an item
|
||||
// Find checks off an item
|
||||
func (is *Items) Find(t *testing.T, obj fs.Object, precision time.Duration) {
|
||||
i, ok := is.byName[obj.Remote()]
|
||||
if !ok {
|
||||
@ -103,7 +105,7 @@ func (is *Items) Find(t *testing.T, obj fs.Object, precision time.Duration) {
|
||||
i.Check(t, obj, precision)
|
||||
}
|
||||
|
||||
// Check all done
|
||||
// Done checks all finished
|
||||
func (is *Items) Done(t *testing.T) {
|
||||
if len(is.byName) != 0 {
|
||||
for name := range is.byName {
|
||||
@ -113,7 +115,8 @@ func (is *Items) Done(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the fs to see if it has the expected contents
|
||||
// CheckListingWithPrecision checks the fs to see if it has the
|
||||
// expected contents with the given precision.
|
||||
func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision time.Duration) {
|
||||
is := NewItems(items)
|
||||
oldErrors := fs.Stats.GetErrors()
|
||||
@ -143,13 +146,13 @@ func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision ti
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the fs to see if it has the expected contents
|
||||
// CheckListing checks the fs to see if it has the expected contents
|
||||
func CheckListing(t *testing.T, f fs.Fs, items []Item) {
|
||||
precision := f.Precision()
|
||||
CheckListingWithPrecision(t, f, items, precision)
|
||||
}
|
||||
|
||||
// Parse a time string or explode
|
||||
// Time parses a time string or logs a fatal error
|
||||
func Time(timeString string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339Nano, timeString)
|
||||
if err != nil {
|
||||
@ -158,7 +161,7 @@ func Time(timeString string) time.Time {
|
||||
return t
|
||||
}
|
||||
|
||||
// Create a random string
|
||||
// RandomString create a random string for test purposes
|
||||
func RandomString(n int) string {
|
||||
source := "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
out := make([]byte, n)
|
||||
@ -168,7 +171,7 @@ func RandomString(n int) string {
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// Creates a temporary directory name for local remotes
|
||||
// LocalRemote creates a temporary directory name for local remotes
|
||||
func LocalRemote() (path string, err error) {
|
||||
path, err = ioutil.TempDir("", "rclone")
|
||||
if err == nil {
|
||||
@ -179,7 +182,7 @@ func LocalRemote() (path string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Make a random bucket or subdirectory name
|
||||
// RandomRemoteName makes a random bucket or subdirectory name
|
||||
//
|
||||
// Returns a random remote name plus the leaf name
|
||||
func RandomRemoteName(remoteName string) (string, string, error) {
|
||||
@ -202,7 +205,7 @@ func RandomRemoteName(remoteName string) (string, string, error) {
|
||||
return remoteName, leafName, nil
|
||||
}
|
||||
|
||||
// Make a random bucket or subdirectory on the remote
|
||||
// RandomRemote makes a random bucket or subdirectory on the remote
|
||||
//
|
||||
// Call the finalise function returned to Purge the fs at the end (and
|
||||
// the parent if necessary)
|
||||
@ -241,6 +244,7 @@ func RandomRemote(remoteName string, subdir bool) (fs.Fs, func(), error) {
|
||||
return remote, finalise, nil
|
||||
}
|
||||
|
||||
// TestMkdir tests Mkdir works
|
||||
func TestMkdir(t *testing.T, remote fs.Fs) {
|
||||
err := fs.Mkdir(remote)
|
||||
if err != nil {
|
||||
@ -249,6 +253,7 @@ func TestMkdir(t *testing.T, remote fs.Fs) {
|
||||
CheckListing(t, remote, []Item{})
|
||||
}
|
||||
|
||||
// TestPurge tests Purge works
|
||||
func TestPurge(t *testing.T, remote fs.Fs) {
|
||||
err := fs.Purge(remote)
|
||||
if err != nil {
|
||||
@ -257,6 +262,7 @@ func TestPurge(t *testing.T, remote fs.Fs) {
|
||||
CheckListing(t, remote, []Item{})
|
||||
}
|
||||
|
||||
// TestRmdir tests Rmdir works
|
||||
func TestRmdir(t *testing.T, remote fs.Fs) {
|
||||
err := fs.Rmdir(remote)
|
||||
if err != nil {
|
||||
|
@ -1,9 +1,9 @@
|
||||
// Generic tests for testing the Fs and Object interfaces
|
||||
// Package fstests provides generic tests for testing the Fs and Object interfaces
|
||||
//
|
||||
// Run go generate to write the tests for the remotes
|
||||
package fstests
|
||||
|
||||
//go:generate go run gen_tests.go
|
||||
package fstests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -22,12 +22,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
remote fs.Fs
|
||||
remote fs.Fs
|
||||
// RemoteName should be set to the name of the remote for testing
|
||||
RemoteName = ""
|
||||
subRemoteName = ""
|
||||
subRemoteLeaf = ""
|
||||
NilObject fs.Object
|
||||
file1 = fstest.Item{
|
||||
// NilObject should be set to a nil Object from the Fs under test
|
||||
NilObject fs.Object
|
||||
file1 = fstest.Item{
|
||||
ModTime: fstest.Time("2001-02-03T04:05:06.499999999Z"),
|
||||
Path: "file name.txt",
|
||||
}
|
||||
@ -42,6 +44,7 @@ func init() {
|
||||
flag.StringVar(&RemoteName, "remote", "", "Set this to override the default remote name (eg s3:)")
|
||||
}
|
||||
|
||||
// TestInit tests basic intitialisation
|
||||
func TestInit(t *testing.T) {
|
||||
var err error
|
||||
fs.LoadConfig()
|
||||
@ -60,7 +63,7 @@ func TestInit(t *testing.T) {
|
||||
}
|
||||
|
||||
remote, err = fs.NewFs(subRemoteName)
|
||||
if err == fs.NotFoundInConfigFile {
|
||||
if err == fs.ErrorNotFoundInConfigFile {
|
||||
log.Printf("Didn't find %q in config file - skipping tests", RemoteName)
|
||||
return
|
||||
}
|
||||
@ -76,8 +79,7 @@ func skipIfNotOk(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a description of the FS
|
||||
|
||||
// TestFsString tests the String method
|
||||
func TestFsString(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
str := remote.String()
|
||||
@ -86,18 +88,13 @@ func TestFsString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type TestFile struct {
|
||||
ModTime time.Time
|
||||
Path string
|
||||
Size int64
|
||||
Md5sum string
|
||||
}
|
||||
|
||||
// TestFsRmdirEmpty tests deleting an empty directory
|
||||
func TestFsRmdirEmpty(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.TestRmdir(t, remote)
|
||||
}
|
||||
|
||||
// TestFsRmdirNotFound tests deleting a non existent directory
|
||||
func TestFsRmdirNotFound(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
err := remote.Rmdir()
|
||||
@ -106,17 +103,20 @@ func TestFsRmdirNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsMkdir tests tests making a directory
|
||||
func TestFsMkdir(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.TestMkdir(t, remote)
|
||||
fstest.TestMkdir(t, remote)
|
||||
}
|
||||
|
||||
// TestFsListEmpty tests listing an empty directory
|
||||
func TestFsListEmpty(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.CheckListing(t, remote, []fstest.Item{})
|
||||
}
|
||||
|
||||
// TestFsListDirEmpty tests listing the directories from an empty directory
|
||||
func TestFsListDirEmpty(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
for obj := range remote.ListDir() {
|
||||
@ -124,6 +124,7 @@ func TestFsListDirEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsNewFsObjectNotFound tests not finding a object
|
||||
func TestFsNewFsObjectNotFound(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
if remote.NewFsObject("potato") != nil {
|
||||
@ -156,16 +157,19 @@ func testPut(t *testing.T, file *fstest.Item) {
|
||||
file.Check(t, obj, remote.Precision())
|
||||
}
|
||||
|
||||
// TestFsPutFile1 tests putting a file
|
||||
func TestFsPutFile1(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
testPut(t, &file1)
|
||||
}
|
||||
|
||||
// TestFsPutFile2 tests putting a file into a subdirectory
|
||||
func TestFsPutFile2(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
testPut(t, &file2)
|
||||
}
|
||||
|
||||
// TestFsListDirFile2 tests the files are correctly uploaded
|
||||
func TestFsListDirFile2(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
found := false
|
||||
@ -181,6 +185,7 @@ func TestFsListDirFile2(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsListDirRoot tests that DirList works in the root
|
||||
func TestFsListDirRoot(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
rootRemote, err := fs.NewFs(RemoteName)
|
||||
@ -198,6 +203,7 @@ func TestFsListDirRoot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsListRoot tests List works in the root
|
||||
func TestFsListRoot(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
rootRemote, err := fs.NewFs(RemoteName)
|
||||
@ -237,22 +243,26 @@ func TestFsListRoot(t *testing.T) {
|
||||
t.Errorf("Didn't find %q (%v) and %q (%v) or no files (count %d)", f1, found1, f2, found2, count)
|
||||
}
|
||||
|
||||
// TestFsListFile1 tests file present
|
||||
func TestFsListFile1(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
|
||||
}
|
||||
|
||||
// TestFsNewFsObject tests NewFsObject
|
||||
func TestFsNewFsObject(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
file1.Check(t, obj, remote.Precision())
|
||||
}
|
||||
|
||||
// TestFsListFile1and2 tests two files present
|
||||
func TestFsListFile1and2(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.CheckListing(t, remote, []fstest.Item{file1, file2})
|
||||
}
|
||||
|
||||
// TestFsCopy tests Copy
|
||||
func TestFsCopy(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
|
||||
@ -288,6 +298,7 @@ func TestFsCopy(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// TestFsMove tests Move
|
||||
func TestFsMove(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
|
||||
@ -333,6 +344,8 @@ func TestFsMove(t *testing.T) {
|
||||
// If it isn't possible then return fs.ErrorCantDirMove
|
||||
//
|
||||
// If destination exists then return fs.ErrorDirExists
|
||||
|
||||
// TestFsDirMove tests DirMove
|
||||
func TestFsDirMove(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
|
||||
@ -377,6 +390,7 @@ func TestFsDirMove(t *testing.T) {
|
||||
fstest.CheckListing(t, newRemote, []fstest.Item{})
|
||||
}
|
||||
|
||||
// TestFsRmdirFull tests removing a non empty directory
|
||||
func TestFsRmdirFull(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
err := remote.Rmdir()
|
||||
@ -385,6 +399,7 @@ func TestFsRmdirFull(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFsPrecision tests the Precision of the Fs
|
||||
func TestFsPrecision(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
precision := remote.Precision()
|
||||
@ -397,6 +412,7 @@ func TestFsPrecision(t *testing.T) {
|
||||
// FIXME check expected precision
|
||||
}
|
||||
|
||||
// TestObjectString tests the Object String method
|
||||
func TestObjectString(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -411,6 +427,7 @@ func TestObjectString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectFs tests the object can be found
|
||||
func TestObjectFs(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -419,6 +436,7 @@ func TestObjectFs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectRemote tests the Remote is correct
|
||||
func TestObjectRemote(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -427,6 +445,7 @@ func TestObjectRemote(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectMd5sum tests the MD5SUM of the object is correct
|
||||
func TestObjectMd5sum(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -439,12 +458,14 @@ func TestObjectMd5sum(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectModTime tests the ModTime of the object is correct
|
||||
func TestObjectModTime(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
file1.CheckModTime(t, obj, obj.ModTime(), remote.Precision())
|
||||
}
|
||||
|
||||
// TestObjectSetModTime tests that SetModTime works
|
||||
func TestObjectSetModTime(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
newModTime := fstest.Time("2011-12-13T14:15:16.999999999Z")
|
||||
@ -456,6 +477,7 @@ func TestObjectSetModTime(t *testing.T) {
|
||||
TestObjectModTime(t)
|
||||
}
|
||||
|
||||
// TestObjectSize tests that Size works
|
||||
func TestObjectSize(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -464,6 +486,7 @@ func TestObjectSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectOpen tests that Open works
|
||||
func TestObjectOpen(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -489,6 +512,7 @@ func TestObjectOpen(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectUpdate tests that Update works
|
||||
func TestObjectUpdate(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
buf := bytes.NewBufferString(fstest.RandomString(200))
|
||||
@ -508,6 +532,7 @@ func TestObjectUpdate(t *testing.T) {
|
||||
file1.Check(t, obj, remote.Precision())
|
||||
}
|
||||
|
||||
// TestObjectStorable tests that Storable works
|
||||
func TestObjectStorable(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -516,6 +541,7 @@ func TestObjectStorable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLimitedFs tests that a LimitedFs is created
|
||||
func TestLimitedFs(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
remoteName := subRemoteName + "/" + file2.Path
|
||||
@ -532,6 +558,7 @@ func TestLimitedFs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLimitedFsNotFound tests that a LimitedFs is not created if no object
|
||||
func TestLimitedFsNotFound(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
remoteName := subRemoteName + "/not found.txt"
|
||||
@ -546,6 +573,7 @@ func TestLimitedFsNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestObjectRemove tests Remove
|
||||
func TestObjectRemove(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
obj := findObject(t, file1.Path)
|
||||
@ -556,6 +584,7 @@ func TestObjectRemove(t *testing.T) {
|
||||
fstest.CheckListing(t, remote, []fstest.Item{file2})
|
||||
}
|
||||
|
||||
// TestObjectPurge tests Purge
|
||||
func TestObjectPurge(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
fstest.TestPurge(t, remote)
|
||||
@ -565,6 +594,7 @@ func TestObjectPurge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestFinalise tidies up after the previous tests
|
||||
func TestFinalise(t *testing.T) {
|
||||
skipIfNotOk(t)
|
||||
if strings.HasPrefix(RemoteName, "/") {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Google Cloud Storage interface
|
||||
// Package googlecloudstorage provides an interface to Google Cloud Storage
|
||||
package googlecloudstorage
|
||||
|
||||
/*
|
||||
@ -55,7 +55,7 @@ var (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "google cloud storage",
|
||||
NewFs: NewFs,
|
||||
Config: func(name string) {
|
||||
@ -144,12 +144,12 @@ type FsObjectStorage struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsStorage) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsStorage) Root() string {
|
||||
if f.root == "" {
|
||||
return f.bucket
|
||||
@ -254,7 +254,7 @@ func (f *FsStorage) newFsObjectWithInfo(remote string, info *storage.Object) fs.
|
||||
return o
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsStorage) NewFsObject(remote string) fs.Object {
|
||||
@ -302,7 +302,7 @@ func (f *FsStorage) list(directories bool, fn func(string, *storage.Object)) {
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsStorage) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
if f.bucket == "" {
|
||||
@ -324,7 +324,7 @@ func (f *FsStorage) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Lists the buckets
|
||||
// ListDir lists the buckets
|
||||
func (f *FsStorage) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
if f.bucket == "" {
|
||||
@ -412,8 +412,8 @@ func (f *FsStorage) Rmdir() error {
|
||||
return f.svc.Buckets.Delete(f.bucket).Do()
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
func (fs *FsStorage) Precision() time.Duration {
|
||||
// Precision returns the precision
|
||||
func (f *FsStorage) Precision() time.Duration {
|
||||
return time.Nanosecond
|
||||
}
|
||||
|
||||
@ -451,7 +451,7 @@ func (f *FsStorage) Copy(src fs.Object, remote string) (fs.Object, error) {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectStorage) Fs() fs.Fs {
|
||||
return o.storage
|
||||
}
|
||||
@ -464,7 +464,7 @@ func (o *FsObjectStorage) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectStorage) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -499,9 +499,8 @@ func (o *FsObjectStorage) setMetaData(info *storage.Object) {
|
||||
if err == nil {
|
||||
o.modTime = modTime
|
||||
return
|
||||
} else {
|
||||
fs.Debug(o, "Failed to read mtime from metadata: %s", err)
|
||||
}
|
||||
fs.Debug(o, "Failed to read mtime from metadata: %s", err)
|
||||
}
|
||||
|
||||
// Fallback to the Updated time
|
||||
@ -549,7 +548,7 @@ func metadataFromModTime(modTime time.Time) map[string]string {
|
||||
return metadata
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
func (o *FsObjectStorage) SetModTime(modTime time.Time) {
|
||||
// This only adds metadata so will perserve other metadata
|
||||
object := storage.Object{
|
||||
@ -565,7 +564,7 @@ func (o *FsObjectStorage) SetModTime(modTime time.Time) {
|
||||
o.setMetaData(newObject)
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns a boolean as to whether this object is storable
|
||||
func (o *FsObjectStorage) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Local filesystem interface
|
||||
// Package local provides a filesystem interface
|
||||
package local
|
||||
|
||||
import (
|
||||
@ -10,19 +10,19 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/ncw/rclone/fs"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "local",
|
||||
NewFs: NewFs,
|
||||
})
|
||||
@ -71,12 +71,12 @@ func NewFs(name, root string) (fs.Fs, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsLocal) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsLocal) Root() string {
|
||||
return f.root
|
||||
}
|
||||
@ -110,7 +110,7 @@ func (f *FsLocal) newFsObjectWithInfo(remote string, info os.FileInfo) fs.Object
|
||||
return o
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsLocal) NewFsObject(remote string) fs.Object {
|
||||
@ -195,7 +195,7 @@ func (f *FsLocal) cleanUtf8(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// ListDir walks the path returning a channel of FsObjects
|
||||
func (f *FsLocal) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
go func() {
|
||||
@ -220,7 +220,7 @@ func (f *FsLocal) ListDir() fs.DirChan {
|
||||
fs.Stats.Error()
|
||||
fs.ErrorLog(f, "Failed to open directory: %s: %s", path, err)
|
||||
} else {
|
||||
dir.Count += 1
|
||||
dir.Count++
|
||||
dir.Bytes += fi.Size()
|
||||
}
|
||||
return nil
|
||||
@ -238,7 +238,7 @@ func (f *FsLocal) ListDir() fs.DirChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Puts the FsObject to the local filesystem
|
||||
// Put the FsObject to the local filesystem
|
||||
func (f *FsLocal) Put(in io.Reader, remote string, modTime time.Time, size int64) (fs.Object, error) {
|
||||
// Temporary FsObject under construction - info filled in by Update()
|
||||
o := f.newFsObject(remote)
|
||||
@ -262,7 +262,7 @@ func (f *FsLocal) Rmdir() error {
|
||||
return os.Remove(f.root)
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
// Precision of the file system
|
||||
func (f *FsLocal) Precision() (precision time.Duration) {
|
||||
f.precisionOk.Do(func() {
|
||||
f.precision = f.readPrecision()
|
||||
@ -347,7 +347,7 @@ func (f *FsLocal) Purge() error {
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantMove
|
||||
func (dstFs *FsLocal) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
func (f *FsLocal) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
srcObj, ok := src.(*FsObjectLocal)
|
||||
if !ok {
|
||||
fs.Debug(src, "Can't move - not same remote type")
|
||||
@ -355,7 +355,7 @@ func (dstFs *FsLocal) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
}
|
||||
|
||||
// Temporary FsObject under construction
|
||||
dstObj := dstFs.newFsObject(remote)
|
||||
dstObj := f.newFsObject(remote)
|
||||
|
||||
// Check it is a file if it exists
|
||||
err := dstObj.lstat()
|
||||
@ -389,14 +389,15 @@ func (dstFs *FsLocal) Move(src fs.Object, remote string) (fs.Object, error) {
|
||||
return dstObj, nil
|
||||
}
|
||||
|
||||
// Move src to this remote using server side move operations.
|
||||
// DirMove moves src directory to this remote using server side move
|
||||
// operations.
|
||||
//
|
||||
// Will only be called if src.Fs().Name() == f.Name()
|
||||
//
|
||||
// If it isn't possible then return fs.ErrorCantDirMove
|
||||
//
|
||||
// If destination exists then return fs.ErrorDirExists
|
||||
func (dstFs *FsLocal) DirMove(src fs.Fs) error {
|
||||
func (f *FsLocal) DirMove(src fs.Fs) error {
|
||||
srcFs, ok := src.(*FsLocal)
|
||||
if !ok {
|
||||
fs.Debug(srcFs, "Can't move directory - not same remote type")
|
||||
@ -413,18 +414,18 @@ func (dstFs *FsLocal) DirMove(src fs.Fs) error {
|
||||
}
|
||||
|
||||
// Check if destination exists
|
||||
_, err = os.Lstat(dstFs.root)
|
||||
_, err = os.Lstat(f.root)
|
||||
if !os.IsNotExist(err) {
|
||||
return fs.ErrorDirExists
|
||||
}
|
||||
|
||||
// Do the move
|
||||
return os.Rename(srcFs.root, dstFs.root)
|
||||
return os.Rename(srcFs.root, f.root)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectLocal) Fs() fs.Fs {
|
||||
return o.local
|
||||
}
|
||||
@ -437,7 +438,7 @@ func (o *FsObjectLocal) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectLocal) Remote() string {
|
||||
return o.local.cleanUtf8(o.remote)
|
||||
}
|
||||
@ -480,7 +481,7 @@ func (o *FsObjectLocal) ModTime() time.Time {
|
||||
return o.info.ModTime()
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
func (o *FsObjectLocal) SetModTime(modTime time.Time) {
|
||||
err := os.Chtimes(o.path, modTime, modTime)
|
||||
if err != nil {
|
||||
@ -495,7 +496,7 @@ func (o *FsObjectLocal) SetModTime(modTime time.Time) {
|
||||
}
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns a boolean showing if this object is storable
|
||||
func (o *FsObjectLocal) Storable() bool {
|
||||
mode := o.info.Mode()
|
||||
if mode&(os.ModeSymlink|os.ModeNamedPipe|os.ModeSocket|os.ModeDevice) != 0 {
|
||||
|
@ -210,24 +210,24 @@ func Config(name string, config *oauth2.Config) error {
|
||||
return err
|
||||
}
|
||||
state := fmt.Sprintf("%x", stateBytes)
|
||||
authUrl := config.AuthCodeURL(state)
|
||||
authURL := config.AuthCodeURL(state)
|
||||
|
||||
// Prepare webserver
|
||||
server := authServer{
|
||||
state: state,
|
||||
bindAddress: bindAddress,
|
||||
authUrl: authUrl,
|
||||
authURL: authURL,
|
||||
}
|
||||
if useWebServer {
|
||||
server.code = make(chan string, 1)
|
||||
go server.Start()
|
||||
defer server.Stop()
|
||||
authUrl = "http://" + bindAddress + "/auth"
|
||||
authURL = "http://" + bindAddress + "/auth"
|
||||
}
|
||||
|
||||
// Generate a URL for the user to visit for authorization.
|
||||
_ = open.Start(authUrl)
|
||||
fmt.Printf("If your browser doesn't open automatically go to the following link: %s\n", authUrl)
|
||||
_ = open.Start(authURL)
|
||||
fmt.Printf("If your browser doesn't open automatically go to the following link: %s\n", authURL)
|
||||
fmt.Printf("Log in and authorize rclone for access\n")
|
||||
|
||||
var authCode string
|
||||
@ -258,7 +258,7 @@ type authServer struct {
|
||||
listener net.Listener
|
||||
bindAddress string
|
||||
code chan string
|
||||
authUrl string
|
||||
authURL string
|
||||
}
|
||||
|
||||
// startWebServer runs an internal web server to receive config details
|
||||
@ -274,7 +274,7 @@ func (s *authServer) Start() {
|
||||
return
|
||||
})
|
||||
mux.HandleFunc("/auth", func(w http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(w, req, s.authUrl, 307)
|
||||
http.Redirect(w, req, s.authURL, 307)
|
||||
return
|
||||
})
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
|
10
rclone.go
10
rclone.go
@ -35,6 +35,7 @@ var (
|
||||
retries = pflag.IntP("retries", "", 3, "Retry operations this many times if they fail")
|
||||
)
|
||||
|
||||
// Command holds info about the current running command
|
||||
type Command struct {
|
||||
Name string
|
||||
Help string
|
||||
@ -59,6 +60,7 @@ func (cmd *Command) checkArgs(args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Commands is a slice of possible Command~s
|
||||
var Commands = []Command{
|
||||
{
|
||||
Name: "copy",
|
||||
@ -249,7 +251,7 @@ func fatal(message string, args ...interface{}) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse the command line flags
|
||||
// ParseFlags parses the command line flags
|
||||
func ParseFlags() {
|
||||
pflag.Usage = syntaxError
|
||||
pflag.Parse()
|
||||
@ -272,7 +274,7 @@ func ParseFlags() {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the command from the command line
|
||||
// ParseCommand parses the command from the command line
|
||||
func ParseCommand() (*Command, []string) {
|
||||
args := pflag.Args()
|
||||
if len(args) < 1 {
|
||||
@ -318,7 +320,7 @@ func ParseCommand() (*Command, []string) {
|
||||
return command, args
|
||||
}
|
||||
|
||||
// Create a Fs from a name
|
||||
// NewFs creates a Fs from a name
|
||||
func NewFs(remote string) fs.Fs {
|
||||
f, err := fs.NewFs(remote)
|
||||
if err != nil {
|
||||
@ -328,7 +330,7 @@ func NewFs(remote string) fs.Fs {
|
||||
return f
|
||||
}
|
||||
|
||||
// Print the stats every statsInterval
|
||||
// StartStats prints the stats every statsInterval
|
||||
func StartStats() {
|
||||
if *statsInterval <= 0 {
|
||||
return
|
||||
|
34
s3/s3.go
34
s3/s3.go
@ -1,4 +1,4 @@
|
||||
// S3 interface
|
||||
// Package s3 provides an interface to Amazon S3 oject storage
|
||||
package s3
|
||||
|
||||
// FIXME need to prevent anything but ListDir working for s3://
|
||||
@ -35,7 +35,7 @@ import (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "s3",
|
||||
NewFs: NewFs,
|
||||
// AWS endpoints: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
|
||||
@ -153,12 +153,12 @@ type FsObjectS3 struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsS3) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsS3) Root() string {
|
||||
if f.root == "" {
|
||||
return f.bucket
|
||||
@ -192,15 +192,15 @@ func s3ParsePath(path string) (bucket, directory string, err error) {
|
||||
// s3Connection makes a connection to s3
|
||||
func s3Connection(name string) (*s3.S3, error) {
|
||||
// Make the auth
|
||||
accessKeyId := fs.ConfigFile.MustValue(name, "access_key_id")
|
||||
if accessKeyId == "" {
|
||||
accessKeyID := fs.ConfigFile.MustValue(name, "access_key_id")
|
||||
if accessKeyID == "" {
|
||||
return nil, errors.New("access_key_id not found")
|
||||
}
|
||||
secretAccessKey := fs.ConfigFile.MustValue(name, "secret_access_key")
|
||||
if secretAccessKey == "" {
|
||||
return nil, errors.New("secret_access_key not found")
|
||||
}
|
||||
auth := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "")
|
||||
auth := credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "")
|
||||
|
||||
endpoint := fs.ConfigFile.MustValue(name, "endpoint")
|
||||
region := fs.ConfigFile.MustValue(name, "region")
|
||||
@ -226,7 +226,7 @@ func s3Connection(name string) (*s3.S3, error) {
|
||||
if req.Service.Config.Credentials == credentials.AnonymousCredentials {
|
||||
return
|
||||
}
|
||||
sign(accessKeyId, secretAccessKey, req.HTTPRequest)
|
||||
sign(accessKeyID, secretAccessKey, req.HTTPRequest)
|
||||
}
|
||||
c.Handlers.Sign.Clear()
|
||||
c.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
|
||||
@ -239,7 +239,7 @@ func s3Connection(name string) (*s3.S3, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewFsS3 contstructs an FsS3 from the path, bucket:path
|
||||
// NewFs contstructs an FsS3 from the path, bucket:path
|
||||
func NewFs(name, root string) (fs.Fs, error) {
|
||||
bucket, directory, err := s3ParsePath(root)
|
||||
if err != nil {
|
||||
@ -310,7 +310,7 @@ func (f *FsS3) newFsObjectWithInfo(remote string, info *s3.Object) fs.Object {
|
||||
return o
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsS3) NewFsObject(remote string) fs.Object {
|
||||
@ -384,7 +384,7 @@ func (f *FsS3) list(directories bool, fn func(string, *s3.Object)) {
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsS3) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
if f.bucket == "" {
|
||||
@ -405,7 +405,7 @@ func (f *FsS3) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Lists the buckets
|
||||
// ListDir lists the buckets
|
||||
func (f *FsS3) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
if f.bucket == "" {
|
||||
@ -486,7 +486,7 @@ func (f *FsS3) Rmdir() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
// Precision of the remote
|
||||
func (f *FsS3) Precision() time.Duration {
|
||||
return time.Nanosecond
|
||||
}
|
||||
@ -524,7 +524,7 @@ func (f *FsS3) Copy(src fs.Object, remote string) (fs.Object, error) {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectS3) Fs() fs.Fs {
|
||||
return o.s3
|
||||
}
|
||||
@ -537,7 +537,7 @@ func (o *FsObjectS3) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectS3) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -619,7 +619,7 @@ func (o *FsObjectS3) ModTime() time.Time {
|
||||
return modTime
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
func (o *FsObjectS3) SetModTime(modTime time.Time) {
|
||||
err := o.readMetaData()
|
||||
if err != nil {
|
||||
@ -648,7 +648,7 @@ func (o *FsObjectS3) SetModTime(modTime time.Time) {
|
||||
}
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable raturns a boolean indicating if this object is storable
|
||||
func (o *FsObjectS3) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Swift interface
|
||||
// Package swift provides an interface to the Swift object storage system
|
||||
package swift
|
||||
|
||||
import (
|
||||
@ -16,7 +16,7 @@ import (
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.FsInfo{
|
||||
fs.Register(&fs.Info{
|
||||
Name: "swift",
|
||||
NewFs: NewFs,
|
||||
Options: []fs.Option{{
|
||||
@ -76,12 +76,12 @@ type FsObjectSwift struct {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// The name of the remote (as passed into NewFs)
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *FsSwift) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// The root of the remote (as passed into NewFs)
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *FsSwift) Root() string {
|
||||
if f.root == "" {
|
||||
return f.container
|
||||
@ -122,14 +122,14 @@ func swiftConnection(name string) (*swift.Connection, error) {
|
||||
if apiKey == "" {
|
||||
return nil, errors.New("key not found")
|
||||
}
|
||||
authUrl := fs.ConfigFile.MustValue(name, "auth")
|
||||
if authUrl == "" {
|
||||
authURL := fs.ConfigFile.MustValue(name, "auth")
|
||||
if authURL == "" {
|
||||
return nil, errors.New("auth not found")
|
||||
}
|
||||
c := &swift.Connection{
|
||||
UserName: userName,
|
||||
ApiKey: apiKey,
|
||||
AuthUrl: authUrl,
|
||||
AuthUrl: authURL,
|
||||
UserAgent: fs.UserAgent,
|
||||
Tenant: fs.ConfigFile.MustValue(name, "tenant"),
|
||||
Region: fs.ConfigFile.MustValue(name, "region"),
|
||||
@ -201,7 +201,7 @@ func (f *FsSwift) newFsObjectWithInfo(remote string, info *swift.Object) fs.Obje
|
||||
return fs
|
||||
}
|
||||
|
||||
// Return an FsObject from a path
|
||||
// NewFsObject returns an FsObject from a path
|
||||
//
|
||||
// May return nil if an error occurred
|
||||
func (f *FsSwift) NewFsObject(remote string) fs.Object {
|
||||
@ -249,7 +249,7 @@ func (f *FsSwift) list(directories bool, fn func(string, *swift.Object)) {
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the path returning a channel of FsObjects
|
||||
// List walks the path returning a channel of FsObjects
|
||||
func (f *FsSwift) List() fs.ObjectsChan {
|
||||
out := make(fs.ObjectsChan, fs.Config.Checkers)
|
||||
if f.container == "" {
|
||||
@ -271,7 +271,7 @@ func (f *FsSwift) List() fs.ObjectsChan {
|
||||
return out
|
||||
}
|
||||
|
||||
// Lists the containers
|
||||
// ListDir lists the containers
|
||||
func (f *FsSwift) ListDir() fs.DirChan {
|
||||
out := make(fs.DirChan, fs.Config.Checkers)
|
||||
if f.container == "" {
|
||||
@ -331,8 +331,8 @@ func (f *FsSwift) Rmdir() error {
|
||||
return f.c.ContainerDelete(f.container)
|
||||
}
|
||||
|
||||
// Return the precision
|
||||
func (fs *FsSwift) Precision() time.Duration {
|
||||
// Precision of the remote
|
||||
func (f *FsSwift) Precision() time.Duration {
|
||||
return time.Nanosecond
|
||||
}
|
||||
|
||||
@ -361,7 +361,7 @@ func (f *FsSwift) Copy(src fs.Object, remote string) (fs.Object, error) {
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Return the parent Fs
|
||||
// Fs returns the parent Fs
|
||||
func (o *FsObjectSwift) Fs() fs.Fs {
|
||||
return o.swift
|
||||
}
|
||||
@ -374,7 +374,7 @@ func (o *FsObjectSwift) String() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Return the remote path
|
||||
// Remote returns the remote path
|
||||
func (o *FsObjectSwift) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
@ -426,7 +426,7 @@ func (o *FsObjectSwift) ModTime() time.Time {
|
||||
return modTime
|
||||
}
|
||||
|
||||
// Sets the modification time of the local fs object
|
||||
// SetModTime sets the modification time of the local fs object
|
||||
func (o *FsObjectSwift) SetModTime(modTime time.Time) {
|
||||
err := o.readMetaData()
|
||||
if err != nil {
|
||||
@ -442,7 +442,7 @@ func (o *FsObjectSwift) SetModTime(modTime time.Time) {
|
||||
}
|
||||
}
|
||||
|
||||
// Is this object storable
|
||||
// Storable returns if this object is storable
|
||||
func (o *FsObjectSwift) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user