ftp: Improve performance to speed up --files-from and NewObject

This commit uses the MLST command (where available) to get the status
for single files rather than listing the parent directory and looking
for the file. This makes actions such as using `--files-from` much quicker.

* use getEntry to lookup remote files when supported
*  findItem now expects the full path directly

It makes the expected argument similar to the getInfo method, the
difference now is that one is returning a FileInfo whereas
the other is returning an ftp Entry.

Fixes #6225

Co-authored-by: Nick Craig-Wood <nick@craig-wood.com>
This commit is contained in:
Anthony Pessy 2022-12-05 17:19:04 +01:00 committed by GitHub
parent 313493d51b
commit 1628ca0d46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -657,8 +657,7 @@ func (f *Fs) dirFromStandardPath(dir string) string {
// findItem finds a directory entry for the name in its parent directory // findItem finds a directory entry for the name in its parent directory
func (f *Fs) findItem(ctx context.Context, remote string) (entry *ftp.Entry, err error) { func (f *Fs) findItem(ctx context.Context, remote string) (entry *ftp.Entry, err error) {
// defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err) // defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err)
fullPath := path.Join(f.root, remote) if remote == "" || remote == "." || remote == "/" {
if fullPath == "" || fullPath == "." || fullPath == "/" {
// if root, assume exists and synthesize an entry // if root, assume exists and synthesize an entry
return &ftp.Entry{ return &ftp.Entry{
Name: "", Name: "",
@ -666,13 +665,32 @@ func (f *Fs) findItem(ctx context.Context, remote string) (entry *ftp.Entry, err
Time: time.Now(), Time: time.Now(),
}, nil }, nil
} }
dir := path.Dir(fullPath)
base := path.Base(fullPath)
c, err := f.getFtpConnection(ctx) c, err := f.getFtpConnection(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("findItem: %w", err) return nil, fmt.Errorf("findItem: %w", err)
} }
// returns TRUE if MLST is supported which is required to call GetEntry
if c.IsTimePreciseInList() {
entry, err := c.GetEntry(f.opt.Enc.FromStandardPath(remote))
f.putFtpConnection(&c, err)
if err != nil {
err = translateErrorFile(err)
if err == fs.ErrorObjectNotFound {
return nil, nil
}
return nil, err
}
if entry != nil {
f.entryToStandard(entry)
}
return entry, nil
}
dir := path.Dir(remote)
base := path.Base(remote)
files, err := c.List(f.dirFromStandardPath(dir)) files, err := c.List(f.dirFromStandardPath(dir))
f.putFtpConnection(&c, err) f.putFtpConnection(&c, err)
if err != nil { if err != nil {
@ -691,7 +709,7 @@ func (f *Fs) findItem(ctx context.Context, remote string) (entry *ftp.Entry, err
// it returns the error fs.ErrorObjectNotFound. // it returns the error fs.ErrorObjectNotFound.
func (f *Fs) NewObject(ctx context.Context, remote string) (o fs.Object, err error) { func (f *Fs) NewObject(ctx context.Context, remote string) (o fs.Object, err error) {
// defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err) // defer fs.Trace(remote, "")("o=%v, err=%v", &o, &err)
entry, err := f.findItem(ctx, remote) entry, err := f.findItem(ctx, path.Join(f.root, remote))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -713,7 +731,7 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (o fs.Object, err err
// dirExists checks the directory pointed to by remote exists or not // dirExists checks the directory pointed to by remote exists or not
func (f *Fs) dirExists(ctx context.Context, remote string) (exists bool, err error) { func (f *Fs) dirExists(ctx context.Context, remote string) (exists bool, err error) {
entry, err := f.findItem(ctx, remote) entry, err := f.findItem(ctx, path.Join(f.root, remote))
if err != nil { if err != nil {
return false, fmt.Errorf("dirExists: %w", err) return false, fmt.Errorf("dirExists: %w", err)
} }
@ -857,32 +875,18 @@ func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, opt
// getInfo reads the FileInfo for a path // getInfo reads the FileInfo for a path
func (f *Fs) getInfo(ctx context.Context, remote string) (fi *FileInfo, err error) { func (f *Fs) getInfo(ctx context.Context, remote string) (fi *FileInfo, err error) {
// defer fs.Trace(remote, "")("fi=%v, err=%v", &fi, &err) // defer fs.Trace(remote, "")("fi=%v, err=%v", &fi, &err)
dir := path.Dir(remote) file, err := f.findItem(ctx, remote)
base := path.Base(remote)
c, err := f.getFtpConnection(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("getInfo: %w", err) return nil, err
} } else if file != nil {
files, err := c.List(f.dirFromStandardPath(dir)) info := &FileInfo{
f.putFtpConnection(&c, err) Name: remote,
if err != nil { Size: file.Size,
return nil, translateErrorFile(err) ModTime: file.Time,
} precise: f.fLstTime,
IsDir: file.Type == ftp.EntryTypeFolder,
for i := range files {
file := files[i]
f.entryToStandard(file)
if file.Name == base {
info := &FileInfo{
Name: remote,
Size: file.Size,
ModTime: file.Time,
precise: f.fLstTime,
IsDir: file.Type == ftp.EntryTypeFolder,
}
return info, nil
} }
return info, nil
} }
return nil, fs.ErrorObjectNotFound return nil, fs.ErrorObjectNotFound
} }