Skip to content

Commit aa51c1e

Browse files
committed
Fix ZIP external attributes on move actions
1 parent aefb01b commit aa51c1e

2 files changed

Lines changed: 95 additions & 19 deletions

File tree

src/Zio.Tests/FileSystems/TestZipArchiveFileSystemCompact.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,56 @@ public void TestDirectoryMove()
128128
Assert.True(fs.FileExists("/dir2/file2.txt"));
129129
}
130130

131+
#if !NET472
132+
[Fact]
133+
public void TestDirectoryMoveAttributes()
134+
{
135+
fs.CreateDirectory("/dir");
136+
fs.WriteAllText("/dir/file.txt", "test");
137+
138+
fs.SetAttributes("/dir", FileAttributes.Temporary);
139+
Assert.Equal(FileAttributes.Directory | FileAttributes.Temporary, fs.GetAttributes("/dir"));
140+
141+
fs.SetAttributes("/dir/file.txt", FileAttributes.Temporary);
142+
143+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/dir/file.txt"));
144+
145+
fs.MoveDirectory("/dir", "/moved");
146+
147+
Assert.True(fs.DirectoryExists("/moved"));
148+
Assert.Equal(FileAttributes.Directory | FileAttributes.Temporary, fs.GetAttributes("/moved"));
149+
150+
Assert.True(fs.FileExists("/moved/file.txt"));
151+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/moved/file.txt"));
152+
}
153+
154+
[Fact]
155+
public void TestFileMoveAttributes()
156+
{
157+
fs.WriteAllText("/file.txt", "test");
158+
fs.SetAttributes("/file.txt", FileAttributes.Temporary);
159+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/file.txt"));
160+
161+
fs.MoveFile("/file.txt", "/moved.txt");
162+
163+
Assert.True(fs.FileExists("/moved.txt"));
164+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/moved.txt"));
165+
}
166+
167+
[Fact]
168+
public void TestCopyFileAttributes()
169+
{
170+
fs.WriteAllText("/file.txt", "test");
171+
fs.SetAttributes("/file.txt", FileAttributes.Temporary);
172+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/file.txt"));
173+
174+
fs.CopyFile("/file.txt", "/copy.txt", true);
175+
176+
Assert.True(fs.FileExists("/copy.txt"));
177+
Assert.Equal(FileAttributes.Temporary, fs.GetAttributes("/copy.txt"));
178+
}
179+
#endif
180+
131181
[Fact]
132182
public void TestFile()
133183
{

src/Zio/FileSystems/ZipArchiveFileSystem.cs

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,18 @@ protected override void CopyFileImpl(UPath srcPath, UPath destPath, bool overwri
267267
using var srcStream = srcEntry.Open();
268268
srcStream.CopyTo(destStream);
269269
}
270-
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
271-
destEntry.ExternalAttributes = srcEntry.ExternalAttributes | (int)FileAttributes.Archive;
272-
#endif
270+
CopyEntryProperties(srcEntry, destEntry, includeLastWriteTime: false);
273271
TryGetDispatcher()?.RaiseCreated(destPath);
274272
}
275273

276274
/// <inheritdoc />
277275
protected override void CreateDirectoryImpl(UPath path)
276+
{
277+
CreateDirectoryWithoutNotification(path);
278+
TryGetDispatcher()?.RaiseCreated(path);
279+
}
280+
281+
private ZipArchiveEntry CreateDirectoryWithoutNotification(UPath path)
278282
{
279283
if (FileExistsImpl(path))
280284
{
@@ -295,8 +299,7 @@ protected override void CreateDirectoryImpl(UPath path)
295299
}
296300
}
297301

298-
CreateEntry(path, isDirectory: true);
299-
TryGetDispatcher()?.RaiseCreated(path);
302+
return CreateEntry(path, isDirectory: true);
300303
}
301304

302305
/// <inheritdoc />
@@ -653,28 +656,33 @@ protected override void MoveDirectoryImpl(UPath srcPath, UPath destPath)
653656
throw FileSystemExceptionHelper.NewDirectoryNotFoundException(srcPath);
654657
}
655658

656-
CreateDirectoryImpl(destPath);
659+
var rootEntry = CreateDirectoryWithoutNotification(destPath);
657660
foreach (var internalEntry in entries)
658661
{
659662
var entry = internalEntry.Entry;
660663

664+
ZipArchiveEntry destEntry;
665+
661666
if (entry.FullName.Length == srcDir.Length)
662667
{
663-
RemoveEntry(entry);
664-
continue;
668+
destEntry = rootEntry;
665669
}
666-
667-
var entryName = entry.FullName.Substring(srcDir.Length);
668-
var isDirectory = internalEntry.IsDirectory;
669-
var destEntry = CreateEntry(UPath.Combine(destPath, entryName), isDirectory: isDirectory);
670-
671-
if (!isDirectory)
670+
else
672671
{
673-
using var entryStream = entry.Open();
674-
using var destEntryStream = destEntry.Open();
675-
entryStream.CopyTo(destEntryStream);
672+
var entryName = entry.FullName.Substring(srcDir.Length);
673+
var isDirectory = internalEntry.IsDirectory;
674+
destEntry = CreateEntry(UPath.Combine(destPath, entryName), isDirectory: isDirectory);
675+
676+
if (!isDirectory)
677+
{
678+
using var entryStream = entry.Open();
679+
using var destEntryStream = destEntry.Open();
680+
entryStream.CopyTo(destEntryStream);
681+
}
676682
}
677683

684+
CopyEntryProperties(entry, destEntry);
685+
678686
TryGetDispatcher()?.RaiseCreated(destPath);
679687
RemoveEntry(entry);
680688
TryGetDispatcher()?.RaiseDeleted(srcPath);
@@ -717,6 +725,7 @@ protected override void MoveFileImpl(UPath srcPath, UPath destPath)
717725
}
718726

719727
destEntry = CreateEntry(destPath.FullName);
728+
CopyEntryProperties(srcEntry, destEntry);
720729
TryGetDispatcher()?.RaiseCreated(destPath);
721730
using (var destStream = destEntry.Open())
722731
{
@@ -827,6 +836,7 @@ protected override void ReplaceFileImpl(UPath srcPath, UPath destPath, UPath des
827836
if (!destBackupPath.IsEmpty)
828837
{
829838
var destBackupEntry = CreateEntry(destBackupPath.FullName);
839+
CopyEntryProperties(destEntry, destBackupEntry);
830840
using var destBackupStream = destBackupEntry.Open();
831841
using var destStream = destEntry.Open();
832842
destStream.CopyTo(destBackupStream);
@@ -836,6 +846,7 @@ protected override void ReplaceFileImpl(UPath srcPath, UPath destPath, UPath des
836846
}
837847

838848
var newEntry = CreateEntry(destPath.FullName);
849+
CopyEntryProperties(sourceEntry, newEntry);
839850
using (var newStream = newEntry.Open())
840851
{
841852
using (var sourceStream = sourceEntry.Open())
@@ -985,11 +996,11 @@ private string GetArchivePath(UPath path, bool isDirectory)
985996
{
986997
if (ctx.LeadingSlashInArchive)
987998
{
988-
ctx.path.FullName.AsSpan().CopyTo(span.Slice(0, ctx.path.FullName.Length));
999+
ctx.path.FullName.AsSpan().CopyTo(span);
9891000
}
9901001
else
9911002
{
992-
ctx.path.FullName.AsSpan(1, ctx.path.FullName.Length - 1).CopyTo(span);
1003+
ctx.path.FullName.AsSpan(1).CopyTo(span);
9931004
}
9941005

9951006
if (ctx.isDirectory)
@@ -1003,6 +1014,21 @@ private string GetArchivePath(UPath path, bool isDirectory)
10031014

10041015
private static readonly char[] s_slashChars = { '/', '\\' };
10051016

1017+
private static void CopyEntryProperties(ZipArchiveEntry source, ZipArchiveEntry dest, bool includeLastWriteTime = true)
1018+
{
1019+
if (includeLastWriteTime)
1020+
{
1021+
dest.LastWriteTime = source.LastWriteTime;
1022+
}
1023+
1024+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
1025+
dest.ExternalAttributes = source.ExternalAttributes;
1026+
#endif
1027+
#if NET6_0_OR_GREATER
1028+
dest.Comment = source.Comment;
1029+
#endif
1030+
}
1031+
10061032
private static ReadOnlySpan<char> GetName(ZipArchiveEntry entry)
10071033
{
10081034
var name = entry.FullName.TrimEnd(s_slashChars);

0 commit comments

Comments
 (0)