如何正确使用MAXIMUM_ALLOWED



我创建了一个小型框架,为多个文件系统/API(即Win32、Posix、NFS(提供统一的API。所说的API有点类似于Posix——要访问一个文件;打开";其提供用于预期目的的提示(rwrw(。类似open_file("/abc/log.txt", access::rw)

在这个框架中支持Win32 API让我头疼;陈述性的";Win32的性质——您应该预先知道计划对给定句柄执行哪些操作,并将相关的dwDesiredAccess传递到相关的(Nt)CreateFile()调用中。不幸的是,除了通用的r/w/rw提示之外,框架不知道客户端将执行什么操作(即更改所有者、写入属性等(。我不愿意让Win32概念泄漏到我的框架中(即,我不喜欢在open_file()中添加等效的dwDesiredAccess(。

以下是我尝试过的:

1.最大_ALLOWED

想法:用MAXIMUM_ALLOWED打开相关句柄——我会得到我能得到的一切,如果缺少一些权限,相关操作(例如set_mime()(将用access denied失败。

问题:

  • 它不适用于只读文件或卷((Nt)CreateFile()使用access denied失败(
  • MSDN警告说,如果FAT卷正在进行碎片整理,尝试以这种方式打开目录将失败
  • 一般来说,使用MAXIMUM_ALLOWED似乎出于某种原因而不受欢迎

2.必要时重新打开对象

想法:通过GENERIC_READGENERIC_WRITE表示r/w/rw,对于所有需要额外访问的操作(例如delete()需要DELETE(,重新打开具有所需访问权限的对象。

问题:

  • 重新打开对象并不便宜
  • 通过第二个对象所做的更改可以被静默覆盖,例如:
    • set_mtime()FILE_WRITE_ATTRIBUTES|SYNCHRONIZE重新打开文件
    • 调用NtSetInformationFile(... FileBasicInformation)更新元数据并关闭句柄
    • 当原始句柄关闭后,它会导致数据刷新,并静默地覆盖先前由set_mtime()设置的ModifiedTime

3.复制句柄而不是重新打开对象

想法:与上一节相同,但不是重新打开对象——重复原始句柄(请求新的访问权限(:

HANDLE h;
HANDLE hp = GetCurrentProcess();
CHECK_WIN32( DuplicateHandle(hp, hFile, hp, &h, FILE_WRITE_ATTRIBUTES|SYNCHRONIZE, FALSE, 0) );

问题:

  • 每次我需要执行(非纯读/写(操作时都要复制(并关闭(文件句柄,这似乎太多了,而且有些昂贵
  • DuplicateHandle()文档警告(未提供任何详细信息(,请求额外访问可能会失败。它在我检查过的所有用例中都运行良好(通常在使用GENERIC_READ打开的句柄上请求DELETE/FILE_WRITE_ATTRIBUTES之类的东西(,但显然Win32 API没有提供任何保证:-/

。。。除此之外,这种方法似乎奏效了。

底线:

我正在寻找解决MAXIMUM_ALLOWED问题的方法。(或者对替代方法的建议,也许?(

编辑:这是重新打开文件不是一个好主意的另一个原因。

无法可靠地使用MAXIMUM_ALLOWED--R/O文件和卷会导致它出错。设计不当的功能。

另一种方法是获得最小的访问;展开";根据需要(通过重新打开具有新dwAccessRequired标志的文件(。这不起作用:

  • 如果您临时打开文件,则当原始句柄关闭时(底层内核对象将数据刷新到磁盘(,通过新句柄所做的一些更改(例如mtime修改(将在稍后被擦除

  • 如果你试图用新的句柄替换旧的句柄,这意味着昂贵的刷新(在旧句柄关闭时(+MT同步,这意味着您无法从多个线程有效地使用我的file对象(我知道现在由于FILE_SYNCHRONOUS_IO_NONALERT,所有操作都是序列化的,但它将在短期内修复(

唉,DuplicateHandle()无法授予新的访问权限,所以这也没有帮助。

基本上,我只需要一个线程安全的BOOL ExtendAccess(HANDLE h, DWORD dwAdditionalAccess)函数。看起来即使通过NTneneneba API也无法使用它——只能在内核模式下使用。

幸运的是,这个框架总是在特权帐户下使用,这意味着我可以启用SE_BACKUP_NAME,使用FILE_OPEN_FOR_BACKUP_INTENT,通过请求访问(在只读卷的情况下有最小的回退(,并避免处理限制性DACL。啊,是的,处理delete()中的ReadOnly属性。如果用户想打开只读文件进行写入,还没有决定该怎么办。。。

我最终得到了这个:

W32Handle open_file_(HANDLE hparent, UNICODE_STRING zwpath, access a, disposition d, truncate t)
{
...
ACCESS_MASK access = [a]() -> ACCESS_MASK {
switch(a)
{
case access::r  : return GENERIC_READ;
case access::w  : [[fallthrough]];                      // MSDN suggests to use GENERIC_READ with GENERIC_WRITE over network (performance reasons)
case access::rw : return GENERIC_READ|GENERIC_WRITE;
}
UNREACHEABLE;
}();
constexpr DWORD write_access = FILE_WRITE_ATTRIBUTES|DELETE|WRITE_OWNER;    // we want to always have these (for apply, unlink, chown, etc)
access |= write_access;
access |= SYNCHRONIZE|READ_CONTROL|ACCESS_SYSTEM_SECURITY;                  // add "read DACL/SACL" rights (for full_metadata)
ULONG flags = FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE|FILE_OPEN_FOR_BACKUP_INTENT;
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &zwpath, 0, hparent, NULL);
HANDLE h;
IO_STATUS_BLOCK io;
NTSTATUS r = ZwCreateFile(&h, access, &oa, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_VALID_FLAGS, disposition, flags, NULL, 0);
if (r == STATUS_SUCCESS) return W32Handle(h);
if (r == STATUS_MEDIA_WRITE_PROTECTED)      // try again without write flags
{
access &= ~write_access;
r = ZwCreateFile(&h, access, &oa, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_VALID_FLAGS, disposition, flags, NULL, 0);
if (r == STATUS_SUCCESS) return W32Handle(h);
}
HR_THROW_(HRESULT_FROM_NT(r), "%s: Failed to open file", __func__);
}

总体来说API很糟糕,一个特殊情况的意大利面条。我希望我有自己的SMB客户端。

相关内容

  • 没有找到相关文章

最新更新