VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/direnum-win.cpp@ 106061

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.9 KB
Line 
1/* $Id: direnum-win.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Directory Enumeration, Windows.
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 <iprt/win/windows.h>
43
44#include <iprt/dir.h>
45#include <iprt/path.h>
46#include <iprt/mem.h>
47#include <iprt/string.h>
48#include <iprt/assert.h>
49#include <iprt/err.h>
50#include <iprt/file.h>
51#include <iprt/log.h>
52#include "internal/fs.h"
53#include "internal/dir.h"
54
55
56size_t rtDirNativeGetStructSize(const char *pszPath)
57{
58 NOREF(pszPath);
59 return sizeof(RTDIRINTERNAL);
60}
61
62
63int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative))
64{
65 RT_NOREF(hRelativeDir, pvNativeRelative);
66
67 /*
68 * Setup the search expression.
69 *
70 * pszPathBuf is pointing to the return 4K return buffer for the RTPathReal()
71 * call in rtDirOpenCommon(), so all we gota do is check that we don't overflow
72 * it when adding the wildcard expression.
73 */
74/** @todo the pszPathBuf argument was removed in order to support paths longer than RTPATH_MAX. Rewrite this code. */
75 size_t cbExpr;
76 const char *pszExpr;
77 if (pDir->enmFilter == RTDIRFILTER_WINNT)
78 {
79 pszExpr = pDir->pszFilter;
80 cbExpr = pDir->cchFilter + 1;
81 }
82 else
83 {
84 pszExpr = "*";
85 cbExpr = sizeof("*");
86 }
87 if (pDir->cchPath + cbExpr > RTPATH_MAX)
88 return VERR_FILENAME_TOO_LONG;
89 memcpy(pszPathBuf + pDir->cchPath, pszExpr, cbExpr);
90
91
92 /*
93 * Attempt opening the search.
94 */
95 PRTUTF16 pwszName;
96 int rc = RTPathWinFromUtf8(pwszPathBuf, &pwszName, 0 /*fFlags*/);
97 if (RT_SUCCESS(rc))
98 {
99 pDir->hDir = FindFirstFileW((LPCWSTR)pwszName, &pDir->Data);
100 if (pDir->hDir != INVALID_HANDLE_VALUE)
101 pDir->fDataUnread = true;
102 else
103 {
104 DWORD dwErr = GetLastError();
105 /* Theoretical case of an empty directory or more normal case of no matches. */
106 if ( dwErr == ERROR_FILE_NOT_FOUND
107 || dwErr == ERROR_NO_MORE_FILES /* ???*/)
108 pDir->fDataUnread = false;
109 else
110 rc = RTErrConvertFromWin32(GetLastError());
111 }
112 RTPathWinFree(pwszName);
113 }
114
115 return rc;
116}
117
118
119RTDECL(int) RTDirClose(PRTDIRINTERNAL pDir)
120{
121 /*
122 * Validate input.
123 */
124 if (!pDir)
125 return VERR_INVALID_PARAMETER;
126 if (pDir->u32Magic != RTDIR_MAGIC)
127 {
128 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
129 return VERR_INVALID_PARAMETER;
130 }
131
132 /*
133 * Close the handle.
134 */
135 pDir->u32Magic++;
136 if (pDir->hDir != INVALID_HANDLE_VALUE)
137 {
138 BOOL fRc = FindClose(pDir->hDir);
139 Assert(fRc);
140 pDir->hDir = INVALID_HANDLE_VALUE;
141 }
142 RTStrFree(pDir->pszName);
143 pDir->pszName = NULL;
144 RTMemFree(pDir);
145
146 return VINF_SUCCESS;
147}
148
149
150RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
151{
152 PPRTDIRINTERNAL pDir = hDir;
153
154 /*
155 * Validate input.
156 */
157 if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
158 {
159 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
160 return VERR_INVALID_PARAMETER;
161 }
162 if (!pDirEntry)
163 {
164 AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
165 return VERR_INVALID_PARAMETER;
166 }
167 size_t cbDirEntry = sizeof(*pDirEntry);
168 if (pcbDirEntry)
169 {
170 cbDirEntry = *pcbDirEntry;
171 if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRY, szName[2]))
172 {
173 AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])));
174 return VERR_INVALID_PARAMETER;
175 }
176 }
177
178 /*
179 * Fetch data?
180 */
181 if (!pDir->fDataUnread)
182 {
183 RTStrFree(pDir->pszName);
184 pDir->pszName = NULL;
185
186 BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
187 if (!fRc)
188 {
189 int iErr = GetLastError();
190 if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
191 return VERR_NO_MORE_FILES;
192 return RTErrConvertFromWin32(iErr);
193 }
194 }
195
196 /*
197 * Convert the filename to UTF-8.
198 */
199 if (!pDir->pszName)
200 {
201 int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
202 if (RT_FAILURE(rc))
203 {
204 pDir->pszName = NULL;
205 return rc;
206 }
207 pDir->cchName = strlen(pDir->pszName);
208 }
209
210 /*
211 * Check if we've got enough space to return the data.
212 */
213 const char *pszName = pDir->pszName;
214 const size_t cchName = pDir->cchName;
215 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
216 if (pcbDirEntry)
217 *pcbDirEntry = cbRequired;
218 if (cbRequired > cbDirEntry)
219 return VERR_BUFFER_OVERFLOW;
220
221 /*
222 * Setup the returned data.
223 */
224 pDir->fDataUnread = false;
225 pDirEntry->INodeId = 0; /** @todo we can use the fileid here if we must (see GetFileInformationByHandle). */
226 pDirEntry->enmType = pDir->Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
227 ? RTDIRENTRYTYPE_DIRECTORY : RTDIRENTRYTYPE_FILE;
228 pDirEntry->cbName = (uint16_t)cchName;
229 Assert(pDirEntry->cbName == cchName);
230 memcpy(pDirEntry->szName, pszName, cchName + 1);
231
232 return VINF_SUCCESS;
233}
234
235
236RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
237{
238 PPRTDIRINTERNAL pDir = hDir;
239 /** @todo Symlinks: Find[First|Next]FileW will return info about
240 the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
241 /*
242 * Validate input.
243 */
244 if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
245 {
246 AssertMsgFailed(("Invalid pDir=%p\n", pDir));
247 return VERR_INVALID_PARAMETER;
248 }
249 if (!pDirEntry)
250 {
251 AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
252 return VERR_INVALID_PARAMETER;
253 }
254 if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
255 || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
256 {
257 AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
258 return VERR_INVALID_PARAMETER;
259 }
260 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
261 size_t cbDirEntry = sizeof(*pDirEntry);
262 if (pcbDirEntry)
263 {
264 cbDirEntry = *pcbDirEntry;
265 if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2]))
266 {
267 AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])));
268 return VERR_INVALID_PARAMETER;
269 }
270 }
271
272 /*
273 * Fetch data?
274 */
275 if (!pDir->fDataUnread)
276 {
277 RTStrFree(pDir->pszName);
278 pDir->pszName = NULL;
279
280 BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
281 if (!fRc)
282 {
283 int iErr = GetLastError();
284 if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
285 return VERR_NO_MORE_FILES;
286 return RTErrConvertFromWin32(iErr);
287 }
288 }
289
290 /*
291 * Convert the filename to UTF-8.
292 */
293 if (!pDir->pszName)
294 {
295 int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
296 if (RT_FAILURE(rc))
297 {
298 pDir->pszName = NULL;
299 return rc;
300 }
301 pDir->cchName = strlen(pDir->pszName);
302 }
303
304 /*
305 * Check if we've got enough space to return the data.
306 */
307 const char *pszName = pDir->pszName;
308 const size_t cchName = pDir->cchName;
309 const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
310 if (pcbDirEntry)
311 *pcbDirEntry = cbRequired;
312 if (cbRequired > cbDirEntry)
313 return VERR_BUFFER_OVERFLOW;
314
315 /*
316 * Setup the returned data.
317 */
318 pDir->fDataUnread = false;
319 pDirEntry->cbName = (uint16_t)cchName;
320 Assert(pDirEntry->cbName == cchName);
321 memcpy(pDirEntry->szName, pszName, cchName + 1);
322 if (pDir->Data.cAlternateFileName[0])
323 {
324 /* copy and calc length */
325 PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName;
326 PRTUTF16 pwszDst = pDirEntry->wszShortName;
327 uint32_t off = 0;
328 while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off])
329 {
330 pwszDst[off] = pwszSrc[off];
331 off++;
332 }
333 pDirEntry->cwcShortName = (uint16_t)off;
334
335 /* zero the rest */
336 do
337 pwszDst[off++] = '\0';
338 while (off < RT_ELEMENTS(pDirEntry->wszShortName));
339 }
340 else
341 {
342 memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
343 pDirEntry->cwcShortName = 0;
344 }
345
346 pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32)
347 | (uint64_t)pDir->Data.nFileSizeLow;
348 pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject;
349
350 Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime));
351 RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime);
352 RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime);
353 RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime);
354 pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime;
355
356 pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
357 pszName, cchName, pDir->Data.dwReserved0, 0);
358
359 /*
360 * Requested attributes (we cannot provide anything actually).
361 */
362 switch (enmAdditionalAttribs)
363 {
364 case RTFSOBJATTRADD_EASIZE:
365 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
366 pDirEntry->Info.Attr.u.EASize.cb = 0;
367 break;
368
369 case RTFSOBJATTRADD_UNIX:
370 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
371 pDirEntry->Info.Attr.u.Unix.uid = ~0U;
372 pDirEntry->Info.Attr.u.Unix.gid = ~0U;
373 pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
374 pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */
375 pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */
376 pDirEntry->Info.Attr.u.Unix.fFlags = 0;
377 pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
378 pDirEntry->Info.Attr.u.Unix.Device = 0;
379 break;
380
381 case RTFSOBJATTRADD_NOTHING:
382 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
383 break;
384
385 case RTFSOBJATTRADD_UNIX_OWNER:
386 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
387 pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U;
388 pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
389 break;
390
391 case RTFSOBJATTRADD_UNIX_GROUP:
392 pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
393 pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U;
394 pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
395 break;
396
397 default:
398 AssertMsgFailed(("Impossible!\n"));
399 return VERR_INTERNAL_ERROR;
400 }
401
402 return VINF_SUCCESS;
403}
404
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