VirtualBox

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

Last change on this file since 96124 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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