VirtualBox

Ignore:
Timestamp:
Mar 26, 2020 11:58:18 AM (5 years ago)
Author:
vboxsync
Message:

Guest Control/VBoxServiceToolbox: Resolved several @todos for the ls tool (rewritten ls directory traversing, ++). bugref:9320

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp

    r82968 r83434  
    8383    VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE
    8484} VBOXSERVICETOOLBOXOUTPUTFLAG;
     85
     86/** The size of the directory entry buffer we're using. */
     87#define VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
    8588
    8689
     
    121124    char       *pszName;
    122125} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
    123 
    124 typedef struct VBOXSERVICETOOLBOXDIRENTRY
    125 {
    126     /** Our node. */
    127     RTLISTNODE   Node;
    128     /** The actual entry. */
    129     RTDIRENTRYEX dirEntry;
    130 } VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;
    131126
    132127/** ID cache entry. */
     
    845840}
    846841
    847 
    848 /**
    849  * Helper routine for ls tool doing the actual parsing and output of
    850  * a specified directory.
     842/**
     843 * Helper routine for ls tool for handling sub directories.
    851844 *
    852845 * @return  IPRT status code.
    853  * @param   pszDir          Directory (path) to ouptut.
     846 * @param   pszDir          Pointer to the directory buffer.
     847 * @param   cchDir          The length of pszDir in pszDir.
     848 * @param   pDirEntry       Pointer to the directory entry.
    854849 * @param   fFlags          Flags of type VBOXSERVICETOOLBOXLSFLAG.
    855850 * @param   fOutputFlags    Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
    856851 * @param   pIdCache        The ID cache.
    857852 */
    858 static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
    859 {
    860     AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
     853static int vgsvcToolboxLsHandleDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
     854                                      uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
     855{
     856    Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
    861857
    862858    if (fFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
     
    865861        RTPrintf("%s:\n", pszDir);
    866862
    867     char szPathAbs[RTPATH_MAX + 1];
    868     int rc = RTPathAbs(pszDir, szPathAbs, sizeof(szPathAbs));
     863    /* Make sure we've got some room in the path, to save us extra work further down. */
     864    if (cchDir + 3 >= RTPATH_MAX)
     865    {
     866        if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     867            RTMsgError("Path too long: '%s'\n", pszDir);
     868        return VERR_BUFFER_OVERFLOW;
     869    }
     870
     871    /* Open directory. */
     872    RTDIR hDir;
     873    int rc = RTDirOpen(&hDir, pszDir);
    869874    if (RT_FAILURE(rc))
    870875    {
    871876        if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
    872             RTMsgError("Failed to retrieve absolute path of '%s', rc=%Rrc\n", pszDir, rc);
     877            RTMsgError("Failed to open directory '%s', rc=%Rrc\n", pszDir, rc);
    873878        return rc;
    874879    }
    875880
    876     RTDIR hDir;
    877     rc = RTDirOpen(&hDir, szPathAbs);
     881    /* Ensure we've got a trailing slash (there is space for it see above). */
     882    if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
     883    {
     884        pszDir[cchDir++] = RTPATH_SLASH;
     885        pszDir[cchDir]   = '\0';
     886    }
     887
     888    /*
     889     * Process the files and subdirs.
     890     */
     891    for (;;)
     892    {
     893        /* Get the next directory. */
     894        size_t cbDirEntry = VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE;
     895        rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
     896        if (RT_FAILURE(rc))
     897            break;
     898
     899        /* Skip the dot and dot-dot links. */
     900        if (RTDirEntryExIsStdDotLink(pDirEntry))
     901            continue;
     902
     903        /* Check length. */
     904        if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
     905        {
     906            if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     907                RTMsgError("Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
     908            rc = VERR_BUFFER_OVERFLOW;
     909            break;
     910        }
     911
     912        switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
     913        {
     914            case RTFS_TYPE_SYMLINK:
     915            {
     916                if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
     917                    break;
     918                RT_FALL_THRU();
     919            }
     920            case RTFS_TYPE_DIRECTORY:
     921            {
     922                if (RTDirEntryExIsStdDotLink(pDirEntry))
     923                    continue;
     924
     925                if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
     926                    continue;
     927
     928                memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
     929                int rc2 = vgsvcToolboxLsHandleDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags, fOutputFlags, pIdCache);
     930                if (RT_SUCCESS(rc))
     931                    rc = rc2;
     932                break;
     933            }
     934
     935            case RTFS_TYPE_FILE:
     936            {
     937                rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
     938                                             pIdCache, &pDirEntry->Info);
     939                break;
     940            }
     941
     942            default:
     943            {
     944                if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     945                    RTMsgError("Entry '%.*s%s' of mode %#x not supported, skipping",
     946                               cchDir, pszDir, pDirEntry->szName, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK);
     947                break;
     948            }
     949        }
     950    }
     951    if (rc != VERR_NO_MORE_FILES)
     952    {
     953        if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     954            RTMsgError("RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
     955    }
     956
     957    rc = RTDirClose(hDir);
    878958    if (RT_FAILURE(rc))
    879959    {
    880960        if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
    881             RTMsgError("Failed to open directory '%s', rc=%Rrc\n", szPathAbs, rc);
     961            RTMsgError("RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
     962    }
     963
     964    return rc;
     965}
     966
     967/**
     968 * Helper routine for ls tool doing the actual parsing and output of
     969 * a specified directory.
     970 *
     971 * @return  IPRT status code.
     972 * @param   pszDir          Absolute path to directory to ouptut.
     973 * @param   fFlags          Flags of type VBOXSERVICETOOLBOXLSFLAG.
     974 * @param   fOutputFlags    Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
     975 * @param   pIdCache        The ID cache.
     976 */
     977static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
     978{
     979    AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
     980    AssertPtrReturn(pIdCache, VERR_INVALID_PARAMETER);
     981
     982    char szPath[RTPATH_MAX];
     983    int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
     984    if (RT_FAILURE(rc))
     985    {
     986        if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     987            RTMsgError("RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
    882988        return rc;
    883989    }
    884990
    885     RTLISTANCHOR dirList;
    886     RTListInit(&dirList);
    887 
    888     /* To prevent races we need to read in the directory entries once
    889      * and process them afterwards: First loop is displaying the current
    890      * directory's content and second loop is diving deeper into
    891      * sub directories (if wanted). */
    892 /** @todo r=bird: Which races are these exactly???  Please, do considering that directory with half a
    893  * million files in it, because this isn't going to fly well there (especially not in the recursive case)...
    894  * So, this needs to be rewritten unless there is an actual race you're avoiding by doing this! */
    895     do
    896     {
    897         RTDIRENTRYEX DirEntry;
    898         rc = RTDirReadEx(hDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
    899         if (RT_SUCCESS(rc))
    900         {
    901             PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
    902             if (pNode)
    903             {
    904                 memcpy(&pNode->dirEntry, &DirEntry, sizeof(RTDIRENTRYEX));
    905                 RTListAppend(&dirList, &pNode->Node);
    906             }
    907             else
    908                 rc = VERR_NO_MEMORY;
    909         }
    910         /** @todo r=bird: missing DirEntry overflow handling. */
    911     } while (RT_SUCCESS(rc));
    912 
    913     if (rc == VERR_NO_MORE_FILES)
    914         rc = VINF_SUCCESS;
    915 
    916     int rc2 = RTDirClose(hDir);
    917     if (RT_FAILURE(rc2))
    918     {
    919         if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
    920             RTMsgError("Failed to close dir '%s', rc=%Rrc\n", pszDir, rc2);
    921         if (RT_SUCCESS(rc))
    922             rc = rc2;
    923     }
    924 
    925     if (RT_SUCCESS(rc))
    926     {
    927         PVBOXSERVICETOOLBOXDIRENTRY pNodeIt;
    928         RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
    929         {
    930             rc = vgsvcToolboxPrintFsInfo(pNodeIt->dirEntry.szName, pNodeIt->dirEntry.cbName, fOutputFlags,
    931                                          szPathAbs, pIdCache, &pNodeIt->dirEntry.Info);
    932             if (RT_FAILURE(rc))
    933                 break;
    934         }
    935 
    936         /* If everything went fine we do the second run (if needed) ... */
    937         if (   RT_SUCCESS(rc)
    938             && (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
    939         {
    940             /* Process all sub-directories. */
    941             RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
    942             {
    943                 RTFMODE fMode = pNodeIt->dirEntry.Info.Attr.fMode;
    944                 switch (fMode & RTFS_TYPE_MASK)
    945                 {
    946                     case RTFS_TYPE_SYMLINK:
    947                         if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
    948                             break;
    949                         RT_FALL_THRU();
    950                     case RTFS_TYPE_DIRECTORY:
    951                     {
    952                         const char *pszName = pNodeIt->dirEntry.szName;
    953                         if (   !RTStrICmp(pszName, ".")  /** @todo r=bird: Please do explain what the upper/lower casing of  '.' is! I'm really curious. */
    954                             || !RTStrICmp(pszName, "..")) /** @todo r=bird: There is a RTDir API for checking these. Use it! */
    955                         {
    956                             /* Skip dot directories. */
    957                             continue;
    958                         }
    959 
    960                         char szPath[RTPATH_MAX]; /** @todo r=bird: This is going to kill your stack pretty quickly if deep
    961                                                   * directory nesting.  There is another buffer further up the function too.
    962                                                   * You need to share the path buffer between recursions!  There should be
    963                                                   * several examples of how to efficiently traverse a tree. */
    964                         rc = RTPathJoin(szPath, sizeof(szPath), pszDir, pNodeIt->dirEntry.szName);
    965                         if (RT_SUCCESS(rc))
    966                             rc = vgsvcToolboxLsHandleDir(szPath, fFlags, fOutputFlags, pIdCache);
    967                         break;
    968                     }
    969 
    970                     default: /* Ignore the rest. */
    971                         break;
    972                 }
    973                 if (RT_FAILURE(rc))
    974                     break;
    975             }
    976         }
    977     }
    978 
    979     /* Clean up the mess. */
    980     PVBOXSERVICETOOLBOXDIRENTRY pNode, pSafe;
    981     RTListForEachSafe(&dirList, pNode, pSafe, VBOXSERVICETOOLBOXDIRENTRY, Node)
    982     {
    983         RTListNodeRemove(&pNode->Node);
    984         RTMemFree(pNode);
    985     }
    986     return rc;
     991    union
     992    {
     993        uint8_t         abPadding[VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE];
     994        RTDIRENTRYEX    DirEntry;
     995    } uBuf;
     996    return vgsvcToolboxLsHandleDirSub(szPath, strlen(szPath), &uBuf.DirEntry, fFlags, fOutputFlags, pIdCache);
    987997}
    988998
     
    10301040    uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
    10311041
    1032     while (   (ch = RTGetOpt(&GetState, &ValueUnion))
    1033            && RT_SUCCESS(rc) /** @todo r=bird: WTF is this doing here? rc isn't set in the loop!! And there is an AssertRCReturn after the previous place it was set. */)
     1042    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    10341043    {
    10351044        /* For options that require an argument, ValueUnion has received the value. */
     
    10801089    }
    10811090
    1082     if (RT_SUCCESS(rc)) /** @todo r=bird: WTF?!? The state handling here is certifiably insane. Crap like this drives me CRAZY!! */
    1083     {
    1084         /* Print magic/version. */
    1085         if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
    1086         {
    1087             rc = vgsvcToolboxStrmInit();
    1088             if (RT_FAILURE(rc))
    1089                 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
    1090             vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
    1091         }
    1092 
    1093         VGSVCTOOLBOXIDCACHE IdCache;
    1094         RT_ZERO(IdCache);
    1095 
    1096         ch = RTGetOpt(&GetState, &ValueUnion);
    1097         do
    1098         {
    1099             char *pszEntry = NULL; /** @todo r=bird: Bad name choice. pszEntry sounds like RTDIRENTRY::szName, i.e. no path. */
    1100 
    1101             if (ch == 0) /* Use current directory if no element specified. */
     1091    /* Print magic/version. */
     1092    if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
     1093    {
     1094        rc = vgsvcToolboxStrmInit();
     1095        if (RT_FAILURE(rc))
     1096            RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
     1097        vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
     1098    }
     1099
     1100    VGSVCTOOLBOXIDCACHE IdCache;
     1101    RT_ZERO(IdCache);
     1102
     1103    char szDirCur[RTPATH_MAX];
     1104    rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
     1105    if (RT_FAILURE(rc))
     1106    {
     1107        RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
     1108        return RTEXITCODE_FAILURE;
     1109    }
     1110
     1111    ch = RTGetOpt(&GetState, &ValueUnion);
     1112    do
     1113    {
     1114        char const *pszPath;
     1115
     1116        if (ch == 0) /* Use current directory if no element specified. */
     1117            pszPath = szDirCur;
     1118        else
     1119            pszPath = ValueUnion.psz;
     1120
     1121        RTFSOBJINFO objInfo;
     1122        int rc2 = RTPathQueryInfoEx(pszPath, &objInfo,
     1123                                    RTFSOBJATTRADD_UNIX,
     1124                                    fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
     1125        if (RT_SUCCESS(rc2))
     1126        {
     1127            if (   RTFS_IS_FILE(objInfo.Attr.fMode)
     1128                || (   RTFS_IS_SYMLINK(objInfo.Attr.fMode)
     1129                    && (fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS)))
    11021130            {
    1103                 char szDirCur[RTPATH_MAX + 1];  /** @todo r=bird: Just put this outside the if(ch==0) and make pszEntry point to it.  There is no need to duplicate any strings here! */
    1104                 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
    1105                 if (RT_FAILURE(rc))
    1106                     RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
    1107 
    1108                 pszEntry = RTStrDup(szDirCur);
    1109                 if (!pszEntry)
    1110                     RTMsgError("Allocating current directory failed\n");
    1111             }
    1112             else
    1113             {
    1114                 pszEntry = RTStrDup(ValueUnion.psz);
    1115                 if (!pszEntry)
    1116                     RTMsgError("Allocating directory '%s' failed\n", ValueUnion.psz);
    1117             }
    1118 
    1119             /** @todo r=bird: RTFileExists == RTPathQueryInfo, so just do
    1120              *        RTPathQueryInfoEx here!  Also, you _need_ to figure out whether or
    1121              *        not to follow "commandline" links! */
    1122             if (RTFileExists(pszEntry))
    1123             {
    1124                 RTFSOBJINFO objInfo;
    1125                 int rc2 = RTPathQueryInfoEx(pszEntry, &objInfo,
    1126                                             RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK /** @todo Follow link? */);
    1127                 if (RT_FAILURE(rc2))
    1128                 {
    1129                     if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
    1130                         RTMsgError("Cannot access '%s': No such file or directory\n", pszEntry);
    1131                     rc = VERR_FILE_NOT_FOUND;
    1132                     /* Do not break here -- process every element in the list
    1133                      * and keep failing rc. */
    1134                 }
    1135                 else
    1136                 {
    1137                     rc2 = vgsvcToolboxPrintFsInfo(pszEntry, strlen(pszEntry), fOutputFlags, NULL, &IdCache, &objInfo);
    1138                     if (RT_FAILURE(rc2))
    1139                         rc = rc2;
    1140                 }
    1141             }
    1142             else
    1143             {
    1144                 int rc2 = vgsvcToolboxLsHandleDir(pszEntry, fFlags, fOutputFlags, &IdCache);
    1145                 if (RT_FAILURE(rc2))
     1131                rc2 = vgsvcToolboxPrintFsInfo(pszPath, strlen(pszPath), fOutputFlags, NULL, &IdCache, &objInfo);
     1132                if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
    11461133                    rc = rc2;
    11471134            }
    1148 
    1149             RTStrFree(pszEntry);
    1150         } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
    1151 
    1152         if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
    1153             vgsvcToolboxPrintStrmTermination();
    1154     }
    1155     else if (fVerbose)
    1156         RTMsgError("Failed with rc=%Rrc\n", rc);
     1135            else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
     1136            {
     1137                rc2 = vgsvcToolboxLsHandleDir(pszPath, fFlags, fOutputFlags, &IdCache);
     1138                if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
     1139                    rc = rc2;
     1140            }
     1141        }
     1142        else
     1143        {
     1144            if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
     1145                RTMsgError("Cannot access '%s': No such file or directory\n", pszPath);
     1146            if (RT_SUCCESS(rc))
     1147                rc = VERR_FILE_NOT_FOUND;
     1148            /* Do not break here -- process every element in the list
     1149             * and keep failing rc. */
     1150        }
     1151
     1152    } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
     1153
     1154    if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
     1155        vgsvcToolboxPrintStrmTermination();
    11571156
    11581157    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
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