source: trunk/src/lib/nt/ntunlink.c@ 3682

Last change on this file since 3682 was 3682, checked in by bird, 4 weeks ago

lib/nt,kmk: Fixed around rm/unlink semantics and general support for long file names in lib/nt.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/* $Id: ntunlink.c 3682 2025-08-12 23:34:19Z bird $ */
2/** @file
3 * MSC + NT unlink and variations.
4 */
5
6/*
7 * Copyright (c) 2005-2017 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <io.h> /* for _get_osfhandle */
36#include "ntunlink.h"
37
38#include "ntstuff.h"
39#include "nthlp.h"
40#include "nthlpmisc.h"
41
42
43static MY_NTSTATUS birdMakeWritable(HANDLE hRoot, MY_UNICODE_STRING *pNtPath)
44{
45 MY_NTSTATUS rcNt;
46 HANDLE hFile;
47
48 rcNt = birdOpenFileUniStr(hRoot,
49 pNtPath,
50 FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
51 FILE_ATTRIBUTE_NORMAL,
52 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
53 FILE_OPEN,
54 FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
55 OBJ_CASE_INSENSITIVE,
56 &hFile);
57 if (MY_NT_SUCCESS(rcNt))
58 {
59 MY_FILE_BASIC_INFORMATION BasicInfo;
60 MY_IO_STATUS_BLOCK Ios;
61 DWORD dwAttr;
62
63 Ios.Information = -1;
64 Ios.u.Status = -1;
65 memset(&BasicInfo, 0, sizeof(BasicInfo));
66 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
67
68 if (MY_NT_SUCCESS(rcNt) && MY_NT_SUCCESS(Ios.u.Status) && BasicInfo.FileAttributes != FILE_ATTRIBUTE_READONLY)
69 dwAttr = BasicInfo.FileAttributes & ~FILE_ATTRIBUTE_READONLY;
70 else
71 dwAttr = FILE_ATTRIBUTE_NORMAL;
72 memset(&BasicInfo, 0, sizeof(BasicInfo));
73 BasicInfo.FileAttributes = dwAttr;
74
75 Ios.Information = -1;
76 Ios.u.Status = -1;
77 rcNt = g_pfnNtSetInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
78
79 birdCloseFile(hFile);
80 }
81
82 return rcNt;
83}
84
85
86static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, const wchar_t *pwszFile, int fReadOnlyToo, int fFast, int fRmDir)
87{
88 MY_UNICODE_STRING NtPath;
89 int rc;
90
91 if (hRoot == INVALID_HANDLE_VALUE)
92 hRoot = NULL;
93 if (hRoot == NULL)
94 {
95 if (pwszFile)
96 rc = birdDosToNtPathW(pwszFile, &NtPath);
97 else
98 rc = birdDosToNtPath(pszFile, &NtPath);
99 }
100 else
101 {
102 if (pwszFile)
103 rc = birdDosToRelativeNtPathW(pwszFile, &NtPath);
104 else
105 rc = birdDosToRelativeNtPath(pszFile, &NtPath);
106 }
107 if (rc == 0)
108 {
109 MY_NTSTATUS rcNt;
110 if (fFast)
111 {
112 /*
113 * This uses FILE_DELETE_ON_CLOSE.
114 *
115 * It is only suitable if in a hurry and when 100% sure it is a regular file.
116 * It will follow symbolic links by default, so subject to races and abuse.
117 *
118 * If used on a directory and the directory isn't empty, it will return success
119 * instead of STATUS_CANNOT_DELETE.
120 *
121 * To stay out of trouble, we always use the OBJ_DONT_REPARSE flag here. This
122 * is a relative new addition (windows 10, build unknown), so if it ain't
123 * supported or we encounter a reparse object we just fall back to the regular
124 * deletion code.
125 */
126 static int volatile s_iSupportsDontReparse = 0;
127 if (s_iSupportsDontReparse >= 0 && !fRmDir)
128 {
129 MY_OBJECT_ATTRIBUTES ObjAttr;
130 MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_DONT_REPARSE | OBJ_CASE_INSENSITIVE, hRoot, NULL /*pSecAttr*/);
131 rcNt = g_pfnNtDeleteFile(&ObjAttr);
132
133 /* In case some file system does things differently than NTFS. */
134 if (rcNt == STATUS_CANNOT_DELETE && fReadOnlyToo)
135 {
136 birdMakeWritable(hRoot, &NtPath);
137 rcNt = g_pfnNtDeleteFile(&ObjAttr);
138 }
139
140 /* Do fallback. */
141 if (rcNt == STATUS_REPARSE_POINT_ENCOUNTERED)
142 fFast = 0;
143 else if (rcNt == STATUS_INVALID_PARAMETER)
144 {
145 s_iSupportsDontReparse = -1;
146 fFast = 0;
147 }
148 }
149 else
150 {
151 rcNt = STATUS_INVALID_PARAMETER;
152 fFast = 0;
153 }
154 }
155
156 if (!fFast)
157 {
158 /*
159 * Use the set information stuff. Here we can better control reparsing.
160 */
161 HANDLE hFile;
162 for (;;)
163 {
164 MY_IO_STATUS_BLOCK Ios;
165
166 rcNt = birdOpenFileUniStr(hRoot,
167 &NtPath,
168 DELETE | SYNCHRONIZE,
169 FILE_ATTRIBUTE_NORMAL,
170 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
171 FILE_OPEN,
172 (!fRmDir ? FILE_NON_DIRECTORY_FILE : FILE_DIRECTORY_FILE)
173 | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
174 OBJ_CASE_INSENSITIVE,
175 &hFile);
176
177 /* Windows distinguishes between symlinks to directories and to files, so
178 unlink(symlink-dir) will fail and we have to retry w/o the non-dir-file
179 flag and make sure it didn't turn into a pure directory. */
180 if ( rcNt == STATUS_FILE_IS_A_DIRECTORY
181 && !fRmDir)
182 {
183 rcNt = birdOpenFileUniStr(hRoot,
184 &NtPath,
185 DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
186 FILE_ATTRIBUTE_REPARSE_POINT,
187 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
188 FILE_OPEN,
189 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
190 OBJ_CASE_INSENSITIVE,
191 &hFile);
192 if (MY_NT_SUCCESS(rcNt))
193 {
194 MY_FILE_BASIC_INFORMATION BasicInfo;
195 memset(&BasicInfo, 0, sizeof(BasicInfo));
196
197 Ios.Information = -1;
198 Ios.u.Status = -1;
199
200 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
201 if ( !MY_NT_SUCCESS(rcNt)
202 || !MY_NT_SUCCESS(Ios.u.Status)
203 || (BasicInfo.FileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
204 == FILE_ATTRIBUTE_DIRECTORY)
205 {
206 birdCloseFile(hFile);
207 rcNt = STATUS_FILE_IS_A_DIRECTORY;
208 break;
209 }
210 }
211 }
212
213 if (MY_NT_SUCCESS(rcNt))
214 {
215 MY_FILE_DISPOSITION_INFORMATION DispInfo;
216 DispInfo.DeleteFile = TRUE;
217
218 Ios.Information = -1;
219 Ios.u.Status = -1;
220
221 rcNt = g_pfnNtSetInformationFile(hFile, &Ios, &DispInfo, sizeof(DispInfo), MyFileDispositionInformation);
222
223 birdCloseFile(hFile);
224 }
225 if (rcNt != STATUS_CANNOT_DELETE || !fReadOnlyToo)
226 break;
227
228 fReadOnlyToo = 0;
229 birdMakeWritable(hRoot, &NtPath);
230 }
231 }
232
233 birdFreeNtPath(&NtPath);
234
235 if (MY_NT_SUCCESS(rcNt))
236 rc = 0;
237 else if (rcNt == STATUS_SHARING_VIOLATION && fRmDir)
238 rc = birdSetErrnoToDirNotEmpty(); /* probably the case... */
239 else
240 rc = birdSetErrnoFromNt(rcNt);
241 }
242 return rc;
243}
244
245
246/*
247 * unlink:
248 */
249
250int birdUnlink(const char *pszFile)
251{
252 return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
253}
254
255
256int birdUnlinkW(const wchar_t *pwszFile)
257{
258 return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
259}
260
261
262int birdUnlinkEx(void *hRoot, const char *pszFile)
263{
264 return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
265}
266
267
268int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile)
269{
270 return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
271}
272
273
274int birdUnlinkForced(const char *pszFile)
275{
276 return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
277}
278
279
280int birdUnlinkForcedW(const wchar_t *pwszFile)
281{
282 return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
283}
284
285
286int birdUnlinkForcedEx(void *hRoot, const char *pszFile)
287{
288 return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
289}
290
291
292int birdUnlinkForcedExW(void *hRoot, const wchar_t *pwszFile)
293{
294 return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
295}
296
297
298int birdUnlinkForcedFast(const char *pszFile)
299{
300 return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
301}
302
303
304int birdUnlinkForcedFastW(const wchar_t *pwszFile)
305{
306 return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
307}
308
309
310int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile)
311{
312 return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
313}
314
315
316int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile)
317{
318 return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
319}
320
321
322/*
323 * rmdir
324 */
325
326int birdRmDir(const char *pszFile)
327{
328 return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
329}
330
331
332int birdRmDirW(const wchar_t *pwszFile)
333{
334 return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
335}
336
337
338int birdRmDirEx(void *hRoot, const char *pszFile)
339{
340 return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
341}
342
343
344int birdRmDirExW(void *hRoot, const wchar_t *pwszFile)
345{
346 return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
347}
348
349
350int birdRmDirForced(const char *pszFile)
351{
352 return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
353}
354
355
356int birdRmDirForcedW(const wchar_t *pwszFile)
357{
358 return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
359}
360
361
362int birdRmDirForcedEx(void *hRoot, const char *pszFile)
363{
364 return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
365}
366
367
368int birdRmDirForcedExW(void *hRoot, const wchar_t *pwszFile)
369{
370 return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 1 /*fRmDir*/);
371}
372
373
374/**
375 * Implements unlinkat().
376 */
377int birdUnlinkAt(int fdDir, const char *pszPath, int fFlags)
378{
379 HANDLE hDirRoot;
380
381 /** @todo validate input. */
382 fFlags &= AT_REMOVEDIR;
383
384 /*
385 * Check the path its effectively a AT_FDCWD call.
386 */
387 if (fdDir != AT_FDCWD)
388 {
389 if (IS_SLASH(pszPath[0]))
390 {
391 if (IS_SLASH(pszPath[1]) && !IS_SLASH(pszPath[2]) && pszPath[2] != '\0')
392 fdDir = AT_FDCWD;
393 }
394 else if (IS_ALPHA(pszPath[0]) && pszPath[1] == ':')
395 {
396 if (IS_SLASH(pszPath[2]))
397 fdDir = AT_FDCWD;
398 else
399 /*
400 * Drive letter relative path like "C:kernel32.dll".
401 * We could try use fdDir as the CWD here if it refers to the same drive,
402 * however that's can be implemented later...
403 */
404 fdDir = AT_FDCWD;
405 }
406 }
407
408 /*
409 * Determine hDirRoot.
410 */
411 if (fdDir == AT_FDCWD)
412 hDirRoot = NULL;
413 else
414 {
415 hDirRoot = (HANDLE)_get_osfhandle(fdDir);
416 if (hDirRoot == INVALID_HANDLE_VALUE || hDirRoot == NULL)
417 return birdSetErrnoToBadFileNo();
418 }
419
420 return birdUnlinkInternal(hDirRoot, pszPath, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, !!fFlags /*fRmDir*/);
421}
422
Note: See TracBrowser for help on using the repository browser.