Repository: lsd-rs/lsd (language: rust)

Issue:
Respect hidden flag in Windows
Unlike *nix systems, windows uses a separate flag to mark a file as hidden. We can make `lsd` ignore them when not using `-a` or `-A`.

Previous discussion in https://github.com/Peltoche/lsd/issues/712


Discussion / hints:
Ref: https://github.com/BurntSushi/winapi-util/commit/16385cb76d859815e5ba531092caf8448f4c4435
        Oh by the way, windows has 2 different kinds of hidden files.  There's normal hidden files and operating system protected files.  Those NTUSER.dat files are the second kind.  The user almost never has to interact with OS protected files, I've never done so once in my 10 years or so of using Windows.  I would suggest making a dedicated command line flag to show OS protected files, and normal hidden files can be shown with just -a or -A.

_Originally posted by @OldWorldOrdr in https://github.com/Peltoche/lsd/issues/712#issuecomment-1279290651_
      

Fix patch:
diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index 990a35f26..7bc7ba530 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -6,7 +6,7 @@ env:
   PROJECT_NAME: lsd
   PROJECT_DESC: "An ls command with a lot of pretty colors."
   PROJECT_AUTH: "Peltoche <peltoche@halium.fr>"
-  RUST_MIN_SRV: "1.62.0"
+  RUST_MIN_SRV: "1.64.0"
 
 on: [push, pull_request]
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83d900392..ecb53df19 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,9 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - In keeping with the coreutils change, add quotes and escapes for necessary filenames from [merelymyself](https://github.com/merelymyself)
 - Add support for icon theme from [zwpaper](https://github.com/zwpaper)
 - Add icon for kt and kts from [LeeWeeder](https://github.com/LeeWeeder)
+- Add `--system-protected` to include files with the Windows `system` flag set,
+  on other platform the same as `--all` [#752](https://github.com/Peltoche/lsd/issues/752)
 
 ### Fixed
 - Do not quote filename when piping into another program from [TeamTamoad](https://github.com/TeamTamoad)
+- Respect `hidden` flag on Windows [#752](https://github.com/Peltoche/lsd/issues/752)
 
 ## [0.23.1] - 2022-09-13
 
diff --git a/Cargo.lock b/Cargo.lock
index 872812e8f..8b3f8082c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -441,7 +441,7 @@ dependencies = [
  "users",
  "version_check",
  "wild",
- "winapi",
+ "windows",
  "xattr",
  "xdg",
  "yaml-rust",
@@ -1037,49 +1037,106 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.42.0",
+ "windows_i686_gnu 0.42.0",
+ "windows_i686_msvc 0.42.0",
+ "windows_x86_64_gnu 0.42.0",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.42.0",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_msvc",
+ "windows_aarch64_msvc 0.36.1",
+ "windows_i686_gnu 0.36.1",
+ "windows_i686_msvc 0.36.1",
+ "windows_x86_64_gnu 0.36.1",
+ "windows_x86_64_msvc 0.36.1",
 ]
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
 [[package]]
 name = "xattr"
 version = "0.2.2"
diff --git a/Cargo.toml b/Cargo.toml
index 3e4421a1c..996c7d2e2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -45,7 +45,7 @@ users = "0.11.*"
 xattr = "0.2.*"
 
 [target.'cfg(windows)'.dependencies]
-winapi = {version = "0.3.*", features = ["aclapi", "accctrl", "winnt", "winerror", "securitybaseapi", "winbase"]}
+windows = { version = "0.43.0", features = ["Win32_Foundation", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Memory"] }
 
 [dependencies.clap]
 features = ["suggestions", "color", "wrap_help"]
diff --git a/build.rs b/build.rs
index 918f457b6..6c64b7f38 100644
--- a/build.rs
+++ b/build.rs
@@ -18,8 +18,8 @@ include!("src/app.rs");
 
 fn main() {
     // rustc version too small or can't figure it out
-    if version_check::is_min_version("1.62.0") != Some(true) {
-        eprintln!("'lsd' requires rustc >= 1.62.0");
+    if version_check::is_min_version("1.64.0") != Some(true) {
+        eprintln!("'lsd' requires rustc >= 1.64.0");
         exit(1);
     }
 
diff --git a/src/app.rs b/src/app.rs
index 971d01353..137a5e6d6 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -348,6 +348,12 @@ pub fn build() -> App<'static> {
                 .long("header")
                 .help("Display block headers"),
         )
+        .arg(
+            Arg::with_name("system-protected")
+                .long("system-protected")
+                .help("Includes files with the windows system protection flag set. This is the same as --all on other platforms")
+                .hide(!cfg!(windows)),
+        )
 }
 
 fn validate_date_argument(arg: &str) -> Result<(), String> {
diff --git a/src/flags/display.rs b/src/flags/display.rs
index 96850dba2..2ac5f69bc 100644
--- a/src/flags/display.rs
+++ b/src/flags/display.rs
@@ -12,6 +12,8 @@ use serde::Deserialize;
 #[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize, Default)]
 #[serde(rename_all = "kebab-case")]
 pub enum Display {
+    /// windows only, used to show files with system protected flag
+    SystemProtected,
     All,
     AlmostAll,
     DirectoryOnly,
@@ -32,6 +34,12 @@ impl Configurable<Self> for Display {
             Some(Self::AlmostAll)
         } else if matches.is_present("all") {
             Some(Self::All)
+        } else if matches.is_present("system-protected") {
+            #[cfg(windows)]
+            return Some(Self::SystemProtected);
+
+            #[cfg(not(windows))]
+            return Some(Self::All);
         } else {
             None
         }
@@ -63,6 +71,20 @@ mod test {
         assert_eq!(None, Display::from_arg_matches(&matches));
     }
 
+    #[test]
+    fn test_from_arg_matches_system_protected() {
+        let argv = ["lsd", "--system-protected"];
+        let matches = app::build().get_matches_from_safe(argv).unwrap();
+        #[cfg(windows)]
+        assert_eq!(
+            Some(Display::SystemProtected),
+            Display::from_arg_matches(&matches)
+        );
+
+        #[cfg(not(windows))]
+        assert_eq!(Some(Display::All), Display::from_arg_matches(&matches));
+    }
+
     #[test]
     fn test_from_arg_matches_all() {
         let argv = ["lsd", "--all"];
diff --git a/src/main.rs b/src/main.rs
index 78bdac926..a0f54f03f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,7 +25,7 @@ extern crate yaml_rust;
 extern crate users;
 
 #[cfg(windows)]
-extern crate winapi;
+extern crate windows;
 
 mod app;
 mod color;
diff --git a/src/meta/mod.rs b/src/meta/mod.rs
index 20c2af348..aaa3ff539 100644
--- a/src/meta/mod.rs
+++ b/src/meta/mod.rs
@@ -83,7 +83,9 @@ impl Meta {
 
         let mut content: Vec<Meta> = Vec::new();
 
-        if Display::All == flags.display && flags.layout != Layout::Tree {
+        if matches!(flags.display, Display::All | Display::SystemProtected)
+            && flags.layout != Layout::Tree
+        {
             let mut current_meta = self.clone();
             current_meta.name.name = ".".to_owned();
 
@@ -109,8 +111,23 @@ impl Meta {
                 continue;
             }
 
-            if flags.display == Display::VisibleOnly && name.to_string_lossy().starts_with('.') {
-                continue;
+            #[cfg(windows)]
+            let is_hidden =
+                name.to_string_lossy().starts_with('.') || windows_utils::is_path_hidden(&path);
+            #[cfg(not(windows))]
+            let is_hidden = name.to_string_lossy().starts_with('.');
+
+            #[cfg(windows)]
+            let is_system = windows_utils::is_path_system(&path);
+            #[cfg(not(windows))]
+            let is_system = false;
+
+            match flags.display {
+                // show hidden files, but ignore system protected files
+                Display::All | Display::AlmostAll if is_system => continue,
+                // ignore hidden and system protected files
+                Display::VisibleOnly if is_hidden || is_system => continue,
+                _ => {}
             }
 
             let mut entry_meta = match Self::from_path(&path, flags.dereference.0) {
diff --git a/src/meta/windows_utils.rs b/src/meta/windows_utils.rs
index 456f850ba..b2f6e27a0 100644
--- a/src/meta/windows_utils.rs
+++ b/src/meta/windows_utils.rs
@@ -1,13 +1,11 @@
 use std::ffi::{OsStr, OsString};
 use std::io;
+use std::mem::MaybeUninit;
 use std::os::windows::ffi::{OsStrExt, OsStringExt};
 use std::path::Path;
-use std::ptr::null_mut;
 
-use winapi::ctypes::c_void;
-use winapi::shared::winerror;
-use winapi::um::accctrl::TRUSTEE_W;
-use winapi::um::winnt;
+use windows::Win32::Foundation::PSID;
+use windows::Win32::Security::{self, Authorization::TRUSTEE_W, ACL};
 
 use super::{Owner, Permissions};
 
@@ -30,10 +28,10 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
     // These pointers will be populated by GetNamedSecurityInfoW
     // sd_ptr points at a new buffer that must be freed
     // The others point at (opaque) things inside that buffer
-    let mut owner_sid_ptr = null_mut();
-    let mut group_sid_ptr = null_mut();
-    let mut dacl_ptr = null_mut();
-    let mut sd_ptr = null_mut();
+    let mut owner_sid_ptr = MaybeUninit::uninit();
+    let mut group_sid_ptr = MaybeUninit::uninit();
+    let mut dacl_ptr = MaybeUninit::uninit();
+    let mut sd_ptr = MaybeUninit::uninit();
 
     // Assumptions:
     // - windows_path is a null-terminated WTF-16-encoded string
@@ -43,28 +41,32 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
     //   flags are set
     // - sd_ptr must be freed with LocalFree
     let error_code = unsafe {
-        winapi::um::aclapi::GetNamedSecurityInfoW(
-            windows_path.as_ptr(),
-            winapi::um::accctrl::SE_FILE_OBJECT,
-            winnt::OWNER_SECURITY_INFORMATION
-                | winnt::GROUP_SECURITY_INFORMATION
-                | winnt::DACL_SECURITY_INFORMATION,
-            &mut owner_sid_ptr,
-            &mut group_sid_ptr,
-            &mut dacl_ptr,
-            null_mut(),
-            &mut sd_ptr,
+        Security::Authorization::GetNamedSecurityInfoW(
+            windows::core::PCWSTR::from_raw(windows_path.as_ptr()),
+            Security::Authorization::SE_FILE_OBJECT,
+            Security::OWNER_SECURITY_INFORMATION
+                | Security::GROUP_SECURITY_INFORMATION
+                | Security::DACL_SECURITY_INFORMATION,
+            Some(owner_sid_ptr.as_mut_ptr()),
+            Some(group_sid_ptr.as_mut_ptr()),
+            Some(dacl_ptr.as_mut_ptr()),
+            None,
+            sd_ptr.as_mut_ptr(),
         )
     };
 
-    if error_code != winerror::ERROR_SUCCESS {
-        return Err(std::io::Error::from_raw_os_error(error_code as i32));
+    if error_code.is_err() {
+        return Err(std::io::Error::from_raw_os_error(error_code.0 as i32));
     }
 
     // Assumptions:
     // - owner_sid_ptr is valid
     // - group_sid_ptr is valid
     // (both OK because GetNamedSecurityInfoW returned success)
+    let owner_sid_ptr = unsafe { owner_sid_ptr.assume_init() };
+    let group_sid_ptr = unsafe { group_sid_ptr.assume_init() };
+    let dacl_ptr = unsafe { dacl_ptr.assume_init() };
+    let sd_ptr = unsafe { sd_ptr.assume_init() };
 
     let owner = match unsafe { lookup_account_sid(owner_sid_ptr) } {
         Ok((n, d)) => {
@@ -104,8 +106,9 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
     // Assumptions: None
     // "This function cannot fail"
     //     -- Windows Dev Center docs
-    let mut world_sid_len: u32 = unsafe { winapi::um::securitybaseapi::GetSidLengthRequired(1) };
+    let mut world_sid_len: u32 = unsafe { Security::GetSidLengthRequired(1) };
     let mut world_sid = vec![0u8; world_sid_len as usize];
+    let world_sid_ptr = PSID(world_sid.as_mut_ptr() as *mut _);
 
     // Assumptions:
     // - world_sid_len is no larger than the number of bytes available at
@@ -113,61 +116,65 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
     // - world_sid is appropriately aligned (if there are strange crashes this
     //   might be why)
     let result = unsafe {
-        winapi::um::securitybaseapi::CreateWellKnownSid(
-            winnt::WinWorldSid,
-            null_mut(),
-            world_sid.as_mut_ptr() as *mut _,
+        Security::CreateWellKnownSid(
+            Security::WinWorldSid,
+            PSID::default(),
+            world_sid_ptr,
             &mut world_sid_len,
         )
     };
 
-    if result == 0 {
+    if result.ok().is_err() {
         // Failed to create the SID
         // Assumptions: Same as the other identical calls
         unsafe {
-            winapi::um::winbase::LocalFree(sd_ptr);
+            windows::Win32::System::Memory::LocalFree(sd_ptr.0 as _);
         }
 
         // Assumptions: None (GetLastError shouldn't ever fail)
         return Err(io::Error::from_raw_os_error(unsafe {
-            winapi::um::errhandlingapi::GetLastError()
+            windows::Win32::Foundation::GetLastError().0
         } as i32));
     }
 
     // Assumptions:
     // - xxxxx_sid_ptr are valid pointers to SIDs
     // - xxxxx_trustee is only valid as long as its SID pointer is
-    let mut owner_trustee = unsafe { trustee_from_sid(owner_sid_ptr) };
-    let mut group_trustee = unsafe { trustee_from_sid(group_sid_ptr) };
-    let mut world_trustee = unsafe { trustee_from_sid(world_sid.as_mut_ptr() as *mut _) };
+    let owner_trustee = unsafe { trustee_from_sid(owner_sid_ptr) };
+    let group_trustee = unsafe { trustee_from_sid(group_sid_ptr) };
+    let world_trustee = unsafe { trustee_from_sid(world_sid_ptr) };
 
     // Assumptions:
     // - xxxxx_trustee are still valid (including underlying SID)
     // - dacl_ptr is still valid
-    let owner_access_mask = unsafe { get_acl_access_mask(dacl_ptr as *mut _, &mut owner_trustee) }?;
-
-    let group_access_mask = unsafe { get_acl_access_mask(dacl_ptr as *mut _, &mut group_trustee) }?;
+    let owner_access_mask = unsafe { get_acl_access_mask(dacl_ptr, &owner_trustee) }?;
 
-    let world_access_mask = unsafe { get_acl_access_mask(dacl_ptr as *mut _, &mut world_trustee) }?;
+    let group_access_mask = unsafe { get_acl_access_mask(dacl_ptr, &group_trustee) }?;
 
-    let has_bit = |field: u32, bit: u32| field & bit != 0;
+    let world_access_mask = unsafe { get_acl_access_mask(dacl_ptr, &world_trustee) }?;
 
-    let permissions = Permissions {
-        user_read: has_bit(owner_access_mask, winnt::FILE_GENERIC_READ),
-        user_write: has_bit(owner_access_mask, winnt::FILE_GENERIC_WRITE),
-        user_execute: has_bit(owner_access_mask, winnt::FILE_GENERIC_EXECUTE),
+    let permissions = {
+        use windows::Win32::Storage::FileSystem::{
+            FILE_ACCESS_FLAGS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE,
+        };
+        let has_bit = |field: u32, bit: FILE_ACCESS_FLAGS| field & bit.0 != 0;
+        Permissions {
+            user_read: has_bit(owner_access_mask, FILE_GENERIC_READ),
+            user_write: has_bit(owner_access_mask, FILE_GENERIC_WRITE),
+            user_execute: has_bit(owner_access_mask, FILE_GENERIC_EXECUTE),
 
-        group_read: has_bit(group_access_mask, winnt::FILE_GENERIC_READ),
-        group_write: has_bit(group_access_mask, winnt::FILE_GENERIC_WRITE),
-        group_execute: has_bit(group_access_mask, winnt::FILE_GENERIC_EXECUTE),
+            group_read: has_bit(group_access_mask, FILE_GENERIC_READ),
+            group_write: has_bit(group_access_mask, FILE_GENERIC_WRITE),
+            group_execute: has_bit(group_access_mask, FILE_GENERIC_EXECUTE),
 
-        other_read: has_bit(world_access_mask, winnt::FILE_GENERIC_READ),
-        other_write: has_bit(world_access_mask, winnt::FILE_GENERIC_WRITE),
-        other_execute: has_bit(world_access_mask, winnt::FILE_GENERIC_EXECUTE),
+            other_read: has_bit(world_access_mask, FILE_GENERIC_READ),
+            other_write: has_bit(world_access_mask, FILE_GENERIC_WRITE),
+            other_execute: has_bit(world_access_mask, FILE_GENERIC_EXECUTE),
 
-        sticky: false,
-        setuid: false,
-        setgid: false,
+            sticky: false,
+            setuid: false,
+            setgid: false,
+        }
     };
 
     // Assumptions:
@@ -177,7 +184,7 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
     //   options. It's not much memory, so leaking it on failure is
     //   *probably* fine)
     unsafe {
-        winapi::um::winbase::LocalFree(sd_ptr);
+        windows::Win32::System::Memory::LocalFree(sd_ptr.0 as _);
     }
 
     Ok((owner, permissions))
@@ -190,24 +197,21 @@ pub fn get_file_data(path: &Path) -> Result<(Owner, Permissions), io::Error> {
 /// - trustee_ptr points to a valid trustee data structure
 /// - Both remain valid through the function call (no long-term requirement)
 unsafe fn get_acl_access_mask(
-    acl_ptr: *mut c_void,
-    trustee_ptr: *mut TRUSTEE_W,
+    acl_ptr: *const ACL,
+    trustee_ptr: *const TRUSTEE_W,
 ) -> Result<u32, io::Error> {
     let mut access_mask = 0;
 
     // Assumptions:
     // - All function assumptions
     // - Result is not valid until return value is checked
-    let err_code = winapi::um::aclapi::GetEffectiveRightsFromAclW(
-        acl_ptr as *mut _,
-        trustee_ptr,
-        &mut access_mask,
-    );
+    let err_code =
+        Security::Authorization::GetEffectiveRightsFromAclW(acl_ptr, trustee_ptr, &mut access_mask);
 
-    if err_code == winerror::ERROR_SUCCESS {
+    if err_code.is_ok() {
         Ok(access_mask)
     } else {
-        Err(io::Error::from_raw_os_error(err_code as i32))
+        Err(io::Error::from_raw_os_error(err_code.0 as i32))
     }
 }
 
@@ -219,10 +223,10 @@ unsafe fn get_acl_access_mask(
 /// Note: winapi's TRUSTEE_W looks different from the one in the MS docs because
 /// of some unusual pre-processor macros in the original .h file. The winapi
 /// version is correct (MS's doc generator messed up)
-unsafe fn trustee_from_sid(sid_ptr: *mut c_void) -> TRUSTEE_W {
-    let mut trustee: TRUSTEE_W = std::mem::zeroed();
+unsafe fn trustee_from_sid<P: Into<PSID>>(sid_ptr: P) -> TRUSTEE_W {
+    let mut trustee = TRUSTEE_W::default();
 
-    winapi::um::aclapi::BuildTrusteeWithSidW(&mut trustee, sid_ptr);
+    Security::Authorization::BuildTrusteeWithSidW(&mut trustee, sid_ptr);
 
     trustee
 }
@@ -233,7 +237,7 @@ unsafe fn trustee_from_sid(sid_ptr: *mut c_void) -> TRUSTEE_W {
 /// function execution
 ///
 /// Returns null-terminated Vec's, one for the name and one for the domain.
-unsafe fn lookup_account_sid(sid: *mut c_void) -> Result<(Vec<u16>, Vec<u16>), std::io::Error> {
+unsafe fn lookup_account_sid(sid: PSID) -> Result<(Vec<u16>, Vec<u16>), std::io::Error> {
     let mut name_size: u32 = BUF_SIZE;
     let mut domain_size: u32 = BUF_SIZE;
 
@@ -244,22 +248,22 @@ unsafe fn lookup_account_sid(sid: *mut c_void) -> Result<(Vec<u16>, Vec<u16>), s
         let old_name_size = name_size;
         let old_domain_size = domain_size;
 
-        let mut sid_name_use = 0;
+        let mut sid_name_use = MaybeUninit::uninit();
 
         // Assumptions:
         // - sid is a valid pointer to a SID data structure
         // - name_size and domain_size accurately reflect the sizes
-        let result = winapi::um::winbase::LookupAccountSidW(
-            null_mut(),
+        let result = Security::LookupAccountSidW(
+            None,
             sid,
-            name.as_mut_ptr(),
+            windows::core::PWSTR(name.as_mut_ptr()),
             &mut name_size,
-            domain.as_mut_ptr(),
+            windows::core::PWSTR(domain.as_mut_ptr()),
             &mut domain_size,
-            &mut sid_name_use,
+            sid_name_use.as_mut_ptr(),
         );
 
-        if result != 0 {
+        if result.ok().is_ok() {
             // Success!
             return Ok((name, domain));
         } else if name_size != old_name_size || domain_size != old_domain_size {
@@ -271,7 +275,7 @@ unsafe fn lookup_account_sid(sid: *mut c_void) -> Result<(Vec<u16>, Vec<u16>), s
             // Possibly foreign item originating from another machine
             // TODO: Calculate permissions since it has to be possible if Explorer knows.
             return Err(io::Error::from_raw_os_error(
-                winapi::um::errhandlingapi::GetLastError() as i32,
+                windows::Win32::Foundation::GetLastError().0 as i32,
             ));
         }
     }
@@ -299,6 +303,46 @@ fn buf_from_os(os: &OsStr) -> Vec<u16> {
     buf
 }
 
+/// Checks wether the given [`FILE_FLAGS_AND_ATTRIBUTES`] are set for the given
+/// [`Path`]
+///
+/// [`FILE_FLAGS_AND_ATTRIBUTES`]: windows::Win32::Storage::FileSystem::FILE_FLAGS_AND_ATTRIBUTES
+#[inline]
+fn has_path_attribute(
+    path: &Path,
+    flags: windows::Win32::Storage::FileSystem::FILE_FLAGS_AND_ATTRIBUTES,
+) -> bool {
+    let windows_path = buf_from_os(path.as_os_str());
+    let file_attributes = unsafe {
+        windows::Win32::Storage::FileSystem::GetFileAttributesW(windows::core::PCWSTR(
+            windows_path.as_ptr(),
+        ))
+    };
+    file_attributes & flags.0 > 0
+}
+
+/// Checks whether the windows [`hidden`] attribute is set for the given
+/// [`Path`]
+///
+/// [`hidden`]: windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN
+pub fn is_path_hidden(path: &Path) -> bool {
+    has_path_attribute(
+        path,
+        windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN,
+    )
+}
+
+/// Checks whether the windows [`system`] attribute is set for the given
+/// [`Path`]
+///
+/// [`system`]: windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_SYSTEM
+pub fn is_path_system(path: &Path) -> bool {
+    has_path_attribute(
+        path,
+        windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_SYSTEM,
+    )
+}
+
 #[cfg(test)]
 mod test {
     use super::*;


Test patch: