VirtualBox

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

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use