简短:
在我的c ++项目中,我需要读取/写入扩展文件属性。我使用备用数据流(ADS)对其进行了管理。我的问题是,要打开ADS,我需要使用CreateFile
API。但这不能满足我的需求。 NtCreateFile将满足我的所有需求。 (或选择NtSetEaFile
和NtQueryEaFile
)但是NtCreateFile
不能从Win32控制台应用程序直接访问。
我知道我可以通过GetProcAdres
轻松使用此功能。但是我想知道大家的意见,如果我确实错过了什么?其他一些库已经在使用这种模式,例如Chromium(https://github.com/chromium-googlesource-mirror/chromium/blob/1c1996b75d3611f56d14e2b30e7ae4eabc101486/src/sandbox/src/win_utils.cc函数:ResolveNTFunctionPtr
)
但是我不确定,因为C ++项目不是一个业余项目,我问自己这是否危险。
我猜想NtCreateFile
可能是最安全的方法,因为winternl.h
标头已对其进行了很好的说明和支持。特别是因为自Windows 2000以来此方法一直未更改。但是NtSetEaFile
,NtQueryEaFile
完全适合我的需求。它们只有一半记录在案。存在ZwSetEaFile
和ZwQueryEaFile
的文档(自Windows 2000以来未更改)。
为什么要这么做:
我想通过ADS从文件写入和读取扩展属性。但是,如果是第一次编写给定文件的扩展属性,我需要使用OPEN_ALWAYS
打开文件。如果文件不存在,即使我不访问文件的内容流,它也会创建一个新文件。为了避免这种情况,我首先获取原始文件的句柄,并使用此HANDLE检查该文件是否仍然存在。
但是我不想写任何访问权限降低的文件,因为从我的角度来看,这是一个非常糟糕的模式。用户需要随时拥有对任何文件的完全访问权限。因此,我们打开所有带有标志FILE_SHARE_DELETE
的HANDLES。 FILE_SHARE_READ
| FILE_SHARE_WRITE
。现在我开始比赛了。
auto hFile = CreateFileW(originalPath, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, …).
// this is the little race: if somebody at least rename originalPath the
// second CreateFileW call will cause the creation of a empty file with the
// path originalPath (the old path).
auto hADS = CreateFileW(originalPath + adsName, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, …).
这是一个主要问题,尤其是因为这在我们的测试中有时会发生。 NtCreateFile
将解决此问题,因为我可以在第一个HANDLE
的帮助下创建第二个HANDLE
。因此,没有种族。否则NtSetEaFile
和NtQueryEaFile
会有所帮助,因为我只需要一个手柄。
问题是,将来不必保存该应用程序,因为ADS只能在NTFS
上运行。谁知道NTFS
何时会被交换。但是我不想要片状的行为。我想相信这个方法。如果API将来会更改并且软件需要适应它,我很好。但我想确定的是,所有高于或等于7的Windows都可以处理。有人分享一些经验吗?我很想听听他们的话。
答案 0 :(得分:3)
这个问题是错误的。为您的问题建议的解决方案不是使用NtCreateFile,而是使用dwCreationDisposition设置为OPEN_EXISTING的CreateFile。
OPEN_EXISTING
仅打开文件或设备(如果存在)。如果指定文件或 设备不存在,功能失败,最后一个错误代码是 设置为ERROR_FILE_NOT_FOUND。
仅打开文件(如果存在)并设置所需的内容。如果文件已重命名,则CreateFile返回ERROR_FILE_NOT_FOUND。
问题
现在,对于您提出的解决方案,什么是更好的方法,或者为什么在Win32控制台应用程序(???)中不能使用ntdll.dll。 同样,您的“更好”的方法-GetProcAddress与使用针对ntdll.dll的链接“相同”。在 Windows 11 或 Windows 12 或 Windows 3030 中,该功能可能已删除,并且两种解决方案(静态导入与动态导入)都将失败。
答案 1 :(得分:0)
如果此类API是文档,使用它们并不是真的不安全。对于NtSetEaFile
,NtQueryEaFile
和NtCreateFile
,您可以在Microsoft Doc中找到说明。 (请记住NtXxx == ZwXxx)
但是此API将来可能会更改,并且Microsoft不保证它将在下一Windows版本中提供相同的方法。如果可以,请使用公共API,因为这样做很安全。如果不是,则逐案决定。在这种情况下,自Windows 2000以来,API中的三种方法均未更改。另外,例如,NtSetEaFile
和NtQueryEaFile
被Microsoft用于WSL(Linux的Windows子系统)。特别是NtCreateFile
被广泛的OpenSource Projects使用。因此,该API更改的可能性很小。
在我的用例中,另一个方面很重要。因为我想使用ADS,但是NTFS仅支持ADS。因此,使用ADS并不能确保将来的兼容性。因此,使用NtSetEaFile
和NtQueryEaFile
对我来说很清楚。
但是如何使用这种API?可以进行动态或静态链接。这取决于您的需求,哪个更好。如果是静态链接,则需要下载最新的WDK(Windows驱动程序工具包)并链接到ntdll.lib。如果是动态链接,则可以直接通过GetModuleHandle
访问dll,并使用GetProcAddress
查找方法的地址。在Windows下,ntdll.dll
可以从任何应用程序访问。在这两种情况下,您都没有直接的头文件。您必须自己定义头文件,或使用WDK来获取它们。
在我的项目中,动态链接是最佳选择。原因是,在每个Windows上都将选择正确的实现,并且在该方法不可用的情况下,我有机会停用软件中的功能,而不会崩溃。由于最后一个原因,Microsoft建议采用动态方式。
简单的伪代码(动态情况):
typedef struct _FILE_FULL_EA_INFORMATION {
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef NTSTATUS(WINAPI *NtSetEaFileFunction)(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK
IoStatusBlock,
IN PVOID Buffer,
IN ULONG Length);
HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
NtSetEaFileFunction function = nullptr;
FARPROC *function_ptr = reinterpret_cast<FARPROC *>(&function);
*function_ptr = GetProcAddress(ntdll, "NtQueryEaFile");
// function could be used normally.
另一个答案不正确。原因是我遇到问题的原因是,我需要使用OPEN_ALWAYS
。当然,如果不需要此标志,一切都很好。但就我而言,我需要创建ADS。没有OPEN_ALWAYS
标志就不会创建它。