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

[Storage] [DataMovement] Fixed File Attributes with ReadOnly does not transfer / copy correctly bug #48096

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- Changed `FromClient` methods to `static` methods.

### Bugs Fixed
- Fixed File Attributes with ReadOnly does not transfer / copy correctly bug #2167

### Other Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.DataMovement.Files.Shares",
"Tag": "net/storage/Azure.Storage.DataMovement.Files.Shares_1510e7b5ae"
"Tag": "net/storage/Azure.Storage.DataMovement.Files.Shares_147f0416cd"
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ internal async Task CreateAsync(
IDictionary<string, string> metadata = _options?.GetFileMetadata(properties?.RawProperties);
string filePermission = _options?.GetFilePermission(properties?.RawProperties);
FileSmbProperties smbProperties = _options?.GetFileSmbProperties(properties, _destinationPermissionKey);
// if transfer is not empty and File Attribute contains ReadOnly, we should not set it before creating the file.
if ((properties == null || properties.ResourceLength > 0) && IsReadOnlySet(smbProperties.FileAttributes))
{
smbProperties.FileAttributes = default;
}

await ShareFileClient.CreateAsync(
maxSize: maxSize,
httpHeaders: httpHeaders,
Expand All @@ -90,20 +96,27 @@ await ShareFileClient.CreateAsync(
cancellationToken: cancellationToken).ConfigureAwait(false);
}

private bool IsReadOnlySet(NtfsFileAttributes? fileAttributes)
{
return fileAttributes?.HasFlag(NtfsFileAttributes.ReadOnly) ?? false;
}

protected override async Task CompleteTransferAsync(
bool overwrite,
StorageResourceCompleteTransferOptions completeTransferOptions,
CancellationToken cancellationToken = default)
{
CancellationHelper.ThrowIfCancellationRequested(cancellationToken);

// Call Set Properties if FileChangedOn is to be preserved or manually set
// as it can be changed during a transfer.
if (_options?._isFileChangedOnSet == false || _options?.FileChangedOn != null)
StorageResourceItemProperties sourceProperties = completeTransferOptions?.SourceProperties;
FileSmbProperties smbProperties = _options?.GetFileSmbProperties(sourceProperties);
// Call Set Properties
// if transfer is not empty and original File Attribute contains ReadOnly
// or if FileChangedOn is to be preserved or manually set
if (((sourceProperties == null || sourceProperties.ResourceLength > 0) && IsReadOnlySet(smbProperties.FileAttributes))
|| (_options?._isFileChangedOnSet == false || _options?.FileChangedOn != null))
{
StorageResourceItemProperties sourceProperties = completeTransferOptions?.SourceProperties;
ShareFileHttpHeaders httpHeaders = _options?.GetShareFileHttpHeaders(sourceProperties?.RawProperties);
FileSmbProperties smbProperties = _options?.GetFileSmbProperties(sourceProperties);
await ShareFileClient.SetHttpHeadersAsync(new()
{
HttpHeaders = httpHeaders,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ private async Task<ShareFileClient> CreateFileClientWithPermissionKeyAsync(
ShareClientOptions options = null,
Stream contents = null,
TransferPropertiesTestType propertiesType = TransferPropertiesTestType.Default,
NtfsFileAttributes fileAttribute = _defaultFileAttributes,
CancellationToken cancellationToken = default)
{
objectName ??= GetNewObjectName();
Expand All @@ -145,14 +146,14 @@ private async Task<ShareFileClient> CreateFileClientWithPermissionKeyAsync(
string permissionKey = default;
if (propertiesType == TransferPropertiesTestType.Preserve)
{
PermissionInfo permissionInfo = await container.CreatePermissionAsync(new ShareFilePermission() { Permission = _defaultPermissions } );
PermissionInfo permissionInfo = await container.CreatePermissionAsync(new ShareFilePermission() { Permission = _defaultPermissions });
permissionKey = permissionInfo.FilePermissionKey;
}
await fileClient.CreateAsync(
maxSize: objectLength.Value,
new ShareFileCreateOptions()
{
HttpHeaders = new ShareFileHttpHeaders()
HttpHeaders = new ShareFileHttpHeaders()
{
ContentLanguage = _defaultContentLanguage,
ContentDisposition = _defaultContentDisposition,
Expand All @@ -161,7 +162,7 @@ await fileClient.CreateAsync(
Metadata = _defaultMetadata,
SmbProperties = new FileSmbProperties()
{
FileAttributes = _defaultFileAttributes,
FileAttributes = fileAttribute,
FilePermissionKey = permissionKey,
FileCreatedOn = _defaultFileCreatedOn,
FileChangedOn = _defaultFileChangedOn,
Expand Down Expand Up @@ -236,7 +237,7 @@ protected override StorageResourceItem GetDestinationStorageResourceItem(
ContentLanguage = _defaultContentLanguage,
CacheControl = _defaultCacheControl,
ContentType = _defaultContentType,
FileMetadata = _defaultMetadata,
FileMetadata = _defaultMetadata,
FileAttributes = _defaultFileAttributes,
FileCreatedOn = _defaultFileCreatedOn,
FileChangedOn = _defaultFileChangedOn,
Expand Down Expand Up @@ -416,6 +417,111 @@ await VerifyPropertiesCopyAsync(
cancellationToken: cancellationTokenSource.Token);
}

[RecordedTest]
[Combinatorial]
public async Task ShareFileToShareFile_ManuallySetFileAttributes(
[Values(NtfsFileAttributes.ReadOnly, NtfsFileAttributes.Hidden, NtfsFileAttributes.Archive, (NtfsFileAttributes.ReadOnly | NtfsFileAttributes.Archive), (NtfsFileAttributes.ReadOnly | NtfsFileAttributes.Hidden))] NtfsFileAttributes fileAttribute,
[Values(0, 1024)] long fileLength)
{
// Arrange
await using IDisposingContainer<ShareClient> source = await GetSourceDisposingContainerAsync();
await using IDisposingContainer<ShareClient> destination = await GetDestinationDisposingContainerAsync();

// Create source
ShareFileClient sourceClient = await CreateFileClientWithPermissionKeyAsync(
container: source.Container,
objectLength: fileLength,
createResource: true);
StorageResourceItem sourceResource = GetSourceStorageResourceItem(sourceClient);

// Create destination
ShareFileClient destinationClient = await GetDestinationObjectClientAsync(
container: destination.Container,
createResource: false);
// Manually setting destination File Attributes
ShareFileStorageResourceOptions destFileAttributeOptions = new();
destFileAttributeOptions.FileAttributes = fileAttribute;
StorageResourceItem destinationResource = new ShareFileStorageResource(destinationClient, destFileAttributeOptions);

TransferOptions options = new TransferOptions();
TestEventsRaised testEventsRaised = new TestEventsRaised(options);
TransferManager transferManager = new TransferManager();

// Act - Start transfer and await for completion.
TransferOperation transfer = await transferManager.StartTransferAsync(
sourceResource,
destinationResource,
options);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await TestTransferWithTimeout.WaitForCompletionAsync(
transfer,
testEventsRaised,
cancellationTokenSource.Token);

// Assert
Assert.IsTrue(transfer.HasCompleted);
Assert.AreEqual(TransferState.Completed, transfer.Status.State);
await testEventsRaised.AssertSingleCompletedCheck();
using Stream sourceStream = await sourceClient.OpenReadAsync(cancellationToken: cancellationTokenSource.Token);
using Stream destinationStream = await destinationClient.OpenReadAsync(cancellationToken: cancellationTokenSource.Token);
Assert.AreEqual(sourceStream, destinationStream);
// Verify destination File Attributes
ShareFileProperties destinationProperties = await destinationClient.GetPropertiesAsync(cancellationToken: cancellationTokenSource.Token);
Assert.AreEqual(fileAttribute, destinationProperties.SmbProperties.FileAttributes);
}

[RecordedTest]
[Combinatorial]
public async Task ShareFileToShareFile_PreserveFromSourceFileAttributes(
[Values(NtfsFileAttributes.ReadOnly, NtfsFileAttributes.Hidden, NtfsFileAttributes.Archive, (NtfsFileAttributes.ReadOnly | NtfsFileAttributes.Archive), (NtfsFileAttributes.ReadOnly | NtfsFileAttributes.Hidden))] NtfsFileAttributes fileAttribute,
[Values(0, 1024)] long fileLength)
{
// Arrange
await using IDisposingContainer<ShareClient> source = await GetSourceDisposingContainerAsync();
await using IDisposingContainer<ShareClient> destination = await GetDestinationDisposingContainerAsync();

// Create source
ShareFileClient sourceClient = await CreateFileClientWithPermissionKeyAsync(
container: source.Container,
objectLength: fileLength,
createResource: true,
fileAttribute: fileAttribute);
// Manually setting source File Attributes
ShareFileStorageResourceOptions sourceFileAttributeOptions = new();
sourceFileAttributeOptions.FileAttributes = fileAttribute;
StorageResourceItem sourceResource = new ShareFileStorageResource(sourceClient, sourceFileAttributeOptions);

// Create destination
ShareFileClient destinationClient = await GetDestinationObjectClientAsync(
container: destination.Container,
createResource: false);
// No options set, so preserve from source
StorageResourceItem destinationResource = new ShareFileStorageResource(destinationClient);

TransferOptions options = new TransferOptions();
TestEventsRaised testEventsRaised = new TestEventsRaised(options);
TransferManager transferManager = new TransferManager();

// Act - Start transfer and await for completion.
TransferOperation transfer = await transferManager.StartTransferAsync(
sourceResource,
destinationResource,
options);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await TestTransferWithTimeout.WaitForCompletionAsync(
transfer,
testEventsRaised,
cancellationTokenSource.Token);

// Assert
Assert.IsTrue(transfer.HasCompleted);
Assert.AreEqual(TransferState.Completed, transfer.Status.State);
await testEventsRaised.AssertSingleCompletedCheck();
using Stream sourceStream = await sourceClient.OpenReadAsync(cancellationToken: cancellationTokenSource.Token);
using Stream destinationStream = await destinationClient.OpenReadAsync(cancellationToken: cancellationTokenSource.Token);
Assert.AreEqual(sourceStream, destinationStream);
}

[RecordedTest]
public async Task ShareFileToShareFile_PermissionKeyDefault()
{
Expand Down