backends: make NewObject return fs.ErrorIsDir if possible

This changes the interface to NewObject so that if NewObject is called
on a directory then it should return fs.ErrorIsDir if possible without
doing any extra work, otherwise fs.ErrorObjectNotFound.

Tested on integration test server with:

go run integration-test.go -tests backend -run TestIntegration/FsMkdir/FsPutFiles/FsNewObjectDir -branch fix-stat -maxtries 1
This commit is contained in:
Nick Craig-Wood 2021-09-06 13:54:08 +01:00
parent af732c5431
commit 3fbaa4c0b0
19 changed files with 42 additions and 23 deletions

View file

@ -1184,6 +1184,9 @@ func (o *Object) Size() int64 {
// setMetaData sets the metadata from info
func (o *Object) setMetaData(info *api.Item) (err error) {
if info.Type == api.ItemTypeFolder {
return fs.ErrorIsDir
}
if info.Type != api.ItemTypeFile {
return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
}

View file

@ -1374,7 +1374,7 @@ func (f *Fs) newObjectWithExportInfo(
}
switch {
case info.MimeType == driveFolderType:
return nil, fs.ErrorNotAFile
return nil, fs.ErrorIsDir
case info.MimeType == shortcutMimeType:
// We can only get here if f.opt.SkipShortcuts is set
// and not from a listing. This is unlikely.
@ -2922,7 +2922,7 @@ func (f *Fs) makeShortcut(ctx context.Context, srcPath string, dstFs *Fs, dstPat
}
isDir = true
} else if srcObj, err := srcFs.NewObject(ctx, srcPath); err != nil {
if err != fs.ErrorNotAFile {
if err != fs.ErrorIsDir {
return nil, errors.Wrap(err, "can't find source")
}
// source was a directory
@ -2942,7 +2942,7 @@ func (f *Fs) makeShortcut(ctx context.Context, srcPath string, dstFs *Fs, dstPat
if err != fs.ErrorObjectNotFound {
if err == nil {
err = errors.New("existing file")
} else if err == fs.ErrorNotAFile {
} else if err == fs.ErrorIsDir {
err = errors.New("existing directory")
}
return nil, errors.Wrap(err, "not overwriting shortcut target")

View file

@ -624,6 +624,9 @@ func (f *Fs) getFileMetadata(ctx context.Context, filePath string) (fileInfo *fi
}
fileInfo, ok := entry.(*files.FileMetadata)
if !ok {
if _, ok = entry.(*files.FolderMetadata); ok {
return nil, fs.ErrorIsDir
}
return nil, fs.ErrorNotAFile
}
return fileInfo, nil

View file

@ -1089,7 +1089,7 @@ func (o *Object) Size() int64 {
// setMetaData sets the metadata from info
func (o *Object) setMetaData(info *api.Item) (err error) {
if info.Type != api.ItemTypeFile {
return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
return fs.ErrorIsDir
}
o.hasMetaData = true
o.size = info.Size

View file

@ -599,7 +599,9 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.Jo
if err != nil {
return nil, errors.Wrap(err, "read metadata failed")
}
if result.XMLName.Local != "file" {
if result.XMLName.Local == "folder" {
return nil, fs.ErrorIsDir
} else if result.XMLName.Local != "file" {
return nil, fs.ErrorNotAFile
}
return &result, nil
@ -762,7 +764,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
// Renew the token in the background
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
_, err := f.readMetaDataForPath(ctx, "")
if err == fs.ErrorNotAFile {
if err == fs.ErrorNotAFile || err == fs.ErrorIsDir {
err = nil
}
return err
@ -784,7 +786,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
}
_, err := f.NewObject(context.TODO(), remote)
if err != nil {
if errors.Cause(err) == fs.ErrorObjectNotFound || errors.Cause(err) == fs.ErrorNotAFile {
if uErr := errors.Cause(err); uErr == fs.ErrorObjectNotFound || uErr == fs.ErrorNotAFile || uErr == fs.ErrorIsDir {
// File doesn't exist so return old f
f.root = root
return f, nil

View file

@ -344,7 +344,7 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (obj fs.Object, err e
return nil, translateErrorsObject(err)
}
if info.Type == "dir" {
return nil, fs.ErrorNotAFile
return nil, fs.ErrorIsDir
}
return &Object{
fs: f,

View file

@ -401,7 +401,7 @@ func (f *Fs) newObjectWithInfo(remote string, info os.FileInfo) (fs.Object, erro
}
if o.mode.IsDir() {
return nil, errors.Wrapf(fs.ErrorNotAFile, "%q", remote)
return nil, fs.ErrorIsDir
}
return o, nil
}

View file

@ -1958,7 +1958,7 @@ func (o *Object) readMetaData(ctx context.Context, force bool) error {
}
newObj, ok := entry.(*Object)
if !ok || dirSize >= 0 {
return fs.ErrorNotAFile
return fs.ErrorIsDir
}
if newObj.remote != o.remote {
return fmt.Errorf("File %q path has changed to %q", o.remote, newObj.remote)

View file

@ -303,7 +303,7 @@ func (f *Fs) findObject(rootNode *mega.Node, file string) (node *mega.Node, err
if err == mega.ENOENT {
return nil, fs.ErrorObjectNotFound
} else if err == nil && node.GetType() != mega.FILE {
return nil, fs.ErrorNotAFile
return nil, fs.ErrorIsDir // all other node types are directories
}
return node, err
}
@ -958,7 +958,7 @@ func (o *Object) Size() int64 {
// setMetaData sets the metadata from info
func (o *Object) setMetaData(info *mega.Node) (err error) {
if info.GetType() != mega.FILE {
return fs.ErrorNotAFile
return fs.ErrorIsDir // all other node types are directories
}
o.info = info
return nil

View file

@ -1715,7 +1715,7 @@ func (o *Object) Size() int64 {
// setMetaData sets the metadata from info
func (o *Object) setMetaData(info *api.Item) (err error) {
if info.GetFolder() != nil {
return errors.Wrapf(fs.ErrorNotAFile, "%q", o.remote)
return fs.ErrorIsDir
}
o.hasMetaData = true
o.size = info.GetSize()

View file

@ -151,7 +151,7 @@ func (o *Object) readEntry(ctx context.Context) (f *putio.File, err error) {
return nil, err
}
if resp.File.IsDir() {
return nil, fs.ErrorNotAFile
return nil, fs.ErrorIsDir
}
return &resp.File, err
}

View file

@ -1383,7 +1383,7 @@ func (o *Object) stat(ctx context.Context) error {
return errors.Wrap(err, "stat failed")
}
if info.IsDir() {
return errors.Wrapf(fs.ErrorNotAFile, "%q", o.remote)
return fs.ErrorIsDir
}
o.setMetadata(info)
return nil

View file

@ -337,9 +337,13 @@ func (f *Fs) readMetaDataForIDPath(ctx context.Context, id, path string, directo
if directoriesOnly && item.Type != api.ItemTypeFolder {
return nil, fs.ErrorIsFile
}
if filesOnly && item.Type != api.ItemTypeFile {
if filesOnly {
if item.Type == api.ItemTypeFolder {
return nil, fs.ErrorIsDir
} else if item.Type != api.ItemTypeFile {
return nil, fs.ErrorNotAFile
}
}
return &item, nil
}

View file

@ -317,7 +317,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, depth string)
return nil, fs.ErrorObjectNotFound
}
if itemIsDir(&item) {
return nil, fs.ErrorNotAFile
return nil, fs.ErrorIsDir
}
return &item.Props, nil
}
@ -469,7 +469,7 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
}
_, err := f.NewObject(ctx, remote)
if err != nil {
if errors.Cause(err) == fs.ErrorObjectNotFound || errors.Cause(err) == fs.ErrorNotAFile {
if errors.Cause(err) == fs.ErrorObjectNotFound || errors.Cause(err) == fs.ErrorIsDir {
// File doesn't exist so return old f
f.root = root
return f, nil

View file

@ -949,7 +949,9 @@ func (o *Object) readMetaData(ctx context.Context) (err error) {
if err != nil {
return err
}
if info.ResourceType != "file" {
if info.ResourceType == "dir" {
return fs.ErrorIsDir
} else if info.ResourceType != "file" {
return fs.ErrorNotAFile
}
return o.setMetaData(info)

View file

@ -1121,7 +1121,7 @@ func (o *Object) Size() int64 {
// setMetaData sets the metadata from info
func (o *Object) setMetaData(info *api.Item) (err error) {
if info.Attributes.IsFolder {
return fs.ErrorNotAFile
return fs.ErrorIsDir
}
o.hasMetaData = true
o.size = info.Attributes.StorageInfo.Size

View file

@ -37,6 +37,7 @@ var (
ErrorListAborted = errors.New("list aborted")
ErrorListBucketRequired = errors.New("bucket or container name is needed in remote")
ErrorIsFile = errors.New("is a file not a directory")
ErrorIsDir = errors.New("is a directory not a file")
ErrorNotAFile = errors.New("is not a regular file")
ErrorNotDeleting = errors.New("not deleting files as there were IO errors")
ErrorNotDeletingDirs = errors.New("not deleting directories as there were IO errors")

View file

@ -28,6 +28,10 @@ type Fs interface {
// NewObject finds the Object at remote. If it can't be found
// it returns the error ErrorObjectNotFound.
//
// If remote points to a directory then it should return
// ErrorIsDir if possible without doing any extra work,
// otherwise ErrorObjectNotFound.
NewObject(ctx context.Context, remote string) (Object, error)
// Put in to the remote path with the modTime given of the given size

View file

@ -1044,13 +1044,13 @@ func Run(t *testing.T, opt *Opt) {
fstest.CheckListing(t, f, []fstest.Item{file1, file2})
})
// TestFsNewObjectDir tests NewObject on a directory which should produce an error
// TestFsNewObjectDir tests NewObject on a directory which should produce fs.ErrorIsDir if possible or fs.ErrorObjectNotFound if not
t.Run("FsNewObjectDir", func(t *testing.T) {
skipIfNotOk(t)
dir := path.Dir(file2.Path)
obj, err := f.NewObject(ctx, dir)
assert.Nil(t, obj)
assert.NotNil(t, err)
assert.True(t, err == fs.ErrorIsDir || err == fs.ErrorObjectNotFound, fmt.Sprintf("Wrong error: expecting fs.ErrorIsDir or fs.ErrorObjectNotFound but got: %#v", err))
})
// TestFsPurge tests Purge