VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/fs-win.cpp

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.4 KB
Line 
1/* $Id: fs-win.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - File System, Win32.
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_FS
42#include <iprt/win/windows.h>
43
44#include <iprt/fs.h>
45#include <iprt/path.h>
46#include <iprt/string.h>
47#include <iprt/param.h>
48#include <iprt/err.h>
49#include <iprt/log.h>
50#include <iprt/assert.h>
51#include "internal/fs.h"
52
53/* from ntdef.h */
54typedef LONG NTSTATUS;
55
56/* from ntddk.h */
57typedef struct _IO_STATUS_BLOCK {
58 union {
59 NTSTATUS Status;
60 PVOID Pointer;
61 };
62 ULONG_PTR Information;
63} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
64
65typedef enum _FSINFOCLASS {
66 FileFsAttributeInformation = 5,
67} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
68
69/* from ntifs.h */
70
71typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
72 ULONG FileSystemAttributes;
73 LONG MaximumComponentNameLength;
74 ULONG FileSystemNameLength;
75 WCHAR FileSystemName[1];
76} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
77
78extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS);
79
80/**
81 * Checks quickly if this is an correct root specification.
82 * Root specs ends with a slash of some kind.
83 *
84 * @returns indicator.
85 * @param pszFsPath Path to check.
86 */
87static bool rtFsIsRoot(const char *pszFsPath)
88{
89 /*
90 * UNC has exactly two slashes..
91 *
92 * Anything else starting with slashe(s) requires
93 * expansion and will have to take the long road.
94 */
95 if (RTPATH_IS_SLASH(pszFsPath[0]))
96 {
97 if ( !RTPATH_IS_SLASH(pszFsPath[1])
98 || RTPATH_IS_SLASH(pszFsPath[2]))
99 return false;
100
101 /* end of machine name */
102 const char *pszSlash = strpbrk(pszFsPath + 2, "\\/");
103 if (!pszSlash)
104 return false;
105
106 /* end of service name. */
107 pszSlash = strpbrk(pszSlash + 1, "\\/");
108 if (!pszSlash)
109 return false;
110
111 return pszSlash[1] == '\0';
112 }
113
114 /*
115 * Ok the other alternative is driver letter.
116 */
117 return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z'
118 && pszFsPath[1] == ':'
119 && RTPATH_IS_SLASH(pszFsPath[2])
120 && !pszFsPath[3];
121}
122
123
124
125/**
126 * Finds the root of the specified volume.
127 *
128 * @returns iprt status code.
129 * @param pszFsPath Path within the filesystem. Verified as one byte or more.
130 * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(),
131 */
132static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot)
133{
134 /*
135 * Do straight forward stuff first,
136 */
137 if (rtFsIsRoot(pszFsPath))
138 return RTStrToUtf16(pszFsPath, ppwszFsRoot);
139
140 /*
141 * Expand and add slash (if required).
142 */
143 char szFullPath[RTPATH_MAX];
144 int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath));
145 if (RT_FAILURE(rc))
146 return rc;
147 size_t cb = strlen(szFullPath);
148 if (!RTPATH_IS_SLASH(szFullPath[cb - 1]))
149 {
150 AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG);
151 szFullPath[cb] = '\\';
152 szFullPath[++cb] = '\0';
153 }
154
155 /*
156 * Convert the path.
157 */
158 rc = RTStrToUtf16(szFullPath, ppwszFsRoot);
159 if (RT_FAILURE(rc))
160 return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
161
162 /*
163 * Walk the path until our proper API is happy or there is no more path left.
164 */
165 PRTUTF16 pwszStart = *ppwszFsRoot;
166 if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0))
167 {
168 PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart);
169 PRTUTF16 pwszMin = pwszStart + 2;
170 do
171 {
172 /* Strip off the last path component. */
173 while (pwszEnd-- > pwszMin)
174 if (RTPATH_IS_SLASH(*pwszEnd))
175 break;
176 AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */
177 pwszEnd[1] = '\0';
178 } while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0));
179 }
180
181 return VINF_SUCCESS;
182}
183
184/**
185 * Frees string returned by rtFsGetRoot().
186 */
187static void rtFsFreeRoot(PRTUTF16 pwszFsRoot)
188{
189 RTUtf16Free(pwszFsRoot);
190}
191
192
193RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
194 uint32_t *pcbBlock, uint32_t *pcbSector)
195{
196 /*
197 * Validate & get valid root path.
198 */
199 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
200 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
201 PRTUTF16 pwszFsRoot;
202 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
203 if (RT_FAILURE(rc))
204 return rc;
205
206 /*
207 * Free and total.
208 */
209 if (pcbTotal || pcbFree)
210 {
211 ULARGE_INTEGER cbTotal;
212 ULARGE_INTEGER cbFree;
213 if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL))
214 {
215 if (pcbTotal)
216 *pcbTotal = cbTotal.QuadPart;
217 if (pcbFree)
218 *pcbFree = cbFree.QuadPart;
219 }
220 else
221 {
222 DWORD Err = GetLastError();
223 rc = RTErrConvertFromWin32(Err);
224 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
225 pszFsPath, Err, rc));
226 }
227 }
228
229 /*
230 * Block and sector size.
231 */
232 if ( RT_SUCCESS(rc)
233 && (pcbBlock || pcbSector))
234 {
235 DWORD dwDummy1, dwDummy2;
236 DWORD cbSector;
237 DWORD cSectorsPerCluster;
238 if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2))
239 {
240 if (pcbBlock)
241 *pcbBlock = cbSector * cSectorsPerCluster;
242 if (pcbSector)
243 *pcbSector = cbSector;
244 }
245 else
246 {
247 DWORD Err = GetLastError();
248 rc = RTErrConvertFromWin32(Err);
249 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n",
250 pszFsPath, Err, rc));
251 }
252 }
253
254 rtFsFreeRoot(pwszFsRoot);
255 return rc;
256}
257
258
259/**
260 * Query the serial number of a filesystem.
261 *
262 * @returns iprt status code.
263 * @param pszFsPath Path within the mounted filesystem.
264 * @param pu32Serial Where to store the serial number.
265 */
266RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
267{
268 /*
269 * Validate & get valid root path.
270 */
271 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
272 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
273 AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
274 PRTUTF16 pwszFsRoot;
275 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
276 if (RT_FAILURE(rc))
277 return rc;
278
279 /*
280 * Do work.
281 */
282 DWORD dwMaxName;
283 DWORD dwFlags;
284 DWORD dwSerial;
285 if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
286 *pu32Serial = dwSerial;
287 else
288 {
289 DWORD Err = GetLastError();
290 rc = RTErrConvertFromWin32(Err);
291 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
292 pszFsPath, Err, rc));
293 }
294
295 rtFsFreeRoot(pwszFsRoot);
296 return rc;
297}
298
299
300/**
301 * Query the properties of a mounted filesystem.
302 *
303 * @returns iprt status code.
304 * @param pszFsPath Path within the mounted filesystem.
305 * @param pProperties Where to store the properties.
306 */
307RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
308{
309 /*
310 * Validate & get valid root path.
311 */
312 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
313 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
314 AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
315 PRTUTF16 pwszFsRoot;
316 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
317 if (RT_FAILURE(rc))
318 return rc;
319
320 /*
321 * Do work.
322 */
323 DWORD dwMaxName;
324 DWORD dwFlags;
325 DWORD dwSerial;
326 if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
327 {
328 memset(pProperties, 0, sizeof(*pProperties));
329 pProperties->cbMaxComponent = dwMaxName;
330 pProperties->fFileCompression = !!(dwFlags & FILE_FILE_COMPRESSION);
331 pProperties->fCompressed = !!(dwFlags & FILE_VOLUME_IS_COMPRESSED);
332 pProperties->fReadOnly = !!(dwFlags & FILE_READ_ONLY_VOLUME);
333 pProperties->fSupportsUnicode = !!(dwFlags & FILE_UNICODE_ON_DISK);
334 pProperties->fCaseSensitive = false; /* win32 is case preserving only */
335 /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS
336 * as well perchance? If so, better mention it instead of just setting
337 * fCaseSensitive to false. */
338 pProperties->fRemote = false; /* no idea yet */
339 }
340 else
341 {
342 DWORD Err = GetLastError();
343 rc = RTErrConvertFromWin32(Err);
344 Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n",
345 pszFsPath, Err, rc));
346 }
347
348 rtFsFreeRoot(pwszFsRoot);
349 return rc;
350}
351
352
353RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
354{
355 return false;
356}
357
358
359/**
360 * Internal helper for comparing a WCHAR string with a char string.
361 *
362 * @returns @c true if equal, @c false if not.
363 * @param pwsz1 The first string.
364 * @param cch1 The length of the first string, in bytes.
365 * @param psz2 The second string.
366 * @param cch2 The length of the second string.
367 */
368static bool rtFsWinAreEqual(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
369{
370 if (cch1 != cch2 * 2)
371 return false;
372 while (cch2-- > 0)
373 {
374 unsigned ch1 = *pwsz1++;
375 unsigned ch2 = (unsigned char)*psz2++;
376 if (ch1 != ch2)
377 return false;
378 }
379 return true;
380}
381
382
383RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
384{
385 *penmType = RTFSTYPE_UNKNOWN;
386
387 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
388 AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
389
390 /*
391 * Convert the path and try open it.
392 */
393 PRTUTF16 pwszFsPath;
394 int rc = RTPathWinFromUtf8(&pwszFsPath, pszFsPath, 0 /*fFlags*/);
395 if (RT_SUCCESS(rc))
396 {
397 HANDLE hFile = CreateFileW(pwszFsPath,
398 GENERIC_READ,
399 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
400 NULL,
401 OPEN_EXISTING,
402 FILE_FLAG_BACKUP_SEMANTICS,
403 NULL);
404 if (hFile != INVALID_HANDLE_VALUE)
405 {
406 /*
407 * Use the NT api directly to get the file system name.
408 */
409 char abBuf[8192];
410 IO_STATUS_BLOCK Ios;
411 NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios,
412 abBuf, sizeof(abBuf),
413 FileFsAttributeInformation);
414 if (rcNt >= 0)
415 {
416 PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf;
417#define IS_FS(szName) \
418 rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FileSystemNameLength, szName, sizeof(szName) - 1)
419 if (IS_FS("NTFS"))
420 *penmType = RTFSTYPE_NTFS;
421 else if (IS_FS("FAT"))
422 *penmType = RTFSTYPE_FAT;
423 else if (IS_FS("FAT32"))
424 *penmType = RTFSTYPE_FAT;
425 else if (IS_FS("EXFAT"))
426 *penmType = RTFSTYPE_EXFAT;
427 else if (IS_FS("VBoxSharedFolderFS"))
428 *penmType = RTFSTYPE_VBOXSHF;
429#undef IS_FS
430 }
431 else
432 rc = RTErrConvertFromNtStatus(rcNt);
433 CloseHandle(hFile);
434 }
435 else
436 rc = RTErrConvertFromWin32(GetLastError());
437 RTPathWinFree(pwszFsPath);
438 }
439 return rc;
440}
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