VirtualBox

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

Last change on this file was 108481, checked in by vboxsync, 2 months ago

Some minor compile fixes when targetting Linux with clang, bugref:10874

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 24.0 KB
Line 
1/* $Id: dir-posix.cpp 108481 2025-03-07 17:02:28Z vboxsync $ */
2/** @file
3 * IPRT - Directory manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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_CLANG_PREREQ(3, 4) /* Needs to come first because clang also triggers on RT_GNUC_PREREQ() but doesn't work there. */
434# pragma clang diagnostic push
435# pragma clang diagnostic ignored "-Wdeprecated-declarations"
436#elif RT_GNUC_PREREQ(4, 6)
437# pragma GCC diagnostic push
438# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
439#endif
440 int rc = readdir_r(pDir->pDir, &pDir->Data, &pResult);
441#if RT_CLANG_PREREQ(3, 4)
442# pragma clang diagnostic pop
443#elif RT_GNUC_PREREQ(4, 6)
444# pragma GCC diagnostic pop
445#endif
446 if (rc)
447 {
448 rc = RTErrConvertFromErrno(rc);
449 /** @todo Consider translating ENOENT (The current
450 * position of the directory stream is invalid)
451 * differently. */
452 AssertMsg(rc == VERR_FILE_NOT_FOUND, ("%Rrc\n", rc));
453 return rc;
454 }
455 if (!pResult)
456 return VERR_NO_MORE_FILES;
457 }
458
459 /*
460 * Convert the filename to UTF-8.
461 */
462 if (!pDir->pszName)
463 {
464 int rc = rtPathFromNative(&pDir->pszName, pDir->Data.d_name, pDir->pszPath);
465 if (RT_FAILURE(rc))
466 {
467 pDir->pszName = NULL;
468 return rc;
469 }
470 pDir->cchName = strlen(pDir->pszName);
471 }
472 if ( !pDir->pfnFilter
473 || pDir->pfnFilter(pDir, pDir->pszName))
474 break;
475 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
476 pDir->pszName = NULL;
477 pDir->fDataUnread = false;
478 }
479
480 pDir->fDataUnread = true;
481 return VINF_SUCCESS;
482}
483
484
485#ifdef HAVE_DIRENT_D_TYPE
486/**
487 * Converts the d_type field to IPRT directory entry type.
488 *
489 * @returns IPRT directory entry type.
490 * @param Unix
491 */
492static RTDIRENTRYTYPE rtDirType(int iType)
493{
494 switch (iType)
495 {
496 case DT_UNKNOWN: return RTDIRENTRYTYPE_UNKNOWN;
497 case DT_FIFO: return RTDIRENTRYTYPE_FIFO;
498 case DT_CHR: return RTDIRENTRYTYPE_DEV_CHAR;
499 case DT_DIR: return RTDIRENTRYTYPE_DIRECTORY;
500 case DT_BLK: return RTDIRENTRYTYPE_DEV_BLOCK;
501 case DT_REG: return RTDIRENTRYTYPE_FILE;
502 case DT_LNK: return RTDIRENTRYTYPE_SYMLINK;
503 case DT_SOCK: return RTDIRENTRYTYPE_SOCKET;
504 case DT_WHT: return RTDIRENTRYTYPE_WHITEOUT;
505 default:
506 AssertMsgFailed(("iType=%d\n", iType));
507 return RTDIRENTRYTYPE_UNKNOWN;
508 }
509}
510#endif /*HAVE_DIRENT_D_TYPE */
511
512
513RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
514{
515 PRTDIRINTERNAL pDir = hDir;
516
517 /*
518 * Validate and digest input.
519 */
520 if (!rtDirValidHandle(pDir))
521 return VERR_INVALID_PARAMETER;
522 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
523
524 size_t cbDirEntry = sizeof(*pDirEntry);
525 if (pcbDirEntry)
526 {
527 AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
528 cbDirEntry = *pcbDirEntry;
529 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
530 ("Invalid *pcbDirEntry=%d (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
531 VERR_INVALID_PARAMETER);
532 }
533
534 /*
535 * Fetch more data if necessary and/or convert the name.
536 */
537 int rc = rtDirReadMore(pDir);
538 if (RT_SUCCESS(rc))
539 {
540 /*
541 * Check if we've got enough space to return the data.
542 */
543 const char *pszName = pDir->pszName;
544 const size_t cchName = pDir->cchName;
545 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
546 if (pcbDirEntry)
547 *pcbDirEntry = cbRequired;
548 if (cbRequired <= cbDirEntry)
549 {
550 /*
551 * Setup the returned data.
552 */
553 pDirEntry->INodeId = pDir->Data.d_ino; /* may need #ifdefing later */
554#ifdef HAVE_DIRENT_D_TYPE
555 pDirEntry->enmType = rtDirType(pDir->Data.d_type);
556#else
557 pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
558#endif
559 pDirEntry->cbName = (uint16_t)cchName;
560 Assert(pDirEntry->cbName == cchName);
561 memcpy(pDirEntry->szName, pszName, cchName + 1);
562
563 /* free cached data */
564 pDir->fDataUnread = false;
565 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
566 pDir->pszName = NULL;
567 }
568 else
569 rc = VERR_BUFFER_OVERFLOW;
570 }
571
572 LogFlow(("RTDirRead(%p:{%s}, %p:{%s}, %p:{%u}): returns %Rrc\n",
573 pDir, pDir->pszPath, pDirEntry, RT_SUCCESS(rc) ? pDirEntry->szName : "<failed>",
574 pcbDirEntry, pcbDirEntry ? *pcbDirEntry : 0, rc));
575 return rc;
576}
577
578
579/**
580 * Fills dummy info into the info structure.
581 * This function is called if we cannot stat the file.
582 *
583 * @param pInfo The struct in question.
584 * @param
585 */
586static void rtDirSetDummyInfo(PRTFSOBJINFO pInfo, RTDIRENTRYTYPE enmType)
587{
588 pInfo->cbObject = 0;
589 pInfo->cbAllocated = 0;
590 RTTimeSpecSetNano(&pInfo->AccessTime, 0);
591 RTTimeSpecSetNano(&pInfo->ModificationTime, 0);
592 RTTimeSpecSetNano(&pInfo->ChangeTime, 0);
593 RTTimeSpecSetNano(&pInfo->BirthTime, 0);
594 memset(&pInfo->Attr, 0, sizeof(pInfo->Attr));
595 pInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
596 switch (enmType)
597 {
598 default:
599 case RTDIRENTRYTYPE_UNKNOWN: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL; break;
600 case RTDIRENTRYTYPE_FIFO: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FIFO; break;
601 case RTDIRENTRYTYPE_DEV_CHAR: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_CHAR; break;
602 case RTDIRENTRYTYPE_DIRECTORY: pInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY; break;
603 case RTDIRENTRYTYPE_DEV_BLOCK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_BLOCK; break;
604 case RTDIRENTRYTYPE_FILE: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE; break;
605 case RTDIRENTRYTYPE_SYMLINK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SYMLINK; break;
606 case RTDIRENTRYTYPE_SOCKET: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SOCKET; break;
607 case RTDIRENTRYTYPE_WHITEOUT: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_WHITEOUT; break;
608 }
609}
610
611
612RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
613 RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
614{
615 PRTDIRINTERNAL pDir = hDir;
616
617 /*
618 * Validate and digest input.
619 */
620 if (!rtDirValidHandle(pDir))
621 return VERR_INVALID_PARAMETER;
622 AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
623 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
624 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
625 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
626 VERR_INVALID_PARAMETER);
627 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
628 size_t cbDirEntry = sizeof(*pDirEntry);
629 if (pcbDirEntry)
630 {
631 AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
632 cbDirEntry = *pcbDirEntry;
633 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
634 ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
635 VERR_INVALID_PARAMETER);
636 }
637
638 /*
639 * Fetch more data if necessary and/or convert the name.
640 */
641 int rc = rtDirReadMore(pDir);
642 if (RT_SUCCESS(rc))
643 {
644 /*
645 * Check if we've got enough space to return the data.
646 */
647 const char *pszName = pDir->pszName;
648 const size_t cchName = pDir->cchName;
649 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
650 if (pcbDirEntry)
651 *pcbDirEntry = cbRequired;
652 if (cbRequired <= cbDirEntry)
653 {
654 /*
655 * Setup the returned data.
656 */
657 pDirEntry->cwcShortName = 0;
658 pDirEntry->wszShortName[0] = 0;
659 pDirEntry->cbName = (uint16_t)cchName;
660 Assert(pDirEntry->cbName == cchName);
661 memcpy(pDirEntry->szName, pszName, cchName + 1);
662
663 /* get the info data */
664 size_t cch = cchName + pDir->cchPath + 1;
665 char *pszNamePath = (char *)alloca(cch);
666 if (pszNamePath)
667 {
668 memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
669 memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
670 rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags);
671 }
672 else
673 rc = VERR_NO_MEMORY;
674 if (RT_FAILURE(rc))
675 {
676#ifdef HAVE_DIRENT_D_TYPE
677 rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
678#else
679 rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
680#endif
681 rc = VWRN_NO_DIRENT_INFO;
682 }
683
684 /* free cached data */
685 pDir->fDataUnread = false;
686 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
687 pDir->pszName = NULL;
688 }
689 else
690 rc = VERR_BUFFER_OVERFLOW;
691 }
692
693 return rc;
694}
695
696
697RTDECL(int) RTDirRewind(RTDIR hDir)
698{
699 PRTDIRINTERNAL pDir = hDir;
700
701 /*
702 * Validate and digest input.
703 */
704 if (!rtDirValidHandle(pDir))
705 return VERR_INVALID_PARAMETER;
706
707 /*
708 * Do the rewinding.
709 */
710 /** @todo OS/2 does not rescan the directory as it should. */
711 rewinddir(pDir->pDir);
712 pDir->fDataUnread = false;
713
714 return VINF_SUCCESS;
715}
716
717
718RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
719{
720 /*
721 * Validate input.
722 */
723 AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
724 AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
725 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
726 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
727 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
728
729 /*
730 * Take common cause with RTPathRename.
731 */
732 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_DIRECTORY);
733
734 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}): returns %Rrc\n",
735 pszSrc, pszSrc, pszDst, pszDst, rc));
736 return rc;
737}
738
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