Changeset 105631 in vbox for trunk/src/VBox/Runtime/r3/nt/RTPathUnlink-r3-nt.cpp
- Timestamp:
- Aug 9, 2024 12:59:18 AM (5 months ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/nt/RTPathUnlink-r3-nt.cpp
r105630 r105631 42 42 #include "internal-r3-nt.h" 43 43 44 #include <iprt/ file.h>44 #include <iprt/path.h> 45 45 #include <iprt/err.h> 46 46 … … 48 48 49 49 50 RT DECL(int) RTFileDelete(const char *pszFilename)50 RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink) 51 51 { 52 /* 53 * Validate input. 54 */ 55 AssertPtrReturn(pszPath, VERR_INVALID_POINTER); 56 AssertReturn(*pszPath, VERR_INVALID_NAME); 57 AssertReturn(!(fUnlink & ~RTPATHUNLINK_FLAGS_NO_SYMLINKS), VERR_INVALID_FLAGS); 58 52 59 /* 53 60 * Convert and normalize the path. … … 55 62 UNICODE_STRING NtName; 56 63 HANDLE hRootDir; 57 int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, psz Filename);64 int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath); 58 65 if (RT_SUCCESS(rc)) 59 66 { 60 ULONG fUnwantedFileAttribs = FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY;61 62 67 /* 63 * Try open it as a file or reparse point. 64 * 65 * Note! This will succeed on directory alternate data streams, despite 66 * the FILE_NON_DIRECTORY_FILE flag. 67 * 68 * OTOH, it will open the reparse point even if an ADS is 69 * specified in the path (i.e. given symlink 'foo' targeting 70 * directory 'bar\', attempts to open 'foo::$INDEX_ALLOCATION' 71 * will result in opening the 'foo' reparse point and any 72 * attempts to delete it will only delete 'foo', the 'bar' 73 * directory will be left untouched - so safe). 68 * Try open the file system object without preference to directory or 69 * non-directory, while making sure we don't follow reparse points. 74 70 */ 75 71 HANDLE hPath = RTNT_INVALID_HANDLE_VALUE; … … 78 74 InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRootDir, NULL); 79 75 80 ULONG fOpenOptions = FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT 81 | FILE_SYNCHRONOUS_IO_NONALERT; 76 ULONG fOpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT; 82 77 NTSTATUS rcNt = NtCreateFile(&hPath, 83 DELETE | FILE_READ_ATTRIBUTES |SYNCHRONIZE,78 DELETE | SYNCHRONIZE, 84 79 &ObjAttr, 85 80 &Ios, … … 91 86 NULL /*EaBuffer*/, 92 87 0 /*EaLength*/); 93 if (NT_SUCCESS(rcNt)) 94 { 95 /* 96 * Check if it is a reparse point. 97 * 98 * DeleteFileW and MoveFileExW (on build 19040) will re-open reparse 99 * points other than symlinks, mount points (junctions?) and named 100 * pipe symlinks. We OTOH, will just fail as that could be a 101 * potentially security problem, since unknown reparse point behaviour 102 * could be used for exploits if they work in a similar manner to the 103 * three just mentioned. 104 * 105 * To delete symbolic links, use RTSymlinkDelete. 106 * 107 * Alternative: We could model this on linux instead and also allow 108 * this function unlink symlinks, mount points and global reparse stuff, 109 * but fail on any other reparse point. This would make the APIs work 110 * more or less the same across the platforms. (Code is #if 0'ed below.) 111 * 112 * See @bugref{10632}. 113 */ 114 FILE_ATTRIBUTE_TAG_INFORMATION TagInfo = {0, 0}; 115 RTNT_IO_STATUS_BLOCK_REINIT(&Ios); 116 rcNt = NtQueryInformationFile(hPath, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation); 117 if (NT_SUCCESS(rcNt)) 118 { 119 if (TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 120 { 121 #if 0 122 if ( TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK 123 || TagInfo.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT 124 || TagInfo.ReparseTag == IO_REPARSE_TAG_GLOBAL_REPARSE) 125 fUnwantedFileAttribs = 0; /* Consider all symlinks to be files, even the ones pointing at directories. */ 126 else 127 #endif 128 { 129 NtClose(hPath); 130 hPath = RTNT_INVALID_HANDLE_VALUE; 131 rcNt = STATUS_DIRECTORY_IS_A_REPARSE_POINT; 132 rc = VERR_IS_A_SYMLINK; 133 } 134 } 135 } 136 else if (rcNt == STATUS_INVALID_PARAMETER || rcNt == STATUS_NOT_IMPLEMENTED) 137 rcNt = STATUS_SUCCESS; 138 else 139 { 140 NtClose(hPath); 141 hPath = RTNT_INVALID_HANDLE_VALUE; 142 } 143 } 88 144 89 /* 145 90 * Retry w/o the FILE_OPEN_REPARSE_POINT if the file system returns … … 147 92 * grok the reparse point stuff. 148 93 */ 149 elseif (rcNt == STATUS_INVALID_PARAMETER)94 if (rcNt == STATUS_INVALID_PARAMETER) 150 95 { 151 96 fOpenOptions &= ~FILE_OPEN_REPARSE_POINT; … … 153 98 RTNT_IO_STATUS_BLOCK_REINIT(&Ios); 154 99 rcNt = NtCreateFile(&hPath, 155 DELETE | FILE_READ_ATTRIBUTES |SYNCHRONIZE,100 DELETE | SYNCHRONIZE, 156 101 &ObjAttr, 157 102 &Ios, … … 164 109 0 /*EaLength*/); 165 110 } 166 /* else:167 DeleteFileW will retry opening the file w/o FILE_READ_ATTRIBUTES here when168 the status code is STATUS_ACCESS_DENIED. We OTOH will not do that as we will169 be querying attributes to check that it is a file and not a directory. */170 171 111 if (NT_SUCCESS(rcNt)) 172 112 { 173 113 /* 174 * Recheck that this is a file and not a directory or a reparse point we 175 * don't approve of. 176 * 177 * This prevents us from accidentally deleting a directory via the 178 * ::$INDEX_ALLOCATION stream, at the cost of not being able to delete 179 * any alternate data streams on directories using this API. 180 * 181 * See @bugref{10632}. 114 * Delete it. 182 115 */ 183 FILE_BASIC_INFORMATION BasicInfo = {}; 184 RTNT_IO_STATUS_BLOCK_REINIT(&Ios); 185 rcNt = NtQueryInformationFile(hPath, &Ios, &BasicInfo, sizeof(BasicInfo), FileBasicInformation); 116 FILE_DISPOSITION_INFORMATION DeleteInfo = { TRUE }; 117 rcNt = NtSetInformationFile(hPath, &Ios, &DeleteInfo, sizeof(DeleteInfo), FileDispositionInformation); 186 118 if (NT_SUCCESS(rcNt)) 187 { 188 if (!(BasicInfo.FileAttributes & fUnwantedFileAttribs)) 189 { 190 /* 191 * Okay, it is a file. Delete it. 192 */ 193 FILE_DISPOSITION_INFORMATION DeleteInfo = { TRUE }; 194 rcNt = NtSetInformationFile(hPath, &Ios, &DeleteInfo, sizeof(DeleteInfo), FileDispositionInformation); 195 } 196 else if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) 197 rc = VERR_IS_A_DIRECTORY; 198 else 199 rc = VERR_IS_A_SYMLINK; 200 } 119 rc = VINF_SUCCESS; 201 120 else 202 121 rc = RTErrConvertFromNtStatus(rcNt); … … 206 125 rc = RTErrConvertFromNtStatus(rcNt); 207 126 } 208 else if (RT_SUCCESS_NP(rc))127 else 209 128 rc = RTErrConvertFromNtStatus(rcNt); 210 129 RTNtPathFree(&NtName, &hRootDir);
Note:
See TracChangeset
for help on using the changeset viewer.