From 0ca640858038d81922602503cec666bfd9d980ae Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 18 May 2017 17:23:02 +0100 Subject: [PATCH] ftp: rework mkdir to be more efficient --- ftp/ftp.go | 83 ++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/ftp/ftp.go b/ftp/ftp.go index d8aca9476..0f9d44ecb 100644 --- a/ftp/ftp.go +++ b/ftp/ftp.go @@ -336,11 +336,15 @@ func (f *Fs) Precision() time.Duration { // nil and the error func (f *Fs) Put(in io.Reader, src fs.ObjectInfo) (fs.Object, error) { // fs.Debugf(f, "Trying to put file %s", src.Remote()) + err := f.mkParentDir(src.Remote()) + if err != nil { + return nil, errors.Wrap(err, "Put mkParentDir failed") + } o := &Object{ fs: f, remote: src.Remote(), } - err := o.Update(in, src) + err = o.Update(in, src) return o, err } @@ -374,39 +378,46 @@ func (f *Fs) getInfo(remote string) (fi *FileInfo, err error) { return nil, fs.ErrorObjectNotFound } +// mkdir makes the directory and parents using unrooted paths func (f *Fs) mkdir(abspath string) error { - _, err := f.getInfo(abspath) - if err == fs.ErrorObjectNotFound { - // fs.Debugf(f, "Trying to create directory %s", abspath) - c, connErr := f.getFtpConnection() - if connErr != nil { - return errors.Wrap(connErr, "mkdir") - } - err = c.MakeDir(abspath) - f.putFtpConnection(&c) + if abspath == "." || abspath == "/" { + return nil } + fi, err := f.getInfo(abspath) + if err == nil { + if fi.IsDir { + return nil + } + return fs.ErrorIsFile + } else if err != fs.ErrorObjectNotFound { + return errors.Wrapf(err, "mkdir %q failed", abspath) + } + parent := path.Dir(abspath) + err = f.mkdir(parent) + if err != nil { + return err + } + c, connErr := f.getFtpConnection() + if connErr != nil { + return errors.Wrap(connErr, "mkdir") + } + err = c.MakeDir(abspath) + f.putFtpConnection(&c) return err } +// mkParentDir makes the parent of remote if necessary and any +// directories above that +func (f *Fs) mkParentDir(remote string) error { + parent := path.Dir(remote) + return f.mkdir(path.Join(f.root, parent)) +} + // Mkdir creates the directory if it doesn't exist func (f *Fs) Mkdir(dir string) (err error) { // defer fs.Trace(dir, "")("err=%v", &err) - // This actually works as mkdir -p - abspath := path.Join(f.root, dir) - tokens := strings.Split(abspath, "/") - curdir := "" - for i := range tokens { - curdir += tokens[i] - if curdir == "" { - continue - } - err := f.mkdir(curdir) - if err != nil { - return err - } - curdir += "/" - } - return nil + root := path.Join(f.root, dir) + return f.mkdir(root) } // Rmdir removes the directory (container, bucket) if empty @@ -509,21 +520,6 @@ func (o *Object) Open(options ...fs.OpenOption) (rc io.ReadCloser, err error) { return rc, nil } -// makeAllDir creates the parent directories for the object -func (o *Object) makeAllDir() error { - dir, _ := path.Split(o.remote) - tokens := strings.Split(dir, "/") - dir = "" - for i := range tokens { - dir += tokens[i] + "/" - err := o.fs.Mkdir(dir) - if err != nil { - return err - } - } - return nil -} - // Update the already existing object // // Copy the reader into the object updating modTime and size @@ -531,11 +527,6 @@ func (o *Object) makeAllDir() error { // The new object may have been created if an error is returned func (o *Object) Update(in io.Reader, src fs.ObjectInfo) (err error) { // defer fs.Trace(o, "src=%v", src)("err=%v", &err) - // Create all upper directory first... - err = o.makeAllDir() - if err != nil { - return errors.Wrap(err, "update mkdir") - } path := path.Join(o.fs.root, o.remote) // remove the file if upload failed remove := func() {