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: %lx\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, %lx)\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
)) / sizeof(WCHAR
) );
312 TRACE("read_nt_symlink got stat=%lx, 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 %ld 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("%#lx, %s, %s\n", flags
, debugstr_w(device
), debugstr_w(target
));
395 if (flags
& ~(DDD_RAW_TARGET_PATH
| DDD_REMOVE_DEFINITION
))
396 FIXME("Ignoring flags %#lx.\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
)
440 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
448 DWORD dosdev
, ret
= 0;
450 if ((dosdev
= RtlIsDosDeviceName_U( devname
)))
452 memcpy( name
, devname
+ HIWORD(dosdev
)/sizeof(WCHAR
), LOWORD(dosdev
) );
453 name
[LOWORD(dosdev
)/sizeof(WCHAR
)] = 0;
457 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, sizeof(L
"\\DosDevices\\") + lstrlenW(devname
)*sizeof(WCHAR
) )))
459 SetLastError( ERROR_OUTOFMEMORY
);
462 lstrcpyW( buffer
, L
"\\DosDevices\\" );
463 lstrcatW( buffer
, devname
);
464 status
= read_nt_symlink( buffer
, target
, bufsize
);
465 HeapFree( GetProcessHeap(), 0, buffer
);
466 if (!set_ntstatus( status
)) return 0;
467 ret
= lstrlenW( target
) + 1;
468 if (ret
< bufsize
) target
[ret
++] = 0; /* add an extra null */
471 else /* return a list of all devices */
473 UNICODE_STRING nt_name
= RTL_CONSTANT_STRING( L
"\\DosDevices" );
474 OBJECT_ATTRIBUTES attr
;
478 attr
.Length
= sizeof(attr
);
479 attr
.RootDirectory
= 0;
480 attr
.ObjectName
= &nt_name
;
481 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
482 attr
.SecurityDescriptor
= NULL
;
483 attr
.SecurityQualityOfService
= NULL
;
484 status
= NtOpenDirectoryObject( &handle
, FILE_LIST_DIRECTORY
, &attr
);
488 DIRECTORY_BASIC_INFORMATION
*info
= (DIRECTORY_BASIC_INFORMATION
*)data
;
491 while (!NtQueryDirectoryObject( handle
, info
, sizeof(data
), 1, 0, &ctx
, &len
))
493 if (p
+ info
->ObjectName
.Length
/sizeof(WCHAR
) + 1 >= target
+ bufsize
)
495 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
499 memcpy( p
, info
->ObjectName
.Buffer
, info
->ObjectName
.Length
);
500 p
+= info
->ObjectName
.Length
/sizeof(WCHAR
);
506 *p
++ = 0; /* terminating null */
512 /***********************************************************************
513 * GetLogicalDrives (kernelbase.@)
515 DWORD WINAPI DECLSPEC_HOTPATCH
GetLogicalDrives(void)
517 OBJECT_ATTRIBUTES attr
;
518 UNICODE_STRING nt_name
= RTL_CONSTANT_STRING( L
"\\DosDevices\\" );
523 nt_name
.Length
-= sizeof(WCHAR
); /* without trailing slash */
524 attr
.Length
= sizeof(attr
);
525 attr
.RootDirectory
= 0;
526 attr
.ObjectName
= &nt_name
;
527 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
528 attr
.SecurityDescriptor
= NULL
;
529 attr
.SecurityQualityOfService
= NULL
;
530 status
= NtOpenDirectoryObject( &handle
, FILE_LIST_DIRECTORY
, &attr
);
534 DIRECTORY_BASIC_INFORMATION
*info
= (DIRECTORY_BASIC_INFORMATION
*)data
;
537 while (!NtQueryDirectoryObject( handle
, info
, sizeof(data
), 1, 0, &ctx
, &len
))
538 if(info
->ObjectName
.Length
== 2*sizeof(WCHAR
) && info
->ObjectName
.Buffer
[1] == ':')
539 bitmask
|= 1 << (info
->ObjectName
.Buffer
[0] - 'A');
548 /***********************************************************************
549 * GetLogicalDriveStringsW (kernelbase.@)
551 UINT WINAPI DECLSPEC_HOTPATCH
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
553 DWORD drives
= GetLogicalDrives();
556 for (drive
= count
= 0; drive
< 26; drive
++) if (drives
& (1 << drive
)) count
++;
557 if ((count
* 4) + 1 > len
) return count
* 4 + 1;
559 for (drive
= 0; drive
< 26; drive
++)
561 if (drives
& (1 << drive
))
563 *buffer
++ = 'A' + drive
;
574 /***********************************************************************
575 * GetDriveTypeW (kernelbase.@)
577 UINT WINAPI DECLSPEC_HOTPATCH
GetDriveTypeW( LPCWSTR root
)
579 FILE_FS_DEVICE_INFORMATION info
;
585 if (!open_device_root( root
, &handle
))
587 /* CD ROM devices do not necessarily have a volume, but a drive type */
588 ret
= get_mountmgr_drive_type( root
);
589 if (ret
== DRIVE_CDROM
|| ret
== DRIVE_REMOVABLE
)
592 return DRIVE_NO_ROOT_DIR
;
595 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsDeviceInformation
);
597 if (status
!= STATUS_SUCCESS
)
599 SetLastError( RtlNtStatusToDosError(status
) );
604 switch (info
.DeviceType
)
606 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
: ret
= DRIVE_CDROM
; break;
607 case FILE_DEVICE_VIRTUAL_DISK
: ret
= DRIVE_RAMDISK
; break;
608 case FILE_DEVICE_NETWORK_FILE_SYSTEM
: ret
= DRIVE_REMOTE
; break;
609 case FILE_DEVICE_DISK_FILE_SYSTEM
:
610 if (info
.Characteristics
& FILE_REMOTE_DEVICE
) ret
= DRIVE_REMOTE
;
611 else if (info
.Characteristics
& FILE_REMOVABLE_MEDIA
) ret
= DRIVE_REMOVABLE
;
612 else if ((ret
= get_mountmgr_drive_type( root
)) == DRIVE_UNKNOWN
) ret
= DRIVE_FIXED
;
619 TRACE( "%s -> %d\n", debugstr_w(root
), ret
);
624 /***********************************************************************
625 * GetDriveTypeA (kernelbase.@)
627 UINT WINAPI DECLSPEC_HOTPATCH
GetDriveTypeA( LPCSTR root
)
631 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return DRIVE_NO_ROOT_DIR
;
632 return GetDriveTypeW( rootW
);
636 /***********************************************************************
637 * GetDiskFreeSpaceExW (kernelbase.@)
639 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
640 PULARGE_INTEGER total
, PULARGE_INTEGER totalfree
)
642 FILE_FS_SIZE_INFORMATION info
;
648 TRACE( "%s,%p,%p,%p\n", debugstr_w(root
), avail
, total
, totalfree
);
650 if (!open_device_root( root
, &handle
)) return FALSE
;
652 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsSizeInformation
);
654 if (!set_ntstatus( status
)) return FALSE
;
656 units
= info
.SectorsPerAllocationUnit
* info
.BytesPerSector
;
657 if (total
) total
->QuadPart
= info
.TotalAllocationUnits
.QuadPart
* units
;
658 if (totalfree
) totalfree
->QuadPart
= info
.AvailableAllocationUnits
.QuadPart
* units
;
659 /* FIXME: this one should take quotas into account */
660 if (avail
) avail
->QuadPart
= info
.AvailableAllocationUnits
.QuadPart
* units
;
665 /***********************************************************************
666 * GetDiskFreeSpaceExA (kernelbase.@)
668 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
669 PULARGE_INTEGER total
, PULARGE_INTEGER totalfree
)
673 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return FALSE
;
674 return GetDiskFreeSpaceExW( rootW
, avail
, total
, totalfree
);
678 /***********************************************************************
679 * GetDiskFreeSpaceW (kernelbase.@)
681 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
682 LPDWORD sector_bytes
, LPDWORD free_clusters
,
683 LPDWORD total_clusters
)
685 FILE_FS_SIZE_INFORMATION info
;
691 TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root
),
692 cluster_sectors
, sector_bytes
, free_clusters
, total_clusters
);
694 if (!open_device_root( root
, &handle
)) return FALSE
;
696 status
= NtQueryVolumeInformationFile( handle
, &io
, &info
, sizeof(info
), FileFsSizeInformation
);
698 if (!set_ntstatus( status
)) return FALSE
;
700 units
= info
.SectorsPerAllocationUnit
* info
.BytesPerSector
;
702 if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */
703 /* cap the size and available at 2GB as per specs */
704 if (info
.TotalAllocationUnits
.QuadPart
* units
> 0x7fffffff) {
705 info
.TotalAllocationUnits
.QuadPart
= 0x7fffffff / units
;
706 if (info
.AvailableAllocationUnits
.QuadPart
* units
> 0x7fffffff)
707 info
.AvailableAllocationUnits
.QuadPart
= 0x7fffffff / units
;
709 /* nr. of clusters is always <= 65335 */
710 while( info
.TotalAllocationUnits
.QuadPart
> 65535 ) {
711 info
.TotalAllocationUnits
.QuadPart
/= 2;
712 info
.AvailableAllocationUnits
.QuadPart
/= 2;
713 info
.SectorsPerAllocationUnit
*= 2;
717 if (cluster_sectors
) *cluster_sectors
= info
.SectorsPerAllocationUnit
;
718 if (sector_bytes
) *sector_bytes
= info
.BytesPerSector
;
719 if (free_clusters
) *free_clusters
= info
.AvailableAllocationUnits
.u
.LowPart
;
720 if (total_clusters
) *total_clusters
= info
.TotalAllocationUnits
.u
.LowPart
;
721 TRACE("%#08lx, %#08lx, %#08lx, %#08lx\n", info
.SectorsPerAllocationUnit
, info
.BytesPerSector
,
722 info
.AvailableAllocationUnits
.u
.LowPart
, info
.TotalAllocationUnits
.u
.LowPart
);
727 /***********************************************************************
728 * GetDiskFreeSpaceA (kernelbase.@)
730 BOOL WINAPI DECLSPEC_HOTPATCH
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
731 LPDWORD sector_bytes
, LPDWORD free_clusters
,
732 LPDWORD total_clusters
)
736 if (root
&& !(rootW
= file_name_AtoW( root
, FALSE
))) return FALSE
;
737 return GetDiskFreeSpaceW( rootW
, cluster_sectors
, sector_bytes
, free_clusters
, total_clusters
);
741 static BOOL
is_dos_path( const UNICODE_STRING
*path
)
743 static const WCHAR global_prefix
[4] = {'\\','?','?','\\'};
744 return path
->Length
>= 7 * sizeof(WCHAR
)
745 && !memcmp(path
->Buffer
, global_prefix
, sizeof(global_prefix
))
746 && path
->Buffer
[5] == ':' && path
->Buffer
[6] == '\\';
749 /* resolve all symlinks in a path in-place; return FALSE if allocation failed */
750 static BOOL
resolve_symlink( UNICODE_STRING
*path
)
752 OBJECT_NAME_INFORMATION
*info
;
753 OBJECT_ATTRIBUTES attr
;
759 InitializeObjectAttributes( &attr
, path
, OBJ_CASE_INSENSITIVE
, 0, NULL
);
760 if (NtOpenFile( &file
, SYNCHRONIZE
, &attr
, &io
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
761 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
))
764 if (NtQueryObject( file
, ObjectNameInformation
, NULL
, 0, &size
) != STATUS_INFO_LENGTH_MISMATCH
)
770 if (!(info
= HeapAlloc( GetProcessHeap(), 0, size
)))
776 status
= NtQueryObject( file
, ObjectNameInformation
, info
, size
, NULL
);
781 RtlFreeUnicodeString( path
);
782 status
= RtlDuplicateUnicodeString( 0, &info
->Name
, path
);
783 HeapFree( GetProcessHeap(), 0, info
);
787 /***********************************************************************
788 * GetVolumePathNameW (kernelbase.@)
790 BOOL WINAPI DECLSPEC_HOTPATCH
GetVolumePathNameW( const WCHAR
*path
, WCHAR
*volume_path
, DWORD length
)
792 FILE_ATTRIBUTE_TAG_INFORMATION attr_info
;
793 FILE_BASIC_INFORMATION basic_info
;
794 OBJECT_ATTRIBUTES attr
;
795 UNICODE_STRING nt_name
;
798 if (path
&& !wcsncmp(path
, L
"\\??\\", 4))
800 WCHAR current_drive
[MAX_PATH
];
802 GetCurrentDirectoryW( ARRAY_SIZE(current_drive
), current_drive
);
805 WCHAR ret_path
[4] = {current_drive
[0], ':', '\\', 0};
806 lstrcpynW( volume_path
, ret_path
, length
);
810 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
814 if (!volume_path
|| !length
|| !RtlDosPathNameToNtPathName_U( path
, &nt_name
, NULL
, NULL
))
816 SetLastError( ERROR_INVALID_PARAMETER
);
820 if (!is_dos_path( &nt_name
))
822 RtlFreeUnicodeString( &nt_name
);
823 WARN("invalid path %s\n", debugstr_w(path
));
824 SetLastError( ERROR_INVALID_NAME
);
828 InitializeObjectAttributes( &attr
, &nt_name
, OBJ_CASE_INSENSITIVE
, 0, NULL
);
830 while (nt_name
.Length
> 7 * sizeof(WCHAR
))
835 if (!NtQueryAttributesFile( &attr
, &basic_info
)
836 && (basic_info
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
)
837 && (basic_info
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
838 && !NtOpenFile( &file
, SYNCHRONIZE
| FILE_READ_ATTRIBUTES
, &attr
, &io
,
839 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
840 FILE_OPEN_REPARSE_POINT
| FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
))
842 status
= NtQueryInformationFile( file
, &io
, &attr_info
,
843 sizeof(attr_info
), FileAttributeTagInformation
);
848 if (attr_info
.ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
851 if (!resolve_symlink( &nt_name
))
853 SetLastError( ERROR_OUTOFMEMORY
);
859 if (nt_name
.Buffer
[(nt_name
.Length
/ sizeof(WCHAR
)) - 1] == '\\')
860 nt_name
.Length
-= sizeof(WCHAR
);
861 while (nt_name
.Length
&& nt_name
.Buffer
[(nt_name
.Length
/ sizeof(WCHAR
)) - 1] != '\\')
862 nt_name
.Length
-= sizeof(WCHAR
);
865 nt_name
.Buffer
[nt_name
.Length
/ sizeof(WCHAR
)] = 0;
867 if (NtQueryAttributesFile( &attr
, &basic_info
))
869 RtlFreeUnicodeString( &nt_name
);
870 WARN("nonexistent path %s -> %s\n", debugstr_w(path
), debugstr_w( nt_name
.Buffer
));
871 SetLastError( ERROR_FILE_NOT_FOUND
);
875 if (!wcsncmp(path
, L
"\\\\.\\", 4) || !wcsncmp(path
, L
"\\\\?\\", 4))
877 if (length
>= nt_name
.Length
/ sizeof(WCHAR
))
879 memcpy(volume_path
, path
, 4 * sizeof(WCHAR
));
880 lstrcpynW( volume_path
+ 4, nt_name
.Buffer
+ 4, length
- 4 );
882 TRACE("%s -> %s\n", debugstr_w(path
), debugstr_w(volume_path
));
884 RtlFreeUnicodeString( &nt_name
);
888 else if (length
>= (nt_name
.Length
/ sizeof(WCHAR
)) - 4)
890 lstrcpynW( volume_path
, nt_name
.Buffer
+ 4, length
);
891 volume_path
[0] = towupper(volume_path
[0]);
893 TRACE("%s -> %s\n", debugstr_w(path
), debugstr_w(volume_path
));
895 RtlFreeUnicodeString( &nt_name
);
899 RtlFreeUnicodeString( &nt_name
);
900 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
905 static MOUNTMGR_MOUNT_POINTS
*query_mount_points( HANDLE mgr
, MOUNTMGR_MOUNT_POINT
*input
, DWORD insize
)
907 MOUNTMGR_MOUNT_POINTS
*output
;
908 DWORD outsize
= 1024;
913 if (!(output
= HeapAlloc( GetProcessHeap(), 0, outsize
)))
915 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
918 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_POINTS
, input
, insize
, output
, outsize
, &br
, NULL
)) break;
919 outsize
= output
->Size
;
920 HeapFree( GetProcessHeap(), 0, output
);
921 if (GetLastError() != ERROR_MORE_DATA
) return NULL
;
926 /***********************************************************************
927 * GetVolumePathNamesForVolumeNameW (kernelbase.@)
929 BOOL WINAPI DECLSPEC_HOTPATCH
GetVolumePathNamesForVolumeNameW( LPCWSTR volumename
, LPWSTR volumepathname
,
930 DWORD buflen
, PDWORD returnlen
)
932 static const WCHAR dosdevicesW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
935 MOUNTMGR_MOUNT_POINT
*spec
;
936 MOUNTMGR_MOUNT_POINTS
*link
, *target
= NULL
;
941 TRACE("%s, %p, %lu, %p\n", debugstr_w(volumename
), volumepathname
, buflen
, returnlen
);
943 if (!volumename
|| (len
= lstrlenW( volumename
)) != 49)
945 SetLastError( ERROR_INVALID_NAME
);
948 mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
949 if (mgr
== INVALID_HANDLE_VALUE
) return FALSE
;
951 size
= sizeof(*spec
) + sizeof(WCHAR
) * (len
- 1); /* remove trailing backslash */
952 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
953 spec
->SymbolicLinkNameOffset
= sizeof(*spec
);
954 spec
->SymbolicLinkNameLength
= size
- sizeof(*spec
);
955 name
= (WCHAR
*)((char *)spec
+ spec
->SymbolicLinkNameOffset
);
956 memcpy( name
, volumename
, size
- sizeof(*spec
) );
957 name
[1] = '?'; /* map \\?\ to \??\ */
959 target
= query_mount_points( mgr
, spec
, size
);
960 HeapFree( GetProcessHeap(), 0, spec
);
965 if (!target
->NumberOfMountPoints
)
967 SetLastError( ERROR_FILE_NOT_FOUND
);
971 path
= volumepathname
;
972 for (i
= 0; i
< target
->NumberOfMountPoints
; i
++)
975 if (target
->MountPoints
[i
].DeviceNameOffset
)
977 const WCHAR
*device
= (const WCHAR
*)((const char *)target
+ target
->MountPoints
[i
].DeviceNameOffset
);
978 USHORT device_len
= target
->MountPoints
[i
].DeviceNameLength
;
980 size
= sizeof(*spec
) + device_len
;
981 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
982 spec
->DeviceNameOffset
= sizeof(*spec
);
983 spec
->DeviceNameLength
= device_len
;
984 memcpy( (char *)spec
+ spec
->DeviceNameOffset
, device
, device_len
);
986 link
= query_mount_points( mgr
, spec
, size
);
987 HeapFree( GetProcessHeap(), 0, spec
);
989 else if (target
->MountPoints
[i
].UniqueIdOffset
)
991 const WCHAR
*id
= (const WCHAR
*)((const char *)target
+ target
->MountPoints
[i
].UniqueIdOffset
);
992 USHORT id_len
= target
->MountPoints
[i
].UniqueIdLength
;
994 size
= sizeof(*spec
) + id_len
;
995 if (!(spec
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, size
))) goto done
;
996 spec
->UniqueIdOffset
= sizeof(*spec
);
997 spec
->UniqueIdLength
= id_len
;
998 memcpy( (char *)spec
+ spec
->UniqueIdOffset
, id
, id_len
);
1000 link
= query_mount_points( mgr
, spec
, size
);
1001 HeapFree( GetProcessHeap(), 0, spec
);
1003 if (!link
) continue;
1004 for (j
= 0; j
< link
->NumberOfMountPoints
; j
++)
1006 const WCHAR
*linkname
;
1008 if (!link
->MountPoints
[j
].SymbolicLinkNameOffset
) continue;
1009 linkname
= (const WCHAR
*)((const char *)link
+ link
->MountPoints
[j
].SymbolicLinkNameOffset
);
1011 if (link
->MountPoints
[j
].SymbolicLinkNameLength
== sizeof(dosdevicesW
) + 2 * sizeof(WCHAR
) &&
1012 !wcsnicmp( linkname
, dosdevicesW
, ARRAY_SIZE( dosdevicesW
)))
1015 if (volumepathname
&& len
< buflen
)
1017 path
[0] = linkname
[ARRAY_SIZE( dosdevicesW
)];
1025 HeapFree( GetProcessHeap(), 0, link
);
1027 if (buflen
<= len
) SetLastError( ERROR_MORE_DATA
);
1028 else if (volumepathname
)
1030 volumepathname
[len
] = 0;
1033 if (returnlen
) *returnlen
= len
+ 1;
1036 HeapFree( GetProcessHeap(), 0, target
);
1042 /***********************************************************************
1043 * FindFirstVolumeW (kernelbase.@)
1045 HANDLE WINAPI DECLSPEC_HOTPATCH
FindFirstVolumeW( LPWSTR volume
, DWORD len
)
1049 HANDLE mgr
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, 0, FILE_SHARE_READ
|FILE_SHARE_WRITE
,
1050 NULL
, OPEN_EXISTING
, 0, 0 );
1051 if (mgr
== INVALID_HANDLE_VALUE
) return INVALID_HANDLE_VALUE
;
1055 MOUNTMGR_MOUNT_POINT input
;
1056 MOUNTMGR_MOUNT_POINTS
*output
;
1058 if (!(output
= HeapAlloc( GetProcessHeap(), 0, size
)))
1060 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
1063 memset( &input
, 0, sizeof(input
) );
1065 if (!DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_POINTS
, &input
, sizeof(input
),
1066 output
, size
, &br
, NULL
))
1068 if (GetLastError() != ERROR_MORE_DATA
) break;
1069 size
= output
->Size
;
1070 HeapFree( GetProcessHeap(), 0, output
);
1074 /* abuse the Size field to store the current index */
1076 if (!FindNextVolumeW( output
, volume
, len
))
1078 HeapFree( GetProcessHeap(), 0, output
);
1079 return INVALID_HANDLE_VALUE
;
1084 return INVALID_HANDLE_VALUE
;
1088 /***********************************************************************
1089 * FindNextVolumeW (kernelbase.@)
1091 BOOL WINAPI DECLSPEC_HOTPATCH
FindNextVolumeW( HANDLE handle
, LPWSTR volume
, DWORD len
)
1093 MOUNTMGR_MOUNT_POINTS
*data
= handle
;
1095 while (data
->Size
< data
->NumberOfMountPoints
)
1097 static const WCHAR volumeW
[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1098 WCHAR
*link
= (WCHAR
*)((char *)data
+ data
->MountPoints
[data
->Size
].SymbolicLinkNameOffset
);
1099 DWORD size
= data
->MountPoints
[data
->Size
].SymbolicLinkNameLength
;
1101 /* skip non-volumes */
1102 if (size
< sizeof(volumeW
) || memcmp( link
, volumeW
, sizeof(volumeW
) )) continue;
1103 if (size
+ sizeof(WCHAR
) >= len
* sizeof(WCHAR
))
1105 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
1108 memcpy( volume
, link
, size
);
1109 volume
[1] = '\\'; /* map \??\ to \\?\ */
1110 volume
[size
/ sizeof(WCHAR
)] = '\\'; /* Windows appends a backslash */
1111 volume
[size
/ sizeof(WCHAR
) + 1] = 0;
1112 TRACE( "returning entry %lu %s\n", data
->Size
- 1, debugstr_w(volume
) );
1115 SetLastError( ERROR_NO_MORE_FILES
);
1120 /***********************************************************************
1121 * FindVolumeClose (kernelbase.@)
1123 BOOL WINAPI DECLSPEC_HOTPATCH
FindVolumeClose( HANDLE handle
)
1125 return HeapFree( GetProcessHeap(), 0, handle
);
1129 /***********************************************************************
1130 * DeleteVolumeMountPointW (kernelbase.@)
1132 BOOL WINAPI
/* DECLSPEC_HOTPATCH */ DeleteVolumeMountPointW( LPCWSTR mountpoint
)
1134 FIXME("(%s), stub!\n", debugstr_w(mountpoint
));
1139 /***********************************************************************
1140 * GetVolumeInformationByHandleW (kernelbase.@)
1142 BOOL WINAPI
GetVolumeInformationByHandleW( HANDLE handle
, WCHAR
*label
, DWORD label_len
,
1143 DWORD
*serial
, DWORD
*filename_len
, DWORD
*flags
,
1144 WCHAR
*fsname
, DWORD fsname_len
)
1148 TRACE( "%p\n", handle
);
1150 if (label
|| serial
)
1152 char buffer
[sizeof(FILE_FS_VOLUME_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
1153 FILE_FS_VOLUME_INFORMATION
*info
= (FILE_FS_VOLUME_INFORMATION
*)buffer
;
1155 if (!set_ntstatus( NtQueryVolumeInformationFile( handle
, &io
, info
, sizeof(buffer
),
1156 FileFsVolumeInformation
) ))
1161 if (label_len
< info
->VolumeLabelLength
/ sizeof(WCHAR
) + 1)
1163 SetLastError( ERROR_BAD_LENGTH
);
1166 memcpy( label
, info
->VolumeLabel
, info
->VolumeLabelLength
);
1167 label
[info
->VolumeLabelLength
/ sizeof(WCHAR
)] = 0;
1169 if (serial
) *serial
= info
->VolumeSerialNumber
;
1172 if (filename_len
|| flags
|| fsname
)
1174 char buffer
[sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
1175 FILE_FS_ATTRIBUTE_INFORMATION
*info
= (FILE_FS_ATTRIBUTE_INFORMATION
*)buffer
;
1177 if (!set_ntstatus( NtQueryVolumeInformationFile( handle
, &io
, info
, sizeof(buffer
),
1178 FileFsAttributeInformation
) ))
1183 if (fsname_len
< info
->FileSystemNameLength
/ sizeof(WCHAR
) + 1)
1185 SetLastError( ERROR_BAD_LENGTH
);
1188 memcpy( fsname
, info
->FileSystemName
, info
->FileSystemNameLength
);
1189 fsname
[info
->FileSystemNameLength
/ sizeof(WCHAR
)] = 0;
1191 if (filename_len
) *filename_len
= info
->MaximumComponentNameLength
;
1192 if (flags
) *flags
= info
->FileSystemAttributes
;