VirtualBox

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

Last change on this file since 5841 was 5346, checked in by vboxsync, 17 years ago

On solaris ENOSYS doesn't mean what we think it does. mkdir returns it on a NFS mount point for instance.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/* $Id: dir-posix.cpp 5346 2007-10-17 10:09:18Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Directory manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP RTLOGGROUP_DIR
23#include <errno.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <dirent.h>
28#include <stdio.h>
29
30#include <iprt/dir.h>
31#include <iprt/path.h>
32#include <iprt/alloc.h>
33#include <iprt/alloca.h>
34#include <iprt/string.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/log.h>
38#include "internal/dir.h"
39#include "internal/fs.h"
40#include "internal/path.h"
41
42#if !defined(RT_OS_SOLARIS)
43# define HAVE_DIRENT_D_TYPE 1
44#endif
45
46
47RTDECL(bool) RTDirExists(const char *pszPath)
48{
49 bool fRc = false;
50 char *pszNativePath;
51 int rc = rtPathToNative(&pszNativePath, pszPath);
52 if (RT_SUCCESS(rc))
53 {
54 struct stat s;
55 fRc = !stat(pszNativePath, &s)
56 && S_ISDIR(s.st_mode);
57
58 rtPathFreeNative(pszNativePath);
59 }
60
61 LogFlow(("RTDirExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
62 return fRc;
63}
64
65
66RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode)
67{
68 int rc;
69 fMode = rtFsModeNormalize(fMode, pszPath, 0);
70 if (rtFsModeIsValidPermissions(fMode))
71 {
72 char *pszNativePath;
73 rc = rtPathToNative(&pszNativePath, pszPath);
74 if (RT_SUCCESS(rc))
75 {
76 if (mkdir(pszNativePath, fMode & RTFS_UNIX_MASK))
77 {
78#ifdef RT_OS_SOLARIS
79 if (errno == ENOSYS) /* ENOSYS has a slight different meaning (mkdir on nfs mount point). */
80 rc = VERR_ALREADY_EXISTS;
81 else
82#endif
83 rc = RTErrConvertFromErrno(errno);
84 }
85 }
86
87 rtPathFreeNative(pszNativePath);
88 }
89 else
90 {
91 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
92 rc = VERR_INVALID_FMODE;
93 }
94 LogFlow(("RTDirCreate(%p={%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
95 return rc;
96}
97
98
99RTDECL(int) RTDirRemove(const char *pszPath)
100{
101 char *pszNativePath;
102 int rc = rtPathToNative(&pszNativePath, pszPath);
103 if (RT_SUCCESS(rc))
104 {
105 if (rmdir(pszNativePath))
106 rc = RTErrConvertFromErrno(errno);
107
108 rtPathFreeNative(pszNativePath);
109 }
110
111 LogFlow(("RTDirRemove(%p={%s}): returns %Rrc\n", pszPath, pszPath, rc));
112 return rc;
113}
114
115
116int rtOpenDirNative(PRTDIR pDir, char *pszPathBuf)
117{
118 /*
119 * Convert to a native path and try opendir.
120 */
121 char *pszNativePath;
122 int rc = rtPathToNative(&pszNativePath, pDir->pszPath);
123 if (RT_SUCCESS(rc))
124 {
125 pDir->pDir = opendir(pszNativePath);
126 if (pDir->pDir)
127 {
128 /*
129 * Init data.
130 */
131 pDir->fDataUnread = false;
132 memset(&pDir->Data, 0, sizeof(pDir->Data)); /* not strictly necessary */
133 }
134 else
135 rc = RTErrConvertFromErrno(errno);
136
137 rtPathFreeNative(pszNativePath);
138 }
139
140 return rc;
141}
142
143
144RTDECL(int) RTDirClose(PRTDIR pDir)
145{
146 /*
147 * Validate input.
148 */
149 if (!pDir)
150 return VERR_INVALID_PARAMETER;
151 if (pDir->u32Magic != RTDIR_MAGIC)
152 {
153 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
154 return VERR_INVALID_PARAMETER;
155 }
156
157 /*
158 * Close the handle.
159 */
160 int rc = VINF_SUCCESS;
161 pDir->u32Magic = RTDIR_MAGIC_DEAD;
162 if (closedir(pDir->pDir))
163 {
164 rc = RTErrConvertFromErrno(errno);
165 AssertMsgFailed(("closedir(%p) -> errno=%d (%Rrc)\n", pDir->pDir, errno, rc));
166 }
167
168 RTMemFree(pDir);
169 return rc;
170}
171
172
173/**
174 * Ensure that there is unread data in the buffer
175 * and that there is a converted filename hanging around.
176 *
177 * @returns IPRT status code.
178 * @param pDir the open directory. Fully validated.
179 */
180static int rtDirReadMore(PRTDIR pDir)
181{
182 /** @todo try avoid the rematching on buffer overflow errors. */
183 for (;;)
184 {
185 /*
186 * Fetch data?
187 */
188 if (!pDir->fDataUnread)
189 {
190 struct dirent *pResult = NULL;
191 int rc = readdir_r(pDir->pDir, &pDir->Data, &pResult);
192 if (rc)
193 {
194 rc = RTErrConvertFromErrno(rc);
195 AssertRC(rc);
196 return rc;
197 }
198 if (!pResult)
199 return VERR_NO_MORE_FILES;
200 }
201
202#ifndef RT_DONT_CONVERT_FILENAMES
203 /*
204 * Convert the filename to UTF-8.
205 */
206 if (!pDir->pszName)
207 {
208 int rc = rtPathFromNativeEx(&pDir->pszName, pDir->Data.d_name, pDir->pszPath);
209 if (RT_FAILURE(rc))
210 {
211 pDir->pszName = NULL;
212 return rc;
213 }
214 pDir->cchName = strlen(pDir->pszName);
215 }
216 if ( !pDir->pfnFilter
217 || pDir->pfnFilter(pDir, pDir->pszName))
218 break;
219 RTStrFree(pDir->pszName);
220 pDir->pszName = NULL;
221#else
222 if ( !pDir->pfnFilter
223 || pDir->pfnFilter(pDir, pDir->Data.d_name))
224 break;
225#endif
226 pDir->fDataUnread = false;
227 }
228
229 pDir->fDataUnread = true;
230 return VINF_SUCCESS;
231}
232
233
234#ifdef HAVE_DIRENT_D_TYPE
235/**
236 * Converts the d_type field to IPRT directory entry type.
237 *
238 * @returns IPRT directory entry type.
239 * @param Unix
240 */
241static RTDIRENTRYTYPE rtDirType(int iType)
242{
243 switch (iType)
244 {
245 case DT_UNKNOWN: return RTDIRENTRYTYPE_UNKNOWN;
246 case DT_FIFO: return RTDIRENTRYTYPE_FIFO;
247 case DT_CHR: return RTDIRENTRYTYPE_DEV_CHAR;
248 case DT_DIR: return RTDIRENTRYTYPE_DIRECTORY;
249 case DT_BLK: return RTDIRENTRYTYPE_DEV_BLOCK;
250 case DT_REG: return RTDIRENTRYTYPE_FILE;
251 case DT_LNK: return RTDIRENTRYTYPE_SYMLINK;
252 case DT_SOCK: return RTDIRENTRYTYPE_SOCKET;
253 case DT_WHT: return RTDIRENTRYTYPE_WHITEOUT;
254 default:
255 AssertMsgFailed(("iType=%d\n", iType));
256 return RTDIRENTRYTYPE_UNKNOWN;
257 }
258}
259#endif /*HAVE_DIRENT_D_TYPE */
260
261
262RTDECL(int) RTDirRead(PRTDIR pDir, PRTDIRENTRY pDirEntry, unsigned *pcbDirEntry)
263{
264 /*
265 * Validate and digest input.
266 */
267 if (!rtDirValidHandle(pDir))
268 return VERR_INVALID_PARAMETER;
269 AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER);
270
271 unsigned cbDirEntry = sizeof(*pDirEntry);
272 if (pcbDirEntry)
273 {
274 AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER);
275 cbDirEntry = *pcbDirEntry;
276 AssertMsgReturn(cbDirEntry >= (unsigned)RT_OFFSETOF(RTDIRENTRY, szName[2]),
277 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
278 VERR_INVALID_PARAMETER);
279 }
280
281 /*
282 * Fetch more data if necessary and/or convert the name.
283 */
284 int rc = rtDirReadMore(pDir);
285 if (RT_SUCCESS(rc))
286 {
287 /*
288 * Check if we've got enough space to return the data.
289 */
290#ifdef RT_DONT_CONVERT_FILENAMES
291 const char *pszName = pDir->Data.d_name;
292 const size_t cchName = strlen(pszName);
293#else
294 const char *pszName = pDir->pszName;
295 const size_t cchName = pDir->cchName;
296#endif
297 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRY, szName[1]) + cchName;
298 if (pcbDirEntry)
299 *pcbDirEntry = cbRequired;
300 if (cbRequired <= cbDirEntry)
301 {
302 /*
303 * Setup the returned data.
304 */
305 pDirEntry->INodeId = pDir->Data.d_ino; /* may need #ifdefing later */
306#ifdef HAVE_DIRENT_D_TYPE
307 pDirEntry->enmType = rtDirType(pDir->Data.d_type);
308#else
309 pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
310#endif
311 pDirEntry->cbName = (uint16_t)cchName;
312 Assert(pDirEntry->cbName == cchName);
313 memcpy(pDirEntry->szName, pszName, cchName + 1);
314
315 /* free cached data */
316 pDir->fDataUnread = false;
317#ifndef RT_DONT_CONVERT_FILENAMES
318 RTStrFree(pDir->pszName);
319 pDir->pszName = NULL;
320#endif
321 }
322 else
323 rc = VERR_BUFFER_OVERFLOW;
324 }
325
326 LogFlow(("RTDirRead(%p:{%s}, %p:{%s}, %p:{%u}): returns %Rrc\n",
327 pDir, pDir->pszPath, pDirEntry, RT_SUCCESS(rc) ? pDirEntry->szName : "<failed>",
328 pcbDirEntry, pcbDirEntry ? *pcbDirEntry : 0, rc));
329 return rc;
330}
331
332
333/**
334 * Fills dummy info into the info structure.
335 * This function is called if we cannot stat the file.
336 *
337 * @param pInfo The struct in question.
338 * @param
339 */
340static void rtDirSetDummyInfo(PRTFSOBJINFO pInfo, RTDIRENTRYTYPE enmType)
341{
342 pInfo->cbObject = 0;
343 pInfo->cbAllocated = 0;
344 RTTimeSpecSetNano(&pInfo->AccessTime, 0);
345 RTTimeSpecSetNano(&pInfo->ModificationTime, 0);
346 RTTimeSpecSetNano(&pInfo->ChangeTime, 0);
347 RTTimeSpecSetNano(&pInfo->BirthTime, 0);
348 memset(&pInfo->Attr, 0, sizeof(pInfo->Attr));
349 pInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
350 switch (enmType)
351 {
352 default:
353 case RTDIRENTRYTYPE_UNKNOWN: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL;
354 case RTDIRENTRYTYPE_FIFO: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FIFO;
355 case RTDIRENTRYTYPE_DEV_CHAR: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_CHAR;
356 case RTDIRENTRYTYPE_DIRECTORY: pInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY;
357 case RTDIRENTRYTYPE_DEV_BLOCK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_BLOCK;
358 case RTDIRENTRYTYPE_FILE: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE;
359 case RTDIRENTRYTYPE_SYMLINK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SYMLINK;
360 case RTDIRENTRYTYPE_SOCKET: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SOCKET;
361 case RTDIRENTRYTYPE_WHITEOUT: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_WHITEOUT;
362 }
363}
364
365
366RTDECL(int) RTDirReadEx(PRTDIR pDir, PRTDIRENTRYEX pDirEntry, unsigned *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs)
367{
368 /*
369 * Validate and digest input.
370 */
371 if (!rtDirValidHandle(pDir))
372 return VERR_INVALID_PARAMETER;
373 AssertMsgReturn(VALID_PTR(pDirEntry), ("%p\n", pDirEntry), VERR_INVALID_POINTER);
374 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
375 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
376 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
377 VERR_INVALID_PARAMETER);
378 unsigned cbDirEntry = sizeof(*pDirEntry);
379 if (pcbDirEntry)
380 {
381 AssertMsgReturn(VALID_PTR(pcbDirEntry), ("%p\n", pcbDirEntry), VERR_INVALID_POINTER);
382 cbDirEntry = *pcbDirEntry;
383 AssertMsgReturn(cbDirEntry >= (unsigned)RT_OFFSETOF(RTDIRENTRYEX, szName[2]),
384 ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
385 VERR_INVALID_PARAMETER);
386 }
387
388 /*
389 * Fetch more data if necessary and/or convert the name.
390 */
391 int rc = rtDirReadMore(pDir);
392 if (RT_SUCCESS(rc))
393 {
394 /*
395 * Check if we've got enough space to return the data.
396 */
397#ifdef RT_DONT_CONVERT_FILENAMES
398 const char *pszName = pDir->Data.d_name;
399 const size_t cchName = strlen(pszName);
400#else
401 const char *pszName = pDir->pszName;
402 const size_t cchName = pDir->cchName;
403#endif
404 const size_t cbRequired = RT_OFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
405 if (pcbDirEntry)
406 *pcbDirEntry = cbRequired;
407 if (cbRequired <= cbDirEntry)
408 {
409 /*
410 * Setup the returned data.
411 */
412 pDirEntry->cucShortName = 0;
413 pDirEntry->uszShortName[0] = 0;
414 pDirEntry->cbName = (uint16_t)cchName;
415 Assert(pDirEntry->cbName == cchName);
416 memcpy(pDirEntry->szName, pszName, cchName + 1);
417
418 /* get the info data */
419 size_t cch = cchName + pDir->cchPath + 1;
420 char *pszNamePath = (char *)alloca(cch);
421 if (pszNamePath)
422 {
423 memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
424 memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
425 rc = RTPathQueryInfo(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs);
426 }
427 else
428 rc = VERR_NO_MEMORY;
429 if (RT_FAILURE(rc))
430 {
431#ifdef HAVE_DIRENT_D_TYPE
432 rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
433#else
434 rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
435#endif
436 rc = VWRN_NO_DIRENT_INFO;
437 }
438
439 /* free cached data */
440 pDir->fDataUnread = false;
441#ifndef RT_DONT_CONVERT_FILENAMES
442 RTStrFree(pDir->pszName);
443 pDir->pszName = NULL;
444#endif
445 }
446 else
447 rc = VERR_BUFFER_OVERFLOW;
448 }
449
450 return rc;
451}
452
453
454RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
455{
456 /*
457 * Validate input.
458 */
459 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
460 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
461 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
462 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
463 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
464
465 /*
466 * Take common cause with RTPathRename.
467 */
468 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_DIRECTORY);
469
470 LogFlow(("RTDirRename(%p:{%s}, %p:{%s}): returns %Rrc\n",
471 pszSrc, pszSrc, pszDst, pszDst, rc));
472 return rc;
473}
474
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