Unix: Move 'FileNotFound to DirectoryNotFound' handling to GetExceptionForIoErrno.#70661
Unix: Move 'FileNotFound to DirectoryNotFound' handling to GetExceptionForIoErrno.#70661jozkee merged 7 commits intodotnet:mainfrom
Conversation
…onForIoErrno. On Windows, APIs throw DirectoryNotFoundException instead of FileNotFoundException when the base components of a path don't exist. SafeFileHandle, and FileSystemInfo have some specific code to detect that case and to the same on Unix. This change moves that mapping to Interop.IOErrors so it applies to all users of GetExceptionForIoErrno.
|
Tagging subscribers to this area: @dotnet/area-system-io Issue DetailsOn Windows, APIs throw DirectoryNotFoundException instead of SafeFileHandle, and FileSystemInfo have some specific code to detect This change moves that mapping to Interop.IOErrors so it applies
|
@danmoseley @stephentoub I don't understand the error. What does this mean? |
|
On Windows, APIs return different errors for the parent directory not being there ( On Unix, Currently those checks are implemented at different places. To implement the exist check in CI gives me an error I don't understand for using
@dotnet/area-system-io @stephentoub ptal. |
| if (isDirectory && errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) | ||
| errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; | ||
| if (isDirectory && errorCode == Interop.Errors.ERROR_ACCESS_DENIED && ignoreAccessDenied) | ||
| if (ignoreAccessDenied && errorCode == Interop.Errors.ERROR_ACCESS_DENIED) |
There was a problem hiding this comment.
This change is unrelated. I can move it to a separate PR.
|
@dotnet/area-system-io ptal. Example where this change changes applies: DirectoryInfo di = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
FileInfo fi = new FileInfo(Path.Combine(di.FullName, "file"));
fi.OpenWrite().Dispose();
fi.LastWriteTimeUtc = DateTime.UtcNow;
Directory.Delete(di.FullName, recursive: true);
fi.LastWriteTimeUtc = DateTime.UtcNow;On Linux, the last statement currently throws |
@dotnet/interop-contrib what does this error mean? And what can I do to fix it? |
|
System.IO.Ports supports downlevel TFMs. On those TFMs, we try to have the generator generate an old-style DllImport. Some functionality cannot be translated automatically to the old system. I’d recommend that you use an |
|
CI fail is unrelated. @dotnet/area-system-io ptal. |
|
@adamsitnik @jeffhandley ptal. |
| // Windows distinguishes between whether the directory or the file isn't found, | ||
| // and throws a different exception in these cases. We attempt to approximate that | ||
| // here; there is a race condition here, where something could change between | ||
| // when the error occurs and our checks, but it's the best we can do, and the | ||
| // worst case in such a race condition (which could occur if the file system is | ||
| // being manipulated concurrently with these checks) is that we throw a | ||
| // FileNotFoundException instead of DirectoryNotFoundException. |
There was a problem hiding this comment.
Is it worth keeping this comment inside GetExceptionForIoErrno?
| // macOS returns ENOTDIR when the path refers to a file and ends with '/'. | ||
| // Trim the separator so we get EEXIST instead. | ||
| ReadOnlySpan<char> path = PathInternal.TrimEndingDirectorySeparator(fullPath.AsSpan()); | ||
| int result = Interop.Sys.MkDir(path, (int)unixCreateMode); |
There was a problem hiding this comment.
I assume there is no test that ensures we don't get DirectoryNotFoundException when the path ends with '/'? Could you please add it?
|
|
||
| if (attributes == (FileAttributes)(-1)) | ||
| FileSystemInfo.ThrowNotFound(fullPath); | ||
| throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); |
There was a problem hiding this comment.
| throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); | |
| throw Interop.GetExceptionForIoErrno(Interop.Error.ENOENT.Info(), fullPath); |
|
|
||
| if (mode == (UnixFileMode)(-1)) | ||
| FileSystemInfo.ThrowNotFound(fullPath); | ||
| throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); |
There was a problem hiding this comment.
| throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), fullPath); | |
| throw Interop.GetExceptionForIoErrno(Interop.Error.ENOENT.Info(), fullPath); |
|
This appears to have broken main, at least Android AOT builds:
|
|
@jozkee This seems to have broken many, many builds. E.g., https://dev.azure.com/dnceng/public/_build/results?buildId=1944354&view=results |
|
Sorry for that, I don't know why that was not caught by this PR's build. |
The breaking call site was added a few days ago. And the CI build here was 18 days ago. that's why it didn't get caught. |
|
It is because the validation ran 18 days ago. In the meantime this change went in, and collided. It happens, but for something touching common code like this it might be good to ensure validation is fresher. commit 9fd407a |
On Windows, APIs throw DirectoryNotFoundException instead of
FileNotFoundException when the base components of a path don't exist.
SafeFileHandle, and FileSystemInfo have some specific code to detect
that case and do the same on Unix.
This change moves that mapping to Interop.IOErrors so it applies
to all users of GetExceptionForIoErrno.