2 * Volume management functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996, 2004 Alexandre Julliard
6 * Copyright 1999 Petr Tomasek
7 * Copyright 2000 Andreas Mohr
8 * Copyright 2003 Eric Pouech
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #define WIN32_NO_STATUS
37 #define WINE_MOUNTMGR_EXTENSIONS
38 #include "ddk/mountmgr.h"
40 #include "kernelbase.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(volume
);
45 #define BLOCK_SIZE 2048
46 #define SUPERBLOCK_SIZE BLOCK_SIZE
47 #define SYMBOLIC_LINK_QUERY 0x0001
49 #define CDFRAMES_PERSEC 75
50 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
51 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
52 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
54 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
55 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
59 FS_ERROR
, /* error accessing the device */
60 FS_UNKNOWN
, /* unknown file system */
64 FS_UDF
/* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
67 /* read the contents of an NT symlink object */
68 static NTSTATUS
read_nt_symlink( const WCHAR
*name
, WCHAR
*target
, DWORD size
)
71 OBJECT_ATTRIBUTES attr
;
75 attr
.Length
= sizeof(attr
);
76 attr
.RootDirectory
= 0;
77 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
78 attr
.ObjectName
= &nameW
;
79 attr
.SecurityDescriptor
= NULL
;
80 attr
.SecurityQualityOfService
= NULL
;
81 RtlInitUnicodeString( &nameW
, name
);
83 if (!(status
= NtOpenSymbolicLinkObject( &handle
, SYMBOLIC_LINK_QUERY
, &attr
)))
85 UNICODE_STRING targetW
;
86 targetW
.Buffer
= target
;
87 targetW
.MaximumLength
= (size
- 1) * sizeof(WCHAR
);
88 status
= NtQuerySymbolicLinkObject( handle
, &targetW
, NULL
);
89 if (!status
) target
[targetW
.Length
/ sizeof(WCHAR
)] = 0;
95 /* open a handle to a device root */
96 static BOOL
open_device_root( LPCWSTR root
, HANDLE
*handle
)
98 UNICODE_STRING nt_name
;
99 OBJECT_ATTRIBUTES attr
;
103 if (!root
) root
= L
"\\";
104 if (!RtlDosPathNameToNtPathName_U( root
, &nt_name
, NULL
, NULL
))
106 SetLastError( ERROR_PATH_NOT_FOUND
);
109 attr
.Length
= sizeof(attr
);
110 attr
.RootDirectory
= 0;
111 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
112 attr
.ObjectName
= &nt_name
;
113 attr
.SecurityDescriptor
= NULL
;
114 attr
.SecurityQualityOfService
= NULL
;
116 status
= NtOpenFile( handle
, SYNCHRONIZE
, &attr
, &io
, 0,
117 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
118 RtlFreeUnicodeString( &nt_name
);
119 return set_ntstatus( status
);
122 /* query the type of a drive from the mount manager */
123 static DWORD
get_mountmgr_drive_type( LPCWSTR root
)
126 struct mountmgr_unix_drive data
;
129 memset( &data
, 0, sizeof(data
) );
130 if (root
) data
.letter
= root
[0];
133 WCHAR curdir
[MAX_PATH
];
134 GetCurrentDirectoryW( MAX_PATH
, curdir
);
135 if (curdir
[1] != ':' || curdir
[2] != '\\') return DRIVE_UNKNOWN
;
136 data
.letter
= curdir
[0];
139 mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, GENERIC_READ
,
140 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
141 if (mgr
== INVALID_HANDLE_VALUE
) return DRIVE_UNKNOWN
;
143 if (!DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
, &data
, sizeof(data
), &data
,
144 sizeof(data
), &br
, NULL
) && GetLastError() != ERROR_MORE_DATA
)
145 data
.type
= DRIVE_UNKNOWN
;
152 /***********************************************************************
153 * GetVolumeInformationW (kernelbase.@)
155 BOOL WINAPI DECLSPEC_HOTPATCH
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
, DWORD label_len
,
156 DWORD
*serial
, DWORD
*filename_len
, DWORD
*flags
,
157 LPWSTR fsname
, DWORD fsname_len
)
161 UNICODE_STRING nt_name
;
163 OBJECT_ATTRIBUTES attr
;
167 if (!root
) root
= L
"\\";
168 if (!RtlDosPathNameToNtPathName_U( root
, &nt_name
, NULL
, NULL
))
170 SetLastError( ERROR_PATH_NOT_FOUND
);
173 /* there must be exactly one backslash in the name, at the end */
174 for (i
= 4; i
< nt_name
.Length
/ sizeof(WCHAR
); i
++) if (nt_name
.Buffer
[i
] == '\\') break;
175 if (i
!= nt_name
.Length
/ sizeof(WCHAR
) - 1)
177 /* check if root contains an explicit subdir */
178 if (root
[0] && root
[1] == ':') root
+= 2;
179 while (*root
== '\\') root
++;
180 if (wcschr( root
, '\\' ))
181 SetLastError( ERROR_DIR_NOT_ROOT
);
183 SetLastError( ERROR_INVALID_NAME
);
187 attr
.Length
= sizeof(attr
);
188 attr
.RootDirectory
= 0;
189 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
190 attr
.ObjectName
= &nt_name
;
191 attr
.SecurityDescriptor
= NULL
;
192 attr
.SecurityQualityOfService
= NULL
;
194 nt_name
.Length
-= sizeof(WCHAR
); /* without trailing slash */
195 status
= NtOpenFile( &handle
, GENERIC_READ
| SYNCHRONIZE
, &attr
, &io
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
196 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
197 nt_name
.Length
+= sizeof(WCHAR
);
201 TRACE( "cannot open device %s: %x\n", debugstr_w(nt_name
.Buffer
), status
);
202 if (status
== STATUS_ACCESS_DENIED
)
203 MESSAGE( "wine: Read access denied for device %s, FS volume label and serial are not available.\n", debugstr_w(nt_name
.Buffer
) );
204 status
= NtOpenFile( &handle
, SYNCHRONIZE
, &attr
, &io
, 0,
205 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
);
208 if (!set_ntstatus( status
)) goto done
;
210 ret
= GetVolumeInformationByHandleW( handle
, label
, label_len
, serial
, filename_len
, flags
,
211 fsname
, fsname_len
);
215 RtlFreeUnicodeString( &nt_name
);
220 /***********************************************************************
221 * GetVolumeInformationA (kernelbase.@)
223 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
224 DWORD label_len
, DWORD
*serial
,
225 DWORD
*filename_len
, DWORD
*flags
,
226 LPSTR fsname
, DWORD fsname_len
)
229 LPWSTR labelW
, fsnameW
;
232 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return FALSE
;
234 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
235 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
237 if ((ret
= GetVolumeInformationW(rootW
, labelW
, label_len
, serial
,
238 filename_len
, flags
, fsnameW
, fsname_len
)))
240 if (label
) file_name_WtoA( labelW
, -1, label
, label_len
);
241 if (fsname
) file_name_WtoA( fsnameW
, -1, fsname
, fsname_len
);
244 HeapFree( GetProcessHeap(), 0, labelW
);
245 HeapFree( GetProcessHeap(), 0, fsnameW
);
250 /***********************************************************************
251 * GetVolumeNameForVolumeMountPointW (kernelbase.@)
253 BOOL WINAPI
GetVolumeNameForVolumeMountPointW( LPCWSTR path
, LPWSTR volume
, DWORD size
)
255 MOUNTMGR_MOUNT_POINT
*input
= NULL
, *o1
;
256 MOUNTMGR_MOUNT_POINTS
*output
= NULL
;
259 DWORD i
, i_size
= 1024, o_size
= 1024;
260 WCHAR
*nonpersist_name
;
261 WCHAR symlink_name
[MAX_PATH
];
263 HANDLE mgr
= INVALID_HANDLE_VALUE
;
267 TRACE("(%s, %p, %x)\n", debugstr_w(path
), volume
, size
);
268 if (path
[lstrlenW(path
)-1] != '\\')
270 SetLastError( ERROR_INVALID_NAME
);
276 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
279 /* if length of input is > 3 then it must be a mounted folder */
280 if (lstrlenW(path
) > 3)
282 FIXME("Mounted Folders are not yet supported\n");
283 SetLastError( ERROR_NOT_A_REPARSE_POINT
);
287 mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, 0, FILE_SHARE_READ
,
288 NULL
, OPEN_EXISTING
, 0, 0 );
289 if (mgr
== INVALID_HANDLE_VALUE
) return FALSE
;
291 if (!(input
= HeapAlloc( GetProcessHeap(), 0, i_size
)))
293 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
297 if (!(output
= HeapAlloc( GetProcessHeap(), 0, o_size
)))
299 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
303 /* construct the symlink name as "\DosDevices\C:" */
304 lstrcpyW( symlink_name
, L
"\\DosDevices\\" );
305 lstrcatW( symlink_name
, path
);
306 symlink_name
[lstrlenW(symlink_name
)-1] = 0;
308 /* Take the mount point and get the "nonpersistent name" */
309 /* We will then take that and get the volume name */
310 nonpersist_name
= (WCHAR
*)(input
+ 1);
311 status
= read_nt_symlink( symlink_name
, nonpersist_name
, i_size
- sizeof(*input
) );
312 TRACE("read_nt_symlink got stat=%x, for %s, got <%s>\n", status
,
313 debugstr_w(symlink_name
), debugstr_w(nonpersist_name
));
314 if (status
!= STATUS_SUCCESS
)
316 SetLastError( ERROR_FILE_NOT_FOUND
);
320 /* Now take the "nonpersistent name" and ask the mountmgr */
321 /* to give us all the mount points. One of them will be */
322 /* the volume name (format of \??\Volume{). */
323 memset( input
, 0, sizeof(*input
) ); /* clear all input parameters */
324 input
->DeviceNameOffset
= sizeof(*input
);
325 input
->DeviceNameLength
= lstrlenW( nonpersist_name
) * sizeof(WCHAR
);
326 i_size
= input
->DeviceNameOffset
+ input
->DeviceNameLength
;
328 output
->Size
= o_size
;
330 /* now get the true volume name from the mountmgr */
331 if (!DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_POINTS
, input
, i_size
,
332 output
, o_size
, &br
, NULL
))
335 /* Verify and return the data, note string is not null terminated */
336 TRACE("found %d matching mount points\n", output
->NumberOfMountPoints
);
337 if (output
->NumberOfMountPoints
< 1)
339 SetLastError( ERROR_NO_VOLUME_ID
);
342 o1
= &output
->MountPoints
[0];
344 /* look for the volume name in returned values */
345 for(i
=0;i
<output
->NumberOfMountPoints
;i
++)
347 p
= (WCHAR
*)((char *)output
+ o1
->SymbolicLinkNameOffset
);
348 r
= (char *)output
+ o1
->UniqueIdOffset
;
349 TRACE("found symlink=%s, unique=%s, devname=%s\n",
350 debugstr_wn(p
, o1
->SymbolicLinkNameLength
/sizeof(WCHAR
)),
351 debugstr_an(r
, o1
->UniqueIdLength
),
352 debugstr_wn((WCHAR
*)((char *)output
+ o1
->DeviceNameOffset
),
353 o1
->DeviceNameLength
/sizeof(WCHAR
)));
355 if (!wcsncmp( p
, L
"\\??\\Volume{", wcslen(L
"\\??\\Volume{") ))
357 /* is there space in the return variable ?? */
358 if ((o1
->SymbolicLinkNameLength
/sizeof(WCHAR
))+2 > size
)
360 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
363 memcpy( volume
, p
, o1
->SymbolicLinkNameLength
);
364 volume
[o1
->SymbolicLinkNameLength
/ sizeof(WCHAR
)] = 0;
365 lstrcatW( volume
, L
"\\" );
366 /* change second char from '?' to '\' */
375 HeapFree( GetProcessHeap(), 0, input
);
376 HeapFree( GetProcessHeap(), 0, output
);
382 /***********************************************************************
383 * DefineDosDeviceW (kernelbase.@)
385 BOOL WINAPI DECLSPEC_HOTPATCH
DefineDosDeviceW( DWORD flags
, const WCHAR
*device
, const WCHAR
*target
)
387 WCHAR link_name
[15] = L
"\\DosDevices\\";
388 UNICODE_STRING nt_name
, nt_target
;
389 OBJECT_ATTRIBUTES attr
;
393 TRACE("%#x, %s, %s\n", flags
, debugstr_w(device
), debugstr_w(target
));
395 if (flags
& ~(DDD_RAW_TARGET_PATH
| DDD_REMOVE_DEFINITION
))
396 FIXME("Ignoring flags %#x.\n", flags
& ~(DDD_RAW_TARGET_PATH
| DDD_REMOVE_DEFINITION
));
398 lstrcatW( link_name
, device
);
399 RtlInitUnicodeString( &nt_name
, link_name
);
400 InitializeObjectAttributes( &attr
, &nt_name
, OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
, 0, NULL
);
401 if (flags
& DDD_REMOVE_DEFINITION
)
403 if (!set_ntstatus( NtOpenSymbolicLinkObject( &handle
, 0, &attr
) ))
406 status
= NtMakeTemporaryObject( handle
);
409 return set_ntstatus( status
);
412 if (!(flags
& DDD_RAW_TARGET_PATH
))
414 if (!RtlDosPathNameToNtPathName_U( target
, &nt_target
, NULL
, NULL
))
416 SetLastError( ERROR_PATH_NOT_FOUND
);
421 RtlInitUnicodeString( &nt_target
, target
);
423 if (!(status
= NtCreateSymbolicLinkObject( &handle
, SYMBOLIC_LINK_ALL_ACCESS
, &attr
, &nt_target
)))
425 return set_ntstatus( status
);
429 /***********************************************************************
430 * QueryDosDeviceW (kernelbase.@)
432 * returns array of strings terminated by \0, terminated by \0
434 DWORD WINAPI
QueryDosDeviceW( LPCWSTR devname
, LPWSTR target
, DWORD bufsize
)
436 UNICODE_STRING nt_name
;
441 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
449 DWORD dosdev
, ret
= 0;
451 if ((dosdev
= RtlIsDosDeviceName_U( devname
)))
453 memcpy( name
, devname
+ HIWORD(dosdev
)/sizeof(WCHAR
), LOWORD(dosdev
) );
454 name
[LOWORD(dosdev
)/sizeof(WCHAR
)] = 0;
458 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(L
"\\DosDevices\\") + lstrlenW(devname
)*sizeof(WCHAR
) )))
460 SetLastError( ERROR_OUTOFMEMORY
);
463 lstrcpyW( buffer
, L
"\\DosDevices\\" );
464 lstrcatW( buffer
, devname
);
465 status
= read_nt_symlink( buffer
, target
, bufsize
);
466 HeapFree( GetProcessHeap(), 0, buffer
);
467 if (!set_ntstatus( status
)) return 0;
468 ret
= lstrlenW( target
) + 1;
469 if (ret
< bufsize
) target
[ret
++] = 0; /* add an extra null */
472 else /* return a list of all devices */
474 OBJECT_ATTRIBUTES attr
;
478 RtlInitUnicodeString( &nt_name
, L
"\\DosDevices\\" );
479 nt_name
.Length
-= sizeof(WCHAR
); /* without trailing slash */
480 attr
.Length
= sizeof(attr
);
481 attr
.RootDirectory
= 0;
482 attr
.ObjectName
= &nt_name
;
483 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
484 attr
.SecurityDescriptor
= NULL
;
485 attr
.SecurityQualityOfService
= NULL
;
486 status
= NtOpenDirectoryObject( &handle
, FILE_LIST_DIRECTORY
, &attr
);
490 DIRECTORY_BASIC_INFORMATION
*info
= (DIRECTORY_BASIC_INFORMATION
*)data
;
493 while (!NtQueryDirectoryObject( handle
, info
, sizeof(data
), 1, 0, &ctx
, &len
))
495 if (p
+ info
->ObjectName
.Length
/sizeof(WCHAR
) + 1 >= target
+ bufsize
)
497 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
501 memcpy( p
, info
->ObjectName
.Buffer
, info
->ObjectName
.Length
);
502 p
+= info
->ObjectName
.Length
/sizeof(WCHAR
);
508 *p
++ = 0; /* terminating null */
514 /***********************************************************************
515 * GetLogicalDrives (kernelbase.@)
517 DWORD WINAPI DECLSPEC_HOTPATCH
GetLogicalDrives(void)
519 OBJECT_ATTRIBUTES attr
;
520 UNICODE_STRING nt_name
;
525 RtlInitUnicodeString( &nt_name
, L
"\\DosDevices\\" );
526 nt_name
.Length
-= sizeof(WCHAR
); /* without trailing slash */
527 attr
.Length
= sizeof(attr
);
528 attr
.RootDirectory
= 0;
529 attr
.ObjectName
= &nt_name
;
530 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
531 attr
.SecurityDescriptor
= NULL
;
532 attr
.SecurityQualityOfService
= NULL
;
533 status
= NtOpenDirectoryObject( &handle
, FILE_LIST_DIRECTORY
, &attr
);
537 DIRECTORY_BASIC_INFORMATION
*info
= (DIRECTORY_BASIC_INFORMATION
*)data
;
540 while (!NtQueryDirectoryObject( handle
, info
, sizeof(data
), 1, 0, &ctx
, &len
))
541 if(info
->ObjectName
.Length
== 2*sizeof(WCHAR
) && info
->ObjectName
.Buffer
[1] == ':')
542 bitmask
|= 1 << (info
->ObjectName
.Buffer
[0] - 'A');
551 /***********************************************************************
552 * GetLogicalDriveStringsW (kernelbase.@)
554 UINT WINAPI DECLSPEC_HOTPATCH
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
556 DWORD drives
= GetLogicalDrives();
559 for (drive
= count
= 0; drive
< 26; drive
++) if (drives
& (1 << drive
)) count
++;
560 if ((count
* 4) + 1 > len
) return count
* 4 + 1;
562 for (drive
= 0; drive
< 26; drive
++)
564 if (drives
& (1 << drive
))
566 *buffer
++ = 'A' + drive
;
577 /***********************************************************************
578 * GetDriveTypeW (kernelbase.@)
580 UINT WINAPI DECLSPEC_HOTPATCH
GetDriveTypeW( LPCWSTR root
)
582 FILE_FS_DEVICE_INFORMATION info
;
588 if (!open_device_root( root
, &handle
))
590 /* CD ROM devices do not necessarily have a volume, but a drive type */
591 ret
= get_mountmgr_drive_type( root
);
592 if (ret
== DRIVE_CDROM
|| ret
== DRIVE_REMOVABLE
)
595 return DRIVE_NO_ROOT_DIR
;
598 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsDeviceInformation
);
600 if (status
!= STATUS_SUCCESS
)
602 SetLastError( RtlNtStatusToDosError(status
) );
607 switch (info
.DeviceType
)
609 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
: ret
= DRIVE_CDROM
; break;
610 case FILE_DEVICE_VIRTUAL_DISK
: ret
= DRIVE_RAMDISK
; break;
611 case FILE_DEVICE_NETWORK_FILE_SYSTEM
: ret
= DRIVE_REMOTE
; break;
612 case FILE_DEVICE_DISK_FILE_SYSTEM
:
613 if (info
.Characteristics
& FILE_REMOTE_DEVICE
) ret
= DRIVE_REMOTE
;
614 else if (info
.Characteristics
& FILE_REMOVABLE_MEDIA
) ret
= DRIVE_REMOVABLE
;
615 else if ((ret
= get_mountmgr_drive_type( root
)) == DRIVE_UNKNOWN
) ret
= DRIVE_FIXED
;
622 TRACE( "%s -> %d\n", debugstr_w(root
), ret
);
627 /***********************************************************************
628 * GetDriveTypeA (kernelbase.@)
630 UINT WINAPI DECLSPEC_HOTPATCH
GetDriveTypeA( LPCSTR root
)
634 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return DRIVE_NO_ROOT_DIR
;
635 return GetDriveTypeW( rootW
);
639 /***********************************************************************
640 * GetDiskFreeSpaceExW (kernelbase.@)
642 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
643 PULARGE_INTEGER total
, PULARGE_INTEGER totalfree
)
645 FILE_FS_SIZE_INFORMATION info
;
651 TRACE( "%s,%p,%p,%p\n", debugstr_w(root
), avail
, total
, totalfree
);
653 if (!open_device_root( root
, &handle
)) return FALSE
;
655 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsSizeInformation
);
657 if (!set_ntstatus( status
)) return FALSE
;
659 units
= info
.SectorsPerAllocationUnit
* info
.BytesPerSector
;
660 if (total
) total
->QuadPart
= info
.TotalAllocationUnits
.QuadPart
* units
;
661 if (totalfree
) totalfree
->QuadPart
= info
.AvailableAllocationUnits
.QuadPart
* units
;
662 /* FIXME: this one should take quotas into account */
663 if (avail
) avail
->QuadPart
= info
.AvailableAllocationUnits
.QuadPart
* units
;
668 /***********************************************************************
669 * GetDiskFreeSpaceExA (kernelbase.@)
671 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
672 PULARGE_INTEGER total
, PULARGE_INTEGER totalfree
)
676 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return FALSE
;
677 return GetDiskFreeSpaceExW( rootW
, avail
, total
, totalfree
);
681 /***********************************************************************
682 * GetDiskFreeSpaceW (kernelbase.@)
684 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
685 LPDWORD sector_bytes
, LPDWORD free_clusters
,
686 LPDWORD total_clusters
)
688 FILE_FS_SIZE_INFORMATION info
;
694 TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root
),
695 cluster_sectors
, sector_bytes
, free_clusters
, total_clusters
);
697 if (!open_device_root( root
, &handle
)) return FALSE
;
699 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsSizeInformation
);
701 if (!set_ntstatus( status
)) return FALSE
;
703 units
= info
.SectorsPerAllocationUnit
* info
.BytesPerSector
;
705 if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */
706 /* cap the size and available at 2GB as per specs */
707 if (info
.TotalAllocationUnits
.QuadPart
* units
> 0x7fffffff) {
708 info
.TotalAllocationUnits
.QuadPart
= 0x7fffffff / units
;
709 if (info
.AvailableAllocationUnits
.QuadPart
* units
> 0x7fffffff)
710 info
.AvailableAllocationUnits
.QuadPart
= 0x7fffffff / units
;
712 /* nr. of clusters is always <= 65335 */
713 while( info
.TotalAllocationUnits
.QuadPart
> 65535 ) {
714 info
.TotalAllocationUnits
.QuadPart
/= 2;
715 info
.AvailableAllocationUnits
.QuadPart
/= 2;
716 info
.SectorsPerAllocationUnit
*= 2;
720 if (cluster_sectors
) *cluster_sectors
= info
.SectorsPerAllocationUnit
;
721 if (sector_bytes
) *sector_bytes
= info
.BytesPerSector
;
722 if (free_clusters
) *free_clusters
= info
.AvailableAllocationUnits
.u
.LowPart
;
723 if (total_clusters
) *total_clusters
= info
.TotalAllocationUnits
.u
.LowPart
;
724 TRACE("%#08x, %#08x, %#08x, %#08x\n", info
.SectorsPerAllocationUnit
, info
.BytesPerSector
,
725 info
.AvailableAllocationUnits
.u
.LowPart
, info
.TotalAllocationUnits
.u
.LowPart
);
730 /***********************************************************************
731 * GetDiskFreeSpaceA (kernelbase.@)
733 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
734 LPDWORD sector_bytes
, LPDWORD free_clusters
,
735 LPDWORD total_clusters
)
739 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return FALSE
;
740 return GetDiskFreeSpaceW( rootW
, cluster_sectors
, sector_bytes
, free_clusters
, total_clusters
);
744 static BOOL
is_dos_path( const UNICODE_STRING
*path
)
746 static const WCHAR global_prefix
[4] = {'\\','?','?','\\'};
747 return path
->Length
>= 7 * sizeof(WCHAR
)
748 && !memcmp(path
->Buffer
, global_prefix
, sizeof(global_prefix
))
749 && path
->Buffer
[5] == ':' && path
->Buffer
[6] == '\\';
752 /* resolve all symlinks in a path in-place; return FALSE if allocation failed */
753 static BOOL
resolve_symlink( UNICODE_STRING
*path
)
755 OBJECT_NAME_INFORMATION
*info
;
756 OBJECT_ATTRIBUTES attr
;
762 InitializeObjectAttributes( &attr
, path
, OBJ_CASE_INSENSITIVE
, 0, NULL
);
763 if (NtOpenFile( &file
, SYNCHRONIZE
, &attr
, &io
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
764 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
))
767 if (NtQueryObject( file
, ObjectNameInformation
, NULL
, 0, &size
) != STATUS_INFO_LENGTH_MISMATCH
)
773 if (!(info
= HeapAlloc( GetProcessHeap(), 0, size
)))
779 status
= NtQueryObject( file
, ObjectNameInformation
, info
, size
, NULL
);
784 RtlFreeUnicodeString( path
);
785 status
= RtlDuplicateUnicodeString( 0, &info
->Name
, path
);
786 HeapFree( GetProcessHeap(), 0, info
);
790 /***********************************************************************
791 * GetVolumePathNameW (kernelbase.@)
793 BOOL WINAPI DECLSPEC_HOTPATCH
GetVolumePathNameW( const WCHAR
*path
, WCHAR
*volume_path
, DWORD length
)
795 FILE_ATTRIBUTE_TAG_INFORMATION attr_info
;
796 FILE_BASIC_INFORMATION basic_info
;
797 OBJECT_ATTRIBUTES attr
;
798 UNICODE_STRING nt_name
;
801 if (path
&& !wcsncmp(path
, L
"\\??\\", 4))
803 WCHAR current_drive
[MAX_PATH
];
805 GetCurrentDirectoryW( ARRAY_SIZE(current_drive
), current_drive
);
808 WCHAR ret_path
[4] = {current_drive
[0], ':', '\\', 0};
809 lstrcpynW( volume_path
, ret_path
, length
);
813 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
817 if (!volume_path
|| !length
|| !RtlDosPathNameToNtPathName_U( path
, &nt_name
, NULL
, NULL
))
819 SetLastError( ERROR_INVALID_PARAMETER
);
823 if (!is_dos_path( &nt_name
))
825 RtlFreeUnicodeString( &nt_name
);
826 WARN("invalid path %s\n", debugstr_w(path
));
827 SetLastError( ERROR_INVALID_NAME
);
831 InitializeObjectAttributes( &attr
, &nt_name
, OBJ_CASE_INSENSITIVE
, 0, NULL
);
833 while (nt_name
.Length
> 7 * sizeof(WCHAR
))
838 if (!NtQueryAttributesFile( &attr
, &basic_info
)
839 && (basic_info
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
840 && (basic_info
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
841 && !NtOpenFile( &file
, SYNCHRONIZE
| FILE_READ_ATTRIBUTES
, &attr
, &io
,
842 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
843 FILE_OPEN_REPARSE_POINT
| FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
))
845 status
= NtQueryInformationFile( file
, &io
, &attr_info
,
846 sizeof(attr_info
), FileAttributeTagInformation
);
851 if (attr_info
.ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
854 if (!resolve_symlink( &nt_name
))
856 SetLastError( ERROR_OUTOFMEMORY
);
862 if (nt_name
.Buffer
[(nt_name
.Length
/ sizeof(WCHAR
)) - 1] == '\\')
863 nt_name
.Length
-= sizeof(WCHAR
);
864 while (nt_name
.Length
&& nt_name
.Buffer
[(nt_name
.Length
/ sizeof(WCHAR
)) - 1] != '\\')
865 nt_name
.Length
-= sizeof(WCHAR
);
868 nt_name
.Buffer
[nt_name
.Length
/ sizeof(WCHAR
)] = 0;
870 if (NtQueryAttributesFile( &attr
, &basic_info
))
872 RtlFreeUnicodeString( &nt_name
);
873 WARN("nonexistent path %s -> %s\n", debugstr_w(path
), debugstr_w( nt_name
.Buffer
));
874 SetLastError( ERROR_FILE_NOT_FOUND
);
878 if (!wcsncmp(path
, L
"\\\\.\\", 4) || !wcsncmp(path
, L
"\\\\?\\", 4))
880 if (length
>= nt_name
.Length
/ sizeof(WCHAR
))
882 memcpy(volume_path
, path
, 4 * sizeof(WCHAR
));
883 lstrcpynW( volume_path
+ 4, nt_name
.Buffer
+ 4, length
- 4 );
885 TRACE("%s -> %s\n", debugstr_w(path
), debugstr_w(volume_path
));
887 RtlFreeUnicodeString( &nt_name
);
891 else if (length
>= (nt_name
.Length
/ sizeof(WCHAR
)) - 4)
893 lstrcpynW( volume_path
, nt_name
.Buffer
+ 4, length
);
894 volume_path
[0] = towupper(volume_path
[0]);
896 TRACE("%s -> %s\n", debugstr_w(path
), debugstr_w(volume_path
));
898 RtlFreeUnicodeString( &nt_name
);
902 RtlFreeUnicodeString( &nt_name
);
903 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
908 static MOUNTMGR_MOUNT_POINTS
*query_mount_points( HANDLE mgr
, MOUNTMGR_MOUNT_POINT
*input
, DWORD insize
)
910 MOUNTMGR_MOUNT_POINTS
*output
;
911 DWORD outsize
= 1024;
916 if (!(output
= HeapAlloc( GetProcessHeap(), 0, outsize
)))
918 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
921 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_POINTS
, input
, insize
, output
, outsize
, &br
, NULL
)) break;
922 outsize
= output
->Size
;
923 HeapFree( GetProcessHeap(), 0, output
);
924 if (GetLastError() != ERROR_MORE_DATA
) return NULL
;
929 /***********************************************************************
930 * GetVolumePathNamesForVolumeNameW (kernelbase.@)
932 BOOL WINAPI DECLSPEC_HOTPATCH
GetVolumePathNamesForVolumeNameW( LPCWSTR volumename
, LPWSTR volumepathname
,
933 DWORD buflen
, PDWORD returnlen
)
935 static const WCHAR dosdevicesW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
938 MOUNTMGR_MOUNT_POINT
*spec
;
939 MOUNTMGR_MOUNT_POINTS
*link
, *target
= NULL
;
944 TRACE("%s, %p, %u, %p\n", debugstr_w(volumename
), volumepathname
, buflen
, returnlen
);
946 if (!volumename
|| (len
= lstrlenW( volumename
)) != 49)
948 SetLastError( ERROR_INVALID_NAME
);
951 mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
952 if (mgr
== INVALID_HANDLE_VALUE
) return FALSE
;
954 size
= sizeof(*spec
) + sizeof(WCHAR
) * (len
- 1); /* remove trailing backslash */
955 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
956 spec
->SymbolicLinkNameOffset
= sizeof(*spec
);
957 spec
->SymbolicLinkNameLength
= size
- sizeof(*spec
);
958 name
= (WCHAR
*)((char *)spec
+ spec
->SymbolicLinkNameOffset
);
959 memcpy( name
, volumename
, size
- sizeof(*spec
) );
960 name
[1] = '?'; /* map \\?\ to \??\ */
962 target
= query_mount_points( mgr
, spec
, size
);
963 HeapFree( GetProcessHeap(), 0, spec
);
968 if (!target
->NumberOfMountPoints
)
970 SetLastError( ERROR_FILE_NOT_FOUND
);
974 path
= volumepathname
;
975 for (i
= 0; i
< target
->NumberOfMountPoints
; i
++)
978 if (target
->MountPoints
[i
].DeviceNameOffset
)
980 const WCHAR
*device
= (const WCHAR
*)((const char *)target
+ target
->MountPoints
[i
].DeviceNameOffset
);
981 USHORT device_len
= target
->MountPoints
[i
].DeviceNameLength
;
983 size
= sizeof(*spec
) + device_len
;
984 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
985 spec
->DeviceNameOffset
= sizeof(*spec
);
986 spec
->DeviceNameLength
= device_len
;
987 memcpy( (char *)spec
+ spec
->DeviceNameOffset
, device
, device_len
);
989 link
= query_mount_points( mgr
, spec
, size
);
990 HeapFree( GetProcessHeap(), 0, spec
);
992 else if (target
->MountPoints
[i
].UniqueIdOffset
)
994 const WCHAR
*id
= (const WCHAR
*)((const char *)target
+ target
->MountPoints
[i
].UniqueIdOffset
);
995 USHORT id_len
= target
->MountPoints
[i
].UniqueIdLength
;
997 size
= sizeof(*spec
) + id_len
;
998 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
999 spec
->UniqueIdOffset
= sizeof(*spec
);
1000 spec
->UniqueIdLength
= id_len
;
1001 memcpy( (char *)spec
+ spec
->UniqueIdOffset
, id
, id_len
);
1003 link
= query_mount_points( mgr
, spec
, size
);
1004 HeapFree( GetProcessHeap(), 0, spec
);
1006 if (!link
) continue;
1007 for (j
= 0; j
< link
->NumberOfMountPoints
; j
++)
1009 const WCHAR
*linkname
;
1011 if (!link
->MountPoints
[j
].SymbolicLinkNameOffset
) continue;
1012 linkname
= (const WCHAR
*)((const char *)link
+ link
->MountPoints
[j
].SymbolicLinkNameOffset
);
1014 if (link
->MountPoints
[j
].SymbolicLinkNameLength
== sizeof(dosdevicesW
) + 2 * sizeof(WCHAR
) &&
1015 !wcsnicmp( linkname
, dosdevicesW
, ARRAY_SIZE( dosdevicesW
)))
1018 if (volumepathname
&& len
< buflen
)
1020 path
[0] = linkname
[ARRAY_SIZE( dosdevicesW
)];
1028 HeapFree( GetProcessHeap(), 0, link
);
1030 if (buflen
<= len
) SetLastError( ERROR_MORE_DATA
);
1031 else if (volumepathname
)
1033 volumepathname
[len
] = 0;
1036 if (returnlen
) *returnlen
= len
+ 1;
1039 HeapFree( GetProcessHeap(), 0, target
);
1045 /***********************************************************************
1046 * FindFirstVolumeW (kernelbase.@)
1048 HANDLE WINAPI DECLSPEC_HOTPATCH
FindFirstVolumeW( LPWSTR volume
, DWORD len
)
1052 HANDLE mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1053 NULL
, OPEN_EXISTING
, 0, 0 );
1054 if (mgr
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
1058 MOUNTMGR_MOUNT_POINT input
;
1059 MOUNTMGR_MOUNT_POINTS
*output
;
1061 if (!(output
= HeapAlloc( GetProcessHeap(), 0, size
)))
1063 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1066 memset( &input
, 0, sizeof(input
) );
1068 if (!DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_POINTS
, &input
, sizeof(input
),
1069 output
, size
, &br
, NULL
))
1071 if (GetLastError() != ERROR_MORE_DATA
) break;
1072 size
= output
->Size
;
1073 HeapFree( GetProcessHeap(), 0, output
);
1077 /* abuse the Size field to store the current index */
1079 if (!FindNextVolumeW( output
, volume
, len
))
1081 HeapFree( GetProcessHeap(), 0, output
);
1082 return INVALID_HANDLE_VALUE
;
1087 return INVALID_HANDLE_VALUE
;
1091 /***********************************************************************
1092 * FindNextVolumeW (kernelbase.@)
1094 BOOL WINAPI DECLSPEC_HOTPATCH
FindNextVolumeW( HANDLE handle
, LPWSTR volume
, DWORD len
)
1096 MOUNTMGR_MOUNT_POINTS
*data
= handle
;
1098 while (data
->Size
< data
->NumberOfMountPoints
)
1100 static const WCHAR volumeW
[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1101 WCHAR
*link
= (WCHAR
*)((char *)data
+ data
->MountPoints
[data
->Size
].SymbolicLinkNameOffset
);
1102 DWORD size
= data
->MountPoints
[data
->Size
].SymbolicLinkNameLength
;
1104 /* skip non-volumes */
1105 if (size
< sizeof(volumeW
) || memcmp( link
, volumeW
, sizeof(volumeW
) )) continue;
1106 if (size
+ sizeof(WCHAR
) >= len
* sizeof(WCHAR
))
1108 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
1111 memcpy( volume
, link
, size
);
1112 volume
[1] = '\\'; /* map \??\ to \\?\ */
1113 volume
[size
/ sizeof(WCHAR
)] = '\\'; /* Windows appends a backslash */
1114 volume
[size
/ sizeof(WCHAR
) + 1] = 0;
1115 TRACE( "returning entry %u %s\n", data
->Size
- 1, debugstr_w(volume
) );
1118 SetLastError( ERROR_NO_MORE_FILES
);
1123 /***********************************************************************
1124 * FindVolumeClose (kernelbase.@)
1126 BOOL WINAPI DECLSPEC_HOTPATCH
FindVolumeClose( HANDLE handle
)
1128 return HeapFree( GetProcessHeap(), 0, handle
);
1132 /***********************************************************************
1133 * DeleteVolumeMountPointW (kernelbase.@)
1135 BOOL WINAPI
/* DECLSPEC_HOTPATCH */ DeleteVolumeMountPointW( LPCWSTR mountpoint
)
1137 FIXME("(%s), stub!\n", debugstr_w(mountpoint
));
1142 /***********************************************************************
1143 * GetVolumeInformationByHandleW (kernelbase.@)
1145 BOOL WINAPI
GetVolumeInformationByHandleW( HANDLE handle
, WCHAR
*label
, DWORD label_len
,
1146 DWORD
*serial
, DWORD
*filename_len
, DWORD
*flags
,
1147 WCHAR
*fsname
, DWORD fsname_len
)
1151 TRACE( "%p\n", handle
);
1153 if (label
|| serial
)
1155 char buffer
[sizeof(FILE_FS_VOLUME_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
1156 FILE_FS_VOLUME_INFORMATION
*info
= (FILE_FS_VOLUME_INFORMATION
*)buffer
;
1158 if (!set_ntstatus( NtQueryVolumeInformationFile( handle
, &io
, info
, sizeof(buffer
),
1159 FileFsVolumeInformation
) ))
1164 if (label_len
< info
->VolumeLabelLength
/ sizeof(WCHAR
) + 1)
1166 SetLastError( ERROR_BAD_LENGTH
);
1169 memcpy( label
, info
->VolumeLabel
, info
->VolumeLabelLength
);
1170 label
[info
->VolumeLabelLength
/ sizeof(WCHAR
)] = 0;
1172 if (serial
) *serial
= info
->VolumeSerialNumber
;
1175 if (filename_len
|| flags
|| fsname
)
1177 char buffer
[sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
1178 FILE_FS_ATTRIBUTE_INFORMATION
*info
= (FILE_FS_ATTRIBUTE_INFORMATION
*)buffer
;
1180 if (!set_ntstatus( NtQueryVolumeInformationFile( handle
, &io
, info
, sizeof(buffer
),
1181 FileFsAttributeInformation
) ))
1186 if (fsname_len
< info
->FileSystemNameLength
/ sizeof(WCHAR
) + 1)
1188 SetLastError( ERROR_BAD_LENGTH
);
1191 memcpy( fsname
, info
->FileSystemName
, info
->FileSystemNameLength
);
1192 fsname
[info
->FileSystemNameLength
/ sizeof(WCHAR
)] = 0;
1194 if (filename_len
) *filename_len
= info
->MaximumComponentNameLength
;
1195 if (flags
) *flags
= info
->FileSystemAttributes
;