-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
fix: Add dynamic device path handling for windows file access #25851
base: main
Are you sure you want to change the base?
fix: Add dynamic device path handling for windows file access #25851
Conversation
@dsherret @bartlomieju @piscisaureus @lucacasonato Please review this Pull Request. |
|| path_bytes.starts_with(b"//./") | ||
|| path_bytes.starts_with(b"//?/") | ||
|| path_bytes.starts_with(br"\\?\") | ||
|| path_bytes.ends_with(b"$")) | ||
&& !path_bytes.contains(&b':'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is also very crude and probably wrong. To exclude "file paths" then we should look for:
- paths that start with
\\.\
+ letter +:\
- paths that start with
\\.\UNC\
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is also very crude and probably wrong. To exclude "file paths" then we should look for:
- paths that start with
\\.\
+ letter +:\
- paths that start with
\\.\UNC\
It doesn't look for :\
It excludes &&!path_bytes.contains(&b':');
I did not add this \\.\
This was added to #23721.
So, the main issue comes from this circumstance.
So I think this is the correct condition; if not, what should I do?
|| path_bytes.starts_with(b"//./") | ||
|| path_bytes.starts_with(b"//?/") | ||
|| path_bytes.starts_with(br"\\?\") | ||
|| path_bytes.ends_with(b"$")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ends_with(b"$")
seems to broad.
To my knowledge only CONIN$
and CONOUT$
are the only $
-ending special file names. Otherwise, files ending with $
are allowed and are regular files.
In addition to the above code, as Windows is, by default (always?), case-ignoring for these device names, I realized that Since the possible comparisons are all composed of single byte ASCII characters, a simple quick shift of |
And, as the WinOS API is "seperator/slash-agnostic", combinations of both forward and back slashes should be accepted (eg, |
This is TS code that I'm using to accomplish a similar task... // `pathFromURL()`
/** Extract the "path" (absolute file path for 'file://' URLs, otherwise the href URL-string) from the `url`.
* * `no-throw` ~ function returns `undefined` upon any error
@param [url] • URL for path extraction
@tags `no-throw`
*/
export function pathFromURL(url?: URL) {
if (url == null) return undefined;
let path = url.href;
// console.warn('pathFromURL:', { url, path });
if (url.protocol === 'file:') {
try {
path = $path.fromFileUrl(path);
} catch (_error) {
return undefined;
}
}
// console.warn('pathFromURL:', { url, path });
const isWinOS = Deno.build.os === 'windows';
if (isWinOS) {
// WinOS ~ handle special device paths
// ref: [File path formats](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats) @@ <https://archive.is/0shPL>
// ref: [Naming Files, Paths, and Namespaces](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file) @@ <https://archive.is/mQOTg>
// * no further processing for paths with device prefixes (eg, '\\.\', '\\?\')
if (!path.match(/^[/\\][/\\][.?][/\\]/)) {
// * note: this translation is explicitly equivalent to the more sensible (and least surprise) Win11 version, keeping the full file name in the path
// ... Win10 (surprisingly) ignores text past the filePrefix for legacy device names; eg Win10 sees 'CON.txt' as '\\.\CON'
const fileBaseName = $path.basename(path).toLocaleUpperCase();
const filePrefix = fileBaseName.replace(/[.].*$/, '');
const specialDeviceBaseNames = ['CONIN$', 'CONOUT$'];
const specialDevicePrefixes = ([] as string[]).concat(
['CON', 'PRN', 'AUX', 'NUL'], // legacy device names
['COM0', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9'], // legacy COM device names
['LPT0', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'], // legacy LPT device names
);
if (
specialDeviceBaseNames.includes(fileBaseName) ||
specialDevicePrefixes.includes(filePrefix)
) {
path = `\\\\?\\${path}`;
}
}
}
// `\\?\...` is likely the more "correct" prefix as it skips further Windows normalization via `GetFullPathName()`; but Deno and the standard URL class do not support it
// * instead use the usually equivalent `\\.\` prefix for better compatibility with Deno and the standard URL class
path = path.replace(/^[/\\][/\\][?][/\\]/, `${$path.SEP}${$path.SEP}.${$path.SEP}`);
return path;
} |
fix #24791