VirtualBox

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

Last change on this file since 1165 was 1165, checked in by bird, 18 years ago

Optimized kDebIDB a bit for Windows; use nt_fullpath and map the IDB file into memory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.9 KB
Line 
1/* $Id: nt_fullpath.c 1165 2007-09-30 07:36:23Z bird $ */
2/** @file
3 * fixcase - fixes the case of paths, windows specific.
4 */
5
6/*
7 * Copyright (c) 2004-2007 knut st. osmundsen <[email protected]>
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 2 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, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 */
26
27#include <Windows.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <ctype.h>
32#include <direct.h>
33
34/*
35 * Corrects the case of a path.
36 * Expects a fullpath!
37 * Added by bird for the $(abspath ) function and w32ify
38 */
39static void w32_fixcase(char *pszPath)
40{
41 static char s_szLast[260];
42 size_t cchLast;
43
44#ifndef NDEBUG
45# define my_assert(expr) \
46 do { \
47 if (!(expr)) { \
48 printf("my_assert: %s, file %s, line %d\npszPath=%s\npsz=%s\n", \
49 #expr, __FILE__, __LINE__, pszPath, psz); \
50 __debugbreak(); \
51 exit(1); \
52 } \
53 } while (0)
54#else
55# define my_assert(expr) do {} while (0)
56#endif
57
58 char *psz = pszPath;
59 if (*psz == '/' || *psz == '\\')
60 {
61 if (psz[1] == '/' || psz[1] == '\\')
62 {
63 /* UNC */
64 my_assert(psz[1] == '/' || psz[1] == '\\');
65 my_assert(psz[2] != '/' && psz[2] != '\\');
66
67 /* skip server name */
68 psz += 2;
69 while (*psz != '\\' && *psz != '/')
70 {
71 if (!*psz)
72 return;
73 *psz++ = toupper(*psz);
74 }
75
76 /* skip the share name */
77 psz++;
78 my_assert(*psz != '/' && *psz != '\\');
79 while (*psz != '\\' && *psz != '/')
80 {
81 if (!*psz)
82 return;
83 *psz++ = toupper(*psz);
84 }
85 my_assert(*psz == '/' || *psz == '\\');
86 psz++;
87 }
88 else
89 {
90 /* Unix spec */
91 psz++;
92 }
93 }
94 else
95 {
96 /* Drive letter */
97 my_assert(psz[1] == ':');
98 *psz = toupper(*psz);
99 my_assert(psz[0] >= 'A' && psz[0] <= 'Z');
100 my_assert(psz[2] == '/' || psz[2] == '\\');
101 psz += 3;
102 }
103
104 /*
105 * Try make use of the result from the previous call.
106 * This is ignorant to slashes and similar, but may help even so.
107 */
108 if ( s_szLast[0] == pszPath[0]
109 && (psz - pszPath == 1 || s_szLast[1] == pszPath[1])
110 && (psz - pszPath <= 2 || s_szLast[2] == pszPath[2])
111 )
112 {
113 char *pszLast = &s_szLast[psz - pszPath];
114 char *pszCur = psz;
115 for (;;)
116 {
117 const char ch1 = *pszCur;
118 const char ch2 = *pszLast;
119 if ( ch1 != ch2
120 && (ch1 != '\\' || ch2 != '/')
121 && (ch1 != '/' || ch2 != '\\'))
122 {
123 if ( tolower(ch1) != tolower(ch2)
124 && toupper(ch1) != toupper(ch2))
125 break;
126 /* optimistic, component mismatch will be corrected in the next loop. */
127 *pszCur = ch2;
128 }
129 if (ch1 == '/' || ch1 == '\\')
130 psz = pszCur + 1;
131 else if (ch1 == '\0')
132 {
133 psz = pszCur;
134 break;
135 }
136 pszCur++;
137 pszLast++;
138 }
139 }
140
141 /*
142 * Pointing to the first char after the unc or drive specifier,
143 * or in case of a cache hit, the first non-matching char (following a slash of course).
144 */
145 while (*psz)
146 {
147 WIN32_FIND_DATA FindFileData;
148 HANDLE hDir;
149 char chSaved0;
150 char chSaved1;
151 char *pszEnd;
152 int iLongNameDiff;
153
154
155 /* find the end of the component. */
156 pszEnd = psz;
157 while (*pszEnd && *pszEnd != '/' && *pszEnd != '\\')
158 pszEnd++;
159
160 /* replace the end with "?\0" */
161 chSaved0 = pszEnd[0];
162 chSaved1 = pszEnd[1];
163 pszEnd[0] = '?';
164 pszEnd[1] = '\0';
165
166 /* find the right filename. */
167 hDir = FindFirstFile(pszPath, &FindFileData);
168 pszEnd[1] = chSaved1;
169 if (!hDir)
170 {
171 cchLast = psz - pszPath;
172 memcpy(s_szLast, pszPath, cchLast + 1);
173 pszEnd[0] = chSaved0;
174 return;
175 }
176 pszEnd[0] = '\0';
177 while ( (iLongNameDiff = stricmp(FindFileData.cFileName, psz))
178 && stricmp(FindFileData.cAlternateFileName, psz))
179 {
180 if (!FindNextFile(hDir, &FindFileData))
181 {
182 cchLast = psz - pszPath;
183 memcpy(s_szLast, pszPath, cchLast + 1);
184 pszEnd[0] = chSaved0;
185 return;
186 }
187 }
188 strcpy(psz, !iLongNameDiff ? FindFileData.cFileName : FindFileData.cAlternateFileName);
189 pszEnd[0] = chSaved0;
190 FindClose(hDir);
191
192 /* advance to the next component */
193 if (!chSaved0)
194 {
195 psz = pszEnd;
196 break;
197 }
198 psz = pszEnd + 1;
199 my_assert(*psz != '/' && *psz != '\\');
200 }
201
202 /* *psz == '\0', the end. */
203 cchLast = psz - pszPath;
204 memcpy(s_szLast, pszPath, cchLast + 1);
205#undef my_assert
206}
207
208#define MY_FileNameInformation 9
209
210typedef struct _MY_FILE_NAME_INFORMATION
211{
212 ULONG FileNameLength;
213 WCHAR FileName[1];
214} MY_FILE_NAME_INFORMATION, *PMY_FILE_NAME_INFORMATION;
215
216typedef struct _IO_STATUS_BLOCK
217{
218 union
219 {
220 LONG Status;
221 PVOID Pointer;
222 };
223 ULONG_PTR Information;
224} MY_IO_STATUS_BLOCK, *PMY_IO_STATUS_BLOCK;
225
226static BOOL g_fInitialized = FALSE;
227static LONG (NTAPI *g_pfnNtQueryInformationFile)(HANDLE FileHandle,
228 PMY_IO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation,
229 ULONG Length, ULONG FileInformationClass);
230
231
232int
233nt_get_filename_info(const char *pszPath, char *pszFull, size_t cchFull)
234{
235 static char abBuf[8192];
236 PMY_FILE_NAME_INFORMATION pFileNameInfo = (PMY_FILE_NAME_INFORMATION)abBuf;
237 MY_IO_STATUS_BLOCK Ios;
238 LONG rcNt;
239 HANDLE hFile;
240
241 /*
242 * Check for NtQueryInformationFile the first time around.
243 */
244 if (!g_fInitialized)
245 {
246 g_fInitialized = TRUE;
247 if (!getenv("KMK_DONT_USE_NT_QUERY_INFORMATION_FILE"))
248 *(FARPROC *)&g_pfnNtQueryInformationFile =
249 GetProcAddress(LoadLibrary("ntdll.dll"), "NtQueryInformationFile");
250 }
251 if (!g_pfnNtQueryInformationFile)
252 return -1;
253
254 /*
255 * Try open the path and query its file name information.
256 */
257 hFile = CreateFile(pszPath,
258 GENERIC_READ,
259 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
260 NULL,
261 OPEN_EXISTING,
262 FILE_FLAG_BACKUP_SEMANTICS,
263 NULL);
264 if (hFile)
265 {
266 memset(&Ios, 0, sizeof(Ios));
267 rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, abBuf, sizeof(abBuf), MY_FileNameInformation);
268 CloseHandle(hFile);
269 if (rcNt >= 0)
270 {
271 /*
272 * The FileNameInformation we get is relative to where the volume is mounted,
273 * so we have to extract the driveletter prefix ourselves.
274 *
275 * FIXME: This will probably not work for volumes mounted in NTFS sub-directories.
276 */
277 int cchOut;
278 int fUnc = 0;
279 char *psz = pszFull;
280 if (pszPath[0] == '\\' || pszPath[0] == '/')
281 {
282 /* unc or root of volume */
283 if ( (pszPath[1] == '\\' || pszPath[1] == '/')
284 && (pszPath[2] != '\\' || pszPath[2] == '/'))
285 {
286 /* unc - we get the server + name back */
287 *psz++ = '\\';
288 fUnc = 1;
289 }
290 else
291 {
292 /* root slash */
293 *psz++ = _getdrive() + 'A' - 1;
294 *psz++ = ':';
295 }
296 }
297 else if (pszPath[1] == ':' && isalpha(pszPath[0]))
298 {
299 /* drive letter */
300 *psz++ = toupper(pszPath[0]);
301 *psz++ = ':';
302 }
303 else
304 {
305 /* relative */
306 *psz++ = _getdrive() + 'A' - 1;
307 *psz++ = ':';
308 }
309
310 cchOut = WideCharToMultiByte(CP_ACP, 0,
311 pFileNameInfo->FileName, pFileNameInfo->FileNameLength / sizeof(WCHAR),
312 psz, (int)(cchFull - (psz - pszFull) - 2), NULL, NULL);
313 if (cchOut > 0)
314 {
315 const char *pszEnd;
316
317 /* upper case the server and share */
318 if (fUnc)
319 {
320 for (psz++; *psz != '/' && *psz != '\\'; psz++)
321 *psz = toupper(*psz);
322 for (psz++; *psz != '/' && *psz != '\\'; psz++)
323 *psz = toupper(*psz);
324 }
325
326 /* add trailing slash on directories if input has it. */
327 pszEnd = strchr(pszPath, '\0');
328 if ( (pszEnd[-1] == '/' || pszEnd[-1] == '\\')
329 && psz[cchOut - 1] != '\\'
330 && psz[cchOut - 1] != '//')
331 psz[cchOut++] = '\\';
332
333 /* make sure it's terminated */
334 psz[cchOut] = '\0';
335 return 0;
336 }
337 return -3;
338 }
339 }
340 return -2;
341}
342
343/**
344 * Somewhat similar to fullpath, except that it will fix
345 * the case of existing path components.
346 */
347void
348nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull)
349{
350#if 0
351 static int s_cHits = 0;
352 static int s_cFallbacks = 0;
353#endif
354
355 /*
356 * The simple case, the file / dir / whatever exists and can be
357 * queried without problems.
358 */
359 if (nt_get_filename_info(pszPath, pszFull, cchFull) == 0)
360 {
361#if 0
362 fprintf(stderr, "nt #%d - %s\n", ++s_cHits, pszFull);
363#endif
364 return;
365 }
366 if (g_pfnNtQueryInformationFile)
367 {
368 /* do _fullpath and drop off path elements until we get a hit... - later */
369 }
370
371 /*
372 * For now, simply fall back on the old method.
373 */
374 _fullpath(pszFull, pszPath, cchFull);
375 w32_fixcase(pszFull);
376#if 0
377 fprintf(stderr, "fb #%d - %s\n", ++s_cFallbacks, pszFull);
378#endif
379}
380
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