VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 17 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
RevLine 
[1]1/* $Id: fs-win.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
[8245]3 * IPRT - File System, Win32.
[1]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[1]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[5999]11 *
[96407]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 *
[5999]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]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
[5999]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.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[1]35 */
36
37
[57358]38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
[1]41#define LOG_GROUP RTLOGGROUP_FS
[62592]42#include <iprt/win/windows.h>
[1]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
[30276]53/* from ntdef.h */
54typedef LONG NTSTATUS;
[1]55
[30276]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;
[57974]74 ULONG FileSystemNameLength;
[30276]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
[1]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
[7418]124
[1]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.
[7418]130 * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(),
[1]131 */
[7418]132static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot)
[1]133{
134 /*
135 * Do straight forward stuff first,
136 */
137 if (rtFsIsRoot(pszFsPath))
[7418]138 return RTStrToUtf16(pszFsPath, ppwszFsRoot);
[1]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 */
[7418]158 rc = RTStrToUtf16(szFullPath, ppwszFsRoot);
[1]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 */
[7418]165 PRTUTF16 pwszStart = *ppwszFsRoot;
166 if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0))
[1]167 {
[7418]168 PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart);
169 PRTUTF16 pwszMin = pwszStart + 2;
[1]170 do
171 {
172 /* Strip off the last path component. */
[7418]173 while (pwszEnd-- > pwszMin)
174 if (RTPATH_IS_SLASH(*pwszEnd))
[1]175 break;
[7418]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));
[1]179 }
180
181 return VINF_SUCCESS;
182}
183
184/**
185 * Frees string returned by rtFsGetRoot().
186 */
[7418]187static void rtFsFreeRoot(PRTUTF16 pwszFsRoot)
[1]188{
[7418]189 RTUtf16Free(pwszFsRoot);
[1]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 */
[90789]199 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
200 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
[7418]201 PRTUTF16 pwszFsRoot;
202 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
[1]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;
[7418]213 if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL))
[1]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);
[13837]224 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
[1]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;
[7418]238 if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2))
[1]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);
[13837]249 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n",
[1]250 pszFsPath, Err, rc));
251 }
252 }
253
[7418]254 rtFsFreeRoot(pwszFsRoot);
[1]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 */
[90789]271 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
272 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
273 AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
[7418]274 PRTUTF16 pwszFsRoot;
275 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
[1]276 if (RT_FAILURE(rc))
277 return rc;
278
279 /*
280 * Do work.
281 */
282 DWORD dwMaxName;
283 DWORD dwFlags;
284 DWORD dwSerial;
[7418]285 if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
[1]286 *pu32Serial = dwSerial;
287 else
288 {
289 DWORD Err = GetLastError();
290 rc = RTErrConvertFromWin32(Err);
[13837]291 Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
[1]292 pszFsPath, Err, rc));
293 }
[5705]294
[74460]295 rtFsFreeRoot(pwszFsRoot);
[1]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 */
[90789]312 AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
313 AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
314 AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
[7418]315 PRTUTF16 pwszFsRoot;
316 int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
[1]317 if (RT_FAILURE(rc))
318 return rc;
319
320 /*
321 * Do work.
322 */
323 DWORD dwMaxName;
324 DWORD dwFlags;
325 DWORD dwSerial;
[7418]326 if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
[1]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 */
[46008]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. */
[1]338 pProperties->fRemote = false; /* no idea yet */
339 }
340 else
341 {
342 DWORD Err = GetLastError();
343 rc = RTErrConvertFromWin32(Err);
[13837]344 Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n",
[1]345 pszFsPath, Err, rc));
346 }
[5702]347
[74460]348 rtFsFreeRoot(pwszFsRoot);
[1]349 return rc;
350}
351
[30365]352
[57613]353RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
354{
355 return false;
356}
357
358
[30365]359/**
360 * Internal helper for comparing a WCHAR string with a char string.
361 *
[30371]362 * @returns @c true if equal, @c false if not.
[32431]363 * @param pwsz1 The first string.
[57974]364 * @param cch1 The length of the first string, in bytes.
[32431]365 * @param psz2 The second string.
366 * @param cch2 The length of the second string.
[30365]367 */
[30371]368static bool rtFsWinAreEqual(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
[30276]369{
[30371]370 if (cch1 != cch2 * 2)
371 return false;
372 while (cch2-- > 0)
[30365]373 {
374 unsigned ch1 = *pwsz1++;
375 unsigned ch2 = (unsigned char)*psz2++;
376 if (ch1 != ch2)
[30371]377 return false;
[30365]378 }
[30371]379 return true;
[30365]380}
[30276]381
382
[30365]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;
[74460]394 int rc = RTPathWinFromUtf8(&pwszFsPath, pszFsPath, 0 /*fFlags*/);
[30365]395 if (RT_SUCCESS(rc))
[30276]396 {
[30365]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)
[30276]405 {
[30365]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;
[30371]417#define IS_FS(szName) \
[57974]418 rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FileSystemNameLength, szName, sizeof(szName) - 1)
[30371]419 if (IS_FS("NTFS"))
[30365]420 *penmType = RTFSTYPE_NTFS;
[30371]421 else if (IS_FS("FAT"))
[30365]422 *penmType = RTFSTYPE_FAT;
[30371]423 else if (IS_FS("FAT32"))
[30365]424 *penmType = RTFSTYPE_FAT;
[67731]425 else if (IS_FS("EXFAT"))
426 *penmType = RTFSTYPE_EXFAT;
[30371]427 else if (IS_FS("VBoxSharedFolderFS"))
[30365]428 *penmType = RTFSTYPE_VBOXSHF;
[30371]429#undef IS_FS
[30365]430 }
431 else
432 rc = RTErrConvertFromNtStatus(rcNt);
433 CloseHandle(hFile);
[30276]434 }
[30365]435 else
436 rc = RTErrConvertFromWin32(GetLastError());
[74460]437 RTPathWinFree(pwszFsPath);
[30276]438 }
439 return rc;
440}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use