VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/dir-posix.cpp@ 103914

Last change on this file since 103914 was 101160, checked in by vboxsync, 15 months ago

trying to fix additions warning for r3/posix/dir-posix.cpp(111), comparison between signed and unsigned integer expressions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.8 KB
Line 
1/* $Id: dir-posix.cpp 101160 2023-09-18 18:20:50Z vboxsync $ */
2/** @file
3 * IPRT - Directory manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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_DIR
42#include <errno.h>
43#include <unistd.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <fcntl.h>
47#include <dirent.h>
48#include <dlfcn.h>
49#include <stdio.h>
50
51#include <iprt/dir.h>
52#include "internal/iprt.h"
53
54#include <iprt/alloca.h>
55#include <iprt/asm.h>
56#include <iprt/assert.h>
57#include <iprt/err.h>
58#include <iprt/log.h>
59#include <iprt/mem.h>
60#include <iprt/param.h>
61#include <iprt/path.h>
62#include <iprt/string.h>
63#include "internal/dir.h"
64#include "internal/fs.h"
65#include "internal/path.h"
66
67#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_HAIKU)
68# define HAVE_DIRENT_D_TYPE 1
69#endif
70
71
72RTDECL(bool) RTDirExists(const char *pszPath)
73{
74 bool fRc = false;
75 char const *pszNativePath;
76 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
77 if (RT_SUCCESS(rc))
78 {
79 struct stat s;
80 fRc = !stat(pszNativePath, &s)
81 && S_ISDIR(s.st_mode);
82
83 rtPathFreeNative(pszNativePath, pszPath);
84 }
85
86 LogFlow(("RTDirExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
87 return fRc;
88}
89
90
91RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
92{
93 RT_NOREF_PV(fCreate);
94
95 int rc;
96 fMode = rtFsModeNormalize(fMode, pszPath, 0, RTFS_TYPE_DIRECTORY);
97 if (rtFsModeIsValidPermissions(fMode))
98 {
99 char const *pszNativePath;
100 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
101 if (RT_SUCCESS(rc))
102 {
103 struct stat st;
104 if (mkdir(pszNativePath, fMode & RTFS_UNIX_MASK) == 0)
105 {
106 /* If requested, we try make use the permission bits are set
107 correctly when asked. For now, we'll just ignore errors here. */
108 if (fCreate & RTDIRCREATE_FLAGS_IGNORE_UMASK)
109 {
110 if ( stat(pszNativePath, &st)
111 || (st.st_mode & 07777u) != (fMode & 07777u) )
112 chmod(pszNativePath, fMode & RTFS_UNIX_MASK);
113 }
114 rc = VINF_SUCCESS;
115 }
116 else
117 {
118 rc = errno;
119 /*
120 * Solaris mkdir returns ENOSYS on autofs directories, and also
121 * did this apparently for NFS mount points in some Nevada
122 * development builds. It also returned EACCES when it should
123 * have returned EEXIST, which actually is within the POSIX
124 * spec (not that I like this interpretation, but it seems
125 * valid). Check ourselves.
126 */
127 if ( rc == ENOSYS
128 || rc == EACCES)
129 {
130 rc = RTErrConvertFromErrno(rc);
131 if (!stat(pszNativePath, &st))
132 rc = VERR_ALREADY_EXISTS;
133 }
134 else
135 rc = RTErrConvertFromErrno(rc);
136 }
137 }
138
139 rtPathFreeNative(pszNativePath, pszPath);
140 }
141 else
142 {
143 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
144 rc = VERR_INVALID_FMODE;
145 }
146 LogFlow(("RTDirCreate(%p={%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
147 return rc;
148}
149
150
151RTDECL(int) RTDirRemove(const char *pszPath)
152{
153 char const *pszNativePath;
154 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
155 if (RT_SUCCESS(rc))
156 {
157 if (rmdir(pszNativePath))
158 {
159 rc = errno;
160 if (rc == EEXIST) /* Solaris returns this, the rest have ENOTEMPTY. */
161 rc = VERR_DIR_NOT_EMPTY;
162 else if (rc != ENOTDIR)
163 rc = RTErrConvertFromErrno(rc);
164 else
165 {
166 /*
167 * This may be a valid path-not-found or it may be a non-directory in
168 * the final component. FsPerf want us to distinguish between the two,
169 * and trailing slash shouldn't matter because it doesn't on windows...
170 */
171 char *pszFree = NULL;
172 const char *pszStat = pszNativePath;
173 size_t cch = strlen(pszNativePath);
174 if (cch > 2 && RTPATH_IS_SLASH(pszNativePath[cch - 1]))
175 {
176 pszFree = (char *)RTMemTmpAlloc(cch);
177 if (pszFree)
178 {
179 memcpy(pszFree, pszNativePath, cch);
180 do
181 pszFree[--cch] = '\0';
182 while (cch > 2 && RTPATH_IS_SLASH(pszFree[cch - 1]));
183 pszStat = pszFree;
184 }
185 }
186
187 struct stat st;
188 if (!stat(pszStat, &st) && !S_ISDIR(st.st_mode))
189 rc = VERR_NOT_A_DIRECTORY;
190 else
191 rc = VERR_PATH_NOT_FOUND;
192
193 if (pszFree)
194 RTMemTmpFree(pszFree);
195 }
196 }
197
198 rtPathFreeNative(pszNativePath, pszPath);
199 }
200
201 LogFlow(("RTDirRemove(%p={%s}): returns %Rrc\n", pszPath, pszPath, rc));
202 return rc;
203}
204
205
206RTDECL(int) RTDirFlush(const char *pszPath)
207{
208 /*
209 * Linux: The fsync() man page hints at this being required for ensuring
210 * consistency between directory and file in case of a crash.
211 *
212 * Solaris: No mentioned is made of directories on the fsync man page.
213 * While rename+fsync will do what we want on ZFS, the code needs more
214 * careful studying wrt whether the directory entry of a new file is
215 * implicitly synced when the file is synced (it's very likely for ZFS).
216 *
217 * FreeBSD: The FFS fsync code seems to flush the directory entry as well
218 * in some cases. Don't know exactly what's up with rename, but from the
219 * look of things fsync(dir) should work.
220 */
221 int rc;
222#ifdef O_DIRECTORY
223 int fd = open(pszPath, O_RDONLY | O_DIRECTORY, 0);
224#else
225 int fd = open(pszPath, O_RDONLY, 0);
226#endif
227 if (fd >= 0)
228 {
229 if (fsync(fd) == 0)
230 rc = VINF_SUCCESS;
231 else
232 {
233 /* Linux fsync(2) man page documents both errors as an indication
234 * that the file descriptor can't be flushed (seen EINVAL for usual
235 * directories on CIFS). BSD (OS X) fsync(2) documents only the
236 * latter, and Solaris fsync(3C) pretends there is no problem. */
237 if (errno == EROFS || errno == EINVAL)
238 rc = VERR_NOT_SUPPORTED;
239 else
240 rc = RTErrConvertFromErrno(errno);
241 }
242 close(fd);
243 }
244 else
245 rc = RTErrConvertFromErrno(errno);
246 return rc;
247}
248
249
250size_t rtDirNativeGetStructSize(const char *pszPath)
251{
252 long cbNameMax = pathconf(pszPath, _PC_NAME_MAX);
253# ifdef NAME_MAX
254 if (cbNameMax < NAME_MAX) /* This is plain paranoia, but it doesn't hurt. */
255 cbNameMax = NAME_MAX;
256# endif
257# ifdef _XOPEN_NAME_MAX
258 if (cbNameMax < _XOPEN_NAME_MAX) /* Ditto. */
259 cbNameMax = _XOPEN_NAME_MAX;
260# endif
261 size_t cbDir = RT_UOFFSETOF_DYN(RTDIRINTERNAL, Data.d_name[cbNameMax + 1]);
262 if (cbDir < sizeof(RTDIRINTERNAL)) /* Ditto. */
263 cbDir = sizeof(RTDIRINTERNAL);
264 cbDir = RT_ALIGN_Z(cbDir, 8);
265
266 return cbDir;
267}
268
269
270int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative)
271{
272 NOREF(hRelativeDir);
273 NOREF(pvNativeRelative);
274
275 /*
276 * Convert to a native path and try opendir.
277 */
278 char *pszSlash = NULL;
279 char const *pszNativePath;
280 int rc;
281 if ( !(pDir->fFlags & RTDIR_F_NO_FOLLOW)
282 || pDir->fDirSlash
283 || pDir->cchPath <= 1)
284 rc = rtPathToNative(&pszNativePath, pDir->pszPath, NULL);
285 else
286 {
287 pszSlash = (char *)&pDir->pszPath[pDir->cchPath - 1];
288 *pszSlash = '\0';
289 rc = rtPathToNative(&pszNativePath, pDir->pszPath, NULL);
290 }
291 if (RT_SUCCESS(rc))
292 {
293 if ( !(pDir->fFlags & RTDIR_F_NO_FOLLOW)
294 || pDir->fDirSlash)
295 pDir->pDir = opendir(pszNativePath);
296 else
297 {
298 /*
299 * If we can get fdopendir() and have both O_NOFOLLOW and O_DIRECTORY,
300 * we will use open() to safely open the directory without following
301 * symlinks in the final component, and then use fdopendir to get a DIR
302 * from the file descriptor.
303 *
304 * If we cannot get that, we will use lstat() + opendir() as a fallback.
305 *
306 * We ASSUME that support for the O_NOFOLLOW and O_DIRECTORY flags is
307 * older than fdopendir().
308 */
309#if defined(O_NOFOLLOW) && defined(O_DIRECTORY)
310 /* Need to resolve fdopendir dynamically. */
311 typedef DIR * (*PFNFDOPENDIR)(int);
312 static PFNFDOPENDIR s_pfnFdOpenDir = NULL;
313 static bool volatile s_fInitalized = false;
314
315 PFNFDOPENDIR pfnFdOpenDir = s_pfnFdOpenDir;
316 ASMCompilerBarrier();
317 if (s_fInitalized)
318 { /* likely */ }
319 else
320 {
321 pfnFdOpenDir = (PFNFDOPENDIR)(uintptr_t)dlsym(RTLD_DEFAULT, "fdopendir");
322 s_pfnFdOpenDir = pfnFdOpenDir;
323 ASMAtomicWriteBool(&s_fInitalized, true);
324 }
325
326 if (pfnFdOpenDir)
327 {
328 int fd = open(pszNativePath, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0);
329 if (fd >= 0)
330 {
331 pDir->pDir = pfnFdOpenDir(fd);
332 if (RT_UNLIKELY(!pDir->pDir))
333 {
334 rc = RTErrConvertFromErrno(errno);
335 close(fd);
336 }
337 }
338 else
339 {
340 /* WSL returns ELOOP here, but we take no chances that O_NOFOLLOW
341 takes precedence over O_DIRECTORY everywhere. */
342 int iErr = errno;
343 if (iErr == ELOOP || iErr == ENOTDIR)
344 {
345 struct stat St;
346 if ( lstat(pszNativePath, &St) == 0
347 && S_ISLNK(St.st_mode))
348 rc = VERR_IS_A_SYMLINK;
349 else
350 rc = RTErrConvertFromErrno(iErr);
351 }
352 }
353 }
354 else
355#endif
356 {
357 /* Fallback. This contains a race condition. */
358 struct stat St;
359 if ( lstat(pszNativePath, &St) != 0
360 || !S_ISLNK(St.st_mode))
361 pDir->pDir = opendir(pszNativePath);
362 else
363 rc = VERR_IS_A_SYMLINK;
364 }
365 }
366 if (pDir->pDir)
367 {
368 /*
369 * Init data (allocated as all zeros).
370 */
371 pDir->fDataUnread = false; /* spelling it out */
372 }
373 else if (RT_SUCCESS_NP(rc))
374 rc = RTErrConvertFromErrno(errno);
375
376 rtPathFreeNative(pszNativePath, pDir->pszPath);
377 }
378 if (pszSlash)
379 *pszSlash = RTPATH_SLASH;
380 return rc;
381}
382
383
384RTDECL(int) RTDirClose(RTDIR hDir)
385{
386 PRTDIRINTERNAL pDir = hDir;
387
388 /*
389 * Validate input.
390 */
391 if (!pDir)
392 return VERR_INVALID_PARAMETER;
393 if (pDir->u32Magic != RTDIR_MAGIC)
394 {
395 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
396 return VERR_INVALID_PARAMETER;
397 }
398
399 /*
400 * Close the handle.
401 */
402 int rc = VINF_SUCCESS;
403 pDir->u32Magic = RTDIR_MAGIC_DEAD;
404 if (closedir(pDir->pDir))
405 {
406 rc = RTErrConvertFromErrno(errno);
407 AssertMsgFailed(("closedir(%p) -> errno=%d (%Rrc)\n", pDir->pDir, errno, rc));
408 }
409
410 RTMemFree(pDir);
411 return rc;
412}
413
414
415/**
416 * Ensure that there is unread data in the buffer
417 * and that there is a converted filename hanging around.
418 *
419 * @returns IPRT status code.
420 * @param pDir the open directory. Fully validated.
421 */
422static int rtDirReadMore(PRTDIRINTERNAL pDir)
423{
424 /** @todo try avoid the rematching on buffer overflow errors. */
425 for (;;)
426 {
427 /*
428 * Fetch data?
429 */
430 if (!pDir->fDataUnread)
431 {
432 struct dirent *pResult = NULL;
433#if RT_GNUC_PREREQ(4, 6)
434# pragma GCC diagnostic push
435# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
436#endif
437 int rc = readdir_r(pDir->pDir, &pDir->Data, &pResult);
438#if RT_GNUC_PREREQ(4, 6)
439# pragma GCC diagnostic pop
440#endif
441 if (rc)
442 {
443 rc = RTErrConvertFromErrno(rc);
444 /** @todo Consider translating ENOENT (The current
445 * position of the directory stream is invalid)
446 * differently. */
447 AssertMsg(rc == VERR_FILE_NOT_FOUND, ("%Rrc\n", rc));
448 return rc;
449 }
450 if (!pResult)
451 return VERR_NO_MORE_FILES;
452 }
453
454 /*
455 * Convert the filename to UTF-8.
456 */
457 if (!pDir->pszName)
458 {
459 int rc = rtPathFromNative(&pDir->pszName, pDir->Data.d_name, pDir->pszPath);
460 if (RT_FAILURE(rc))
461 {
462 pDir->pszName = NULL;
463 return rc;
464 }
465 pDir->cchName = strlen(pDir->pszName);
466 }
467 if ( !pDir->pfnFilter
468 || pDir->pfnFilter(pDir, pDir->pszName))
469 break;
470 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
471 pDir->pszName = NULL;
472 pDir->fDataUnread = false;
473 }
474
475 pDir->fDataUnread = true;
476 return VINF_SUCCESS;
477}
478
479
480#ifdef HAVE_DIRENT_D_TYPE
481/**
482 * Converts the d_type field to IPRT directory entry type.
483 *
484 * @returns IPRT directory entry type.
485 * @param Unix
486 */
487static RTDIRENTRYTYPE rtDirType(int iType)
488{
489 switch (iType)
490 {
491 case DT_UNKNOWN: return RTDIRENTRYTYPE_UNKNOWN;
492 case DT_FIFO: return RTDIRENTRYTYPE_FIFO;
493 case DT_CHR: return RTDIRENTRYTYPE_DEV_CHAR;
494 case DT_DIR: return RTDIRENTRYTYPE_DIRECTORY;
495 case DT_BLK: return RTDIRENTRYTYPE_DEV_BLOCK;
496 case DT_REG: return RTDIRENTRYTYPE_FILE;
497 case DT_LNK: return RTDIRENTRYTYPE_SYMLINK;
498 case DT_SOCK: return RTDIRENTRYTYPE_SOCKET;
499 case DT_WHT: return RTDIRENTRYTYPE_WHITEOUT;
500 default:
501 AssertMsgFailed(("iType=%d\n", iType));
502 return RTDIRENTRYTYPE_UNKNOWN;
503 }
504}
505#endif /*HAVE_DIRENT_D_TYPE */
506
507
508RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
509{
510 PRTDIRINTERNAL pDir = hDir;
511
512 /*
513 * Validate and digest input.
514 */
515 if (!rtDirValidHandle(pDir))
516 return VERR_INVALID_PARAMETER;
517 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
518
519 size_t cbDirEntry = sizeof(*pDirEntry);
520 if (pcbDirEntry)
521 {
522 AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
523 cbDirEntry = *pcbDirEntry;
524 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
525 ("Invalid *pcbDirEntry=%d (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
526 VERR_INVALID_PARAMETER);
527 }
528
529 /*
530 * Fetch more data if necessary and/or convert the name.
531 */
532 int rc = rtDirReadMore(pDir);
533 if (RT_SUCCESS(rc))
534 {
535 /*
536 * Check if we've got enough space to return the data.
537 */
538 const char *pszName = pDir->pszName;
539 const size_t cchName = pDir->cchName;
540 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
541 if (pcbDirEntry)
542 *pcbDirEntry = cbRequired;
543 if (cbRequired <= cbDirEntry)
544 {
545 /*
546 * Setup the returned data.
547 */
548 pDirEntry->INodeId = pDir->Data.d_ino; /* may need #ifdefing later */
549#ifdef HAVE_DIRENT_D_TYPE
550 pDirEntry->enmType = rtDirType(pDir->Data.d_type);
551#else
552 pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
553#endif
554 pDirEntry->cbName = (uint16_t)cchName;
555 Assert(pDirEntry->cbName == cchName);
556 memcpy(pDirEntry->szName, pszName, cchName + 1);
557
558 /* free cached data */
559 pDir->fDataUnread = false;
560 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
561 pDir->pszName = NULL;
562 }
563 else
564 rc = VERR_BUFFER_OVERFLOW;
565 }
566
567 LogFlow(("RTDirRead(%p:{%s}, %p:{%s}, %p:{%u}): returns %Rrc\n",
568 pDir, pDir->pszPath, pDirEntry, RT_SUCCESS(rc) ? pDirEntry->szName : "<failed>",
569 pcbDirEntry, pcbDirEntry ? *pcbDirEntry : 0, rc));
570 return rc;
571}
572
573
574/**
575 * Fills dummy info into the info structure.
576 * This function is called if we cannot stat the file.
577 *
578 * @param pInfo The struct in question.
579 * @param
580 */
581static void rtDirSetDummyInfo(PRTFSOBJINFO pInfo, RTDIRENTRYTYPE enmType)
582{
583 pInfo->cbObject = 0;
584 pInfo->cbAllocated = 0;
585 RTTimeSpecSetNano(&pInfo->AccessTime, 0);
586 RTTimeSpecSetNano(&pInfo->ModificationTime, 0);
587 RTTimeSpecSetNano(&pInfo->ChangeTime, 0);
588 RTTimeSpecSetNano(&pInfo->BirthTime, 0);
589 memset(&pInfo->Attr, 0, sizeof(pInfo->Attr));
590 pInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
591 switch (enmType)
592 {
593 default:
594 case RTDIRENTRYTYPE_UNKNOWN: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL; break;
595 case RTDIRENTRYTYPE_FIFO: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FIFO; break;
596 case RTDIRENTRYTYPE_DEV_CHAR: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_CHAR; break;
597 case RTDIRENTRYTYPE_DIRECTORY: pInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY; break;
598 case RTDIRENTRYTYPE_DEV_BLOCK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_BLOCK; break;
599 case RTDIRENTRYTYPE_FILE: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE; break;
600 case RTDIRENTRYTYPE_SYMLINK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SYMLINK; break;
601 case RTDIRENTRYTYPE_SOCKET: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SOCKET; break;
602 case RTDIRENTRYTYPE_WHITEOUT: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_WHITEOUT; break;
603 }
604}
605
606
607RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
608 RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
609{
610 PRTDIRINTERNAL pDir = hDir;
611
612 /*
613 * Validate and digest input.
614 */
615 if (!rtDirValidHandle(pDir))
616 return VERR_INVALID_PARAMETER;
617 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
618 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
619 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
620 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
621 VERR_INVALID_PARAMETER);
622 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
623 size_t cbDirEntry = sizeof(*pDirEntry);
624 if (pcbDirEntry)
625 {
626 AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
627 cbDirEntry = *pcbDirEntry;
628 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
629 ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
630 VERR_INVALID_PARAMETER);
631 }
632
633 /*
634 * Fetch more data if necessary and/or convert the name.
635 */
636 int rc = rtDirReadMore(pDir);
637 if (RT_SUCCESS(rc))
638 {
639 /*
640 * Check if we've got enough space to return the data.
641 */
642 const char *pszName = pDir->pszName;
643 const size_t cchName = pDir->cchName;
644 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
645 if (pcbDirEntry)
646 *pcbDirEntry = cbRequired;
647 if (cbRequired <= cbDirEntry)
648 {
649 /*
650 * Setup the returned data.
651 */
652 pDirEntry->cwcShortName = 0;
653 pDirEntry->wszShortName[0] = 0;
654 pDirEntry->cbName = (uint16_t)cchName;
655 Assert(pDirEntry->cbName == cchName);
656 memcpy(pDirEntry->szName, pszName, cchName + 1);
657
658 /* get the info data */
659 size_t cch = cchName + pDir->cchPath + 1;
660 char *pszNamePath = (char *)alloca(cch);
661 if (pszNamePath)
662 {
663 memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
664 memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
665 rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags);
666 }
667 else
668 rc = VERR_NO_MEMORY;
669 if (RT_FAILURE(rc))
670 {
671#ifdef HAVE_DIRENT_D_TYPE
672 rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
673#else
674 rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
675#endif
676 rc = VWRN_NO_DIRENT_INFO;
677 }
678
679 /* free cached data */
680 pDir->fDataUnread = false;
681 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
682 pDir->pszName = NULL;
683 }
684 else
685 rc = VERR_BUFFER_OVERFLOW;
686 }
687
688 return rc;
689}
690
691
692RTDECL(int) RTDirRewind(RTDIR hDir)
693{
694 PRTDIRINTERNAL pDir = hDir;
695
696 /*
697 * Validate and digest input.
698 */
699 if (!rtDirValidHandle(pDir))
700 return VERR_INVALID_PARAMETER;
701
702 /*
703 * Do the rewinding.
704 */
705 /** @todo OS/2 does not rescan the directory as it should. */
706 rewinddir(pDir->pDir);
707 pDir->fDataUnread = false;
708
709 return VINF_SUCCESS;
710}
711
712
713RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
714{
715 /*
716 * Validate input.
717 */
718 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
719 AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
720 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
721 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
722 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
723
724 /*
725 * Take common cause with RTPathRename.
726 */
727 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_DIRECTORY);
728
729 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}): returns %Rrc\n",
730 pszSrc, pszSrc, pszDst, pszDst, rc));
731 return rc;
732}
733
Note: See TracBrowser for help on using the repository browser.

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