Skip to content

Commit 2e0cb6b

Browse files
iremyuxCopilot
andauthored
Improve exception message when passing directory to ZipFile methods (#121917)
Fixes an issue where passing a directory path to `ZipFile` methods resulted in an unclear `UnauthorizedAccessException` without explaining that the problem was passing a directory instead of a file. Modified `GetFileStreamForOpen()` to catch `UnauthorizedAccessException` when `FileStream` fails to open a directory, and re-throw with a clearer error message that explicitly states the path is a directory. Fixes #100720 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent c3d9ccd commit 2e0cb6b

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

src/libraries/System.IO.Compression.ZipFile/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@
100100
<data name="UnauthorizedAccess_IODenied_Path" xml:space="preserve">
101101
<value>Access to the path '{0}' is denied.</value>
102102
</data>
103+
<data name="IO_DirectoryNotAllowed" xml:space="preserve">
104+
<value>The specified path '{0}' is a directory, which is not allowed in this operation.</value>
105+
</data>
103106
<data name="ZipUnsupportedFile" xml:space="preserve">
104107
<value>The file type of '{0}' is not supported for zip archiving.</value>
105108
</data>

src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Create.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,12 @@ private static void CreateZipArchiveFromDirectory(string sourceDirectoryName, Zi
463463

464464
private static FileStream GetFileStreamForOpen(ZipArchiveMode mode, string archiveFileName, bool useAsync)
465465
{
466-
// Relies on FileStream's ctor for checking of archiveFileName
466+
// Check if the path is a directory before attempting to open,
467+
// to match the UnauthorizedAccessException thrown by FileStream's ctor.
468+
if (Directory.Exists(archiveFileName))
469+
{
470+
throw new UnauthorizedAccessException(SR.Format(SR.IO_DirectoryNotAllowed, archiveFileName));
471+
}
467472

468473
(FileMode fileMode, FileAccess access, FileShare fileShare) = mode switch
469474
{
@@ -473,6 +478,7 @@ private static FileStream GetFileStreamForOpen(ZipArchiveMode mode, string archi
473478
_ => throw new ArgumentOutOfRangeException(nameof(mode)),
474479
};
475480

481+
// Relies on FileStream's ctor for checking of archiveFileName
476482
return new FileStream(archiveFileName, fileMode, access, fileShare, bufferSize: FileStreamBufferSize, useAsync);
477483
}
478484

src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Extract.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ public async Task ExtractToDirectoryNull(bool async)
4242
await AssertExtensions.ThrowsAsync<ArgumentNullException>("sourceArchiveFileName", () => CallZipFileExtractToDirectory(async, sourceArchiveFileName: null, GetTestFilePath()));
4343
}
4444

45+
[Theory]
46+
[MemberData(nameof(Get_Booleans_Data))]
47+
public async Task ExtractToDirectory_PassDirectoryAsArchiveFile_ThrowsUnauthorizedAccessException(bool async)
48+
{
49+
string directoryPath = GetTestFilePath();
50+
Directory.CreateDirectory(directoryPath);
51+
52+
await Assert.ThrowsAsync<UnauthorizedAccessException>(() =>
53+
CallZipFileExtractToDirectory(async, directoryPath, GetTestFilePath()));
54+
}
55+
4556
[Theory]
4657
[MemberData(nameof(Get_Booleans_Data))]
4758
public async Task ExtractToDirectoryUnicode(bool async)

src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Open.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,30 @@ public Task InvalidConstructors(bool async)
1818
return Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => CallZipFileOpen(async, "bad file", (ZipArchiveMode)(10)));
1919
}
2020

21+
[Fact]
22+
public void Open_PassDirectory_ThrowsUnauthorizedAccessException()
23+
{
24+
string directoryPath = GetTestFilePath();
25+
Directory.CreateDirectory(directoryPath);
26+
27+
Assert.Throws<UnauthorizedAccessException>(() => ZipFile.Open(directoryPath, ZipArchiveMode.Read));
28+
Assert.Throws<UnauthorizedAccessException>(() => ZipFile.OpenRead(directoryPath));
29+
Assert.Throws<UnauthorizedAccessException>(() => ZipFile.Open(directoryPath, ZipArchiveMode.Create));
30+
Assert.Throws<UnauthorizedAccessException>(() => ZipFile.Open(directoryPath, ZipArchiveMode.Update));
31+
}
32+
33+
[Fact]
34+
public async Task OpenAsync_PassDirectory_ThrowsUnauthorizedAccessException()
35+
{
36+
string directoryPath = GetTestFilePath();
37+
Directory.CreateDirectory(directoryPath);
38+
39+
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => ZipFile.OpenAsync(directoryPath, ZipArchiveMode.Read, default));
40+
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => ZipFile.OpenReadAsync(directoryPath, default));
41+
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => ZipFile.OpenAsync(directoryPath, ZipArchiveMode.Create, default));
42+
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => ZipFile.OpenAsync(directoryPath, ZipArchiveMode.Update, default));
43+
}
44+
2145
[Fact]
2246
public void InvalidFiles()
2347
{

0 commit comments

Comments
 (0)