-again:
- /* Request length of security descriptor */
- status = GetFileSecurityW(path, requestedInformation,
- NULL, 0, &lenNeeded);
- err = GetLastError();
- if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
- DWORD len = lenNeeded;
- char buf[len];
- if (GetFileSecurityW(path, requestedInformation,
- (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
- {
- int security_id = sd_set_add_sd(sd_set, buf, len);
- if (security_id < 0)
+ buf = _buf;
+ bufsize = sizeof(_buf);
+
+ /*
+ * We need the file's security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+ * format, and we currently have a handle opened with as many relevant
+ * permissions as possible. At this point, on Windows there are a number of
+ * options for reading a file's security descriptor:
+ *
+ * GetFileSecurity(): This takes in a path and returns the
+ * SECURITY_DESCRIPTOR_RELATIVE. Problem: this uses an internal handle, not
+ * ours, and the handle created internally doesn't specify
+ * FILE_FLAG_BACKUP_SEMANTICS. Therefore there can be access denied errors on
+ * some files and directories, even when running as the Administrator.
+ *
+ * GetSecurityInfo(): This takes in a handle and returns the security
+ * descriptor split into a bunch of different parts. This should work, but it's
+ * dumb because we have to put the security descriptor back together again.
+ *
+ * BackupRead(): This can read the security descriptor, but this is a
+ * difficult-to-use API, probably only works as the Administrator, and the
+ * format of the returned data is not well documented.
+ *
+ * NtQuerySecurityObject(): This is exactly what we need, as it takes in a
+ * handle and returns the security descriptor in SECURITY_DESCRIPTOR_RELATIVE
+ * format. Only problem is that it's a ntdll function and therefore not
+ * officially part of the Win32 API. Oh well.
+ */
+ while (!(NT_SUCCESS(status = (*func_NtQuerySecurityObject)(h,
+ requestedInformation,
+ (PSECURITY_DESCRIPTOR)buf,
+ bufsize,
+ &lenNeeded))))
+ {
+ switch (status) {
+ case STATUS_BUFFER_OVERFLOW:
+ wimlib_assert(buf == _buf);
+ buf = MALLOC(lenNeeded);
+ if (!buf)