Skip to content

Commit 19e10ed

Browse files
committed
dllinject: Track actual attribute writes instead of the requesting flag
Opening a file with the `FILE_WRITE_ATTRIBUTES` access right merely *requests* write access to attributes, and does not constitute a write in itself. Previously, this caused issues with tools that need to specify this flag for whatever reason, but didn't actually write or change the attributes of the files they opened with this flag. If those tools lie outside the source tree, Tup will in fact delete all of these files, forcing you to either reinstall the tool or recover the missing files from a backup. (Thankfully, write-protecting the compiler's directory prevents this from succeeding!) This change should still address the node.js use case that initially prompted the check for `FILE_WRITE_ATTRIBUTES` in c7160c8. I couldn't find a definitive list of everything that counts as "attributes", but it does seem to be limited to the timestamps and attribute bits covered by the `FILE_BASIC_INFORMATION` structure.
1 parent 9018985 commit 19e10ed

File tree

1 file changed

+38
-2
lines changed

1 file changed

+38
-2
lines changed

src/dllinject/dllinject.c

+38-2
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ static NtSetInformationFile_t NtSetInformationFile_orig;
422422
static access_t _access_orig;
423423
static rename_t rename_orig;
424424

425-
#define TUP_CREATE_WRITE_FLAGS (GENERIC_WRITE | FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES)
425+
#define TUP_CREATE_WRITE_FLAGS (GENERIC_WRITE | FILE_APPEND_DATA | FILE_WRITE_DATA)
426426
/* Including ddk/wdm.h causes other issues, and this is all we need... */
427427
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
428428

@@ -787,10 +787,43 @@ NTSTATUS WINAPI NtSetInformationFile_hook(
787787
DWORD len = 0;
788788
int failed = 0;
789789

790+
/* * A value of 0 in any field preserves the file's current value for this
791+
* specific attribute.
792+
* * -1 or -2 in the timestamp fields disable or enable the default
793+
* timestamp updates for file I/O:
794+
*
795+
* https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_basic_information
796+
*
797+
* * SetFileInformationByHandle() returns ERROR_INVALID_PARAMETER for any
798+
* other negative number.
799+
*
800+
* * Only process regular files, as calling handle_file_w() on a directory
801+
* would turn it into a file in tup's database.
802+
*/
803+
FILE_BASIC_INFORMATION* basic = FileInformation;
804+
int relevant_basic_change = (FileInformationClass == FileBasicInformation) && (
805+
(int64_t)(basic->CreationTime.QuadPart) >= 1 ||
806+
(int64_t)(basic->LastAccessTime.QuadPart) >= 1 ||
807+
(int64_t)(basic->LastWriteTime.QuadPart) >= 1 ||
808+
(int64_t)(basic->ChangeTime.QuadPart) >= 1 ||
809+
basic->FileAttributes != 0
810+
);
811+
if(relevant_basic_change) {
812+
BY_HANDLE_FILE_INFORMATION handle_info;
813+
relevant_basic_change = GetFileInformationByHandle(FileHandle, &handle_info);
814+
if(!relevant_basic_change) {
815+
DEBUG_HOOK("NtSetInformationFile Error - failed to GetFileInformationByHandle\n");
816+
failed = 1;
817+
} else {
818+
relevant_basic_change = !(handle_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
819+
}
820+
}
821+
790822
/* We need to get the old path before calling NtSetInformationFile in
791823
* case of a rename, in which case that information is lost.
792824
*/
793-
if(FileInformationClass == FileRenameInformation ||
825+
if(relevant_basic_change ||
826+
FileInformationClass == FileRenameInformation ||
794827
FileInformationClass == FileRenameInformationEx ||
795828
FileInformationClass == FileDispositionInformation) {
796829
len = GetFinalPathNameByHandleW(FileHandle, widepath, WIDE_PATH_MAX, FILE_NAME_NORMALIZED);
@@ -831,6 +864,9 @@ NTSTATUS WINAPI NtSetInformationFile_hook(
831864
DEBUG_HOOK("NtSetInformationFile[%i]: dont delete on close '%S'\n", FileInformationClass, widepath);
832865
handle_file_w(widepath, len, NULL, ACCESS_WRITE);
833866
}
867+
} else if(relevant_basic_change) {
868+
DEBUG_HOOK("NtSetInformationFile[%i]: set basic information '%S'\n", FileInformationClass, widepath);
869+
handle_file_w(widepath, len, NULL, ACCESS_WRITE);
834870
}
835871

836872
out_exit:

0 commit comments

Comments
 (0)