Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux/POSIX properties persistence #1780

Merged
merged 27 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3cf159f
SIP Implementation
adreed-msft Apr 4, 2022
d80276b
Opt for an interface instead of a struct
adreed-msft May 2, 2022
087199f
Implement upload
adreed-msft May 2, 2022
0ee0c67
Implement file creation
adreed-msft May 4, 2022
145c082
Implement property setting; todo: error handling
adreed-msft May 5, 2022
b3c8ae9
Error handling
adreed-msft May 6, 2022
ab20deb
Add upload support to append/page blob
adreed-msft May 6, 2022
8f54d37
Implement flag
adreed-msft May 10, 2022
8ff7c84
Somewhat fix download
adreed-msft May 12, 2022
2a94119
Merge branch 'dev' into adreed/unix-properties
adreed-msft May 12, 2022
7e6cc76
Handle folder uploading/copying
adreed-msft May 23, 2022
06f4f7d
Merge remote-tracking branch 'origin/adreed/unix-properties' into adr…
adreed-msft May 23, 2022
3017a7e
Remove download
adreed-msft May 23, 2022
e1aeda8
Fix getUNIXProperties
adreed-msft May 23, 2022
0bd00bc
Remove device filtering
adreed-msft May 24, 2022
5e18a72
Convert flag to bool
adreed-msft May 25, 2022
1fecf73
Attempt to fix accidental folder transfers
adreed-msft May 25, 2022
400cbd2
Fix test function signatures
adreed-msft May 25, 2022
be7f803
Add preserve-posix-properties to sync
adreed-msft May 25, 2022
e1920ca
Limit auto-setting to sync and copy
adreed-msft May 25, 2022
6b8b4ce
Default to false
adreed-msft May 26, 2022
9e34de1
Potentially fix testing
adreed-msft May 26, 2022
473eb21
Add check for flag
adreed-msft May 26, 2022
eb01a51
Shift back to single-call
adreed-msft May 27, 2022
ba9127f
Handle comments on PR
adreed-msft May 27, 2022
0702189
Add includeDirectoryStubs to folder property considerations
adreed-msft May 27, 2022
1f39f18
Address final comments
adreed-msft May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions cmd/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ type rawCopyCmdArgs struct {
// Opt-in flag to persist additional SMB properties to Azure Files. Named ...info instead of ...properties
// because the latter was similar enough to preserveSMBPermissions to induce user error
preserveSMBInfo bool
// Opt-in flag to persist additional POSIX properties
preservePOSIXProperties bool
// Opt-in flag to preserve the blob index tags during service to service transfer.
s2sPreserveBlobTags bool
// Flag to enable Window's special privileges
Expand Down Expand Up @@ -638,6 +640,11 @@ func (raw rawCopyCmdArgs) cook() (CookedCopyCmdArgs, error) {
cooked.preserveSMBInfo = false
}

cooked.preservePOSIXProperties = raw.preservePOSIXProperties
if cooked.preservePOSIXProperties && !areBothLocationsPOSIXAware(cooked.FromTo) {
adreed-msft marked this conversation as resolved.
Show resolved Hide resolved
return cooked, fmt.Errorf("in order to use --preserve-posix-properties, both the source and destination must be POSIX-aware (Linux->Blob, Blob->Linux, Blob->Blob)")
adreed-msft marked this conversation as resolved.
Show resolved Hide resolved
}

if err = validatePreserveSMBPropertyOption(cooked.preserveSMBInfo, cooked.FromTo, &cooked.ForceWrite, "preserve-smb-info"); err != nil {
return cooked, err
}
Expand Down Expand Up @@ -907,6 +914,14 @@ func areBothLocationsSMBAware(fromTo common.FromTo) bool {
}
}

func areBothLocationsPOSIXAware(fromTo common.FromTo) bool {
// POSIX properties are stored in blob metadata-- They don't need a special persistence strategy for BlobBlob.
return runtime.GOOS == "linux" && (
// fromTo == common.EFromTo.BlobLocal() || TODO
fromTo == common.EFromTo.LocalBlob()) ||
fromTo == common.EFromTo.BlobBlob()
}

func validatePreserveSMBPropertyOption(toPreserve bool, fromTo common.FromTo, overwrite *common.OverwriteOption, flagName string) error {
if toPreserve && !(fromTo == common.EFromTo.LocalFile() ||
fromTo == common.EFromTo.FileLocal() ||
Expand Down Expand Up @@ -1104,6 +1119,8 @@ type CookedCopyCmdArgs struct {
preservePermissions common.PreservePermissionsOption
// Whether the user wants to preserve the SMB properties ...
preserveSMBInfo bool
// Whether the user wants to preserve the POSIX properties ...
preservePOSIXProperties bool

// Whether to enable Windows special privileges
backupMode bool
Expand Down Expand Up @@ -1869,6 +1886,7 @@ func init() {
cpCmd.PersistentFlags().BoolVar(&raw.asSubdir, "as-subdir", true, "True by default. Places folder sources as subdirectories under the destination.")
cpCmd.PersistentFlags().BoolVar(&raw.preserveOwner, common.PreserveOwnerFlagName, common.PreserveOwnerDefault, "Only has an effect in downloads, and only when --preserve-smb-permissions is used. If true (the default), the file Owner and Group are preserved in downloads. If set to false, --preserve-smb-permissions will still preserve ACLs but Owner and Group will be based on the user running AzCopy")
cpCmd.PersistentFlags().BoolVar(&raw.preserveSMBInfo, "preserve-smb-info", true, "For SMB-aware locations, flag will be set to true by default. Preserves SMB property info (last write time, creation time, attribute bits) between SMB-aware resources (Windows and Azure Files). Only the attribute bits supported by Azure Files will be transferred; any others will be ignored. This flag applies to both files and folders, unless a file-only filter is specified (e.g. include-pattern). The info transferred for folders is the same as that for files, except for Last Write Time which is never preserved for folders.")
cpCmd.PersistentFlags().BoolVar(&raw.preservePOSIXProperties, "preserve-posix-properties", false, "'Preserves' property info gleaned from stat or statx into object metadata.")
cpCmd.PersistentFlags().BoolVar(&raw.forceIfReadOnly, "force-if-read-only", false, "When overwriting an existing file on Windows or Azure Files, force the overwrite to work even if the existing file has its read-only attribute set")
cpCmd.PersistentFlags().BoolVar(&raw.backupMode, common.BackupModeFlagName, false, "Activates Windows' SeBackupPrivilege for uploads, or SeRestorePrivilege for downloads, to allow AzCopy to see read all files, regardless of their file system permissions, and to restore all permissions. Requires that the account running AzCopy already has these permissions (e.g. has Administrator rights or is a member of the 'Backup Operators' group). All this flag does is activate privileges that the account already has")
cpCmd.PersistentFlags().BoolVar(&raw.putMd5, "put-md5", false, "Create an MD5 hash of each file, and save the hash as the Content-MD5 property of the destination blob or file. (By default the hash is NOT created.) Only available when uploading.")
Expand Down
16 changes: 6 additions & 10 deletions cmd/copyEnumeratorInit.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (cca *CookedCopyCmdArgs) initEnumerator(jobPartOrder common.CopyJobPartOrde
jobPartOrder.CpkOptions = cca.CpkOptions
jobPartOrder.PreserveSMBPermissions = cca.preservePermissions
jobPartOrder.PreserveSMBInfo = cca.preserveSMBInfo
jobPartOrder.PreservePOSIXProperties = cca.preservePOSIXProperties

// Infer on download so that we get LMT and MD5 on files download
// On S2S transfers the following rules apply:
Expand All @@ -80,10 +81,7 @@ func (cca *CookedCopyCmdArgs) initEnumerator(jobPartOrder common.CopyJobPartOrde
jobPartOrder.S2SInvalidMetadataHandleOption = cca.s2sInvalidMetadataHandleOption
jobPartOrder.S2SPreserveBlobTags = cca.S2sPreserveBlobTags

traverser, err = InitResourceTraverser(cca.Source, cca.FromTo.From(), &ctx, &srcCredInfo,
&cca.FollowSymlinks, cca.ListOfFilesChannel, cca.Recursive, getRemoteProperties,
cca.IncludeDirectoryStubs, cca.permanentDeleteOption, func(common.EntityType) {}, cca.ListOfVersionIDs,
cca.S2sPreserveBlobTags, cca.LogVerbosity.ToPipelineLogLevel(), cca.CpkOptions)
traverser, err = InitResourceTraverser(cca.Source, cca.FromTo.From(), &ctx, &srcCredInfo, &cca.FollowSymlinks, cca.ListOfFilesChannel, cca.Recursive, getRemoteProperties, cca.IncludeDirectoryStubs, cca.permanentDeleteOption, func(common.EntityType) {}, cca.ListOfVersionIDs, cca.S2sPreserveBlobTags, cca.LogVerbosity.ToPipelineLogLevel(), cca.CpkOptions)
adreed-msft marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return nil, err
Expand Down Expand Up @@ -236,7 +234,7 @@ func (cca *CookedCopyCmdArgs) initEnumerator(jobPartOrder common.CopyJobPartOrde

// decide our folder transfer strategy
var message string
jobPartOrder.Fpo, message = newFolderPropertyOption(cca.FromTo, cca.Recursive, cca.StripTopDir, filters, cca.preserveSMBInfo, cca.preservePermissions.IsTruthy(), cca.isHNStoHNS, strings.EqualFold(cca.Destination.Value, common.Dev_Null))
jobPartOrder.Fpo, message = newFolderPropertyOption(cca.FromTo, cca.Recursive, cca.StripTopDir, filters, cca.preserveSMBInfo, cca.preservePermissions.IsTruthy(), cca.preservePOSIXProperties, cca.isHNStoHNS, strings.EqualFold(cca.Destination.Value, common.Dev_Null), cca.IncludeDirectoryStubs)
if !cca.dryrunMode {
glcm.Info(message)
}
Expand Down Expand Up @@ -354,9 +352,7 @@ func (cca *CookedCopyCmdArgs) isDestDirectory(dst common.ResourceString, ctx *co
return false
}

rt, err := InitResourceTraverser(dst, cca.FromTo.To(), ctx, &dstCredInfo, nil,
nil, false, false, false, common.EPermanentDeleteOption.None(),
func(common.EntityType) {}, cca.ListOfVersionIDs, false, pipeline.LogNone, cca.CpkOptions)
rt, err := InitResourceTraverser(dst, cca.FromTo.To(), ctx, &dstCredInfo, nil, nil, false, false, false, common.EPermanentDeleteOption.None(), func(common.EntityType) {}, cca.ListOfVersionIDs, false, pipeline.LogNone, cca.CpkOptions)

if err != nil {
return false
Expand Down Expand Up @@ -678,7 +674,7 @@ func (cca *CookedCopyCmdArgs) MakeEscapedRelativePath(source bool, dstIsDir bool
}

// we assume that preserveSmbPermissions and preserveSmbInfo have already been validated, such that they are only true if both resource types support them
func newFolderPropertyOption(fromTo common.FromTo, recursive bool, stripTopDir bool, filters []ObjectFilter, preserveSmbInfo, preserveSmbPermissions, isDfsDfs, isDstNull bool) (common.FolderPropertyOption, string) {
func newFolderPropertyOption(fromTo common.FromTo, recursive, stripTopDir bool, filters []ObjectFilter, preserveSmbInfo, preserveSmbPermissions, preservePosixProperties, isDfsDfs, isDstNull, includeDirectoryStubs bool) (common.FolderPropertyOption, string) {

getSuffix := func(willProcess bool) string {
willProcessString := common.IffString(willProcess, "will be processed", "will not be processed")
Expand All @@ -696,7 +692,7 @@ func newFolderPropertyOption(fromTo common.FromTo, recursive bool, stripTopDir b
}
}

bothFolderAware := (fromTo.AreBothFolderAware() || isDfsDfs) && !isDstNull // Copying folders to dev null doesn't make sense.
bothFolderAware := (fromTo.AreBothFolderAware() || isDfsDfs || preservePosixProperties || includeDirectoryStubs) && !isDstNull
isRemoveFromFolderAware := fromTo == common.EFromTo.FileTrash()
if bothFolderAware || isRemoveFromFolderAware {
if !recursive {
Expand Down
4 changes: 1 addition & 3 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,7 @@ func (cooked cookedListCmdArgs) HandleListContainerCommand() (err error) {
}
}

traverser, err := InitResourceTraverser(source, cooked.location, &ctx, &credentialInfo, nil, nil,
true, false, false, common.EPermanentDeleteOption.None(), func(common.EntityType) {},
nil, false, pipeline2.LogNone, common.CpkOptions{})
traverser, err := InitResourceTraverser(source, cooked.location, &ctx, &credentialInfo, nil, nil, true, false, false, common.EPermanentDeleteOption.None(), func(common.EntityType) {}, nil, false, pipeline2.LogNone, common.CpkOptions{})

if err != nil {
return fmt.Errorf("failed to initialize traverser: %s", err.Error())
Expand Down
7 changes: 2 additions & 5 deletions cmd/removeEnumerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ func newRemoveEnumerator(cca *CookedCopyCmdArgs) (enumerator *CopyEnumerator, er
ctx := context.WithValue(context.TODO(), ste.ServiceAPIVersionOverride, ste.DefaultServiceApiVersion)

// Include-path is handled by ListOfFilesChannel.
sourceTraverser, err = InitResourceTraverser(cca.Source, cca.FromTo.From(), &ctx, &cca.credentialInfo,
nil, cca.ListOfFilesChannel, cca.Recursive, false, cca.IncludeDirectoryStubs,
cca.permanentDeleteOption, func(common.EntityType) {}, cca.ListOfVersionIDs, false,
cca.LogVerbosity.ToPipelineLogLevel(), cca.CpkOptions)
sourceTraverser, err = InitResourceTraverser(cca.Source, cca.FromTo.From(), &ctx, &cca.credentialInfo, nil, cca.ListOfFilesChannel, cca.Recursive, false, cca.IncludeDirectoryStubs, cca.permanentDeleteOption, func(common.EntityType) {}, cca.ListOfVersionIDs, false, cca.LogVerbosity.ToPipelineLogLevel(), cca.CpkOptions)

// report failure to create traverser
if err != nil {
Expand All @@ -71,7 +68,7 @@ func newRemoveEnumerator(cca *CookedCopyCmdArgs) (enumerator *CopyEnumerator, er
// (Must enumerate folders when deleting from a folder-aware location. Can't do folder deletion just based on file
// deletion, because that would not handle folders that were empty at the start of the job).
// isHNStoHNS is IGNORED here, because BlobFS locations don't take this route currently.
fpo, message := newFolderPropertyOption(cca.FromTo, cca.Recursive, cca.StripTopDir, filters, false, false, false, false)
fpo, message := newFolderPropertyOption(cca.FromTo, cca.Recursive, cca.StripTopDir, filters, false, false, false, false, false, cca.IncludeDirectoryStubs)
// do not print Info message if in dry run mode
if !cca.dryrunMode {
glcm.Info(message)
Expand Down
40 changes: 24 additions & 16 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ type rawSyncCmdArgs struct {
includeRegex string
excludeRegex string

preservePermissions bool
preserveSMBPermissions bool // deprecated and synonymous with preservePermissions
preserveOwner bool
preserveSMBInfo bool
followSymlinks bool
backupMode bool
putMd5 bool
md5ValidationOption string
preservePermissions bool
preserveSMBPermissions bool // deprecated and synonymous with preservePermissions
preserveOwner bool
preserveSMBInfo bool
preservePOSIXProperties bool
followSymlinks bool
backupMode bool
putMd5 bool
md5ValidationOption string
// this flag indicates the user agreement with respect to deleting the extra files at the destination
// which do not exists at source. With this flag turned on/off, users will not be asked for permission.
// otherwise the user is prompted to make a decision
Expand Down Expand Up @@ -270,6 +271,11 @@ func (raw *rawSyncCmdArgs) cook() (cookedSyncCmdArgs, error) {
cooked.isHNSToHNS = true // override HNS settings, since if a user is tx'ing blob->blob and copying permissions, it's DEFINITELY going to be HNS (since perms don't exist w/o HNS).
}

cooked.preservePOSIXProperties = raw.preservePOSIXProperties
if cooked.preservePOSIXProperties && !areBothLocationsPOSIXAware(cooked.fromTo) {
return cooked, fmt.Errorf("in order to use --preserve-posix-properties, both the source and destination must be POSIX-aware (valid pairings are Linux->Blob, Blob->Linux, Blob->Blob)")
}

cooked.putMd5 = raw.putMd5
if err = validatePutMd5(cooked.putMd5, cooked.fromTo); err != nil {
return cooked, err
Expand Down Expand Up @@ -369,14 +375,15 @@ type cookedSyncCmdArgs struct {
excludeRegex []string

// options
preservePermissions common.PreservePermissionsOption
preserveSMBInfo bool
putMd5 bool
md5ValidationOption common.HashValidationOption
blockSize int64
logVerbosity common.LogLevel
forceIfReadOnly bool
backupMode bool
preservePermissions common.PreservePermissionsOption
preserveSMBInfo bool
preservePOSIXProperties bool
putMd5 bool
md5ValidationOption common.HashValidationOption
blockSize int64
logVerbosity common.LogLevel
forceIfReadOnly bool
backupMode bool

// commandString hold the user given command which is logged to the Job log file
commandString string
Expand Down Expand Up @@ -739,6 +746,7 @@ func init() {
// smb info/permissions can be persisted in the scenario of File -> File
syncCmd.PersistentFlags().BoolVar(&raw.preserveSMBPermissions, "preserve-smb-permissions", false, "False by default. Preserves SMB ACLs between aware resources (Azure Files). This flag applies to both files and folders, unless a file-only filter is specified (e.g. include-pattern).")
syncCmd.PersistentFlags().BoolVar(&raw.preserveSMBInfo, "preserve-smb-info", true, "For SMB-aware locations, flag will be set to true by default. Preserves SMB property info (last write time, creation time, attribute bits) between SMB-aware resources (Azure Files). This flag applies to both files and folders, unless a file-only filter is specified (e.g. include-pattern). The info transferred for folders is the same as that for files, except for Last Write Time which is not preserved for folders. ")
syncCmd.PersistentFlags().BoolVar(&raw.preservePOSIXProperties, "preserve-posix-properties", false, "'Preserves' property info gleaned from stat or statx into object metadata.")

// TODO: enable when we support local <-> File
// syncCmd.PersistentFlags().BoolVar(&raw.forceIfReadOnly, "force-if-read-only", false, "When overwriting an existing file on Windows or Azure Files, force the overwrite to work even if the existing file has its read-only attribute set")
Expand Down
13 changes: 6 additions & 7 deletions cmd/syncEnumerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ func (cca *cookedSyncCmdArgs) initEnumerator(ctx context.Context) (enumerator *s
// TODO: enable symlink support in a future release after evaluating the implications
adreed-msft marked this conversation as resolved.
Show resolved Hide resolved
// GetProperties is enabled by default as sync supports both upload and download.
// This property only supports Files and S3 at the moment, but provided that Files sync is coming soon, enable to avoid stepping on Files sync work
sourceTraverser, err := InitResourceTraverser(cca.source, cca.fromTo.From(), &ctx, &srcCredInfo, nil,
nil, cca.recursive, true, cca.isHNSToHNS, common.EPermanentDeleteOption.None(), func(entityType common.EntityType) {
if entityType == common.EEntityType.File() {
atomic.AddUint64(&cca.atomicSourceFilesScanned, 1)
}
}, nil, cca.s2sPreserveBlobTags, cca.logVerbosity.ToPipelineLogLevel(), cca.cpkOptions)
sourceTraverser, err := InitResourceTraverser(cca.source, cca.fromTo.From(), &ctx, &srcCredInfo, nil, nil, cca.recursive, true, cca.isHNSToHNS, common.EPermanentDeleteOption.None(), func(entityType common.EntityType) {
if entityType == common.EEntityType.File() {
atomic.AddUint64(&cca.atomicSourceFilesScanned, 1)
}
}, nil, cca.s2sPreserveBlobTags, cca.logVerbosity.ToPipelineLogLevel(), cca.cpkOptions)

if err != nil {
return nil, err
Expand Down Expand Up @@ -124,7 +123,7 @@ func (cca *cookedSyncCmdArgs) initEnumerator(ctx context.Context) (enumerator *s
}

// decide our folder transfer strategy
fpo, folderMessage := newFolderPropertyOption(cca.fromTo, cca.recursive, true, filters, cca.preserveSMBInfo, cca.preservePermissions.IsTruthy(), cca.isHNSToHNS, strings.EqualFold(cca.destination.Value, common.Dev_Null)) // sync always acts like stripTopDir=true
fpo, folderMessage := newFolderPropertyOption(cca.fromTo, cca.recursive, true, filters, cca.preserveSMBInfo, cca.preservePermissions.IsTruthy(), false, cca.isHNSToHNS, strings.EqualFold(cca.destination.Value, common.Dev_Null), false) // sync always acts like stripTopDir=true
if !cca.dryrunMode {
glcm.Info(folderMessage)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/syncProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func newSyncTransferProcessor(cca *cookedSyncCmdArgs, numOfTransfersPerPart int,
LogLevel: cca.logVerbosity,
PreserveSMBPermissions: cca.preservePermissions,
PreserveSMBInfo: cca.preserveSMBInfo,
PreservePOSIXProperties: cca.preservePOSIXProperties,
S2SSourceChangeValidation: true,
DestLengthValidation: true,
S2SGetPropertiesInBackend: true,
Expand Down
5 changes: 1 addition & 4 deletions cmd/zc_enumerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,7 @@ type enumerationCounterFunc func(entityType common.EntityType)
// followSymlinks is only required for local resources (defaults to false)
// errorOnDirWOutRecursive is used by copy.

func InitResourceTraverser(resource common.ResourceString, location common.Location, ctx *context.Context,
credential *common.CredentialInfo, followSymlinks *bool, listOfFilesChannel chan string, recursive, getProperties,
includeDirectoryStubs bool, permanentDeleteOption common.PermanentDeleteOption, incrementEnumerationCounter enumerationCounterFunc, listOfVersionIds chan string,
s2sPreserveBlobTags bool, logLevel pipeline.LogLevel, cpkOptions common.CpkOptions) (ResourceTraverser, error) {
func InitResourceTraverser(resource common.ResourceString, location common.Location, ctx *context.Context, credential *common.CredentialInfo, followSymlinks *bool, listOfFilesChannel chan string, recursive, getProperties, includeDirectoryStubs bool, permanentDeleteOption common.PermanentDeleteOption, incrementEnumerationCounter enumerationCounterFunc, listOfVersionIds chan string, s2sPreserveBlobTags bool, logLevel pipeline.LogLevel, cpkOptions common.CpkOptions) (ResourceTraverser, error) {
adreed-msft marked this conversation as resolved.
Show resolved Hide resolved
var output ResourceTraverser
var p *pipeline.Pipeline

Expand Down
Loading