Changeset 83434 in vbox
- Timestamp:
- Mar 26, 2020 11:58:18 AM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp
r82968 r83434 83 83 VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE 84 84 } VBOXSERVICETOOLBOXOUTPUTFLAG; 85 86 /** The size of the directory entry buffer we're using. */ 87 #define VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX) 85 88 86 89 … … 121 124 char *pszName; 122 125 } VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY; 123 124 typedef struct VBOXSERVICETOOLBOXDIRENTRY125 {126 /** Our node. */127 RTLISTNODE Node;128 /** The actual entry. */129 RTDIRENTRYEX dirEntry;130 } VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;131 126 132 127 /** ID cache entry. */ … … 845 840 } 846 841 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. 851 844 * 852 845 * @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. 854 849 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG. 855 850 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG. 856 851 * @param pIdCache The ID cache. 857 852 */ 858 static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache) 859 { 860 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER); 853 static 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'); 861 857 862 858 if (fFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) … … 865 861 RTPrintf("%s:\n", pszDir); 866 862 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); 869 874 if (RT_FAILURE(rc)) 870 875 { 871 876 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); 873 878 return rc; 874 879 } 875 880 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); 878 958 if (RT_FAILURE(rc)) 879 959 { 880 960 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 */ 977 static 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); 882 988 return rc; 883 989 } 884 990 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); 987 997 } 988 998 … … 1030 1040 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE; 1031 1041 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))) 1034 1043 { 1035 1044 /* For options that require an argument, ValueUnion has received the value. */ … … 1080 1089 } 1081 1090 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))) 1102 1130 { 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. */ 1146 1133 rc = rc2; 1147 1134 } 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(); 1157 1156 1158 1157 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
Note:
See TracChangeset
for help on using the changeset viewer.