in cmd/zc_traverser_s3.go [66:186]
func (t *s3Traverser) Traverse(preprocessor objectMorpher, processor objectProcessor, filters []ObjectFilter) (err error) {
invalidAzureBlobName := func(objectKey string) bool {
/* S3 object name is invalid if it ends with period or
one of virtual directories in path ends with period.
This list is not exhaustive
*/
return strings.HasSuffix(objectKey, ".") ||
strings.Contains(objectKey, "./")
}
invalidNameErrorMsg := "Skipping S3 object %s, as it is not a valid Blob name. Rename the object and retry the transfer"
// Check if resource is a single object.
if t.s3URLParts.IsObjectSyntactically() && !t.s3URLParts.IsDirectorySyntactically() && !t.s3URLParts.IsBucketSyntactically() {
objectPath := strings.Split(t.s3URLParts.ObjectKey, "/")
objectName := objectPath[len(objectPath)-1]
oi, err := t.s3Client.StatObject(t.s3URLParts.BucketName, t.s3URLParts.ObjectKey, minio.StatObjectOptions{})
if invalidAzureBlobName(t.s3URLParts.ObjectKey) {
WarnStdoutAndScanningLog(fmt.Sprintf(invalidNameErrorMsg, t.s3URLParts.ObjectKey))
return common.EAzError.InvalidBlobName()
}
// If we actually got object properties, process them.
// Otherwise, treat it as a directory.
// According to IsDirectorySyntactically, objects and folders can share names
if err == nil {
// We had to statObject anyway, get ALL the info.
oie := common.ObjectInfoExtension{ObjectInfo: oi}
storedObject := newStoredObject(
preprocessor,
objectName,
"",
common.EEntityType.File(),
oi.LastModified,
oi.Size,
&oie,
noBlobProps,
oie.NewCommonMetadata(),
t.s3URLParts.BucketName)
err = processIfPassedFilters(
filters,
storedObject,
processor)
_, err = getProcessingError(err)
if err != nil {
return err
}
return nil
}
}
// Append a trailing slash if it is missing.
if !strings.HasSuffix(t.s3URLParts.ObjectKey, "/") && t.s3URLParts.ObjectKey != "" {
t.s3URLParts.ObjectKey += "/"
}
// Ignore *s in URLs and treat them as normal characters
// This is because * is both a valid URL path character and a valid portion of an object key in S3.
searchPrefix := t.s3URLParts.ObjectKey
// It's a bucket or virtual directory.
for objectInfo := range t.s3Client.ListObjectsV2(t.s3URLParts.BucketName, searchPrefix, t.recursive, t.ctx.Done()) {
if objectInfo.Err != nil {
return fmt.Errorf("cannot list objects, %v", objectInfo.Err)
}
if objectInfo.StorageClass == "" {
// Directories are the only objects without storage classes.
continue
}
if invalidAzureBlobName(objectInfo.Key) {
//Throw a warning on console and continue
WarnStdoutAndScanningLog(fmt.Sprintf(invalidNameErrorMsg, objectInfo.Key))
continue
}
objectPath := strings.Split(objectInfo.Key, "/")
objectName := objectPath[len(objectPath)-1]
// re-join the unescaped path.
relativePath := strings.TrimPrefix(objectInfo.Key, searchPrefix)
if strings.HasSuffix(relativePath, "/") {
// If a file has a suffix of /, it's still treated as a folder.
// Thus, akin to the old code. skip it.
continue
}
// default to empty props, but retrieve real ones if required
oie := common.ObjectInfoExtension{ObjectInfo: minio.ObjectInfo{}}
if t.getProperties {
oi, err := t.s3Client.StatObject(t.s3URLParts.BucketName, objectInfo.Key, minio.StatObjectOptions{})
if err != nil {
return err
}
oie = common.ObjectInfoExtension{ObjectInfo: oi}
}
storedObject := newStoredObject(
preprocessor,
objectName,
relativePath,
common.EEntityType.File(),
objectInfo.LastModified,
objectInfo.Size,
&oie,
noBlobProps,
oie.NewCommonMetadata(),
t.s3URLParts.BucketName)
err = processIfPassedFilters(filters,
storedObject,
processor)
_, err = getProcessingError(err)
if err != nil {
return
}
}
return
}