Introduce file_id on Windows (#8130)

Small White created

Added a function `file_id` to get the file id on windows, which is
similar to inode on unix.

Release Notes:

- N/A

Change summary

Cargo.lock           |  1 +
crates/fs/Cargo.toml |  8 +++++++-
crates/fs/src/fs.rs  | 38 ++++++++++++++++++++++++++++++++++++--
3 files changed, 44 insertions(+), 3 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3678,6 +3678,7 @@ dependencies = [
  "text",
  "time",
  "util",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]

crates/fs/Cargo.toml 🔗

@@ -32,11 +32,17 @@ log.workspace = true
 libc = "0.2"
 time.workspace = true
 
-gpui = { workspace = true, optional = true}
+gpui = { workspace = true, optional = true }
 
 [target.'cfg(not(target_os = "macos"))'.dependencies]
 notify = "6.1.1"
 
+[target.'cfg(target_os = "windows")'.dependencies]
+windows-sys = { version = "0.52", features = [
+    "Win32_Foundation",
+    "Win32_Storage_FileSystem",
+] }
+
 [dev-dependencies]
 gpui = { workspace = true, features = ["test-support"] }
 

crates/fs/src/fs.rs 🔗

@@ -245,9 +245,8 @@ impl Fs for RealFs {
         #[cfg(unix)]
         let inode = metadata.ino();
 
-        // todo!("windows")
         #[cfg(windows)]
-        let inode = 0;
+        let inode = file_id(path).await?;
 
         Ok(Some(Metadata {
             inode,
@@ -1337,6 +1336,41 @@ pub fn copy_recursive<'a>(
     .boxed()
 }
 
+// todo!(windows)
+// can we get file id not open the file twice?
+// https://github.com/rust-lang/rust/issues/63010
+#[cfg(target_os = "windows")]
+async fn file_id(path: impl AsRef<Path>) -> Result<u64> {
+    use std::os::windows::io::AsRawHandle;
+
+    use smol::fs::windows::OpenOptionsExt;
+    use windows_sys::Win32::{
+        Foundation::HANDLE,
+        Storage::FileSystem::{
+            GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_FLAG_BACKUP_SEMANTICS,
+        },
+    };
+
+    let file = smol::fs::OpenOptions::new()
+        .read(true)
+        .custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
+        .open(path)
+        .await?;
+
+    let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
+    // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileinformationbyhandle
+    // This function supports Windows XP+
+    smol::unblock(move || {
+        let ret = unsafe { GetFileInformationByHandle(file.as_raw_handle() as HANDLE, &mut info) };
+        if ret == 0 {
+            return Err(anyhow!(format!("{}", std::io::Error::last_os_error())));
+        };
+
+        Ok(((info.nFileIndexHigh as u64) << 32) | (info.nFileIndexLow as u64))
+    })
+    .await
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;