Changeset 2849 in kBuild
- Timestamp:
- Aug 30, 2016 2:28:46 PM (9 years ago)
- Location:
- trunk/src
- Files:
-
- 1 added
- 3 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/w32/pathstuff.c
r2848 r2849 21 21 #include <stdlib.h> 22 22 #include "pathstuff.h" 23 #if 1 /* bird */ 24 # include "nt_fullpath.h" 25 #endif 23 26 24 27 /* … … 92 95 return Path; 93 96 } 94 95 #if 1 /* bird */96 extern void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cchFull);97 #endif98 97 99 98 /* -
trunk/src/lib/Makefile.kmk
r2844 r2849 43 43 kUtil_SOURCES.win = \ 44 44 nt_fullpath.c \ 45 nt_fullpath_cached.c \ 45 46 quote_argv.c \ 46 47 quoted_spawn.c \ -
trunk/src/lib/nt_fullpath.c
r2848 r2849 33 33 #include <ctype.h> 34 34 #include <direct.h> 35 #include <assert.h> 36 37 38 /********************************************************************************************************************************* 39 * Structures and Typedefs * 40 *********************************************************************************************************************************/ 41 typedef struct NTFULLPATHENTRY 42 { 43 /** Pointer to the next entry with the same hash table index. */ 44 struct NTFULLPATHENTRY *pNext; 45 /** The input hash. */ 46 unsigned uHash; 47 /** The input length. */ 48 unsigned cchInput; 49 /** Length of the result. */ 50 unsigned cchResult; 51 /** The result string (stored immediately after this structure). */ 52 const char *pszResult; 53 /** The input string (variable length). */ 54 char szInput[1]; 55 } NTFULLPATHENTRY; 56 typedef NTFULLPATHENTRY *PNTFULLPATHENTRY; 57 58 59 /********************************************************************************************************************************* 60 * Global Variables * 61 *********************************************************************************************************************************/ 62 /** Number of result in the nt_fullpath cache. */ 63 size_t g_cNtFullPathHashEntries = 0; 64 /** Number of bytes used for nt_fullpath cache result entries. */ 65 size_t g_cbNtFullPathHashEntries = 0; 66 /** Number of hash table collsioins in the nt_fullpath cache. */ 67 size_t g_cNtFullPathHashCollisions = 0; 68 /** Hash table. */ 69 PNTFULLPATHENTRY g_apNtFullPathHashTab[16381]; 35 36 #include "nt_fullpath.h" 70 37 71 38 … … 602 569 } 603 570 604 605 606 /**607 * A nt_fullpath frontend which caches the result of previous calls.608 */609 void610 nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cchFull)611 {612 PNTFULLPATHENTRY pEntry;613 unsigned cchInput;614 unsigned idx;615 unsigned cchResult;616 617 /* We use the sdbm hash algorithm here (see kDep.c for full details). */618 unsigned const char *puch = (unsigned const char *)pszPath;619 unsigned uHash = 0;620 unsigned uChar;621 while ((uChar = *puch++) != 0)622 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;623 624 cchInput = (unsigned)((uintptr_t)&puch[-1] - (uintptr_t)pszPath);625 626 /* Do the cache lookup. */627 idx = uHash % (sizeof(g_apNtFullPathHashTab) / sizeof(g_apNtFullPathHashTab[0]));628 for (pEntry = g_apNtFullPathHashTab[idx]; pEntry != NULL; pEntry = pEntry->pNext)629 if ( pEntry->uHash == uHash630 && pEntry->cchInput == cchInput631 && memcmp(pEntry->szInput, pszPath, cchInput) == 0)632 {633 if (cchFull > pEntry->cchResult)634 memcpy(pszFull, pEntry->pszResult, pEntry->cchResult + 1);635 else636 {637 assert(0);638 memcpy(pszFull, pEntry->pszResult, cchFull);639 pszFull[cchFull - 1] = '\0';640 }641 return;642 }643 644 /* Make the call... */645 nt_fullpath(pszPath, pszFull, cchFull);646 647 /* ... and cache the result. */648 cchResult = (unsigned)strlen(pszFull);649 pEntry = malloc(sizeof(*pEntry) + cchInput + cchResult + 1);650 if (pEntry)651 {652 g_cbNtFullPathHashEntries += sizeof(*pEntry) + cchInput + cchResult + 1;653 pEntry->cchInput = cchInput;654 pEntry->cchResult = cchResult;655 pEntry->pszResult = &pEntry->szInput[cchInput + 1];656 pEntry->uHash = uHash;657 memcpy(pEntry->szInput, pszPath, cchInput + 1);658 memcpy((char *)pEntry->pszResult, pszFull, cchResult + 1);659 660 pEntry->pNext = g_apNtFullPathHashTab[idx];661 if (pEntry->pNext)662 g_cNtFullPathHashCollisions++;663 g_apNtFullPathHashTab[idx] = pEntry;664 665 g_cNtFullPathHashEntries++;666 }667 }668 -
trunk/src/lib/nt_fullpath_cached.c
r2848 r2849 35 35 #include <assert.h> 36 36 37 #include "nt_fullpath.h" 38 37 39 38 40 /********************************************************************************************************************************* … … 68 70 /** Hash table. */ 69 71 PNTFULLPATHENTRY g_apNtFullPathHashTab[16381]; 70 71 72 /*73 * Corrects the case of a path.74 * Expects a fullpath!75 * Added by bird for the $(abspath ) function and w32ify76 */77 static void w32_fixcase(char *pszPath)78 {79 static char s_szLast[260];80 size_t cchLast;81 82 #ifndef NDEBUG83 # define my_assert(expr) \84 do { \85 if (!(expr)) { \86 printf("my_assert: %s, file %s, line %d\npszPath=%s\npsz=%s\n", \87 #expr, __FILE__, __LINE__, pszPath, psz); \88 __debugbreak(); \89 exit(1); \90 } \91 } while (0)92 #else93 # define my_assert(expr) do {} while (0)94 #endif95 96 char *psz = pszPath;97 if (*psz == '/' || *psz == '\\')98 {99 if (psz[1] == '/' || psz[1] == '\\')100 {101 /* UNC */102 my_assert(psz[1] == '/' || psz[1] == '\\');103 my_assert(psz[2] != '/' && psz[2] != '\\');104 105 /* skip server name */106 psz += 2;107 while (*psz != '\\' && *psz != '/')108 {109 if (!*psz)110 return;111 *psz++ = toupper(*psz);112 }113 114 /* skip the share name */115 psz++;116 my_assert(*psz != '/' && *psz != '\\');117 while (*psz != '\\' && *psz != '/')118 {119 if (!*psz)120 return;121 *psz++ = toupper(*psz);122 }123 my_assert(*psz == '/' || *psz == '\\');124 psz++;125 }126 else127 {128 /* Unix spec */129 psz++;130 }131 }132 else133 {134 /* Drive letter */135 my_assert(psz[1] == ':');136 *psz = toupper(*psz);137 my_assert(psz[0] >= 'A' && psz[0] <= 'Z');138 my_assert(psz[2] == '/' || psz[2] == '\\');139 psz += 3;140 }141 142 /*143 * Try make use of the result from the previous call.144 * This is ignorant to slashes and similar, but may help even so.145 */146 if ( s_szLast[0] == pszPath[0]147 && (psz - pszPath == 1 || s_szLast[1] == pszPath[1])148 && (psz - pszPath <= 2 || s_szLast[2] == pszPath[2])149 )150 {151 char *pszLast = &s_szLast[psz - pszPath];152 char *pszCur = psz;153 char *pszSrc0 = pszLast;154 char *pszDst0 = pszCur;155 for (;;)156 {157 const char ch1 = *pszCur;158 const char ch2 = *pszLast;159 if ( ch1 != ch2160 && (ch1 != '\\' || ch2 != '/')161 && (ch1 != '/' || ch2 != '\\')162 && tolower(ch1) != tolower(ch2)163 && toupper(ch1) != toupper(ch2))164 break;165 if (ch1 == '/' || ch1 == '\\')166 {167 psz = pszCur + 1;168 *pszLast = ch1; /* preserve the slashes */169 }170 else if (ch1 == '\0')171 {172 psz = pszCur;173 break;174 }175 pszCur++;176 pszLast++;177 }178 if (psz != pszDst0)179 memcpy(pszDst0, pszSrc0, psz - pszDst0);180 }181 182 /*183 * Pointing to the first char after the unc or drive specifier,184 * or in case of a cache hit, the first non-matching char (following a slash of course).185 */186 while (*psz)187 {188 WIN32_FIND_DATA FindFileData;189 HANDLE hDir;190 char chSaved0;191 char chSaved1;192 char *pszEnd;193 int iLongNameDiff;194 size_t cch;195 196 197 /* find the end of the component. */198 pszEnd = psz;199 while (*pszEnd && *pszEnd != '/' && *pszEnd != '\\')200 pszEnd++;201 cch = pszEnd - psz;202 203 /* replace the end with "?\0" */204 chSaved0 = pszEnd[0];205 chSaved1 = pszEnd[1];206 pszEnd[0] = '?';207 pszEnd[1] = '\0';208 209 /* find the right filename. */210 hDir = FindFirstFile(pszPath, &FindFileData);211 pszEnd[1] = chSaved1;212 if (!hDir)213 {214 cchLast = psz - pszPath;215 memcpy(s_szLast, pszPath, cchLast + 1);216 s_szLast[cchLast + 1] = '\0';217 pszEnd[0] = chSaved0;218 return;219 }220 pszEnd[0] = '\0';221 while ( (iLongNameDiff = stricmp(FindFileData.cFileName, psz))222 && stricmp(FindFileData.cAlternateFileName, psz))223 {224 if (!FindNextFile(hDir, &FindFileData))225 {226 cchLast = psz - pszPath;227 memcpy(s_szLast, pszPath, cchLast + 1);228 s_szLast[cchLast + 1] = '\0';229 pszEnd[0] = chSaved0;230 return;231 }232 }233 pszEnd[0] = chSaved0;234 if ( iLongNameDiff /* matched the short name */235 || !FindFileData.cAlternateFileName[0] /* no short name */236 || !memchr(psz, ' ', cch)) /* no spaces in the matching name */237 memcpy(psz, !iLongNameDiff ? FindFileData.cFileName : FindFileData.cAlternateFileName, cch);238 else239 {240 /* replace spacy name with the short name. */241 const size_t cchAlt = strlen(FindFileData.cAlternateFileName);242 const size_t cchDelta = cch - cchAlt;243 my_assert(cchAlt > 0);244 if (!cchDelta)245 memcpy(psz, FindFileData.cAlternateFileName, cch);246 else247 {248 size_t cbLeft = strlen(pszEnd) + 1;249 if ((psz - pszPath) + cbLeft + cchAlt <= _MAX_PATH)250 {251 memmove(psz + cchAlt, pszEnd, cbLeft);252 pszEnd -= cchDelta;253 memcpy(psz, FindFileData.cAlternateFileName, cchAlt);254 }255 else256 fprintf(stderr, "kBuild: case & space fixed filename is growing too long (%d bytes)! '%s'\n",257 (psz - pszPath) + cbLeft + cchAlt, pszPath);258 }259 }260 my_assert(pszEnd[0] == chSaved0);261 FindClose(hDir);262 263 /* advance to the next component */264 if (!chSaved0)265 {266 psz = pszEnd;267 break;268 }269 psz = pszEnd + 1;270 my_assert(*psz != '/' && *psz != '\\');271 }272 273 /* *psz == '\0', the end. */274 cchLast = psz - pszPath;275 memcpy(s_szLast, pszPath, cchLast + 1);276 #undef my_assert277 }278 279 #define MY_FileNameInformation 9280 typedef struct _MY_FILE_NAME_INFORMATION281 {282 ULONG FileNameLength;283 WCHAR FileName[1];284 } MY_FILE_NAME_INFORMATION, *PMY_FILE_NAME_INFORMATION;285 286 #define MY_FileInternalInformation 6287 typedef struct _MY_FILE_INTERNAL_INFORMATION {288 LARGE_INTEGER IndexNumber;289 } MY_FILE_INTERNAL_INFORMATION, *PMY_FILE_INTERNAL_INFORMATION;290 291 #define MY_FileFsVolumeInformation 1292 typedef struct _MY_FILE_FS_VOLUME_INFORMATION293 {294 LARGE_INTEGER VolumeCreationTime;295 ULONG VolumeSerialNumber;296 ULONG VolumeLabelLength;297 BOOLEAN SupportsObjects;298 WCHAR VolumeLabel[/*1*/128];299 } MY_FILE_FS_VOLUME_INFORMATION, *PMY_FILE_FS_VOLUME_INFORMATION;300 301 #define MY_FileFsAttributeInformation 5302 typedef struct _MY_FILE_FS_ATTRIBUTE_INFORMATION303 {304 ULONG FileSystemAttributes;305 LONG MaximumComponentNameLength;306 ULONG FileSystemNameLength;307 WCHAR FileSystemName[/*1*/64];308 } MY_FILE_FS_ATTRIBUTE_INFORMATION, *PMY_FILE_FS_ATTRIBUTE_INFORMATION;309 310 #define MY_FileFsDeviceInformation 4311 typedef struct MY_FILE_FS_DEVICE_INFORMATION312 {313 ULONG DeviceType;314 ULONG Characteristics;315 } MY_FILE_FS_DEVICE_INFORMATION, *PMY_FILE_FS_DEVICE_INFORMATION;316 #define MY_FILE_DEVICE_DISK 7317 #define MY_FILE_DEVICE_DISK_FILE_SYSTEM 8318 #define MY_FILE_DEVICE_FILE_SYSTEM 9319 #define MY_FILE_DEVICE_VIRTUAL_DISK 36320 321 322 typedef struct323 {324 union325 {326 LONG Status;327 PVOID Pointer;328 };329 ULONG_PTR Information;330 } MY_IO_STATUS_BLOCK, *PMY_IO_STATUS_BLOCK;331 332 static BOOL g_fInitialized = FALSE;333 static int g_afNtfsDrives['Z' - 'A' + 1];334 static MY_FILE_FS_VOLUME_INFORMATION g_aVolumeInfo['Z' - 'A' + 1];335 336 static LONG (NTAPI *g_pfnNtQueryInformationFile)(HANDLE FileHandle,337 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,338 ULONG Length, ULONG FileInformationClass);339 static LONG (NTAPI *g_pfnNtQueryVolumeInformationFile)(HANDLE FileHandle,340 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FsInformation,341 ULONG Length, ULONG FsInformationClass);342 343 344 int345 nt_get_filename_info(const char *pszPath, char *pszFull, size_t cchFull)346 {347 static char abBuf[8192];348 PMY_FILE_NAME_INFORMATION pFileNameInfo = (PMY_FILE_NAME_INFORMATION)abBuf;349 PMY_FILE_FS_VOLUME_INFORMATION pFsVolInfo = (PMY_FILE_FS_VOLUME_INFORMATION)abBuf;350 MY_IO_STATUS_BLOCK Ios;351 LONG rcNt;352 HANDLE hFile;353 int cchOut;354 char *psz;355 int iDrv;356 int rc;357 358 /*359 * Check for NtQueryInformationFile the first time around.360 */361 if (!g_fInitialized)362 {363 g_fInitialized = TRUE;364 if (!getenv("KMK_DONT_USE_NT_QUERY_INFORMATION_FILE"))365 {366 *(FARPROC *)&g_pfnNtQueryInformationFile =367 GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryInformationFile");368 *(FARPROC *)&g_pfnNtQueryVolumeInformationFile =369 GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryVolumeInformationFile");370 }371 if ( g_pfnNtQueryInformationFile372 && g_pfnNtQueryVolumeInformationFile)373 {374 unsigned i;375 for (i = 0; i < sizeof(g_afNtfsDrives) / sizeof(g_afNtfsDrives[0]); i++ )376 g_afNtfsDrives[i] = -1;377 }378 else379 {380 g_pfnNtQueryVolumeInformationFile = NULL;381 g_pfnNtQueryInformationFile = NULL;382 }383 }384 if (!g_pfnNtQueryInformationFile)385 return -1;386 387 /*388 * The FileNameInformation we get is relative to where the volume is mounted,389 * so we have to extract the driveletter prefix ourselves.390 *391 * FIXME: This will probably not work for volumes mounted in NTFS sub-directories.392 */393 psz = pszFull;394 if (pszPath[0] == '\\' || pszPath[0] == '/')395 {396 /* unc or root of volume */397 if ( (pszPath[1] == '\\' || pszPath[1] == '/')398 && (pszPath[2] != '\\' || pszPath[2] == '/'))399 {400 #if 0 /* don't bother with unc yet. */401 /* unc - we get the server + name back */402 *psz++ = '\\';403 #endif404 return -1;405 }406 /* root slash */407 *psz++ = _getdrive() + 'A' - 1;408 *psz++ = ':';409 }410 else if (pszPath[1] == ':' && isalpha(pszPath[0]))411 {412 /* drive letter */413 *psz++ = toupper(pszPath[0]);414 *psz++ = ':';415 }416 else417 {418 /* relative */419 *psz++ = _getdrive() + 'A' - 1;420 *psz++ = ':';421 }422 iDrv = *pszFull - 'A';423 424 /*425 * Fat32 doesn't return filenames with the correct case, so restrict it426 * to NTFS volumes for now.427 */428 if (g_afNtfsDrives[iDrv] == -1)429 {430 /* FSCTL_GET_REPARSE_POINT? Enumerate mount points? */431 g_afNtfsDrives[iDrv] = 0;432 psz[0] = '\\';433 psz[1] = '\0';434 #if 1435 hFile = CreateFile(pszFull,436 GENERIC_READ,437 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,438 NULL,439 OPEN_EXISTING,440 FILE_FLAG_BACKUP_SEMANTICS,441 NULL);442 if (hFile != INVALID_HANDLE_VALUE)443 {444 PMY_FILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PMY_FILE_FS_ATTRIBUTE_INFORMATION)abBuf;445 446 memset(&Ios, 0, sizeof(Ios));447 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, abBuf, sizeof(abBuf),448 MY_FileFsAttributeInformation);449 if ( rcNt >= 0450 //&& pFsAttrInfo->FileSystemNameLength == 4451 && pFsAttrInfo->FileSystemName[0] == 'N'452 && pFsAttrInfo->FileSystemName[1] == 'T'453 && pFsAttrInfo->FileSystemName[2] == 'F'454 && pFsAttrInfo->FileSystemName[3] == 'S'455 && pFsAttrInfo->FileSystemName[4] == '\0')456 {457 memset(&Ios, 0, sizeof(Ios));458 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, &g_aVolumeInfo[iDrv],459 sizeof(MY_FILE_FS_VOLUME_INFORMATION),460 MY_FileFsVolumeInformation);461 if (rcNt >= 0)462 {463 DWORD dwDriveType = GetDriveType(pszFull);464 if ( dwDriveType == DRIVE_FIXED465 || dwDriveType == DRIVE_RAMDISK)466 g_afNtfsDrives[iDrv] = 1;467 }468 }469 CloseHandle(hFile);470 }471 #else472 {473 char szFSName[32];474 if ( GetVolumeInformation(pszFull,475 NULL, 0, /* volume name */476 NULL, /* serial number */477 NULL, /* max component */478 NULL, /* volume attribs */479 szFSName,480 sizeof(szFSName))481 && !strcmp(szFSName, "NTFS"))482 {483 g_afNtfsDrives[iDrv] = 1;484 }485 }486 #endif487 }488 if (!g_afNtfsDrives[iDrv])489 return -1;490 491 /*492 * Try open the path and query its file name information.493 */494 hFile = CreateFile(pszPath,495 GENERIC_READ,496 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,497 NULL,498 OPEN_EXISTING,499 FILE_FLAG_BACKUP_SEMANTICS,500 NULL);501 if (hFile != INVALID_HANDLE_VALUE)502 {503 /* check that the driver letter is correct first (reparse / symlink issues). */504 memset(&Ios, 0, sizeof(Ios));505 rcNt = g_pfnNtQueryVolumeInformationFile(hFile, &Ios, pFsVolInfo, sizeof(*pFsVolInfo), MY_FileFsVolumeInformation);506 if (rcNt >= 0)507 {508 /** @todo do a quick search and try correct the drive letter? */509 if ( pFsVolInfo->VolumeCreationTime.QuadPart == g_aVolumeInfo[iDrv].VolumeCreationTime.QuadPart510 && pFsVolInfo->VolumeSerialNumber == g_aVolumeInfo[iDrv].VolumeSerialNumber)511 {512 memset(&Ios, 0, sizeof(Ios));513 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, abBuf, sizeof(abBuf), MY_FileNameInformation);514 if (rcNt >= 0)515 {516 cchOut = WideCharToMultiByte(CP_ACP, 0,517 pFileNameInfo->FileName, pFileNameInfo->FileNameLength / sizeof(WCHAR),518 psz, (int)(cchFull - (psz - pszFull) - 2), NULL, NULL);519 if (cchOut > 0)520 {521 const char *pszEnd;522 #if 0523 /* upper case the server and share */524 if (fUnc)525 {526 for (psz++; *psz != '/' && *psz != '\\'; psz++)527 *psz = toupper(*psz);528 for (psz++; *psz != '/' && *psz != '\\'; psz++)529 *psz = toupper(*psz);530 }531 #endif532 /* add trailing slash on directories if input has it. */533 pszEnd = strchr(pszPath, '\0');534 if ( (pszEnd[-1] == '/' || pszEnd[-1] == '\\')535 && psz[cchOut - 1] != '\\'536 && psz[cchOut - 1] != '//')537 psz[cchOut++] = '\\';538 539 /* make sure it's terminated */540 psz[cchOut] = '\0';541 rc = 0;542 }543 else544 rc = -3;545 }546 else547 rc = -4;548 }549 else550 rc = -5;551 }552 else553 rc = -6;554 CloseHandle(hFile);555 }556 else557 rc = -7;558 return rc;559 }560 561 /**562 * Somewhat similar to fullpath, except that it will fix563 * the case of existing path components.564 */565 void566 nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull)567 {568 #if 0569 static int s_cHits = 0;570 static int s_cFallbacks = 0;571 #endif572 573 /*574 * The simple case, the file / dir / whatever exists and can be575 * queried without problems and spaces.576 */577 if (nt_get_filename_info(pszPath, pszFull, cchFull) == 0)578 {579 /** @todo make nt_get_filename_info return spaceless path. */580 if (strchr(pszFull, ' '))581 w32_fixcase(pszFull);582 #if 0583 fprintf(stdout, "nt #%d - %s\n", ++s_cHits, pszFull);584 fprintf(stdout, " #%d - %s\n", s_cHits, pszPath);585 #endif586 return;587 }588 if (g_pfnNtQueryInformationFile)589 {590 /* do _fullpath and drop off path elements until we get a hit... - later */591 }592 593 /*594 * For now, simply fall back on the old method.595 */596 _fullpath(pszFull, pszPath, cchFull);597 w32_fixcase(pszFull);598 #if 0599 fprintf(stderr, "fb #%d - %s\n", ++s_cFallbacks, pszFull);600 fprintf(stderr, " #%d - %s\n", s_cFallbacks, pszPath);601 #endif602 }603 604 72 605 73
Note:
See TracChangeset
for help on using the changeset viewer.