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:
parent
af732c5431
commit
3fbaa4c0b0
19 changed files with 42 additions and 23 deletions
|
@ -1184,6 +1184,9 @@ func (o *Object) Size() int64 {
|
||||||
|
|
||||||
// setMetaData sets the metadata from info
|
// setMetaData sets the metadata from info
|
||||||
func (o *Object) setMetaData(info *api.Item) (err error) {
|
func (o *Object) setMetaData(info *api.Item) (err error) {
|
||||||
|
if info.Type == api.ItemTypeFolder {
|
||||||
|
return fs.ErrorIsDir
|
||||||
|
}
|
||||||
if info.Type != api.ItemTypeFile {
|
if info.Type != api.ItemTypeFile {
|
||||||
return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
|
return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1374,7 +1374,7 @@ func (f *Fs) newObjectWithExportInfo(
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case info.MimeType == driveFolderType:
|
case info.MimeType == driveFolderType:
|
||||||
return nil, fs.ErrorNotAFile
|
return nil, fs.ErrorIsDir
|
||||||
case info.MimeType == shortcutMimeType:
|
case info.MimeType == shortcutMimeType:
|
||||||
// We can only get here if f.opt.SkipShortcuts is set
|
// We can only get here if f.opt.SkipShortcuts is set
|
||||||
// and not from a listing. This is unlikely.
|
// 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
|
isDir = true
|
||||||
} else if srcObj, err := srcFs.NewObject(ctx, srcPath); err != nil {
|
} 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")
|
return nil, errors.Wrap(err, "can't find source")
|
||||||
}
|
}
|
||||||
// source was a directory
|
// 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 != fs.ErrorObjectNotFound {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = errors.New("existing file")
|
err = errors.New("existing file")
|
||||||
} else if err == fs.ErrorNotAFile {
|
} else if err == fs.ErrorIsDir {
|
||||||
err = errors.New("existing directory")
|
err = errors.New("existing directory")
|
||||||
}
|
}
|
||||||
return nil, errors.Wrap(err, "not overwriting shortcut target")
|
return nil, errors.Wrap(err, "not overwriting shortcut target")
|
||||||
|
|
|
@ -624,6 +624,9 @@ func (f *Fs) getFileMetadata(ctx context.Context, filePath string) (fileInfo *fi
|
||||||
}
|
}
|
||||||
fileInfo, ok := entry.(*files.FileMetadata)
|
fileInfo, ok := entry.(*files.FileMetadata)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if _, ok = entry.(*files.FolderMetadata); ok {
|
||||||
|
return nil, fs.ErrorIsDir
|
||||||
|
}
|
||||||
return nil, fs.ErrorNotAFile
|
return nil, fs.ErrorNotAFile
|
||||||
}
|
}
|
||||||
return fileInfo, nil
|
return fileInfo, nil
|
||||||
|
|
|
@ -1089,7 +1089,7 @@ func (o *Object) Size() int64 {
|
||||||
// setMetaData sets the metadata from info
|
// setMetaData sets the metadata from info
|
||||||
func (o *Object) setMetaData(info *api.Item) (err error) {
|
func (o *Object) setMetaData(info *api.Item) (err error) {
|
||||||
if info.Type != api.ItemTypeFile {
|
if info.Type != api.ItemTypeFile {
|
||||||
return errors.Wrapf(fs.ErrorNotAFile, "%q is %q", o.remote, info.Type)
|
return fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
o.hasMetaData = true
|
o.hasMetaData = true
|
||||||
o.size = info.Size
|
o.size = info.Size
|
||||||
|
|
|
@ -599,7 +599,9 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string) (info *api.Jo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "read metadata failed")
|
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 nil, fs.ErrorNotAFile
|
||||||
}
|
}
|
||||||
return &result, nil
|
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
|
// Renew the token in the background
|
||||||
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
|
f.tokenRenewer = oauthutil.NewRenew(f.String(), ts, func() error {
|
||||||
_, err := f.readMetaDataForPath(ctx, "")
|
_, err := f.readMetaDataForPath(ctx, "")
|
||||||
if err == fs.ErrorNotAFile {
|
if err == fs.ErrorNotAFile || err == fs.ErrorIsDir {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return err
|
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)
|
_, err := f.NewObject(context.TODO(), remote)
|
||||||
if err != nil {
|
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
|
// File doesn't exist so return old f
|
||||||
f.root = root
|
f.root = root
|
||||||
return f, nil
|
return f, nil
|
||||||
|
|
|
@ -344,7 +344,7 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (obj fs.Object, err e
|
||||||
return nil, translateErrorsObject(err)
|
return nil, translateErrorsObject(err)
|
||||||
}
|
}
|
||||||
if info.Type == "dir" {
|
if info.Type == "dir" {
|
||||||
return nil, fs.ErrorNotAFile
|
return nil, fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
return &Object{
|
return &Object{
|
||||||
fs: f,
|
fs: f,
|
||||||
|
|
|
@ -401,7 +401,7 @@ func (f *Fs) newObjectWithInfo(remote string, info os.FileInfo) (fs.Object, erro
|
||||||
|
|
||||||
}
|
}
|
||||||
if o.mode.IsDir() {
|
if o.mode.IsDir() {
|
||||||
return nil, errors.Wrapf(fs.ErrorNotAFile, "%q", remote)
|
return nil, fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1958,7 +1958,7 @@ func (o *Object) readMetaData(ctx context.Context, force bool) error {
|
||||||
}
|
}
|
||||||
newObj, ok := entry.(*Object)
|
newObj, ok := entry.(*Object)
|
||||||
if !ok || dirSize >= 0 {
|
if !ok || dirSize >= 0 {
|
||||||
return fs.ErrorNotAFile
|
return fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
if newObj.remote != o.remote {
|
if newObj.remote != o.remote {
|
||||||
return fmt.Errorf("File %q path has changed to %q", o.remote, newObj.remote)
|
return fmt.Errorf("File %q path has changed to %q", o.remote, newObj.remote)
|
||||||
|
|
|
@ -303,7 +303,7 @@ func (f *Fs) findObject(rootNode *mega.Node, file string) (node *mega.Node, err
|
||||||
if err == mega.ENOENT {
|
if err == mega.ENOENT {
|
||||||
return nil, fs.ErrorObjectNotFound
|
return nil, fs.ErrorObjectNotFound
|
||||||
} else if err == nil && node.GetType() != mega.FILE {
|
} 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
|
return node, err
|
||||||
}
|
}
|
||||||
|
@ -958,7 +958,7 @@ func (o *Object) Size() int64 {
|
||||||
// setMetaData sets the metadata from info
|
// setMetaData sets the metadata from info
|
||||||
func (o *Object) setMetaData(info *mega.Node) (err error) {
|
func (o *Object) setMetaData(info *mega.Node) (err error) {
|
||||||
if info.GetType() != mega.FILE {
|
if info.GetType() != mega.FILE {
|
||||||
return fs.ErrorNotAFile
|
return fs.ErrorIsDir // all other node types are directories
|
||||||
}
|
}
|
||||||
o.info = info
|
o.info = info
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1715,7 +1715,7 @@ func (o *Object) Size() int64 {
|
||||||
// setMetaData sets the metadata from info
|
// setMetaData sets the metadata from info
|
||||||
func (o *Object) setMetaData(info *api.Item) (err error) {
|
func (o *Object) setMetaData(info *api.Item) (err error) {
|
||||||
if info.GetFolder() != nil {
|
if info.GetFolder() != nil {
|
||||||
return errors.Wrapf(fs.ErrorNotAFile, "%q", o.remote)
|
return fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
o.hasMetaData = true
|
o.hasMetaData = true
|
||||||
o.size = info.GetSize()
|
o.size = info.GetSize()
|
||||||
|
|
|
@ -151,7 +151,7 @@ func (o *Object) readEntry(ctx context.Context) (f *putio.File, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if resp.File.IsDir() {
|
if resp.File.IsDir() {
|
||||||
return nil, fs.ErrorNotAFile
|
return nil, fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
return &resp.File, err
|
return &resp.File, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1383,7 +1383,7 @@ func (o *Object) stat(ctx context.Context) error {
|
||||||
return errors.Wrap(err, "stat failed")
|
return errors.Wrap(err, "stat failed")
|
||||||
}
|
}
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return errors.Wrapf(fs.ErrorNotAFile, "%q", o.remote)
|
return fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
o.setMetadata(info)
|
o.setMetadata(info)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -337,9 +337,13 @@ func (f *Fs) readMetaDataForIDPath(ctx context.Context, id, path string, directo
|
||||||
if directoriesOnly && item.Type != api.ItemTypeFolder {
|
if directoriesOnly && item.Type != api.ItemTypeFolder {
|
||||||
return nil, fs.ErrorIsFile
|
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 nil, fs.ErrorNotAFile
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return &item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,7 +317,7 @@ func (f *Fs) readMetaDataForPath(ctx context.Context, path string, depth string)
|
||||||
return nil, fs.ErrorObjectNotFound
|
return nil, fs.ErrorObjectNotFound
|
||||||
}
|
}
|
||||||
if itemIsDir(&item) {
|
if itemIsDir(&item) {
|
||||||
return nil, fs.ErrorNotAFile
|
return nil, fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
return &item.Props, nil
|
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)
|
_, err := f.NewObject(ctx, remote)
|
||||||
if err != nil {
|
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
|
// File doesn't exist so return old f
|
||||||
f.root = root
|
f.root = root
|
||||||
return f, nil
|
return f, nil
|
||||||
|
|
|
@ -949,7 +949,9 @@ func (o *Object) readMetaData(ctx context.Context) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.ResourceType != "file" {
|
if info.ResourceType == "dir" {
|
||||||
|
return fs.ErrorIsDir
|
||||||
|
} else if info.ResourceType != "file" {
|
||||||
return fs.ErrorNotAFile
|
return fs.ErrorNotAFile
|
||||||
}
|
}
|
||||||
return o.setMetaData(info)
|
return o.setMetaData(info)
|
||||||
|
|
|
@ -1121,7 +1121,7 @@ func (o *Object) Size() int64 {
|
||||||
// setMetaData sets the metadata from info
|
// setMetaData sets the metadata from info
|
||||||
func (o *Object) setMetaData(info *api.Item) (err error) {
|
func (o *Object) setMetaData(info *api.Item) (err error) {
|
||||||
if info.Attributes.IsFolder {
|
if info.Attributes.IsFolder {
|
||||||
return fs.ErrorNotAFile
|
return fs.ErrorIsDir
|
||||||
}
|
}
|
||||||
o.hasMetaData = true
|
o.hasMetaData = true
|
||||||
o.size = info.Attributes.StorageInfo.Size
|
o.size = info.Attributes.StorageInfo.Size
|
||||||
|
|
1
fs/fs.go
1
fs/fs.go
|
@ -37,6 +37,7 @@ var (
|
||||||
ErrorListAborted = errors.New("list aborted")
|
ErrorListAborted = errors.New("list aborted")
|
||||||
ErrorListBucketRequired = errors.New("bucket or container name is needed in remote")
|
ErrorListBucketRequired = errors.New("bucket or container name is needed in remote")
|
||||||
ErrorIsFile = errors.New("is a file not a directory")
|
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")
|
ErrorNotAFile = errors.New("is not a regular file")
|
||||||
ErrorNotDeleting = errors.New("not deleting files as there were IO errors")
|
ErrorNotDeleting = errors.New("not deleting files as there were IO errors")
|
||||||
ErrorNotDeletingDirs = errors.New("not deleting directories as there were IO errors")
|
ErrorNotDeletingDirs = errors.New("not deleting directories as there were IO errors")
|
||||||
|
|
|
@ -28,6 +28,10 @@ type Fs interface {
|
||||||
|
|
||||||
// NewObject finds the Object at remote. If it can't be found
|
// NewObject finds the Object at remote. If it can't be found
|
||||||
// it returns the error ErrorObjectNotFound.
|
// 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)
|
NewObject(ctx context.Context, remote string) (Object, error)
|
||||||
|
|
||||||
// Put in to the remote path with the modTime given of the given size
|
// Put in to the remote path with the modTime given of the given size
|
||||||
|
|
|
@ -1044,13 +1044,13 @@ func Run(t *testing.T, opt *Opt) {
|
||||||
fstest.CheckListing(t, f, []fstest.Item{file1, file2})
|
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) {
|
t.Run("FsNewObjectDir", func(t *testing.T) {
|
||||||
skipIfNotOk(t)
|
skipIfNotOk(t)
|
||||||
dir := path.Dir(file2.Path)
|
dir := path.Dir(file2.Path)
|
||||||
obj, err := f.NewObject(ctx, dir)
|
obj, err := f.NewObject(ctx, dir)
|
||||||
assert.Nil(t, obj)
|
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
|
// TestFsPurge tests Purge
|
||||||
|
|
Loading…
Reference in a new issue