diff --git a/backend/azureblob/azureblob.go b/backend/azureblob/azureblob.go index 7cc3396ed..c26f9a6d4 100644 --- a/backend/azureblob/azureblob.go +++ b/backend/azureblob/azureblob.go @@ -417,8 +417,8 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { } _, err := f.NewObject(remote) if err != nil { - if err == fs.ErrorObjectNotFound { - // File doesn't exist so return old f + if err == fs.ErrorObjectNotFound || err == fs.ErrorNotAFile { + // File doesn't exist or is a directory so return old f f.root = oldRoot return f, nil } @@ -474,6 +474,21 @@ func (o *Object) updateMetadataWithModTime(modTime time.Time) { o.meta[modTimeKey] = modTime.Format(timeFormatOut) } +// Returns whether file is a directory marker or not +func isDirectoryMarker(size int64, metadata azblob.Metadata, remote string) bool { + // Directory markers are 0 length + if size == 0 { + // Note that metadata with hdi_isfolder = true seems to be a + // defacto standard for marking blobs as directories. + endsWithSlash := strings.HasSuffix(remote, "/") + if endsWithSlash || remote == "" || metadata["hdi_isfolder"] == "true" { + return true + } + + } + return false +} + // listFn is called from list to handle an object type listFn func(remote string, object *azblob.BlobItem, isDirectory bool) error @@ -539,26 +554,20 @@ func (f *Fs) list(dir string, recurse bool, maxResults uint, fn listFn) error { continue } remote := file.Name[len(f.root):] - // is this a directory marker? - if *file.Properties.ContentLength == 0 { - // Note that metadata with hdi_isfolder = true seems to be a - // defacto standard for marking blobs as directories. - endsWithSlash := strings.HasSuffix(remote, "/") - if endsWithSlash || remote == "" || file.Metadata["hdi_isfolder"] == "true" { - if endsWithSlash { - remote = remote[:len(remote)-1] - } - err = fn(remote, file, true) - if err != nil { - return err - } - // Keep track of directory markers. If recursing then - // there will be no Prefixes so no need to keep track - if !recurse { - directoryMarkers[remote] = struct{}{} - } - continue // skip directory marker + if isDirectoryMarker(*file.Properties.ContentLength, file.Metadata, remote) { + if strings.HasSuffix(remote, "/") { + remote = remote[:len(remote)-1] } + err = fn(remote, file, true) + if err != nil { + return err + } + // Keep track of directory markers. If recursing then + // there will be no Prefixes so no need to keep track + if !recurse { + directoryMarkers[remote] = struct{}{} + } + continue // skip directory marker } // Send object err = fn(remote, file, false) @@ -981,27 +990,37 @@ func (o *Object) setMetadata(metadata azblob.Metadata) { // o.md5 // o.meta func (o *Object) decodeMetaDataFromPropertiesResponse(info *azblob.BlobGetPropertiesResponse) (err error) { + metadata := info.NewMetadata() + size := info.ContentLength() + if isDirectoryMarker(size, metadata, o.remote) { + return fs.ErrorNotAFile + } // NOTE - Client library always returns MD5 as base64 decoded string, Object needs to maintain // this as base64 encoded string. o.md5 = base64.StdEncoding.EncodeToString(info.ContentMD5()) o.mimeType = info.ContentType() - o.size = info.ContentLength() + o.size = size o.modTime = time.Time(info.LastModified()) o.accessTier = azblob.AccessTierType(info.AccessTier()) - o.setMetadata(info.NewMetadata()) + o.setMetadata(metadata) return nil } func (o *Object) decodeMetaDataFromBlob(info *azblob.BlobItem) (err error) { + metadata := info.Metadata + size := *info.Properties.ContentLength + if isDirectoryMarker(size, metadata, o.remote) { + return fs.ErrorNotAFile + } // NOTE - Client library always returns MD5 as base64 decoded string, Object needs to maintain // this as base64 encoded string. o.md5 = base64.StdEncoding.EncodeToString(info.Properties.ContentMD5) o.mimeType = *info.Properties.ContentType - o.size = *info.Properties.ContentLength + o.size = size o.modTime = info.Properties.LastModified o.accessTier = info.Properties.AccessTier - o.setMetadata(info.Metadata) + o.setMetadata(metadata) return nil }