VirtualBox

Ignore:
Timestamp:
Aug 9, 2024 12:59:18 AM (5 months ago)
Author:
vboxsync
Message:

IPRT/RTPathUnlink: Implemented NT and posix versions w/ simple testcase.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/nt/RTPathUnlink-r3-nt.cpp

    r105630 r105631  
    4242#include "internal-r3-nt.h"
    4343
    44 #include <iprt/file.h>
     44#include <iprt/path.h>
    4545#include <iprt/err.h>
    4646
     
    4848
    4949
    50 RTDECL(int) RTFileDelete(const char *pszFilename)
     50RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
    5151{
     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
    5259    /*
    5360     * Convert and normalize the path.
     
    5562    UNICODE_STRING NtName;
    5663    HANDLE         hRootDir;
    57     int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszFilename);
     64    int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
    5865    if (RT_SUCCESS(rc))
    5966    {
    60         ULONG fUnwantedFileAttribs = FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY;
    61 
    6267        /*
    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.
    7470         */
    7571        HANDLE              hPath   = RTNT_INVALID_HANDLE_VALUE;
     
    7874        InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRootDir, NULL);
    7975
    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;
    8277        NTSTATUS rcNt = NtCreateFile(&hPath,
    83                                      DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
     78                                     DELETE | SYNCHRONIZE,
    8479                                     &ObjAttr,
    8580                                     &Ios,
     
    9186                                     NULL /*EaBuffer*/,
    9287                                     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
    14489        /*
    14590         * Retry w/o the FILE_OPEN_REPARSE_POINT if the file system returns
     
    14792         * grok the reparse point stuff.
    14893         */
    149         else if (rcNt == STATUS_INVALID_PARAMETER)
     94        if (rcNt == STATUS_INVALID_PARAMETER)
    15095        {
    15196            fOpenOptions &= ~FILE_OPEN_REPARSE_POINT;
     
    15398            RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
    15499            rcNt = NtCreateFile(&hPath,
    155                                 DELETE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
     100                                DELETE | SYNCHRONIZE,
    156101                                &ObjAttr,
    157102                                &Ios,
     
    164109                                0 /*EaLength*/);
    165110        }
    166         /* else:
    167            DeleteFileW will retry opening the file w/o FILE_READ_ATTRIBUTES here when
    168            the status code is STATUS_ACCESS_DENIED. We OTOH will not do that as we will
    169            be querying attributes to check that it is a file and not a directory. */
    170 
    171111        if (NT_SUCCESS(rcNt))
    172112        {
    173113            /*
    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.
    182115             */
    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);
    186118            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;
    201120            else
    202121                rc = RTErrConvertFromNtStatus(rcNt);
     
    206125                rc = RTErrConvertFromNtStatus(rcNt);
    207126        }
    208         else if (RT_SUCCESS_NP(rc))
     127        else
    209128            rc = RTErrConvertFromNtStatus(rcNt);
    210129        RTNtPathFree(&NtName, &hRootDir);
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette