VirtualBox

source: kBuild/trunk/src/lib/nt_fullpath.c@ 2848

Last change on this file since 2848 was 2848, checked in by bird, 9 years ago

nt_fullepath: cache results.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.3 KB
Line 
1/* $Id: nt_fullpath.c 2848 2016-08-30 14:23:59Z bird $ */
2/** @file
3 * fixcase - fixes the case of paths, windows specific.
4 */
5
6/*
7 * Copyright (c) 2004-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include <Windows.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <ctype.h>
34#include <direct.h>
35#include <assert.h>
36
37
38/*********************************************************************************************************************************
39* Structures and Typedefs *
40*********************************************************************************************************************************/
41typedef 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;
56typedef NTFULLPATHENTRY *PNTFULLPATHENTRY;
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62/** Number of result in the nt_fullpath cache. */
63size_t g_cNtFullPathHashEntries = 0;
64/** Number of bytes used for nt_fullpath cache result entries. */
65size_t g_cbNtFullPathHashEntries = 0;
66/** Number of hash table collsioins in the nt_fullpath cache. */
67size_t g_cNtFullPathHashCollisions = 0;
68/** Hash table. */
69PNTFULLPATHENTRY 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 w32ify
76 */
77static void w32_fixcase(char *pszPath)
78{
79 static char s_szLast[260];
80 size_t cchLast;
81
82#ifndef NDEBUG
83# 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#else
93# define my_assert(expr) do {} while (0)
94#endif
95
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 else
127 {
128 /* Unix spec */
129 psz++;
130 }
131 }
132 else
133 {
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 != ch2
160 && (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 else
239 {
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 else
247 {
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 else
256 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_assert
277}
278
279#define MY_FileNameInformation 9
280typedef struct _MY_FILE_NAME_INFORMATION
281{
282 ULONG FileNameLength;
283 WCHAR FileName[1];
284} MY_FILE_NAME_INFORMATION, *PMY_FILE_NAME_INFORMATION;
285
286#define MY_FileInternalInformation 6
287typedef struct _MY_FILE_INTERNAL_INFORMATION {
288 LARGE_INTEGER IndexNumber;
289} MY_FILE_INTERNAL_INFORMATION, *PMY_FILE_INTERNAL_INFORMATION;
290
291#define MY_FileFsVolumeInformation 1
292typedef struct _MY_FILE_FS_VOLUME_INFORMATION
293{
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 5
302typedef struct _MY_FILE_FS_ATTRIBUTE_INFORMATION
303{
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 4
311typedef struct MY_FILE_FS_DEVICE_INFORMATION
312{
313 ULONG DeviceType;
314 ULONG Characteristics;
315} MY_FILE_FS_DEVICE_INFORMATION, *PMY_FILE_FS_DEVICE_INFORMATION;
316#define MY_FILE_DEVICE_DISK 7
317#define MY_FILE_DEVICE_DISK_FILE_SYSTEM 8
318#define MY_FILE_DEVICE_FILE_SYSTEM 9
319#define MY_FILE_DEVICE_VIRTUAL_DISK 36
320
321
322typedef struct
323{
324 union
325 {
326 LONG Status;
327 PVOID Pointer;
328 };
329 ULONG_PTR Information;
330} MY_IO_STATUS_BLOCK, *PMY_IO_STATUS_BLOCK;
331
332static BOOL g_fInitialized = FALSE;
333static int g_afNtfsDrives['Z' - 'A' + 1];
334static MY_FILE_FS_VOLUME_INFORMATION g_aVolumeInfo['Z' - 'A' + 1];
335
336static LONG (NTAPI *g_pfnNtQueryInformationFile)(HANDLE FileHandle,
337 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
338 ULONG Length, ULONG FileInformationClass);
339static LONG (NTAPI *g_pfnNtQueryVolumeInformationFile)(HANDLE FileHandle,
340 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FsInformation,
341 ULONG Length, ULONG FsInformationClass);
342
343
344int
345nt_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_pfnNtQueryInformationFile
372 && 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 else
379 {
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#endif
404 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 else
417 {
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 it
426 * 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 1
435 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 >= 0
450 //&& pFsAttrInfo->FileSystemNameLength == 4
451 && 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_FIXED
465 || dwDriveType == DRIVE_RAMDISK)
466 g_afNtfsDrives[iDrv] = 1;
467 }
468 }
469 CloseHandle(hFile);
470 }
471#else
472 {
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#endif
487 }
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.QuadPart
510 && 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 0
523 /* 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#endif
532 /* 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 else
544 rc = -3;
545 }
546 else
547 rc = -4;
548 }
549 else
550 rc = -5;
551 }
552 else
553 rc = -6;
554 CloseHandle(hFile);
555 }
556 else
557 rc = -7;
558 return rc;
559}
560
561/**
562 * Somewhat similar to fullpath, except that it will fix
563 * the case of existing path components.
564 */
565void
566nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull)
567{
568#if 0
569 static int s_cHits = 0;
570 static int s_cFallbacks = 0;
571#endif
572
573 /*
574 * The simple case, the file / dir / whatever exists and can be
575 * 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 0
583 fprintf(stdout, "nt #%d - %s\n", ++s_cHits, pszFull);
584 fprintf(stdout, " #%d - %s\n", s_cHits, pszPath);
585#endif
586 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 0
599 fprintf(stderr, "fb #%d - %s\n", ++s_cFallbacks, pszFull);
600 fprintf(stderr, " #%d - %s\n", s_cFallbacks, pszPath);
601#endif
602}
603
604
605
606/**
607 * A nt_fullpath frontend which caches the result of previous calls.
608 */
609void
610nt_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 == uHash
630 && pEntry->cchInput == cchInput
631 && memcmp(pEntry->szInput, pszPath, cchInput) == 0)
632 {
633 if (cchFull > pEntry->cchResult)
634 memcpy(pszFull, pEntry->pszResult, pEntry->cchResult + 1);
635 else
636 {
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
Note: See TracBrowser for help on using the repository browser.

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