diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-03-15 21:35:13 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-16 02:35:13 +0100 |
commit | 48a0b7f98f568bb5c3a15b487459569e38e4c671 (patch) | |
tree | d7a7aba5da5a019874b1c8cd534eb2b22b86b1a9 /ext/fs/lib.rs | |
parent | 92c3ac30346fddc138a5d83cb67c87ac23e69dae (diff) |
feat(fs): support FileInfo.dev on Windows (#18073)
This commit adds support for retrieving `dev` information
when stating files on Windows.
Additionally `Deno.FileInfo` interfaces was changed to always
return 0 for fields that we don't retrieve information for on Windows.
Closes https://github.com/denoland/deno/issues/18053
---------
Co-authored-by: David Sherret <dsherret@gmail.com>
Diffstat (limited to 'ext/fs/lib.rs')
-rw-r--r-- | ext/fs/lib.rs | 171 |
1 files changed, 155 insertions, 16 deletions
diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index 522dea3fb..746d40dff 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -1244,6 +1244,68 @@ fn get_stat(metadata: std::fs::Metadata) -> FsStat { } } +#[cfg(windows)] +#[inline(always)] +fn get_stat2(metadata: std::fs::Metadata, dev: u64) -> FsStat { + let (mtime, mtime_set) = to_msec(metadata.modified()); + let (atime, atime_set) = to_msec(metadata.accessed()); + let (birthtime, birthtime_set) = to_msec(metadata.created()); + + FsStat { + is_file: metadata.is_file(), + is_directory: metadata.is_dir(), + is_symlink: metadata.file_type().is_symlink(), + size: metadata.len(), + mtime_set, + mtime, + atime_set, + atime, + birthtime_set, + birthtime, + dev, + ino: 0, + mode: 0, + nlink: 0, + uid: 0, + gid: 0, + rdev: 0, + blksize: 0, + blocks: 0, + } +} + +#[cfg(not(windows))] +#[inline(always)] +fn get_stat2(metadata: std::fs::Metadata) -> FsStat { + #[cfg(unix)] + use std::os::unix::fs::MetadataExt; + let (mtime, mtime_set) = to_msec(metadata.modified()); + let (atime, atime_set) = to_msec(metadata.accessed()); + let (birthtime, birthtime_set) = to_msec(metadata.created()); + + FsStat { + is_file: metadata.is_file(), + is_directory: metadata.is_dir(), + is_symlink: metadata.file_type().is_symlink(), + size: metadata.len(), + mtime_set, + mtime, + atime_set, + atime, + birthtime_set, + birthtime, + dev: metadata.dev(), + ino: metadata.ino(), + mode: metadata.mode(), + nlink: metadata.nlink(), + uid: metadata.uid(), + gid: metadata.gid(), + rdev: metadata.rdev(), + blksize: metadata.blksize(), + blocks: metadata.blocks(), + } +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct StatArgs { @@ -1251,6 +1313,97 @@ pub struct StatArgs { lstat: bool, } +#[cfg(not(windows))] +fn do_stat(path: PathBuf, lstat: bool) -> Result<FsStat, AnyError> { + let err_mapper = + |err| default_err_mapper(err, format!("stat '{}'", path.display())); + let metadata = if lstat { + std::fs::symlink_metadata(&path).map_err(err_mapper)? + } else { + std::fs::metadata(&path).map_err(err_mapper)? + }; + + Ok(get_stat2(metadata)) +} + +#[cfg(windows)] +fn do_stat(path: PathBuf, lstat: bool) -> Result<FsStat, AnyError> { + use std::os::windows::prelude::OsStrExt; + + use winapi::um::fileapi::CreateFileW; + use winapi::um::fileapi::OPEN_EXISTING; + use winapi::um::handleapi::CloseHandle; + use winapi::um::handleapi::INVALID_HANDLE_VALUE; + use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS; + use winapi::um::winbase::FILE_FLAG_OPEN_REPARSE_POINT; + use winapi::um::winnt::FILE_SHARE_DELETE; + use winapi::um::winnt::FILE_SHARE_READ; + use winapi::um::winnt::FILE_SHARE_WRITE; + + let err_mapper = + |err| default_err_mapper(err, format!("stat '{}'", path.display())); + let metadata = if lstat { + std::fs::symlink_metadata(&path).map_err(err_mapper)? + } else { + std::fs::metadata(&path).map_err(err_mapper)? + }; + + let (p, file_flags) = if lstat { + ( + path, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + ) + } else { + (path.canonicalize()?, FILE_FLAG_BACKUP_SEMANTICS) + }; + unsafe { + let mut path: Vec<_> = p.as_os_str().encode_wide().collect(); + path.push(0); + let file_handle = CreateFileW( + path.as_ptr(), + 0, + FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, + std::ptr::null_mut(), + OPEN_EXISTING, + file_flags, + std::ptr::null_mut(), + ); + if file_handle == INVALID_HANDLE_VALUE { + return Err(std::io::Error::last_os_error().into()); + } + + let result = get_dev(file_handle); + CloseHandle(file_handle); + let dev = result?; + + Ok(get_stat2(metadata, dev)) + } +} + +#[cfg(windows)] +use winapi::um::fileapi::GetFileInformationByHandle; +#[cfg(windows)] +use winapi::um::fileapi::BY_HANDLE_FILE_INFORMATION; + +#[cfg(windows)] +unsafe fn get_dev( + handle: winapi::shared::ntdef::HANDLE, +) -> std::io::Result<u64> { + use winapi::shared::minwindef::FALSE; + + let info = { + let mut info = + std::mem::MaybeUninit::<BY_HANDLE_FILE_INFORMATION>::zeroed(); + if GetFileInformationByHandle(handle, info.as_mut_ptr()) == FALSE { + return Err(std::io::Error::last_os_error()); + } + + info.assume_init() + }; + + Ok(info.dwVolumeSerialNumber as u64) +} + #[op] fn op_stat_sync<P>( state: &mut OpState, @@ -1265,15 +1418,8 @@ where state .borrow_mut::<P>() .check_read(&path, "Deno.statSync()")?; - let err_mapper = - |err| default_err_mapper(err, format!("stat '{}'", path.display())); - let metadata = if lstat { - std::fs::symlink_metadata(&path).map_err(err_mapper)? - } else { - std::fs::metadata(&path).map_err(err_mapper)? - }; - let stat = get_stat(metadata); + let stat = do_stat(path, lstat)?; stat.write(out_buf); Ok(()) @@ -1297,14 +1443,7 @@ where tokio::task::spawn_blocking(move || { debug!("op_stat_async {} {}", path.display(), lstat); - let err_mapper = - |err| default_err_mapper(err, format!("stat '{}'", path.display())); - let metadata = if lstat { - std::fs::symlink_metadata(&path).map_err(err_mapper)? - } else { - std::fs::metadata(&path).map_err(err_mapper)? - }; - Ok(get_stat(metadata)) + do_stat(path, lstat) }) .await .unwrap() |