VirtualBox

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

Last change on this file since 21608 was 21608, checked in by vboxsync, 16 years ago

IPRT: Use FindFirstFile to get file attributes on Windows host, fixes sharing violation error.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.3 KB
Line 
1/* $Id: path-win.cpp 21608 2009-07-15 13:53:00Z vboxsync $ */
2/** @file
3 * IPRT - Path manipulation.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_PATH
36#include <Windows.h>
37
38#include <iprt/path.h>
39#include <iprt/assert.h>
40#include <iprt/string.h>
41#include <iprt/time.h>
42#include <iprt/mem.h>
43#include <iprt/param.h>
44#include <iprt/log.h>
45#include <iprt/err.h>
46#include "internal/path.h"
47#include "internal/fs.h"
48
49
50/**
51 * Get the real (no symlinks, no . or .. components) path, must exist.
52 *
53 * @returns iprt status code.
54 * @param pszPath The path to resolve.
55 * @param pszRealPath Where to store the real path.
56 * @param cchRealPath Size of the buffer.
57 */
58RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
59{
60 /*
61 * Convert to UTF-16, call Win32 APIs, convert back.
62 */
63 PRTUTF16 pwszPath;
64 int rc = RTStrToUtf16(pszPath, &pwszPath);
65 if (!RT_SUCCESS(rc))
66 return (rc);
67
68 LPWSTR lpFile;
69 WCHAR wsz[RTPATH_MAX];
70 rc = GetFullPathNameW((LPCWSTR)pwszPath, RT_ELEMENTS(wsz), &wsz[0], &lpFile);
71 if (rc > 0 && rc < RT_ELEMENTS(wsz))
72 {
73 /* Check that it exists. (Use RTPathAbs() to just resolve the name.) */
74 DWORD dwAttr = GetFileAttributesW(wsz);
75 if (dwAttr != INVALID_FILE_ATTRIBUTES)
76 rc = RTUtf16ToUtf8Ex((PRTUTF16)&wsz[0], RTSTR_MAX, &pszRealPath, cchRealPath, NULL);
77 else
78 rc = RTErrConvertFromWin32(GetLastError());
79 }
80 else if (rc <= 0)
81 rc = RTErrConvertFromWin32(GetLastError());
82 else
83 rc = VERR_FILENAME_TOO_LONG;
84
85 RTUtf16Free(pwszPath);
86
87 return rc;
88}
89
90
91/**
92 * Get the absolute path (no symlinks, no . or .. components), doesn't have to exit.
93 *
94 * @returns iprt status code.
95 * @param pszPath The path to resolve.
96 * @param pszAbsPath Where to store the absolute path.
97 * @param cchAbsPath Size of the buffer.
98 */
99RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
100{
101 /*
102 * Validation.
103 */
104 AssertPtr(pszAbsPath);
105 AssertPtr(pszPath);
106 if (RT_UNLIKELY(!*pszPath))
107 return VERR_INVALID_PARAMETER;
108
109 /*
110 * Convert to UTF-16, call Win32 API, convert back.
111 */
112 LPWSTR pwszPath;
113 int rc = RTStrToUtf16(pszPath, &pwszPath);
114 if (!RT_SUCCESS(rc))
115 return (rc);
116
117 LPWSTR pwszFile; /* Ignored */
118 RTUTF16 wsz[RTPATH_MAX];
119 rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile);
120 if (rc > 0 && rc < RT_ELEMENTS(wsz))
121 {
122 size_t cch;
123 rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch);
124 if (RT_SUCCESS(rc))
125 {
126 /*
127 * Remove trailing slash if the path may be pointing to a directory.
128 * (See posix variant.)
129 */
130 if ( cch > 1
131 && RTPATH_IS_SLASH(pszAbsPath[cch - 1])
132 && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2])
133 && !RTPATH_IS_SLASH(pszAbsPath[cch - 2]))
134 pszAbsPath[cch - 1] = '\0';
135 }
136 }
137 else if (rc <= 0)
138 rc = RTErrConvertFromWin32(GetLastError());
139 else
140 rc = VERR_FILENAME_TOO_LONG;
141
142 RTUtf16Free(pwszPath);
143 return rc;
144}
145
146
147/**
148 * Gets the user home directory.
149 *
150 * @returns iprt status code.
151 * @param pszPath Buffer where to store the path.
152 * @param cchPath Buffer size in bytes.
153 */
154RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
155{
156 RTUTF16 wszPath[RTPATH_MAX];
157 DWORD dwAttr;
158
159 /*
160 * There are multiple definitions for what WE think of as user home...
161 */
162 if ( !GetEnvironmentVariableW(L"HOME", &wszPath[0], RTPATH_MAX)
163 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
164 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
165 {
166 if ( !GetEnvironmentVariableW(L"USERPROFILE", &wszPath[0], RTPATH_MAX)
167 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
168 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
169 {
170 /* %HOMEDRIVE%%HOMEPATH% */
171 if (!GetEnvironmentVariableW(L"HOMEDRIVE", &wszPath[0], RTPATH_MAX))
172 return VERR_PATH_NOT_FOUND;
173 size_t const cwc = RTUtf16Len(&wszPath[0]);
174 if ( !GetEnvironmentVariableW(L"HOMEPATH", &wszPath[cwc], RTPATH_MAX - (DWORD)cwc)
175 || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
176 || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
177 return VERR_PATH_NOT_FOUND;
178 }
179 }
180
181 /*
182 * Convert and return.
183 */
184 return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
185}
186
187
188RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
189{
190 /*
191 * Validate input.
192 */
193 if (!pszPath)
194 {
195 AssertMsgFailed(("Invalid pszPath=%p\n", pszPath));
196 return VERR_INVALID_PARAMETER;
197 }
198 if (!pObjInfo)
199 {
200 AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo));
201 return VERR_INVALID_PARAMETER;
202 }
203 if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
204 || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
205 {
206 AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
207 return VERR_INVALID_PARAMETER;
208 }
209
210 /*
211 * Query file info.
212 */
213#ifndef RT_DONT_CONVERT_FILENAMES
214 PRTUTF16 pwszPath;
215 int rc = RTStrToUtf16(pszPath, &pwszPath);
216 if (RT_FAILURE(rc))
217 return rc;
218 WIN32_FIND_DATAW Data;
219 HANDLE hDir = FindFirstFileW(pwszPath, &Data);
220 if (hDir == INVALID_HANDLE_VALUE)
221 {
222 rc = RTErrConvertFromWin32(GetLastError());
223 RTUtf16Free(pwszPath);
224 return rc;
225 }
226 FindClose(hDir);
227 RTUtf16Free(pwszPath);
228#else
229 WIN32_FIND_DATAA Data;
230 HANDLE hDir = FindFirstFileA(pszPath, &Data);
231 if (hDir == INVALID_HANDLE_VALUE)
232 return RTErrConvertFromWin32(GetLastError());
233 FindClose(hDir);
234#endif
235
236 /*
237 * Setup the returned data.
238 */
239 pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
240 | (uint64_t)Data.nFileSizeLow;
241 pObjInfo->cbAllocated = pObjInfo->cbObject;
242
243 Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
244 RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
245 RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
246 RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
247 pObjInfo->ChangeTime = pObjInfo->ModificationTime;
248
249 pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
250 pszPath, strlen(pszPath));
251
252 /*
253 * Requested attributes (we cannot provide anything actually).
254 */
255 switch (enmAdditionalAttribs)
256 {
257 case RTFSOBJATTRADD_EASIZE:
258 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
259 pObjInfo->Attr.u.EASize.cb = 0;
260 break;
261
262 case RTFSOBJATTRADD_UNIX:
263 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
264 pObjInfo->Attr.u.Unix.uid = ~0U;
265 pObjInfo->Attr.u.Unix.gid = ~0U;
266 pObjInfo->Attr.u.Unix.cHardlinks = 1;
267 pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */
268 pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */
269 pObjInfo->Attr.u.Unix.fFlags = 0;
270 pObjInfo->Attr.u.Unix.GenerationId = 0;
271 pObjInfo->Attr.u.Unix.Device = 0;
272 break;
273
274 case RTFSOBJATTRADD_NOTHING:
275 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
276 break;
277
278 default:
279 AssertMsgFailed(("Impossible!\n"));
280 return VERR_INTERNAL_ERROR;
281 }
282
283 return VINF_SUCCESS;
284}
285
286
287RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
288 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
289{
290 /*
291 * Validate input.
292 */
293 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
294 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
295 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
296 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
297 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
298 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
299
300 /*
301 * Convert the path.
302 */
303 PRTUTF16 pwszPath;
304 int rc = RTStrToUtf16(pszPath, &pwszPath);
305 if (RT_SUCCESS(rc))
306 {
307 HANDLE hFile = CreateFileW(pwszPath,
308 FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
309 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
310 NULL, /* security attribs */
311 OPEN_EXISTING, /* dwCreationDisposition */
312 FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
313 NULL);
314 if (hFile != INVALID_HANDLE_VALUE)
315 {
316 /*
317 * Check if it's a no-op.
318 */
319 if (!pAccessTime && !pModificationTime && !pBirthTime)
320 rc = VINF_SUCCESS; /* NOP */
321 else
322 {
323 /*
324 * Convert the input and call the API.
325 */
326 FILETIME CreationTimeFT;
327 PFILETIME pCreationTimeFT = NULL;
328 if (pBirthTime)
329 pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
330
331 FILETIME LastAccessTimeFT;
332 PFILETIME pLastAccessTimeFT = NULL;
333 if (pAccessTime)
334 pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
335
336 FILETIME LastWriteTimeFT;
337 PFILETIME pLastWriteTimeFT = NULL;
338 if (pModificationTime)
339 pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
340
341 if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
342 rc = VINF_SUCCESS;
343 else
344 {
345 DWORD Err = GetLastError();
346 rc = RTErrConvertFromWin32(Err);
347 Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
348 pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
349 }
350 }
351 BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc);
352 }
353 else
354 {
355 DWORD Err = GetLastError();
356 rc = RTErrConvertFromWin32(Err);
357 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err));
358 }
359
360 RTUtf16Free(pwszPath);
361 }
362
363 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
364 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
365 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
366 return rc;
367}
368
369
370
371
372/**
373 * Internal worker for RTFileRename and RTFileMove.
374 *
375 * @returns iprt status code.
376 * @param pszSrc The source filename.
377 * @param pszDst The destination filename.
378 * @param fFlags The windows MoveFileEx flags.
379 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
380 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
381 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
382 * not a directory (we are NOT checking whether it's a file).
383 */
384int rtPathWin32MoveRename(const char *pszSrc, const char *pszDst, uint32_t fFlags, RTFMODE fFileType)
385{
386 /*
387 * Convert the strings.
388 */
389 PRTUTF16 pwszSrc;
390 int rc = RTStrToUtf16(pszSrc, &pwszSrc);
391 if (RT_SUCCESS(rc))
392 {
393 PRTUTF16 pwszDst;
394 rc = RTStrToUtf16(pszDst, &pwszDst);
395 if (RT_SUCCESS(rc))
396 {
397 /*
398 * Check object type if requested.
399 * This is open to race conditions.
400 */
401 if (fFileType)
402 {
403 DWORD dwAttr = GetFileAttributesW(pwszSrc);
404 if (dwAttr == INVALID_FILE_ATTRIBUTES)
405 rc = RTErrConvertFromWin32(GetLastError());
406 else if (RTFS_IS_DIRECTORY(fFileType))
407 rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
408 else
409 rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
410 }
411 if (RT_SUCCESS(rc))
412 {
413 if (MoveFileExW(pwszSrc, pwszDst, fFlags))
414 rc = VINF_SUCCESS;
415 else
416 {
417 DWORD Err = GetLastError();
418 rc = RTErrConvertFromWin32(Err);
419 Log(("MoveFileExW('%s', '%s', %#x, %RTfmode): fails with rc=%Rrc & lasterr=%d\n",
420 pszSrc, pszDst, fFlags, fFileType, rc, Err));
421 }
422 }
423 RTUtf16Free(pwszDst);
424 }
425 RTUtf16Free(pwszSrc);
426 }
427 return rc;
428}
429
430
431RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
432{
433 /*
434 * Validate input.
435 */
436 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
437 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
438 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
439 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
440 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
441
442 /*
443 * Call the worker.
444 */
445 int rc = rtPathWin32MoveRename(pszSrc, pszDst, fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, 0);
446
447 LogFlow(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
448 return rc;
449}
450
451
452/**
453 * Checks if the path exists.
454 *
455 * Symbolic links will all be attempted resolved.
456 *
457 * @returns true if it exists and false if it doesn't
458 * @param pszPath The path to check.
459 */
460RTDECL(bool) RTPathExists(const char *pszPath)
461{
462 /*
463 * Validate input.
464 */
465 AssertPtrReturn(pszPath, false);
466 AssertReturn(*pszPath, false);
467
468 /*
469 * Try query file info.
470 */
471#ifndef RT_DONT_CONVERT_FILENAMES
472 PRTUTF16 pwszPath;
473 int rc = RTStrToUtf16(pszPath, &pwszPath);
474 if (RT_SUCCESS(rc))
475 {
476 if (GetFileAttributesW(pwszPath) == INVALID_FILE_ATTRIBUTES)
477 rc = VERR_GENERAL_FAILURE;
478 RTUtf16Free(pwszPath);
479 }
480#else
481 int rc = VINF_SUCCESS;
482 if (GetFileAttributesExA(pszPath) == INVALID_FILE_ATTRIBUTES)
483 rc = VERR_GENERAL_FAILURE;
484#endif
485
486 return RT_SUCCESS(rc);
487}
488
489
490RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
491{
492 int rc;
493
494 /*
495 * GetCurrentDirectory may in some cases omit the drive letter, according
496 * to MSDN, thus the GetFullPathName call.
497 */
498#ifndef RT_DONT_CONVERT_FILENAMES
499 RTUTF16 wszCurPath[RTPATH_MAX];
500 if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
501 {
502 RTUTF16 wszFullPath[RTPATH_MAX];
503 if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
504 rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
505 else
506 rc = RTErrConvertFromWin32(GetLastError());
507 }
508 else
509 rc = RTErrConvertFromWin32(GetLastError());
510#else
511 char szCurPath[RTPATH_MAX];
512 if (GetCurrentDirectory(RTPATH_MAX, szCurPath))
513 {
514 if (GetFullPathName(szCurPath, cchPath, pszPath, NULL))
515 rc = VINF_SUCCESS;
516 else
517 rc = RTErrConvertFromWin32(GetLastError());
518 }
519 else
520 rc = RTErrConvertFromWin32(GetLastError());
521#endif
522 return rc;
523}
524
525
526RTDECL(int) RTPathSetCurrent(const char *pszPath)
527{
528 /*
529 * Validate input.
530 */
531 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
532 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
533
534 /*
535 * This interface is almost identical to the Windows API.
536 */
537#ifndef RT_DONT_CONVERT_FILENAMES
538 PRTUTF16 pwszPath;
539 int rc = RTStrToUtf16(pszPath, &pwszPath);
540 if (RT_SUCCESS(rc))
541 {
542 /** @todo improove the slash stripping a bit? */
543 size_t cwc = RTUtf16Len(pwszPath);
544 if ( cwc >= 2
545 && ( pwszPath[cwc - 1] == L'/'
546 || pwszPath[cwc - 1] == L'\\')
547 && pwszPath[cwc - 2] != ':')
548 pwszPath[cwc - 1] = L'\0';
549
550 if (!SetCurrentDirectoryW(pwszPath))
551 rc = RTErrConvertFromWin32(GetLastError());
552
553 RTUtf16Free(pwszPath);
554 }
555#else
556 int rc = VINF_SUCCESS;
557 /** @todo improove the slash stripping a bit? */
558 char const *pszEnd = strchr(pszPath, '\0');
559 size_t const cchPath = pszPath - pszEnd;
560 if ( cchPath >= 2
561 && ( pszEnd[-1] == '/'
562 || pszEnd[-1] == '\\')
563 && pszEnd[-2] == ':')
564 {
565 char *pszCopy = (char *)RTMemTmpAlloc(cchPath);
566 if (pszCopy)
567 {
568 memcpy(pszCopy, pszPath, cchPath - 1);
569 pszCopy[cchPath - 1] = '\0';
570 if (!SetCurrentDirectory(pszCopy))
571 rc = RTErrConvertFromWin32(GetLastError());
572 RTMemTmpFree(pszCopy);
573 }
574 else
575 rc = VERR_NO_MEMORY;
576 }
577 else
578 {
579 if (!SetCurrentDirectory(pszPath))
580 rc = RTErrConvertFromWin32(GetLastError());
581 }
582#endif
583 return rc;
584}
585
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