windows-rs GetNamedSecurityInfoW 错误 87

windows-rs GetNamedSecurityInfoW error 87

我正在尝试使用 windows-rs to use GetNamedSecurityInfoW microsoft api docs 读取文件权限信息,但我一直收到错误代码 87 对应于 ERROR_INVALID_PARAMETER。我做错了什么? (我对生锈或 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(())
}`

GetNamedSecurityInfoW 是一个 API 调用,语义有些复杂。除了对象的描述,还有

  • 描述请求信息的位掩码 (SecurityInfo)
  • 一组提供对结构化数据的访问的输出参数(ppsidOwnerppsidGroupppDaclppSacl
  • 保存数据的实际缓冲区 (ppSecurityDescriptor)。

成功后return,系统分配内存,并通过最终参数将所有权转移给调用者。根据请求的信息 (DACL_SECURITY_INFORMATION),您必须将指针的地址传递给结构化数据(在本例中为 ppDacl)。

修复后,还有两个问题:确保对象名称 (pObjectName) 是 zero-terminated,以及清理系统通过调用为我们分配的缓冲区LocalFree。请注意,ppsidOwnerppsidGroupppDaclppSacl 中的任何一个仅在 ppSecurityDescriptor 有效期间有效。

以下代码解决了眼前的问题:

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(())
}

就界面而言,您应该考虑使用 Path/PathBuf。由于您正在处理路径名,因此 String 会将输入过度限制到无法对所有潜在路径进行编码的程度。

加上zero-termination,函数可以改写成这样:

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<_>>();

    // ...