Ignore:
Timestamp:
Aug 13, 2025, 1:34:19 AM (4 weeks ago)
Author:
bird
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/lib/nt/ntunlink.c

    r3504 r3682  
    3333*   Header Files                                                               *
    3434*******************************************************************************/
     35#include <io.h>  /* for _get_osfhandle */
    3536#include "ntunlink.h"
    3637
    3738#include "ntstuff.h"
    3839#include "nthlp.h"
     40#include "nthlpmisc.h"
    3941
    4042
     
    8284
    8385
    84 static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, const wchar_t *pwszFile, int fReadOnlyToo, int fFast)
     86static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, const wchar_t *pwszFile, int fReadOnlyToo, int fFast, int fRmDir)
    8587{
    8688    MY_UNICODE_STRING   NtPath;
     
    108110        if (fFast)
    109111        {
    110             /* This uses FILE_DELETE_ON_CLOSE. Probably only suitable when in a hurry... */
    111             MY_OBJECT_ATTRIBUTES ObjAttr;
    112             MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, hRoot, NULL /*pSecAttr*/);
    113             rcNt = g_pfnNtDeleteFile(&ObjAttr);
    114 
    115             /* In case some file system does things differently than NTFS. */
    116             if (rcNt == STATUS_CANNOT_DELETE && fReadOnlyToo)
     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)
    117128            {
    118                 birdMakeWritable(hRoot, &NtPath);
     129                MY_OBJECT_ATTRIBUTES ObjAttr;
     130                MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_DONT_REPARSE | OBJ_CASE_INSENSITIVE, hRoot, NULL /*pSecAttr*/);
    119131                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;
    120153            }
    121154        }
    122         else
     155
     156        if (!fFast)
    123157        {
    124             /* Use the set information stuff. Probably more reliable. */
     158            /*
     159             * Use the set information stuff. Here we can better control reparsing.
     160             */
    125161            HANDLE hFile;
    126162            for (;;)
    127163            {
     164                MY_IO_STATUS_BLOCK Ios;
     165
    128166                rcNt = birdOpenFileUniStr(hRoot,
    129167                                          &NtPath,
     
    132170                                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    133171                                          FILE_OPEN,
    134                                           FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
     172                                          (!fRmDir ? FILE_NON_DIRECTORY_FILE : FILE_DIRECTORY_FILE)
     173                                          | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
    135174                                          OBJ_CASE_INSENSITIVE,
    136175                                          &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
    137213                if (MY_NT_SUCCESS(rcNt))
    138214                {
    139215                    MY_FILE_DISPOSITION_INFORMATION DispInfo;
    140                     MY_IO_STATUS_BLOCK              Ios;
    141 
    142216                    DispInfo.DeleteFile = TRUE;
    143217
     
    161235        if (MY_NT_SUCCESS(rcNt))
    162236            rc = 0;
     237        else if (rcNt == STATUS_SHARING_VIOLATION && fRmDir)
     238            rc = birdSetErrnoToDirNotEmpty(); /* probably the case... */
    163239        else
    164240            rc = birdSetErrnoFromNt(rcNt);
     
    168244
    169245
     246/*
     247 * unlink:
     248 */
     249
    170250int birdUnlink(const char *pszFile)
    171251{
    172     return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
     252    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    173253}
    174254
     
    176256int birdUnlinkW(const wchar_t *pwszFile)
    177257{
    178     return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
     258    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    179259}
    180260
     
    182262int birdUnlinkEx(void *hRoot, const char *pszFile)
    183263{
    184     return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
     264    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    185265}
    186266
     
    188268int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile)
    189269{
    190     return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
     270    return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    191271}
    192272
     
    194274int birdUnlinkForced(const char *pszFile)
    195275{
    196     return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
     276    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    197277}
    198278
     
    200280int birdUnlinkForcedW(const wchar_t *pwszFile)
    201281{
    202     return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
     282    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    203283}
    204284
     
    206286int birdUnlinkForcedEx(void *hRoot, const char *pszFile)
    207287{
    208     return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
     288    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    209289}
    210290
     
    212292int birdUnlinkForcedExW(void *hRoot, const wchar_t *pwszFile)
    213293{
    214     return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
     294    return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/, 0 /*fRmDir*/);
    215295}
    216296
     
    218298int birdUnlinkForcedFast(const char *pszFile)
    219299{
    220     return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
     300    return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
    221301}
    222302
     
    224304int birdUnlinkForcedFastW(const wchar_t *pwszFile)
    225305{
    226     return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
     306    return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
    227307}
    228308
     
    230310int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile)
    231311{
    232     return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
     312    return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/, 0 /*fRmDir*/);
    233313}
    234314
     
    236316int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile)
    237317{
    238     return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
    239 }
    240 
     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 TracChangeset for help on using the changeset viewer.