VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/path-win.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 26.6 KB
Line 
1/* $Id: path-win.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Path manipulation.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_PATH
42#include <iprt/win/windows.h>
43#include <iprt/win/shlobj.h>
44
45#include <iprt/path.h>
46#include <iprt/assert.h>
47#include <iprt/ctype.h>
48#include <iprt/err.h>
49#include <iprt/ldr.h>
50#include <iprt/log.h>
51#include <iprt/mem.h>
52#include <iprt/param.h>
53#include <iprt/string.h>
54#include <iprt/time.h>
55#include <iprt/utf16.h>
56#include "internal/path.h"
57#include "internal/fs.h"
58
59/* Needed for lazy loading SHGetFolderPathW in RTPathUserDocuments(). */
60typedef HRESULT WINAPI FNSHGETFOLDERPATHW(HWND, int, HANDLE, DWORD, LPWSTR);
61typedef FNSHGETFOLDERPATHW *PFNSHGETFOLDERPATHW;
62
63/**
64 * Get the real (no symlinks, no . or .. components) path, must exist.
65 *
66 * @returns iprt status code.
67 * @param pszPath The path to resolve.
68 * @param pszRealPath Where to store the real path.
69 * @param cchRealPath Size of the buffer.
70 */
71RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
72{
73 /*
74 * Convert to UTF-16, call Win32 APIs, convert back.
75 */
76 PRTUTF16 pwszPath;
77 int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
78 if (RT_SUCCESS(rc))
79 {
80 LPWSTR lpFile;
81 WCHAR wsz[RTPATH_MAX];
82 rc = GetFullPathNameW((LPCWSTR)pwszPath, RT_ELEMENTS(wsz), &wsz[0], &lpFile);
83 if (rc > 0 && rc < RT_ELEMENTS(wsz))
84 {
85 /* Check that it exists. (Use RTPathAbs() to just resolve the name.) */
86 DWORD dwAttr = GetFileAttributesW(wsz);
87 if (dwAttr != INVALID_FILE_ATTRIBUTES)
88 rc = RTUtf16ToUtf8Ex((PRTUTF16)&wsz[0], RTSTR_MAX, &pszRealPath, cchRealPath, NULL);
89 else
90 rc = RTErrConvertFromWin32(GetLastError());
91 }
92 else if (rc <= 0)
93 rc = RTErrConvertFromWin32(GetLastError());
94 else
95 rc = VERR_FILENAME_TOO_LONG;
96
97 RTPathWinFree(pwszPath);
98 }
99 return rc;
100}
101
102#if 0
103/**
104 * Get the absolute path (no symlinks, no . or .. components), doesn't have to exit.
105 *
106 * @returns iprt status code.
107 * @param pszPath The path to resolve.
108 * @param pszAbsPath Where to store the absolute path.
109 * @param cchAbsPath Size of the buffer.
110 */
111RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
112{
113 /*
114 * Validation.
115 */
116 AssertPtr(pszAbsPath);
117 AssertPtr(pszPath);
118 if (RT_UNLIKELY(!*pszPath))
119 return VERR_INVALID_PARAMETER;
120
121 /*
122 * Convert to UTF-16, call Win32 API, convert back.
123 */
124 LPWSTR pwszPath;
125 int rc = RTStrToUtf16(pszPath, &pwszPath);
126 if (!RT_SUCCESS(rc))
127 return (rc);
128
129 LPWSTR pwszFile; /* Ignored */
130 RTUTF16 wsz[RTPATH_MAX];
131 rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile);
132 if (rc > 0 && rc < RT_ELEMENTS(wsz))
133 {
134 size_t cch;
135 rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch);
136 if (RT_SUCCESS(rc))
137 {
138# if 1 /** @todo This code is completely bonkers. */
139 /*
140 * Remove trailing slash if the path may be pointing to a directory.
141 * (See posix variant.)
142 */
143 if ( cch > 1
144 && RTPATH_IS_SLASH(pszAbsPath[cch - 1])
145 && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2])
146 && !RTPATH_IS_SLASH(pszAbsPath[cch - 2]))
147 pszAbsPath[cch - 1] = '\0';
148# endif
149 }
150 }
151 else if (rc <= 0)
152 rc = RTErrConvertFromWin32(GetLastError());
153 else
154 rc = VERR_FILENAME_TOO_LONG;
155
156 RTUtf16Free(pwszPath);
157 return rc;
158}
159#endif
160
161
162/**
163 * Gets the user home directory.
164 *
165 * @returns iprt status code.
166 * @param pszPath Buffer where to store the path.
167 * @param cchPath Buffer size in bytes.
168 */
169RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
170{
171 /*
172 * Validate input
173 */
174 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
175 AssertReturn(cchPath, VERR_INVALID_PARAMETER);
176
177 RTUTF16 wszPath[RTPATH_MAX];
178 bool fValidFolderPath = false;
179
180 /*
181 * Try with Windows XP+ functionality first.
182 */
183 RTLDRMOD hShell32;
184 int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
185 if (RT_SUCCESS(rc))
186 {
187 PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
188 rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
189 if (RT_SUCCESS(rc))
190 {
191 HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, wszPath);
192 fValidFolderPath = (hrc == S_OK);
193 }
194 RTLdrClose(hShell32);
195 }
196
197 DWORD dwAttr;
198 if ( !fValidFolderPath
199 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
200 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
201 {
202 /*
203 * Fall back to Windows specific environment variables. HOME is not used.
204 */
205 if ( !GetEnvironmentVariableW(L"USERPROFILE", &wszPath[0], RTPATH_MAX)
206 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
207 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
208 {
209 /* %HOMEDRIVE%%HOMEPATH% */
210 if (!GetEnvironmentVariableW(L"HOMEDRIVE", &wszPath[0], RTPATH_MAX))
211 return VERR_PATH_NOT_FOUND;
212 size_t const cwc = RTUtf16Len(&wszPath[0]);
213 if ( !GetEnvironmentVariableW(L"HOMEPATH", &wszPath[cwc], RTPATH_MAX - (DWORD)cwc)
214 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
215 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
216 return VERR_PATH_NOT_FOUND;
217 }
218 }
219
220 /*
221 * Convert and return.
222 */
223 return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
224}
225
226
227RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath)
228{
229 /*
230 * Validate input
231 */
232 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
233 AssertReturn(cchPath, VERR_INVALID_PARAMETER);
234
235 RTLDRMOD hShell32;
236 int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
237 if (RT_SUCCESS(rc))
238 {
239 PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
240 rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
241 if (RT_SUCCESS(rc))
242 {
243 RTUTF16 wszPath[RTPATH_MAX];
244 HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, wszPath);
245 if ( hrc == S_OK /* Found */
246 || hrc == S_FALSE) /* Found, but doesn't exist */
247 {
248 /*
249 * Convert and return.
250 */
251 RTLdrClose(hShell32);
252 return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
253 }
254 }
255 RTLdrClose(hShell32);
256 }
257 return VERR_PATH_NOT_FOUND;
258}
259
260
261#if 0 /* use nt version of this */
262
263RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
264{
265 return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
266}
267#endif
268#if 0
269
270
271RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
272{
273 /*
274 * Validate input.
275 */
276 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
277 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
278 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
279 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
280 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
281 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
282 VERR_INVALID_PARAMETER);
283 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
284
285 /*
286 * Query file info.
287 */
288 uint32_t uReparseTag = RTFSMODE_SYMLINK_REPARSE_TAG;
289 WIN32_FILE_ATTRIBUTE_DATA Data;
290 PRTUTF16 pwszPath;
291 int rc = RTStrToUtf16(pszPath, &pwszPath);
292 if (RT_FAILURE(rc))
293 return rc;
294 if (!GetFileAttributesExW(pwszPath, GetFileExInfoStandard, &Data))
295 {
296 /* Fallback to FindFileFirst in case of sharing violation. */
297 if (GetLastError() == ERROR_SHARING_VIOLATION)
298 {
299 WIN32_FIND_DATAW FindData;
300 HANDLE hDir = FindFirstFileW(pwszPath, &FindData);
301 if (hDir == INVALID_HANDLE_VALUE)
302 {
303 rc = RTErrConvertFromWin32(GetLastError());
304 RTUtf16Free(pwszPath);
305 return rc;
306 }
307 FindClose(hDir);
308
309 Data.dwFileAttributes = FindData.dwFileAttributes;
310 Data.ftCreationTime = FindData.ftCreationTime;
311 Data.ftLastAccessTime = FindData.ftLastAccessTime;
312 Data.ftLastWriteTime = FindData.ftLastWriteTime;
313 Data.nFileSizeHigh = FindData.nFileSizeHigh;
314 Data.nFileSizeLow = FindData.nFileSizeLow;
315 uReparseTag = FindData.dwReserved0;
316 }
317 else
318 {
319 rc = RTErrConvertFromWin32(GetLastError());
320 RTUtf16Free(pwszPath);
321 return rc;
322 }
323 }
324
325 /*
326 * Getting the information for the link target is a bit annoying and
327 * subject to the same access violation mess as above.. :/
328 */
329 /** @todo we're too lazy wrt to error paths here... */
330 if ( (Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
331 && ((fFlags & RTPATH_F_FOLLOW_LINK) || uReparseTag != RTFSMODE_SYMLINK_REPARSE_TAG))
332 {
333 HANDLE hFinal = CreateFileW(pwszPath,
334 GENERIC_READ,
335 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
336 NULL,
337 OPEN_EXISTING,
338 FILE_FLAG_BACKUP_SEMANTICS,
339 NULL);
340 if (hFinal != INVALID_HANDLE_VALUE)
341 {
342 BY_HANDLE_FILE_INFORMATION FileData;
343 if (GetFileInformationByHandle(hFinal, &FileData))
344 {
345 Data.dwFileAttributes = FileData.dwFileAttributes;
346 Data.ftCreationTime = FileData.ftCreationTime;
347 Data.ftLastAccessTime = FileData.ftLastAccessTime;
348 Data.ftLastWriteTime = FileData.ftLastWriteTime;
349 Data.nFileSizeHigh = FileData.nFileSizeHigh;
350 Data.nFileSizeLow = FileData.nFileSizeLow;
351 uReparseTag = 0;
352 }
353 CloseHandle(hFinal);
354 }
355 else if (GetLastError() != ERROR_SHARING_VIOLATION)
356 {
357 rc = RTErrConvertFromWin32(GetLastError());
358 RTUtf16Free(pwszPath);
359 return rc;
360 }
361 }
362
363 RTUtf16Free(pwszPath);
364
365 /*
366 * Setup the returned data.
367 */
368 pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
369 | (uint64_t)Data.nFileSizeLow;
370 pObjInfo->cbAllocated = pObjInfo->cbObject;
371
372 Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
373 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
374 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
375 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
376 pObjInfo->ChangeTime = pObjInfo->ModificationTime;
377
378 pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
379 pszPath, strlen(pszPath), uReparseTag);
380
381 /*
382 * Requested attributes (we cannot provide anything actually).
383 */
384 switch (enmAdditionalAttribs)
385 {
386 case RTFSOBJATTRADD_NOTHING:
387 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
388 break;
389
390 case RTFSOBJATTRADD_UNIX:
391 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
392 pObjInfo->Attr.u.Unix.uid = ~0U;
393 pObjInfo->Attr.u.Unix.gid = ~0U;
394 pObjInfo->Attr.u.Unix.cHardlinks = 1;
395 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */
396 pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */
397 pObjInfo->Attr.u.Unix.fFlags = 0;
398 pObjInfo->Attr.u.Unix.GenerationId = 0;
399 pObjInfo->Attr.u.Unix.Device = 0;
400 break;
401
402 case RTFSOBJATTRADD_UNIX_OWNER:
403 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
404 pObjInfo->Attr.u.UnixOwner.uid = ~0U;
405 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
406 break;
407
408 case RTFSOBJATTRADD_UNIX_GROUP:
409 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
410 pObjInfo->Attr.u.UnixGroup.gid = ~0U;
411 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
412 break;
413
414 case RTFSOBJATTRADD_EASIZE:
415 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
416 pObjInfo->Attr.u.EASize.cb = 0;
417 break;
418
419 default:
420 AssertMsgFailed(("Impossible!\n"));
421 return VERR_INTERNAL_ERROR;
422 }
423
424 return VINF_SUCCESS;
425}
426
427#endif /* using NT version*/
428
429
430RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
431 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
432{
433 return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
434}
435
436
437RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
438 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
439{
440 /*
441 * Validate input.
442 */
443 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
444 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
445 AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
446 AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
447 AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
448 AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
449 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
450
451 /*
452 * Convert the path.
453 */
454 PRTUTF16 pwszPath;
455 int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
456 if (RT_SUCCESS(rc))
457 {
458 HANDLE hFile;
459 if (fFlags & RTPATH_F_FOLLOW_LINK)
460 hFile = CreateFileW(pwszPath,
461 FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
462 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
463 NULL, /* security attribs */
464 OPEN_EXISTING, /* dwCreationDisposition */
465 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
466 NULL);
467 else
468 {
469/** @todo Symlink: Test RTPathSetTimesEx on Windows. (The code is disabled
470 * because it's not tested yet.) */
471#if 0 //def FILE_FLAG_OPEN_REPARSE_POINT
472 hFile = CreateFileW(pwszPath,
473 FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
474 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
475 NULL, /* security attribs */
476 OPEN_EXISTING, /* dwCreationDisposition */
477 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
478 NULL);
479
480 if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
481#endif
482 hFile = CreateFileW(pwszPath,
483 FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
484 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
485 NULL, /* security attribs */
486 OPEN_EXISTING, /* dwCreationDisposition */
487 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
488 NULL);
489 }
490 if (hFile != INVALID_HANDLE_VALUE)
491 {
492 /*
493 * Check if it's a no-op.
494 */
495 if (!pAccessTime && !pModificationTime && !pBirthTime)
496 rc = VINF_SUCCESS; /* NOP */
497 else
498 {
499 /*
500 * Convert the input and call the API.
501 */
502 FILETIME CreationTimeFT;
503 PFILETIME pCreationTimeFT = NULL;
504 if (pBirthTime)
505 pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
506
507 FILETIME LastAccessTimeFT;
508 PFILETIME pLastAccessTimeFT = NULL;
509 if (pAccessTime)
510 pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
511
512 FILETIME LastWriteTimeFT;
513 PFILETIME pLastWriteTimeFT = NULL;
514 if (pModificationTime)
515 pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
516
517 if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
518 rc = VINF_SUCCESS;
519 else
520 {
521 DWORD Err = GetLastError();
522 rc = RTErrConvertFromWin32(Err);
523 Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
524 pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
525 }
526 }
527 BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc);
528 }
529 else
530 {
531 DWORD Err = GetLastError();
532 rc = RTErrConvertFromWin32(Err);
533 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err));
534 }
535
536 RTPathWinFree(pwszPath);
537 }
538
539 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
540 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
541 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
542 return rc;
543}
544
545
546
547
548/**
549 * Internal worker for RTFileRename and RTFileMove.
550 *
551 * @returns iprt status code.
552 * @param pszSrc The source filename.
553 * @param pszDst The destination filename.
554 * @param fFlags The windows MoveFileEx flags.
555 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
556 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
557 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
558 * not a directory (we are NOT checking whether it's a file).
559 */
560DECLHIDDEN(int) rtPathWin32MoveRename(const char *pszSrc, const char *pszDst, uint32_t fFlags, RTFMODE fFileType)
561{
562 /*
563 * Convert the strings.
564 */
565 PRTUTF16 pwszSrc;
566 int rc = RTPathWinFromUtf8(&pwszSrc, pszSrc, 0 /*fFlags*/);
567 if (RT_SUCCESS(rc))
568 {
569 PRTUTF16 pwszDst;
570 rc = RTPathWinFromUtf8(&pwszDst, pszDst, 0 /*fFlags*/);
571 if (RT_SUCCESS(rc))
572 {
573 /*
574 * Check object type if requested.
575 * This is open to race conditions.
576 */
577 if (fFileType)
578 {
579 DWORD dwAttr = GetFileAttributesW(pwszSrc);
580 if (dwAttr == INVALID_FILE_ATTRIBUTES)
581 rc = RTErrConvertFromWin32(GetLastError());
582 else if (RTFS_IS_DIRECTORY(fFileType))
583 rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
584 else
585 rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
586 }
587 if (RT_SUCCESS(rc))
588 {
589 if (MoveFileExW(pwszSrc, pwszDst, fFlags))
590 rc = VINF_SUCCESS;
591 else
592 {
593 DWORD Err = GetLastError();
594 rc = RTErrConvertFromWin32(Err);
595 Log(("MoveFileExW('%s', '%s', %#x, %RTfmode): fails with rc=%Rrc & lasterr=%d\n",
596 pszSrc, pszDst, fFlags, fFileType, rc, Err));
597 }
598 }
599 RTPathWinFree(pwszDst);
600 }
601 RTPathWinFree(pwszSrc);
602 }
603 return rc;
604}
605
606
607RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
608{
609 /*
610 * Validate input.
611 */
612 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
613 AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
614 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
615 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
616 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
617
618 /*
619 * Call the worker.
620 */
621 int rc = rtPathWin32MoveRename(pszSrc, pszDst, fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, 0);
622
623 LogFlow(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
624 return rc;
625}
626
627
628RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
629{
630 RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
631 return VERR_NOT_IMPLEMENTED;
632}
633
634
635RTDECL(bool) RTPathExists(const char *pszPath)
636{
637 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
638}
639
640
641RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
642{
643 /*
644 * Validate input.
645 */
646 AssertPtrReturn(pszPath, false);
647 AssertReturn(*pszPath, false);
648 Assert(RTPATH_F_IS_VALID(fFlags, 0));
649
650 /*
651 * Try query file info.
652 */
653 DWORD dwAttr;
654 PRTUTF16 pwszPath;
655 int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
656 if (RT_SUCCESS(rc))
657 {
658 dwAttr = GetFileAttributesW(pwszPath);
659 RTPathWinFree(pwszPath);
660 }
661 else
662 dwAttr = INVALID_FILE_ATTRIBUTES;
663 if (dwAttr == INVALID_FILE_ATTRIBUTES)
664 return false;
665
666#ifdef FILE_ATTRIBUTE_REPARSE_POINT
667 if ( (fFlags & RTPATH_F_FOLLOW_LINK)
668 && (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT))
669 {
670 AssertFailed();
671 /** @todo Symlinks: RTPathExists+RTPathExistsEx is misbehaving on symbolic
672 * links on Windows. */
673 }
674#endif
675
676 return true;
677}
678
679
680RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
681{
682 int rc;
683
684 if (cchPath > 0)
685 {
686 /*
687 * GetCurrentDirectory may in some cases omit the drive letter, according
688 * to MSDN, thus the GetFullPathName call.
689 */
690 RTUTF16 wszCurPath[RTPATH_MAX];
691 if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
692 {
693 RTUTF16 wszFullPath[RTPATH_MAX];
694 if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
695 {
696 if ( wszFullPath[1] == ':'
697 && RT_C_IS_LOWER(wszFullPath[0]))
698 wszFullPath[0] = RT_C_TO_UPPER(wszFullPath[0]);
699
700 rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
701 }
702 else
703 rc = RTErrConvertFromWin32(GetLastError());
704 }
705 else
706 rc = RTErrConvertFromWin32(GetLastError());
707 }
708 else
709 rc = VERR_BUFFER_OVERFLOW;
710 return rc;
711}
712
713
714RTDECL(int) RTPathSetCurrent(const char *pszPath)
715{
716 /*
717 * Validate input.
718 */
719 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
720 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
721
722 /*
723 * This interface is almost identical to the Windows API.
724 */
725 PRTUTF16 pwszPath;
726 int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
727 if (RT_SUCCESS(rc))
728 {
729 /** @todo improve the slash stripping a bit? */
730 size_t cwc = RTUtf16Len(pwszPath);
731 if ( cwc >= 2
732 && ( pwszPath[cwc - 1] == L'/'
733 || pwszPath[cwc - 1] == L'\\')
734 && pwszPath[cwc - 2] != ':')
735 pwszPath[cwc - 1] = L'\0';
736
737 if (!SetCurrentDirectoryW(pwszPath))
738 rc = RTErrConvertFromWin32(GetLastError());
739
740 RTPathWinFree(pwszPath);
741 }
742 return rc;
743}
744
745
746RTDECL(int) RTPathGetCurrentOnDrive(char chDrive, char *pszPath, size_t cbPath)
747{
748 int rc;
749 if (cbPath > 0)
750 {
751 WCHAR wszInput[4];
752 wszInput[0] = chDrive;
753 wszInput[1] = ':';
754 wszInput[2] = '\0';
755 RTUTF16 wszFullPath[RTPATH_MAX];
756 if (GetFullPathNameW(wszInput, RTPATH_MAX, wszFullPath, NULL))
757 rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL);
758 else
759 rc = RTErrConvertFromWin32(GetLastError());
760 }
761 else
762 rc = VERR_BUFFER_OVERFLOW;
763 return rc;
764}
765
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use