VirtualBox

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

Last change on this file since 36166 was 36166, checked in by vboxsync, 14 years ago

Runtime/r3/posix: RTDirCreate should fail when creating a directory where a file exists.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.5 KB
Line 
1/* $Id: dir-posix.cpp 36166 2011-03-04 11:54:53Z vboxsync $ */
2/** @file
3 * IPRT - Directory manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
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
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DIR
32#include <errno.h>
33#include <unistd.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/fcntl.h>
37#include <fcntl.h>
38#include <dirent.h>
39#include <stdio.h>
40
41#include <iprt/dir.h>
42#include "internal/iprt.h"
43
44#include <iprt/alloca.h>
45#include <iprt/assert.h>
46#include <iprt/err.h>
47#include <iprt/log.h>
48#include <iprt/mem.h>
49#include <iprt/param.h>
50#include <iprt/path.h>
51#include <iprt/string.h>
52#include "internal/dir.h"
53#include "internal/fs.h"
54#include "internal/path.h"
55
56#if !defined(RT_OS_SOLARIS)
57# define HAVE_DIRENT_D_TYPE 1
58#endif
59
60
61RTDECL(bool) RTDirExists(const char *pszPath)
62{
63 bool fRc = false;
64 char const *pszNativePath;
65 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
66 if (RT_SUCCESS(rc))
67 {
68 struct stat s;
69 fRc = !stat(pszNativePath, &s)
70 && S_ISDIR(s.st_mode);
71
72 rtPathFreeNative(pszNativePath, pszPath);
73 }
74
75 LogFlow(("RTDirExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
76 return fRc;
77}
78
79
80RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode)
81{
82 int rc;
83 fMode = rtFsModeNormalize(fMode, pszPath, 0);
84 if (rtFsModeIsValidPermissions(fMode))
85 {
86 char const *pszNativePath;
87 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
88 if (RT_SUCCESS(rc))
89 {
90 if (mkdir(pszNativePath, fMode & RTFS_UNIX_MASK))
91 {
92 rc = errno;
93#ifdef RT_OS_SOLARIS
94 /*
95 * mkdir on nfs mount points has been/is busted in various
96 * during the Nevada development cycle. We've observed:
97 * - Build 111b (2009.06) returns EACCES.
98 * - Build ca. 70-80 returns ENOSYS.
99 */
100 if ( rc == ENOSYS
101 || rc == EACCES)
102 {
103 rc = EEXIST;
104 }
105#endif
106 rc = RTErrConvertFromErrno(rc);
107 if (rc == VERR_ALREADY_EXISTS)
108 {
109 /*
110 * Verify that it really exists as a directory.
111 */
112 struct stat st;
113 if (!stat(pszNativePath, &st) && !S_ISDIR(st.st_mode))
114 rc = VERR_IS_A_FILE;
115 }
116 }
117 }
118
119 rtPathFreeNative(pszNativePath, pszPath);
120 }
121 else
122 {
123 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
124 rc = VERR_INVALID_FMODE;
125 }
126 LogFlow(("RTDirCreate(%p={%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
127 return rc;
128}
129
130
131RTDECL(int) RTDirRemove(const char *pszPath)
132{
133 char const *pszNativePath;
134 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
135 if (RT_SUCCESS(rc))
136 {
137 if (rmdir(pszNativePath))
138 rc = RTErrConvertFromErrno(errno);
139
140 rtPathFreeNative(pszNativePath, pszPath);
141 }
142
143 LogFlow(("RTDirRemove(%p={%s}): returns %Rrc\n", pszPath, pszPath, rc));
144 return rc;
145}
146
147
148RTDECL(int) RTDirFlush(const char *pszPath)
149{
150 /*
151 * Linux: The fsync() man page hints at this being required for ensuring
152 * consistency between directory and file in case of a crash.
153 *
154 * Solaris: No mentioned is made of directories on the fsync man page.
155 * While rename+fsync will do what we want on ZFS, the code needs more
156 * careful studying wrt whether the directory entry of a new file is
157 * implicitly synced when the file is synced (it's very likely for ZFS).
158 *
159 * FreeBSD: The FFS fsync code seems to flush the directory entry as well
160 * in some cases. Don't know exactly what's up with rename, but from the
161 * look of things fsync(dir) should work.
162 */
163 int rc;
164#ifdef O_DIRECTORY
165 int fd = open(pszPath, O_RDONLY | O_DIRECTORY, 0);
166#else
167 int fd = open(pszPath, O_RDONLY, 0);
168#endif
169 if (fd >= 0)
170 {
171 if (fsync(fd) == 0)
172 rc = VINF_SUCCESS;
173 else
174 rc = RTErrConvertFromErrno(errno);
175 close(fd);
176 }
177 else
178 rc = RTErrConvertFromErrno(errno);
179 return rc;
180}
181
182
183int rtDirNativeOpen(PRTDIR pDir, char *pszPathBuf)
184{
185 /*
186 * Convert to a native path and try opendir.
187 */
188 char const *pszNativePath;
189 int rc = rtPathToNative(&pszNativePath, pDir->pszPath, NULL);
190 if (RT_SUCCESS(rc))
191 {
192 pDir->pDir = opendir(pszNativePath);
193 if (pDir->pDir)
194 {
195 /*
196 * Init data.
197 */
198 pDir->fDataUnread = false;
199 memset(&pDir->Data, 0, RT_OFFSETOF(RTDIR, Data.d_name)); /* not strictly necessary */
200 memset(&pDir->Data.d_name[0], 0, pDir->cbMaxName);
201 }
202 else
203 rc = RTErrConvertFromErrno(errno);
204
205 rtPathFreeNative(pszNativePath, pDir->pszPath);
206 }
207
208 return rc;
209}
210
211
212RTDECL(int) RTDirClose(PRTDIR pDir)
213{
214 /*
215 * Validate input.
216 */
217 if (!pDir)
218 return VERR_INVALID_PARAMETER;
219 if (pDir->u32Magic != RTDIR_MAGIC)
220 {
221 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
222 return VERR_INVALID_PARAMETER;
223 }
224
225 /*
226 * Close the handle.
227 */
228 int rc = VINF_SUCCESS;
229 pDir->u32Magic = RTDIR_MAGIC_DEAD;
230 if (closedir(pDir->pDir))
231 {
232 rc = RTErrConvertFromErrno(errno);
233 AssertMsgFailed(("closedir(%p) -> errno=%d (%Rrc)\n", pDir->pDir, errno, rc));
234 }
235
236 RTMemFree(pDir);
237 return rc;
238}
239
240
241/**
242 * Ensure that there is unread data in the buffer
243 * and that there is a converted filename hanging around.
244 *
245 * @returns IPRT status code.
246 * @param pDir the open directory. Fully validated.
247 */
248static int rtDirReadMore(PRTDIR pDir)
249{
250 /** @todo try avoid the rematching on buffer overflow errors. */
251 for (;;)
252 {
253 /*
254 * Fetch data?
255 */
256 if (!pDir->fDataUnread)
257 {
258 struct dirent *pResult = NULL;
259 int rc = readdir_r(pDir->pDir, &pDir->Data, &pResult);
260 if (rc)
261 {
262 rc = RTErrConvertFromErrno(rc);
263 /** @todo Consider translating ENOENT (The current
264 * position of the directory stream is invalid)
265 * differently. */
266 AssertMsg(rc == VERR_FILE_NOT_FOUND, ("%Rrc\n", rc));
267 return rc;
268 }
269 if (!pResult)
270 return VERR_NO_MORE_FILES;
271 }
272
273 /*
274 * Convert the filename to UTF-8.
275 */
276 if (!pDir->pszName)
277 {
278 int rc = rtPathFromNative(&pDir->pszName, pDir->Data.d_name, pDir->pszPath);
279 if (RT_FAILURE(rc))
280 {
281 pDir->pszName = NULL;
282 return rc;
283 }
284 pDir->cchName = strlen(pDir->pszName);
285 }
286 if ( !pDir->pfnFilter
287 || pDir->pfnFilter(pDir, pDir->pszName))
288 break;
289 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
290 pDir->pszName = NULL;
291 pDir->fDataUnread = false;
292 }
293
294 pDir->fDataUnread = true;
295 return VINF_SUCCESS;
296}
297
298
299#ifdef HAVE_DIRENT_D_TYPE
300/**
301 * Converts the d_type field to IPRT directory entry type.
302 *
303 * @returns IPRT directory entry type.
304 * @param Unix
305 */
306static RTDIRENTRYTYPE rtDirType(int iType)
307{
308 switch (iType)
309 {
310 case DT_UNKNOWN: return RTDIRENTRYTYPE_UNKNOWN;
311 case DT_FIFO: return RTDIRENTRYTYPE_FIFO;
312 case DT_CHR: return RTDIRENTRYTYPE_DEV_CHAR;
313 case DT_DIR: return RTDIRENTRYTYPE_DIRECTORY;
314 case DT_BLK: return RTDIRENTRYTYPE_DEV_BLOCK;
315 case DT_REG: return RTDIRENTRYTYPE_FILE;
316 case DT_LNK: return RTDIRENTRYTYPE_SYMLINK;
317 case DT_SOCK: return RTDIRENTRYTYPE_SOCKET;
318 case DT_WHT: return RTDIRENTRYTYPE_WHITEOUT;
319 default:
320 AssertMsgFailed(("iType=%d\n", iType));
321 return RTDIRENTRYTYPE_UNKNOWN;
322 }
323}
324#endif /*HAVE_DIRENT_D_TYPE */
325
326
327RTDECL(int) RTDirRead(PRTDIR pDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
328{
329 /*
330 * Validate and digest input.
331 */
332 if (!rtDirValidHandle(pDir))
333 return VERR_INVALID_PARAMETER;
334 AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER);
335
336 size_t cbDirEntry = sizeof(*pDirEntry);
337 if (pcbDirEntry)
338 {
339 AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER);
340 cbDirEntry = *pcbDirEntry;
341 AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
342 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
343 VERR_INVALID_PARAMETER);
344 }
345
346 /*
347 * Fetch more data if necessary and/or convert the name.
348 */
349 int rc = rtDirReadMore(pDir);
350 if (RT_SUCCESS(rc))
351 {
352 /*
353 * Check if we've got enough space to return the data.
354 */
355 const char *pszName = pDir->pszName;
356 const size_t cchName = pDir->cchName;
357 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRY, szName[1]) + cchName;
358 if (pcbDirEntry)
359 *pcbDirEntry = cbRequired;
360 if (cbRequired <= cbDirEntry)
361 {
362 /*
363 * Setup the returned data.
364 */
365 pDirEntry->INodeId = pDir->Data.d_ino; /* may need #ifdefing later */
366#ifdef HAVE_DIRENT_D_TYPE
367 pDirEntry->enmType = rtDirType(pDir->Data.d_type);
368#else
369 pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
370#endif
371 pDirEntry->cbName = (uint16_t)cchName;
372 Assert(pDirEntry->cbName == cchName);
373 memcpy(pDirEntry->szName, pszName, cchName + 1);
374
375 /* free cached data */
376 pDir->fDataUnread = false;
377 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
378 pDir->pszName = NULL;
379 }
380 else
381 rc = VERR_BUFFER_OVERFLOW;
382 }
383
384 LogFlow(("RTDirRead(%p:{%s}, %p:{%s}, %p:{%u}): returns %Rrc\n",
385 pDir, pDir->pszPath, pDirEntry, RT_SUCCESS(rc) ? pDirEntry->szName : "<failed>",
386 pcbDirEntry, pcbDirEntry ? *pcbDirEntry : 0, rc));
387 return rc;
388}
389
390
391/**
392 * Fills dummy info into the info structure.
393 * This function is called if we cannot stat the file.
394 *
395 * @param pInfo The struct in question.
396 * @param
397 */
398static void rtDirSetDummyInfo(PRTFSOBJINFO pInfo, RTDIRENTRYTYPE enmType)
399{
400 pInfo->cbObject = 0;
401 pInfo->cbAllocated = 0;
402 RTTimeSpecSetNano(&pInfo->AccessTime, 0);
403 RTTimeSpecSetNano(&pInfo->ModificationTime, 0);
404 RTTimeSpecSetNano(&pInfo->ChangeTime, 0);
405 RTTimeSpecSetNano(&pInfo->BirthTime, 0);
406 memset(&pInfo->Attr, 0, sizeof(pInfo->Attr));
407 pInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
408 switch (enmType)
409 {
410 default:
411 case RTDIRENTRYTYPE_UNKNOWN: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL;
412 case RTDIRENTRYTYPE_FIFO: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FIFO;
413 case RTDIRENTRYTYPE_DEV_CHAR: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_CHAR;
414 case RTDIRENTRYTYPE_DIRECTORY: pInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY;
415 case RTDIRENTRYTYPE_DEV_BLOCK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_BLOCK;
416 case RTDIRENTRYTYPE_FILE: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE;
417 case RTDIRENTRYTYPE_SYMLINK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SYMLINK;
418 case RTDIRENTRYTYPE_SOCKET: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SOCKET;
419 case RTDIRENTRYTYPE_WHITEOUT: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_WHITEOUT;
420 }
421}
422
423
424RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
425{
426 /*
427 * Validate and digest input.
428 */
429 if (!rtDirValidHandle(pDir))
430 return VERR_INVALID_PARAMETER;
431 AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER);
432 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
433 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
434 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
435 VERR_INVALID_PARAMETER);
436 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
437 size_t cbDirEntry = sizeof(*pDirEntry);
438 if (pcbDirEntry)
439 {
440 AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER);
441 cbDirEntry = *pcbDirEntry;
442 AssertMsgReturn(cbDirEntry >= (unsigned)RT_OFFSETOF(RTDIRENTRYEX, szName[2]),
443 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
444 VERR_INVALID_PARAMETER);
445 }
446
447 /*
448 * Fetch more data if necessary and/or convert the name.
449 */
450 int rc = rtDirReadMore(pDir);
451 if (RT_SUCCESS(rc))
452 {
453 /*
454 * Check if we've got enough space to return the data.
455 */
456 const char *pszName = pDir->pszName;
457 const size_t cchName = pDir->cchName;
458 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
459 if (pcbDirEntry)
460 *pcbDirEntry = cbRequired;
461 if (cbRequired <= cbDirEntry)
462 {
463 /*
464 * Setup the returned data.
465 */
466 pDirEntry->cwcShortName = 0;
467 pDirEntry->wszShortName[0] = 0;
468 pDirEntry->cbName = (uint16_t)cchName;
469 Assert(pDirEntry->cbName == cchName);
470 memcpy(pDirEntry->szName, pszName, cchName + 1);
471
472 /* get the info data */
473 size_t cch = cchName + pDir->cchPath + 1;
474 char *pszNamePath = (char *)alloca(cch);
475 if (pszNamePath)
476 {
477 memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
478 memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
479 rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags);
480 }
481 else
482 rc = VERR_NO_MEMORY;
483 if (RT_FAILURE(rc))
484 {
485#ifdef HAVE_DIRENT_D_TYPE
486 rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
487#else
488 rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
489#endif
490 rc = VWRN_NO_DIRENT_INFO;
491 }
492
493 /* free cached data */
494 pDir->fDataUnread = false;
495 rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
496 pDir->pszName = NULL;
497 }
498 else
499 rc = VERR_BUFFER_OVERFLOW;
500 }
501
502 return rc;
503}
504
505
506RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
507{
508 /*
509 * Validate input.
510 */
511 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
512 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
513 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
514 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
515 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
516
517 /*
518 * Take common cause with RTPathRename.
519 */
520 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_DIRECTORY);
521
522 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}): returns %Rrc\n",
523 pszSrc, pszSrc, pszDst, pszDst, rc));
524 return rc;
525}
526
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