Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-stdx/src/faccess.rs')
| -rw-r--r-- | helix-stdx/src/faccess.rs | 501 |
1 files changed, 0 insertions, 501 deletions
diff --git a/helix-stdx/src/faccess.rs b/helix-stdx/src/faccess.rs deleted file mode 100644 index b98ae153..00000000 --- a/helix-stdx/src/faccess.rs +++ /dev/null @@ -1,501 +0,0 @@ -//! Functions for managine file metadata. -//! From <https://github.com/Freaky/faccess> - -use std::io; -use std::path::Path; - -use bitflags::bitflags; - -// Licensed under MIT from faccess -bitflags! { - /// Access mode flags for `access` function to test for. - pub struct AccessMode: u8 { - /// Path exists - const EXISTS = 0b0001; - /// Path can likely be read - const READ = 0b0010; - /// Path can likely be written to - const WRITE = 0b0100; - /// Path can likely be executed - const EXECUTE = 0b1000; - } -} - -#[cfg(unix)] -mod imp { - use super::*; - - use rustix::fs::Access; - use std::os::unix::fs::{MetadataExt, PermissionsExt}; - - pub fn access(p: &Path, mode: AccessMode) -> io::Result<()> { - let mut imode = Access::empty(); - - if mode.contains(AccessMode::EXISTS) { - imode |= Access::EXISTS; - } - - if mode.contains(AccessMode::READ) { - imode |= Access::READ_OK; - } - - if mode.contains(AccessMode::WRITE) { - imode |= Access::WRITE_OK; - } - - if mode.contains(AccessMode::EXECUTE) { - imode |= Access::EXEC_OK; - } - - rustix::fs::access(p, imode)?; - Ok(()) - } - - fn chown(p: &Path, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { - let uid = uid.map(rustix::fs::Uid::from_raw); - let gid = gid.map(rustix::fs::Gid::from_raw); - rustix::fs::chown(p, uid, gid)?; - Ok(()) - } - - pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> { - let from_meta = std::fs::metadata(from)?; - let to_meta = std::fs::metadata(to)?; - let from_gid = from_meta.gid(); - let to_gid = to_meta.gid(); - - let mut perms = from_meta.permissions(); - perms.set_mode(perms.mode() & 0o0777); - if from_gid != to_gid && chown(to, None, Some(from_gid)).is_err() { - let new_perms = (perms.mode() & 0o0707) | ((perms.mode() & 0o07) << 3); - perms.set_mode(new_perms); - } - - #[cfg(target_os = "macos")] - { - use std::fs::{File, FileTimes}; - use std::os::macos::fs::FileTimesExt; - - let to_file = File::options().write(true).open(to)?; - let times = FileTimes::new().set_created(from_meta.created()?); - to_file.set_times(times)?; - } - - std::fs::set_permissions(to, perms)?; - - Ok(()) - } - - pub fn hardlink_count(p: &Path) -> std::io::Result<u64> { - let metadata = p.metadata()?; - Ok(metadata.nlink()) - } -} - -// Licensed under MIT from faccess except for `chown`, `copy_metadata` and `is_acl_inherited` -#[cfg(windows)] -mod imp { - - use windows_sys::Win32::Foundation::{CloseHandle, LocalFree, ERROR_SUCCESS, HANDLE}; - use windows_sys::Win32::Security::Authorization::{ - GetNamedSecurityInfoW, SetNamedSecurityInfoW, SE_FILE_OBJECT, - }; - use windows_sys::Win32::Security::{ - AccessCheck, AclSizeInformation, GetAce, GetAclInformation, GetSidIdentifierAuthority, - ImpersonateSelf, IsValidAcl, IsValidSid, MapGenericMask, RevertToSelf, - SecurityImpersonation, ACCESS_ALLOWED_CALLBACK_ACE, ACL, ACL_SIZE_INFORMATION, - DACL_SECURITY_INFORMATION, GENERIC_MAPPING, GROUP_SECURITY_INFORMATION, INHERITED_ACE, - LABEL_SECURITY_INFORMATION, OBJECT_SECURITY_INFORMATION, OWNER_SECURITY_INFORMATION, - PRIVILEGE_SET, PROTECTED_DACL_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSID, - SID_IDENTIFIER_AUTHORITY, TOKEN_DUPLICATE, TOKEN_QUERY, - }; - use windows_sys::Win32::Storage::FileSystem::{ - GetFileInformationByHandle, BY_HANDLE_FILE_INFORMATION, FILE_ACCESS_RIGHTS, - FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, - }; - use windows_sys::Win32::System::Threading::{GetCurrentThread, OpenThreadToken}; - - use super::*; - - use std::ffi::c_void; - - use std::os::windows::{ - ffi::OsStrExt, - fs::{FileTimesExt, OpenOptionsExt}, - io::AsRawHandle, - }; - - use std::fs::{File, FileTimes}; - - struct SecurityDescriptor { - sd: PSECURITY_DESCRIPTOR, - owner: PSID, - group: PSID, - dacl: *mut ACL, - } - - impl Drop for SecurityDescriptor { - fn drop(&mut self) { - if !self.sd.is_null() { - unsafe { - LocalFree(self.sd); - } - } - } - } - - impl SecurityDescriptor { - fn for_path(p: &Path) -> io::Result<SecurityDescriptor> { - let path = std::fs::canonicalize(p)?; - let pathos = path.into_os_string(); - let mut pathw: Vec<u16> = Vec::with_capacity(pathos.len() + 1); - pathw.extend(pathos.encode_wide()); - pathw.push(0); - - let mut sd = std::ptr::null_mut(); - let mut owner = std::ptr::null_mut(); - let mut group = std::ptr::null_mut(); - let mut dacl = std::ptr::null_mut(); - - let err = unsafe { - GetNamedSecurityInfoW( - pathw.as_ptr(), - SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION - | GROUP_SECURITY_INFORMATION - | DACL_SECURITY_INFORMATION - | LABEL_SECURITY_INFORMATION, - &mut owner, - &mut group, - &mut dacl, - std::ptr::null_mut(), - &mut sd, - ) - }; - - if err == ERROR_SUCCESS { - Ok(SecurityDescriptor { - sd, - owner, - group, - dacl, - }) - } else { - Err(io::Error::last_os_error()) - } - } - - fn is_acl_inherited(&self) -> bool { - let mut acl_info: ACL_SIZE_INFORMATION = unsafe { ::core::mem::zeroed() }; - let acl_info_ptr: *mut c_void = &mut acl_info as *mut _ as *mut c_void; - let mut ace: ACCESS_ALLOWED_CALLBACK_ACE = unsafe { ::core::mem::zeroed() }; - - unsafe { - GetAclInformation( - self.dacl, - acl_info_ptr, - std::mem::size_of_val(&acl_info) as u32, - AclSizeInformation, - ) - }; - - for i in 0..acl_info.AceCount { - let mut ptr = &mut ace as *mut _ as *mut c_void; - unsafe { GetAce(self.dacl, i, &mut ptr) }; - if (ace.Header.AceFlags as u32 & INHERITED_ACE) != 0 { - return true; - } - } - - false - } - - fn descriptor(&self) -> &PSECURITY_DESCRIPTOR { - &self.sd - } - - fn owner(&self) -> &PSID { - &self.owner - } - } - - struct ThreadToken(HANDLE); - impl Drop for ThreadToken { - fn drop(&mut self) { - unsafe { - CloseHandle(self.0); - } - } - } - - impl ThreadToken { - fn new() -> io::Result<Self> { - unsafe { - if ImpersonateSelf(SecurityImpersonation) == 0 { - return Err(io::Error::last_os_error()); - } - - let token: *mut HANDLE = std::ptr::null_mut(); - let err = - OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY, 0, token); - - RevertToSelf(); - - if err == 0 { - return Err(io::Error::last_os_error()); - } - - Ok(Self(*token)) - } - } - - fn as_handle(&self) -> &HANDLE { - &self.0 - } - } - - // Based roughly on Tcl's NativeAccess() - // https://github.com/tcltk/tcl/blob/2ee77587e4dc2150deb06b48f69db948b4ab0584/win/tclWinFile.c - fn eaccess(p: &Path, mut mode: FILE_ACCESS_RIGHTS) -> io::Result<()> { - let md = p.metadata()?; - - if !md.is_dir() { - // Read Only is ignored for directories - if mode & FILE_GENERIC_WRITE == FILE_GENERIC_WRITE && md.permissions().readonly() { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "File is read only", - )); - } - - // If it doesn't have the correct extension it isn't executable - if mode & FILE_GENERIC_EXECUTE == FILE_GENERIC_EXECUTE { - if let Some(ext) = p.extension().and_then(|s| s.to_str()) { - match ext { - "exe" | "com" | "bat" | "cmd" => (), - _ => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "File not executable", - )) - } - } - } - } - - return std::fs::OpenOptions::new() - .access_mode(mode) - .open(p) - .map(|_| ()); - } - - let sd = SecurityDescriptor::for_path(p)?; - - // Unmapped Samba users are assigned a top level authority of 22 - // ACL tests are likely to be misleading - const SAMBA_UNMAPPED: SID_IDENTIFIER_AUTHORITY = SID_IDENTIFIER_AUTHORITY { - Value: [0, 0, 0, 0, 0, 22], - }; - unsafe { - let owner = sd.owner(); - if IsValidSid(*owner) != 0 - && (*GetSidIdentifierAuthority(*owner)).Value == SAMBA_UNMAPPED.Value - { - return Ok(()); - } - } - - let token = ThreadToken::new()?; - - let mut privileges: PRIVILEGE_SET = unsafe { std::mem::zeroed() }; - let mut granted_access: u32 = 0; - let mut privileges_length = std::mem::size_of::<PRIVILEGE_SET>() as u32; - let mut result = 0; - - let mapping = GENERIC_MAPPING { - GenericRead: FILE_GENERIC_READ, - GenericWrite: FILE_GENERIC_WRITE, - GenericExecute: FILE_GENERIC_EXECUTE, - GenericAll: FILE_ALL_ACCESS, - }; - - unsafe { MapGenericMask(&mut mode, &mapping) }; - - if unsafe { - AccessCheck( - *sd.descriptor(), - *token.as_handle(), - mode, - &mapping, - &mut privileges, - &mut privileges_length, - &mut granted_access, - &mut result, - ) - } != 0 - { - if result == 0 { - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Permission Denied", - )) - } else { - Ok(()) - } - } else { - Err(io::Error::last_os_error()) - } - } - - pub fn access(p: &Path, mode: AccessMode) -> io::Result<()> { - let mut imode = 0; - - if mode.contains(AccessMode::READ) { - imode |= FILE_GENERIC_READ; - } - - if mode.contains(AccessMode::WRITE) { - imode |= FILE_GENERIC_WRITE; - } - - if mode.contains(AccessMode::EXECUTE) { - imode |= FILE_GENERIC_EXECUTE; - } - - if imode == 0 { - if p.exists() { - Ok(()) - } else { - Err(io::Error::new(io::ErrorKind::NotFound, "Not Found")) - } - } else { - eaccess(p, imode) - } - } - - fn chown(p: &Path, sd: SecurityDescriptor) -> io::Result<()> { - let path = std::fs::canonicalize(p)?; - let pathos = path.as_os_str(); - let mut pathw = Vec::with_capacity(pathos.len() + 1); - pathw.extend(pathos.encode_wide()); - pathw.push(0); - - let mut owner = std::ptr::null_mut(); - let mut group = std::ptr::null_mut(); - let mut dacl = std::ptr::null(); - - let mut si = OBJECT_SECURITY_INFORMATION::default(); - if unsafe { IsValidSid(sd.owner) } != 0 { - si |= OWNER_SECURITY_INFORMATION; - owner = sd.owner; - } - - if unsafe { IsValidSid(sd.group) } != 0 { - si |= GROUP_SECURITY_INFORMATION; - group = sd.group; - } - - if unsafe { IsValidAcl(sd.dacl) } != 0 { - si |= DACL_SECURITY_INFORMATION; - if !sd.is_acl_inherited() { - si |= PROTECTED_DACL_SECURITY_INFORMATION; - } - dacl = sd.dacl as *const _; - } - - let err = unsafe { - SetNamedSecurityInfoW( - pathw.as_ptr(), - SE_FILE_OBJECT, - si, - owner, - group, - dacl, - std::ptr::null(), - ) - }; - - if err == ERROR_SUCCESS { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - - pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> { - let sd = SecurityDescriptor::for_path(from)?; - chown(to, sd)?; - - let meta = std::fs::metadata(from)?; - let perms = meta.permissions(); - - let to_file = File::options().write(true).open(to)?; - let times = FileTimes::new().set_created(meta.created()?); - to_file.set_times(times)?; - - std::fs::set_permissions(to, perms)?; - - Ok(()) - } - - pub fn hardlink_count(p: &Path) -> std::io::Result<u64> { - let file = std::fs::File::open(p)?; - let handle = file.as_raw_handle(); - let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; - - if unsafe { GetFileInformationByHandle(handle, &mut info) } == 0 { - Err(std::io::Error::last_os_error()) - } else { - Ok(info.nNumberOfLinks as u64) - } - } -} - -// Licensed under MIT from faccess except for `copy_metadata` -#[cfg(not(any(unix, windows)))] -mod imp { - use super::*; - - pub fn access(p: &Path, mode: AccessMode) -> io::Result<()> { - if mode.contains(AccessMode::WRITE) { - if std::fs::metadata(p)?.permissions().readonly() { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Path is read only", - )); - } else { - return Ok(()); - } - } - - if p.exists() { - Ok(()) - } else { - Err(io::Error::new(io::ErrorKind::NotFound, "Path not found")) - } - } - - pub fn copy_metadata(from: &path, to: &Path) -> io::Result<()> { - let meta = std::fs::metadata(from)?; - let perms = meta.permissions(); - std::fs::set_permissions(to, perms)?; - - Ok(()) - } -} - -pub fn readonly(p: &Path) -> bool { - match imp::access(p, AccessMode::WRITE) { - Ok(_) => false, - Err(err) if err.kind() == std::io::ErrorKind::NotFound => false, - Err(_) => true, - } -} - -pub fn copy_metadata(from: &Path, to: &Path) -> io::Result<()> { - imp::copy_metadata(from, to) -} - -pub fn hardlink_count(p: &Path) -> io::Result<u64> { - imp::hardlink_count(p) -} |