cfgd_core/util/
fs_perms.rs1pub fn create_symlink(source: &std::path::Path, target: &std::path::Path) -> std::io::Result<()> {
6 #[cfg(unix)]
7 {
8 create_symlink_impl(source, target)
9 }
10 #[cfg(windows)]
11 {
12 use super::paths::PathDisplayExt;
13 create_symlink_impl(source, target).map_err(|e| {
14 if e.raw_os_error() == Some(1314) {
15 return std::io::Error::new(
17 e.kind(),
18 format!(
19 "symlink creation requires Developer Mode or admin privileges: {} -> {}\n\
20 Enable Developer Mode: Settings > Update & Security > For developers",
21 source.posix(),
22 target.posix()
23 ),
24 );
25 }
26 e
27 })
28 }
29}
30
31#[cfg(unix)]
32fn create_symlink_impl(source: &std::path::Path, target: &std::path::Path) -> std::io::Result<()> {
33 std::os::unix::fs::symlink(source, target)
34}
35
36#[cfg(windows)]
37fn create_symlink_impl(source: &std::path::Path, target: &std::path::Path) -> std::io::Result<()> {
38 if source.is_dir() {
39 std::os::windows::fs::symlink_dir(source, target)
40 } else {
41 std::os::windows::fs::symlink_file(source, target)
42 }
43}
44
45#[cfg(unix)]
47pub fn file_permissions_mode(metadata: &std::fs::Metadata) -> Option<u32> {
48 use std::os::unix::fs::PermissionsExt;
49 Some(metadata.permissions().mode() & 0o777)
50}
51
52#[cfg(windows)]
53pub fn file_permissions_mode(_metadata: &std::fs::Metadata) -> Option<u32> {
54 None
55}
56
57#[cfg(unix)]
59pub fn set_file_permissions(path: &std::path::Path, mode: u32) -> std::io::Result<()> {
60 use std::os::unix::fs::PermissionsExt;
61 std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode))
62}
63
64#[cfg(windows)]
65pub fn set_file_permissions(_path: &std::path::Path, _mode: u32) -> std::io::Result<()> {
66 tracing::debug!("set_file_permissions is a no-op on Windows (NTFS uses inherited ACLs)");
67 Ok(())
68}
69
70#[cfg(unix)]
74pub fn is_executable(_path: &std::path::Path, metadata: &std::fs::Metadata) -> bool {
75 use std::os::unix::fs::PermissionsExt;
76 metadata.permissions().mode() & 0o111 != 0
77}
78
79#[cfg(windows)]
80pub fn is_executable(path: &std::path::Path, _metadata: &std::fs::Metadata) -> bool {
81 const EXECUTABLE_EXTENSIONS: &[&str] = &["exe", "cmd", "bat", "ps1", "com"];
82 path.extension()
83 .and_then(|e| e.to_str())
84 .map(|e| EXECUTABLE_EXTENSIONS.contains(&e.to_lowercase().as_str()))
85 .unwrap_or(false)
86}
87
88#[cfg(unix)]
90pub fn is_same_inode(a: &std::path::Path, b: &std::path::Path) -> bool {
91 use std::os::unix::fs::MetadataExt;
92 match (std::fs::metadata(a), std::fs::metadata(b)) {
93 (Ok(ma), Ok(mb)) => ma.ino() == mb.ino() && ma.dev() == mb.dev(),
94 _ => false,
95 }
96}
97
98#[cfg(windows)]
99pub fn is_same_inode(a: &std::path::Path, b: &std::path::Path) -> bool {
100 use std::os::windows::io::AsRawHandle;
101 use windows_sys::Win32::Storage::FileSystem::BY_HANDLE_FILE_INFORMATION;
102 use windows_sys::Win32::Storage::FileSystem::GetFileInformationByHandle;
103
104 fn file_info(path: &std::path::Path) -> Option<BY_HANDLE_FILE_INFORMATION> {
105 let file = std::fs::File::open(path).ok()?;
106 let mut info = unsafe { std::mem::zeroed() };
110 let ret = unsafe { GetFileInformationByHandle(file.as_raw_handle() as _, &mut info) };
115 if ret != 0 { Some(info) } else { None }
116 }
117
118 match (file_info(a), file_info(b)) {
119 (Some(ia), Some(ib)) => {
120 ia.dwVolumeSerialNumber == ib.dwVolumeSerialNumber
121 && ia.nFileIndexHigh == ib.nFileIndexHigh
122 && ia.nFileIndexLow == ib.nFileIndexLow
123 }
124 _ => false,
125 }
126}