in cmd/zc_traverser_blob.go [207:339]
func (t *blobTraverser) Traverse(preprocessor objectMorpher, processor objectProcessor, filters []ObjectFilter) (err error) {
blobURLParts, err := blob.ParseURL(t.rawURL)
if err != nil {
return err
}
// check if the url points to a single blob
blobProperties, isBlob, isDirStub, blobName, err := t.getPropertiesIfSingleBlob()
var respErr *azcore.ResponseError
if errors.As(err, &respErr) {
// Don't error out unless it's a CPK error just yet
// If it's a CPK error, we know it's a single blob and that we can't get the properties on it anyway.
if respErr.ErrorCode == string(bloberror.BlobUsesCustomerSpecifiedEncryption) {
return errors.New("this blob uses customer provided encryption keys (CPK). At the moment, AzCopy does not support CPK-encrypted blobs. " +
"If you wish to make use of this blob, we recommend using one of the Azure Storage SDKs")
}
if respErr.RawResponse == nil {
return fmt.Errorf("cannot list files due to reason %s", respErr)
} else if respErr.StatusCode == 403 { // Some nature of auth error-- Whatever the user is pointing at, they don't have access to, regardless of whether it's a file or a dir stub.
return fmt.Errorf("cannot list files due to reason %s", respErr)
}
}
// schedule the blob in two cases:
// 1. either we are targeting a single blob and the URL wasn't explicitly pointed to a virtual dir
// 2. either we are scanning recursively with includeDirectoryStubs set to true,
// then we add the stub blob that represents the directory
if (isBlob && !strings.HasSuffix(blobURLParts.BlobName, common.AZCOPY_PATH_SEPARATOR_STRING)) ||
(t.includeDirectoryStubs && isDirStub && t.recursive) {
// sanity checking so highlighting doesn't highlight things we're not worried about.
if blobProperties == nil {
panic("isBlob should never be set if getting properties is an error")
}
if azcopyScanningLogger != nil {
azcopyScanningLogger.Log(common.LogDebug, "Detected the root as a blob.")
azcopyScanningLogger.Log(common.LogDebug, fmt.Sprintf("Root entity type: %s", getEntityType(blobProperties.Metadata)))
}
relPath := ""
if strings.HasSuffix(blobName, "/") {
relPath = "\x00" // Because the ste will trim the / suffix from our source, or we may not already have it.
}
blobPropsAdapter := blobPropertiesResponseAdapter{blobProperties}
storedObject := newStoredObject(
preprocessor,
getObjectNameOnly(blobName),
relPath,
getEntityType(blobPropsAdapter.Metadata),
blobPropsAdapter.LastModified(),
blobPropsAdapter.ContentLength(),
blobPropsAdapter,
blobPropsAdapter,
blobPropsAdapter.Metadata,
blobURLParts.ContainerName,
)
if t.s2sPreserveSourceTags {
blobTagsMap, err := t.getBlobTags()
if err != nil {
panic("Couldn't fetch blob tags due to error: " + err.Error())
}
if len(blobTagsMap) > 0 {
storedObject.blobTags = blobTagsMap
}
}
if t.incrementEnumerationCounter != nil {
t.incrementEnumerationCounter(storedObject.entityType)
}
err := processIfPassedFilters(filters, storedObject, processor)
_, err = getProcessingError(err)
// short-circuit if we don't have anything else to scan and permanent delete is not on
if !t.includeDeleted && (isBlob || err != nil) {
return err
}
} else if blobURLParts.BlobName == "" && (t.preservePermissions.IsTruthy() || t.isDFS) {
// If the root is a container and we're copying "folders", we should persist the ACLs there too.
// For DFS, we should always include the container root.
if azcopyScanningLogger != nil {
azcopyScanningLogger.Log(common.LogDebug, "Detected the root as a container.")
}
storedObject := newStoredObject(
preprocessor,
"",
"",
common.EEntityType.Folder(),
time.Now(),
0,
noContentProps,
noBlobProps,
common.Metadata{},
blobURLParts.ContainerName,
)
if t.incrementEnumerationCounter != nil {
t.incrementEnumerationCounter(common.EEntityType.Folder())
}
err := processIfPassedFilters(filters, storedObject, processor)
_, err = getProcessingError(err)
if err != nil {
return err
}
}
// get the container URL so that we can list the blobs
containerClient := t.serviceClient.NewContainerClient(blobURLParts.ContainerName)
// get the search prefix to aid in the listing
// example: for a url like https://test.blob.core.windows.net/test/foo/bar/bla
// the search prefix would be foo/bar/bla
searchPrefix := blobURLParts.BlobName
// append a slash if it is not already present
// example: foo/bar/bla becomes foo/bar/bla/ so that we only list children of the virtual directory
if searchPrefix != "" && !strings.HasSuffix(searchPrefix, common.AZCOPY_PATH_SEPARATOR_STRING) && !t.includeSnapshot && !t.includeDeleted {
searchPrefix += common.AZCOPY_PATH_SEPARATOR_STRING
}
// as a performance optimization, get an extra prefix to do pre-filtering. It's typically the start portion of a blob name.
extraSearchPrefix := FilterSet(filters).GetEnumerationPreFilter(t.recursive)
if t.parallelListing {
return t.parallelList(containerClient, blobURLParts.ContainerName, searchPrefix, extraSearchPrefix, preprocessor, processor, filters)
}
return t.serialList(containerClient, blobURLParts.ContainerName, searchPrefix, extraSearchPrefix, preprocessor, processor, filters)
}