'windows-rs GetNamedSecurityInfoW error 87
I'm trying to use windows-rs to use GetNamedSecurityInfoW microsoft api docs to read file permission information, but I keep getting error code 87
corresponds to ERROR_INVALID_PARAMETER
. What have I done wrong? (I'm not experienced with rust or the windows api)
#[cfg(windows)]
pub unsafe fn get_file_perms(file: String) -> Result<()> {
use windows_sys::core::PCWSTR;
use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;
let file_u16 = file.encode_utf16().collect::<Vec<u16>>();
let lpfile: PCWSTR = file_u16.as_ptr() as PCWSTR;
let acl: *mut *mut windows_sys::Win32::Security::ACL = std::ptr::null_mut();
let security_descriptor: *mut windows_sys::Win32::Security::PSECURITY_DESCRIPTOR = std::ptr::null_mut();
let err = GetNamedSecurityInfoW(
lpfile,
windows_sys::Win32::Security::Authorization::SE_FILE_OBJECT,
windows_sys::Win32::Security::DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
acl,
std::ptr::null_mut(),
security_descriptor,
);
if err != 0
{
println!("{}", err);
return Err(anyhow!("Failed to get file permissions"));
}
Ok(())
}`
Solution 1:[1]
GetNamedSecurityInfoW
is an API call with somewhat complex semantics. Besides a description of the object, there's
- a bitmask (
SecurityInfo
) describing the requested information - a set of output parameters (
ppsidOwner
,ppsidGroup
,ppDacl
,ppSacl
) that provide access to structured data - the actual buffer holding the data (
ppSecurityDescriptor
).
On successful return, the system allocates memory, and transfers ownership to the caller through the final parameter. Depending on the requested information (DACL_SECURITY_INFORMATION
) you will have to pass the addresses of pointers to the structured data (ppDacl
in this case).
With that fixed, there are two more issues left: Making sure that the object name (pObjectName
) is zero-terminated, and cleaning up the buffer the system allocated for us with a call to LocalFree
. Note that any one of ppsidOwner
, ppsidGroup
, ppDacl
, and ppSacl
are valid only for as long as ppSecurityDescriptor
is valid.
The following code fixes the immediate issue:
pub unsafe fn get_file_perms(file: String) -> Result<()> {
use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;
let file_u16 = file.encode_utf16().collect::<Vec<u16>>();
// Pointers that receive the output arguments
let mut acl = std::ptr::null_mut();
let mut security_descriptor = std::ptr::null_mut();
let err = GetNamedSecurityInfoW(
file_u16.as_ptr(),
windows_sys::Win32::Security::Authorization::SE_FILE_OBJECT,
windows_sys::Win32::Security::DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
// Pass the *address* of the pointer
std::ptr::addr_of_mut!(acl),
std::ptr::null_mut(),
// Same here
std::ptr::addr_of_mut!(security_descriptor),
);
if err != 0 {
println!("{}", err);
return Err("Failed to get file permissions".into());
}
// At this point `acl` points into an access control list
// Cleanup up resources (should really be bound to a struct with a `Drop` impl)
windows_sys::Win32::System::Memory::LocalFree(security_descriptor as _);
Ok(())
}
As far as the interface goes, you should consider taking a Path
/PathBuf
instead. Since you are dealing with path names, a String
will unduly restrict the input to the point of not being able to encode all potential paths.
Adding zero-termination, the function can be rewritten to this:
pub unsafe fn get_file_perms(file: impl AsRef<Path>) -> Result<()> {
let file_u16 = file
.as_ref()
.as_os_str()
.encode_wide()
.chain(once(0))
.collect::<Vec<_>>();
// ...
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | IInspectable |