comctl32/tests: Basic test for PGN_CALCSIZE.
[wine.git] / dlls / kernel32 / volume.c
blob68cc762b519217a4fece81e5887574426a7211de
1 /*
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
25 #include "config.h"
26 #include "wine/port.h"
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <stdio.h>
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winnls.h"
37 #include "winternl.h"
38 #include "winioctl.h"
39 #include "ntddcdrm.h"
40 #define WINE_MOUNTMGR_EXTENSIONS
41 #include "ddk/mountmgr.h"
42 #include "kernel_private.h"
43 #include "wine/library.h"
44 #include "wine/unicode.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(volume);
49 #define BLOCK_SIZE 2048
50 #define SUPERBLOCK_SIZE BLOCK_SIZE
51 #define SYMBOLIC_LINK_QUERY 0x0001
53 #define CDFRAMES_PERSEC 75
54 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
55 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
56 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
58 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
59 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
61 enum fs_type
63 FS_ERROR, /* error accessing the device */
64 FS_UNKNOWN, /* unknown file system */
65 FS_FAT1216,
66 FS_FAT32,
67 FS_ISO9660,
68 FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
71 /* read a Unix symlink; returned buffer must be freed by caller */
72 static char *read_symlink( const char *path )
74 char *buffer;
75 int ret, size = 128;
77 for (;;)
79 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size )))
81 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
82 return 0;
84 ret = readlink( path, buffer, size );
85 if (ret == -1)
87 FILE_SetDosError();
88 HeapFree( GetProcessHeap(), 0, buffer );
89 return 0;
91 if (ret != size)
93 buffer[ret] = 0;
94 return buffer;
96 HeapFree( GetProcessHeap(), 0, buffer );
97 size *= 2;
101 /* get the path of a dos device symlink in the $WINEPREFIX/dosdevices directory */
102 static char *get_dos_device_path( LPCWSTR name )
104 const char *config_dir = wine_get_config_dir();
105 char *buffer, *dev;
106 int i;
108 if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
109 strlen(config_dir) + sizeof("/dosdevices/") + 5 )))
111 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
112 return NULL;
114 strcpy( buffer, config_dir );
115 strcat( buffer, "/dosdevices/" );
116 dev = buffer + strlen(buffer);
117 /* no codepage conversion, DOS device names are ASCII anyway */
118 for (i = 0; i < 5; i++)
119 if (!(dev[i] = (char)tolowerW(name[i]))) break;
120 dev[5] = 0;
121 return buffer;
124 /* read the contents of an NT symlink object */
125 static NTSTATUS read_nt_symlink( const WCHAR *name, WCHAR *target, DWORD size )
127 NTSTATUS status;
128 OBJECT_ATTRIBUTES attr;
129 UNICODE_STRING nameW;
130 HANDLE handle;
132 attr.Length = sizeof(attr);
133 attr.RootDirectory = 0;
134 attr.Attributes = OBJ_CASE_INSENSITIVE;
135 attr.ObjectName = &nameW;
136 attr.SecurityDescriptor = NULL;
137 attr.SecurityQualityOfService = NULL;
138 RtlInitUnicodeString( &nameW, name );
140 if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr )))
142 UNICODE_STRING targetW;
143 targetW.Buffer = target;
144 targetW.MaximumLength = (size - 1) * sizeof(WCHAR);
145 status = NtQuerySymbolicLinkObject( handle, &targetW, NULL );
146 if (!status) target[targetW.Length / sizeof(WCHAR)] = 0;
147 NtClose( handle );
149 return status;
152 /* open a handle to a device root */
153 static BOOL open_device_root( LPCWSTR root, HANDLE *handle )
155 static const WCHAR default_rootW[] = {'\\',0};
156 UNICODE_STRING nt_name;
157 OBJECT_ATTRIBUTES attr;
158 IO_STATUS_BLOCK io;
159 NTSTATUS status;
161 if (!root) root = default_rootW;
162 if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
164 SetLastError( ERROR_PATH_NOT_FOUND );
165 return FALSE;
167 attr.Length = sizeof(attr);
168 attr.RootDirectory = 0;
169 attr.Attributes = OBJ_CASE_INSENSITIVE;
170 attr.ObjectName = &nt_name;
171 attr.SecurityDescriptor = NULL;
172 attr.SecurityQualityOfService = NULL;
174 status = NtOpenFile( handle, SYNCHRONIZE, &attr, &io, 0,
175 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
176 RtlFreeUnicodeString( &nt_name );
177 if (status != STATUS_SUCCESS)
179 SetLastError( RtlNtStatusToDosError(status) );
180 return FALSE;
182 return TRUE;
185 /* query the type of a drive from the mount manager */
186 static DWORD get_mountmgr_drive_type( LPCWSTR root )
188 HANDLE mgr;
189 struct mountmgr_unix_drive data;
190 DWORD br;
192 memset( &data, 0, sizeof(data) );
193 if (root) data.letter = root[0];
194 else
196 WCHAR curdir[MAX_PATH];
197 GetCurrentDirectoryW( MAX_PATH, curdir );
198 if (curdir[1] != ':' || curdir[2] != '\\') return DRIVE_UNKNOWN;
199 data.letter = curdir[0];
202 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ,
203 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
204 if (mgr == INVALID_HANDLE_VALUE) return DRIVE_UNKNOWN;
206 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &data, sizeof(data), &data,
207 sizeof(data), &br, NULL ) && GetLastError() != ERROR_MORE_DATA)
208 data.type = DRIVE_UNKNOWN;
210 CloseHandle( mgr );
211 return data.type;
214 /* get the label by reading it from a file at the root of the filesystem */
215 static void get_filesystem_label( const UNICODE_STRING *device, WCHAR *label, DWORD len )
217 static const WCHAR labelW[] = {'.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
218 HANDLE handle;
219 UNICODE_STRING name;
220 IO_STATUS_BLOCK io;
221 OBJECT_ATTRIBUTES attr;
223 label[0] = 0;
225 attr.Length = sizeof(attr);
226 attr.RootDirectory = 0;
227 attr.Attributes = OBJ_CASE_INSENSITIVE;
228 attr.ObjectName = &name;
229 attr.SecurityDescriptor = NULL;
230 attr.SecurityQualityOfService = NULL;
232 name.MaximumLength = device->Length + sizeof(labelW);
233 name.Length = name.MaximumLength - sizeof(WCHAR);
234 if (!(name.Buffer = HeapAlloc( GetProcessHeap(), 0, name.MaximumLength ))) return;
236 memcpy( name.Buffer, device->Buffer, device->Length );
237 memcpy( name.Buffer + device->Length / sizeof(WCHAR), labelW, sizeof(labelW) );
238 if (!NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
239 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))
241 char buffer[256], *p;
242 DWORD size;
244 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
245 CloseHandle( handle );
246 p = buffer + size;
247 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
248 *p = 0;
249 if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, len ))
250 label[len-1] = 0;
252 RtlFreeUnicodeString( &name );
255 /* get the serial number by reading it from a file at the root of the filesystem */
256 static DWORD get_filesystem_serial( const UNICODE_STRING *device )
258 static const WCHAR serialW[] = {'.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
259 HANDLE handle;
260 UNICODE_STRING name;
261 IO_STATUS_BLOCK io;
262 OBJECT_ATTRIBUTES attr;
263 DWORD ret = 0;
265 attr.Length = sizeof(attr);
266 attr.RootDirectory = 0;
267 attr.Attributes = OBJ_CASE_INSENSITIVE;
268 attr.ObjectName = &name;
269 attr.SecurityDescriptor = NULL;
270 attr.SecurityQualityOfService = NULL;
272 name.MaximumLength = device->Length + sizeof(serialW);
273 name.Length = name.MaximumLength - sizeof(WCHAR);
274 if (!(name.Buffer = HeapAlloc( GetProcessHeap(), 0, name.MaximumLength ))) return 0;
276 memcpy( name.Buffer, device->Buffer, device->Length );
277 memcpy( name.Buffer + device->Length / sizeof(WCHAR), serialW, sizeof(serialW) );
278 if (!NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
279 FILE_SYNCHRONOUS_IO_NONALERT ))
281 char buffer[32];
282 DWORD size;
284 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
285 CloseHandle( handle );
286 buffer[size] = 0;
287 ret = strtoul( buffer, NULL, 16 );
289 RtlFreeUnicodeString( &name );
290 return ret;
294 /******************************************************************
295 * VOLUME_FindCdRomDataBestVoldesc
297 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
299 BYTE cur_vd_type, max_vd_type = 0;
300 BYTE buffer[0x800];
301 DWORD size, offs, best_offs = 0, extra_offs = 0;
303 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
305 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
306 * the volume label is displaced forward by 8
308 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
309 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
310 if (size != sizeof(buffer)) break;
311 /* check for non-ISO9660 signature */
312 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
313 cur_vd_type = buffer[extra_offs];
314 if (cur_vd_type == 0xff) /* voldesc set terminator */
315 break;
316 if (cur_vd_type > max_vd_type)
318 max_vd_type = cur_vd_type;
319 best_offs = offs + extra_offs;
322 return best_offs;
326 /***********************************************************************
327 * VOLUME_ReadFATSuperblock
329 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
331 DWORD size;
333 /* try a fixed disk, with a FAT partition */
334 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
335 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
337 if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN; /* not a real device */
338 return FS_ERROR;
341 if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
343 /* FIXME: do really all FAT have their name beginning with
344 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
346 if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
348 /* guess which type of FAT we have */
349 int reasonable;
350 unsigned int sectors,
351 sect_per_fat,
352 total_sectors,
353 num_boot_sectors,
354 num_fats,
355 num_root_dir_ents,
356 bytes_per_sector,
357 sectors_per_cluster,
358 nclust;
359 sect_per_fat = GETWORD(buff, 0x16);
360 if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
361 total_sectors = GETWORD(buff, 0x13);
362 if (!total_sectors)
363 total_sectors = GETLONG(buff, 0x20);
364 num_boot_sectors = GETWORD(buff, 0x0e);
365 num_fats = buff[0x10];
366 num_root_dir_ents = GETWORD(buff, 0x11);
367 bytes_per_sector = GETWORD(buff, 0x0b);
368 sectors_per_cluster = buff[0x0d];
369 /* check if the parameters are reasonable and will not cause
370 * arithmetic errors in the calculation */
371 reasonable = num_boot_sectors < total_sectors &&
372 num_fats < 16 &&
373 bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
374 sectors_per_cluster >= 1;
375 if (!reasonable) return FS_UNKNOWN;
376 sectors = total_sectors - num_boot_sectors - num_fats * sect_per_fat -
377 (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
378 nclust = sectors / sectors_per_cluster;
379 if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
380 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
381 if (nclust < 65525)
383 if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
384 !memcmp(buff+0x36, "FAT", 3))
385 return FS_FAT1216;
388 return FS_UNKNOWN;
392 /***********************************************************************
393 * VOLUME_ReadCDBlock
395 static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
397 DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
399 if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
400 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
401 size != SUPERBLOCK_SIZE)
402 return FALSE;
404 return TRUE;
408 /***********************************************************************
409 * VOLUME_ReadCDSuperblock
411 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
413 int i;
414 DWORD offs;
416 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
417 * Starting from sector 16, we may find :
418 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
419 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
420 * There is no explicit end so read 16 sectors and then give up */
421 for( i=16; i<16+16; i++)
423 if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
424 continue;
426 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
427 * but we assume the volume is well-formatted */
428 if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
431 offs = VOLUME_FindCdRomDataBestVoldesc( handle );
432 if (!offs) return FS_UNKNOWN;
434 if (!VOLUME_ReadCDBlock(handle, buff, offs))
435 return FS_ERROR;
437 /* check for the iso9660 identifier */
438 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
439 return FS_UNKNOWN;
443 /**************************************************************************
444 * UDF_Find_PVD
445 * Find the Primary Volume Descriptor
447 static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] )
449 unsigned int i;
450 DWORD offset;
451 INT locations[] = { 256, -1, -257, 512 };
453 for(i=0; i<sizeof(locations)/sizeof(locations[0]); i++)
455 if (!VOLUME_ReadCDBlock(handle, pvd, locations[i]*BLOCK_SIZE))
456 return FALSE;
458 /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
459 if (pvd[0]==2 && pvd[1]==0)
461 /* Tag location (Uint32) at offset 12, little-endian */
462 offset = pvd[20 + 0];
463 offset |= pvd[20 + 1] << 8;
464 offset |= pvd[20 + 2] << 16;
465 offset |= pvd[20 + 3] << 24;
466 offset *= BLOCK_SIZE;
468 if (!VOLUME_ReadCDBlock(handle, pvd, offset))
469 return FALSE;
471 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
472 if (pvd[0]!=1 || pvd[1]!=0)
473 return FALSE;
475 /* 8 or 16 bits per character -- [U] 2.1.1 */
476 if (!(pvd[24]==8 || pvd[24]==16))
477 return FALSE;
479 return TRUE;
483 return FALSE;
487 /**************************************************************************
488 * VOLUME_GetSuperblockLabel
490 static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, HANDLE handle,
491 enum fs_type type, const BYTE *superblock,
492 WCHAR *label, DWORD len )
494 const BYTE *label_ptr = NULL;
495 DWORD label_len;
497 switch(type)
499 case FS_ERROR:
500 label_len = 0;
501 break;
502 case FS_UNKNOWN:
503 get_filesystem_label( device, label, len );
504 return;
505 case FS_FAT1216:
506 label_ptr = superblock + 0x2b;
507 label_len = 11;
508 break;
509 case FS_FAT32:
510 label_ptr = superblock + 0x47;
511 label_len = 11;
512 break;
513 case FS_ISO9660:
515 BYTE ver = superblock[0x5a];
517 if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */
518 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
519 { /* yippee, unicode */
520 unsigned int i;
522 if (len > 17) len = 17;
523 for (i = 0; i < len-1; i++)
524 label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
525 label[i] = 0;
526 while (i && label[i-1] == ' ') label[--i] = 0;
527 return;
529 label_ptr = superblock + 40;
530 label_len = 32;
531 break;
533 case FS_UDF:
535 BYTE pvd[BLOCK_SIZE];
537 if(!UDF_Find_PVD(handle, pvd))
539 label_len = 0;
540 break;
543 /* [E] 3/10.1.4 and [U] 2.1.1 */
544 if(pvd[24]==8)
546 label_ptr = pvd + 24 + 1;
547 label_len = pvd[24+32-1];
548 break;
550 else
552 unsigned int i;
554 label_len = 1 + pvd[24+32-1];
555 for(i=0; i<label_len && i<len; i+=2)
556 label[i/2] = (pvd[24+1 +i] << 8) | pvd[24+1 +i+1];
557 label[label_len] = 0;
558 return;
562 if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
563 &label_len, (LPCSTR)label_ptr, label_len );
564 label_len /= sizeof(WCHAR);
565 label[label_len] = 0;
566 while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
570 /**************************************************************************
571 * UDF_Find_FSD_Sector
572 * Find the File Set Descriptor used to compute the serial of a UDF volume
574 static int UDF_Find_FSD_Sector( HANDLE handle, BYTE block[] )
576 int i, PVD_sector, PD_sector, PD_length;
578 if(!UDF_Find_PVD(handle,block))
579 goto default_sector;
581 /* Retrieve the tag location of the PVD -- [E] 3/7.2 */
582 PVD_sector = block[12 + 0];
583 PVD_sector |= block[12 + 1] << 8;
584 PVD_sector |= block[12 + 2] << 16;
585 PVD_sector |= block[12 + 3] << 24;
587 /* Find the Partition Descriptor */
588 for(i=PVD_sector+1; ; i++)
590 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
591 goto default_sector;
593 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
594 if(block[0]==5 && block[1]==0)
595 break;
597 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
598 if(block[0]==8 && block[1]==0)
599 goto default_sector;
602 /* Find the partition starting location -- [E] 3/10.5.8 */
603 PD_sector = block[188 + 0];
604 PD_sector |= block[188 + 1] << 8;
605 PD_sector |= block[188 + 2] << 16;
606 PD_sector |= block[188 + 3] << 24;
608 /* Find the partition length -- [E] 3/10.5.9 */
609 PD_length = block[192 + 0];
610 PD_length |= block[192 + 1] << 8;
611 PD_length |= block[192 + 2] << 16;
612 PD_length |= block[192 + 3] << 24;
614 for(i=PD_sector; i<PD_sector+PD_length; i++)
616 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
617 goto default_sector;
619 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
620 if(block[0]==0 && block[1]==1)
621 return i;
624 default_sector:
625 WARN("FSD sector not found, serial may be incorrect\n");
626 return 257;
630 /**************************************************************************
631 * VOLUME_GetSuperblockSerial
633 static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, HANDLE handle,
634 enum fs_type type, const BYTE *superblock )
636 int FSD_sector;
637 BYTE block[BLOCK_SIZE];
639 switch(type)
641 case FS_ERROR:
642 break;
643 case FS_UNKNOWN:
644 return get_filesystem_serial( device );
645 case FS_FAT1216:
646 return GETLONG( superblock, 0x27 );
647 case FS_FAT32:
648 return GETLONG( superblock, 0x33 );
649 case FS_UDF:
650 FSD_sector = UDF_Find_FSD_Sector(handle, block);
651 if (!VOLUME_ReadCDBlock(handle, block, FSD_sector*BLOCK_SIZE))
652 break;
653 superblock = block;
654 /* fallthrough */
655 case FS_ISO9660:
657 BYTE sum[4];
658 int i;
660 sum[0] = sum[1] = sum[2] = sum[3] = 0;
661 for (i = 0; i < 2048; i += 4)
663 /* DON'T optimize this into DWORD !! (breaks overflow) */
664 sum[0] += superblock[i+0];
665 sum[1] += superblock[i+1];
666 sum[2] += superblock[i+2];
667 sum[3] += superblock[i+3];
670 * OK, another braindead one... argh. Just believe it.
671 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
672 * It's true and nobody will ever be able to change it.
674 if ((GetVersion() & 0x80000000) || type == FS_UDF)
675 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
676 else
677 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
680 return 0;
684 /**************************************************************************
685 * VOLUME_GetAudioCDSerial
687 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
689 DWORD serial = 0;
690 int i;
692 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
693 serial += ((toc->TrackData[i].Address[1] << 16) |
694 (toc->TrackData[i].Address[2] << 8) |
695 toc->TrackData[i].Address[3]);
698 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
699 * frames.
700 * There it is collected for correcting the serial when there are less than
701 * 3 tracks.
703 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
705 DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
706 DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
707 serial += dwEnd - dwStart;
709 return serial;
713 /***********************************************************************
714 * GetVolumeInformationW (KERNEL32.@)
716 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
717 DWORD *serial, DWORD *filename_len, DWORD *flags,
718 LPWSTR fsname, DWORD fsname_len )
720 static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
721 static const WCHAR fatW[] = {'F','A','T',0};
722 static const WCHAR fat32W[] = {'F','A','T','3','2',0};
723 static const WCHAR ntfsW[] = {'N','T','F','S',0};
724 static const WCHAR cdfsW[] = {'C','D','F','S',0};
725 static const WCHAR udfW[] = {'U','D','F',0};
726 static const WCHAR default_rootW[] = {'\\',0};
728 HANDLE handle;
729 NTSTATUS status;
730 UNICODE_STRING nt_name;
731 IO_STATUS_BLOCK io;
732 OBJECT_ATTRIBUTES attr;
733 FILE_FS_DEVICE_INFORMATION info;
734 WCHAR *p;
735 enum fs_type type = FS_UNKNOWN;
736 BOOL ret = FALSE;
738 if (!root) root = default_rootW;
739 if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
741 SetLastError( ERROR_PATH_NOT_FOUND );
742 return FALSE;
744 /* there must be exactly one backslash in the name, at the end */
745 p = memchrW( nt_name.Buffer + 4, '\\', (nt_name.Length - 4) / sizeof(WCHAR) );
746 if (p != nt_name.Buffer + nt_name.Length / sizeof(WCHAR) - 1)
748 /* check if root contains an explicit subdir */
749 if (root[0] && root[1] == ':') root += 2;
750 while (*root == '\\') root++;
751 if (strchrW( root, '\\' ))
752 SetLastError( ERROR_DIR_NOT_ROOT );
753 else
754 SetLastError( ERROR_INVALID_NAME );
755 goto done;
758 /* try to open the device */
760 attr.Length = sizeof(attr);
761 attr.RootDirectory = 0;
762 attr.Attributes = OBJ_CASE_INSENSITIVE;
763 attr.ObjectName = &nt_name;
764 attr.SecurityDescriptor = NULL;
765 attr.SecurityQualityOfService = NULL;
767 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
768 status = NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE,
769 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
770 nt_name.Length += sizeof(WCHAR);
772 if (status == STATUS_SUCCESS)
774 BYTE superblock[SUPERBLOCK_SIZE];
775 CDROM_TOC toc;
776 DWORD br;
778 /* check for audio CD */
779 /* FIXME: we only check the first track for now */
780 if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
782 if (!(toc.TrackData[0].Control & 0x04)) /* audio track */
784 TRACE( "%s: found audio CD\n", debugstr_w(nt_name.Buffer) );
785 if (label) lstrcpynW( label, audiocdW, label_len );
786 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
787 CloseHandle( handle );
788 type = FS_ISO9660;
789 goto fill_fs_info;
791 type = VOLUME_ReadCDSuperblock( handle, superblock );
793 else
795 type = VOLUME_ReadFATSuperblock( handle, superblock );
796 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
798 TRACE( "%s: found fs type %d\n", debugstr_w(nt_name.Buffer), type );
799 if (type == FS_ERROR)
801 CloseHandle( handle );
802 goto done;
805 if (label && label_len) VOLUME_GetSuperblockLabel( &nt_name, handle, type, superblock, label, label_len );
806 if (serial) *serial = VOLUME_GetSuperblockSerial( &nt_name, handle, type, superblock );
807 CloseHandle( handle );
808 goto fill_fs_info;
810 else TRACE( "cannot open device %s: %x\n", debugstr_w(nt_name.Buffer), status );
812 /* we couldn't open the device, fallback to default strategy */
814 status = NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
815 if (status != STATUS_SUCCESS)
817 SetLastError( RtlNtStatusToDosError(status) );
818 goto done;
820 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
821 NtClose( handle );
822 if (status != STATUS_SUCCESS)
824 SetLastError( RtlNtStatusToDosError(status) );
825 goto done;
827 if (info.DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) type = FS_ISO9660;
829 if (label && label_len) get_filesystem_label( &nt_name, label, label_len );
830 if (serial) *serial = get_filesystem_serial( &nt_name );
832 fill_fs_info: /* now fill in the information that depends on the file system type */
834 switch(type)
836 case FS_ISO9660:
837 if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
838 if (filename_len) *filename_len = 221;
839 if (flags) *flags = FILE_READ_ONLY_VOLUME;
840 break;
841 case FS_UDF:
842 if (fsname) lstrcpynW( fsname, udfW, fsname_len );
843 if (filename_len) *filename_len = 255;
844 if (flags)
845 *flags = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
846 break;
847 case FS_FAT1216:
848 if (fsname) lstrcpynW( fsname, fatW, fsname_len );
849 case FS_FAT32:
850 if (type == FS_FAT32 && fsname) lstrcpynW( fsname, fat32W, fsname_len );
851 if (filename_len) *filename_len = 255;
852 if (flags) *flags = FILE_CASE_PRESERVED_NAMES; /* FIXME */
853 break;
854 default:
855 if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
856 if (filename_len) *filename_len = 255;
857 if (flags) *flags = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
858 break;
860 ret = TRUE;
862 done:
863 RtlFreeUnicodeString( &nt_name );
864 return ret;
868 /***********************************************************************
869 * GetVolumeInformationA (KERNEL32.@)
871 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
872 DWORD label_len, DWORD *serial,
873 DWORD *filename_len, DWORD *flags,
874 LPSTR fsname, DWORD fsname_len )
876 WCHAR *rootW = NULL;
877 LPWSTR labelW, fsnameW;
878 BOOL ret;
880 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
882 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
883 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
885 if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial,
886 filename_len, flags, fsnameW, fsname_len)))
888 if (label) FILE_name_WtoA( labelW, -1, label, label_len );
889 if (fsname) FILE_name_WtoA( fsnameW, -1, fsname, fsname_len );
892 HeapFree( GetProcessHeap(), 0, labelW );
893 HeapFree( GetProcessHeap(), 0, fsnameW );
894 return ret;
899 /***********************************************************************
900 * SetVolumeLabelW (KERNEL32.@)
902 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
904 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
905 HANDLE handle;
906 enum fs_type type = FS_UNKNOWN;
908 if (!root)
910 WCHAR path[MAX_PATH];
911 GetCurrentDirectoryW( MAX_PATH, path );
912 device[4] = path[0];
914 else
916 if (!root[0] || root[1] != ':')
918 SetLastError( ERROR_INVALID_NAME );
919 return FALSE;
921 device[4] = root[0];
924 /* try to open the device */
926 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
927 NULL, OPEN_EXISTING, 0, 0 );
928 if (handle != INVALID_HANDLE_VALUE)
930 BYTE superblock[SUPERBLOCK_SIZE];
932 type = VOLUME_ReadFATSuperblock( handle, superblock );
933 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
934 CloseHandle( handle );
935 if (type != FS_UNKNOWN)
937 /* we can't set the label on FAT or CDROM file systems */
938 TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
939 SetLastError( ERROR_ACCESS_DENIED );
940 return FALSE;
943 else
945 TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
946 if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
949 /* we couldn't open the device, fallback to default strategy */
951 switch(GetDriveTypeW( root ))
953 case DRIVE_UNKNOWN:
954 case DRIVE_NO_ROOT_DIR:
955 SetLastError( ERROR_NOT_READY );
956 break;
957 case DRIVE_REMOVABLE:
958 case DRIVE_FIXED:
960 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
962 labelW[0] = device[4];
964 if (!label[0]) /* delete label file when setting an empty label */
965 return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
967 handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
968 CREATE_ALWAYS, 0, 0 );
969 if (handle != INVALID_HANDLE_VALUE)
971 char buffer[64];
972 DWORD size;
974 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
975 buffer[sizeof(buffer)-2] = 0;
976 strcat( buffer, "\n" );
977 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
978 CloseHandle( handle );
979 return TRUE;
981 break;
983 case DRIVE_REMOTE:
984 case DRIVE_RAMDISK:
985 case DRIVE_CDROM:
986 SetLastError( ERROR_ACCESS_DENIED );
987 break;
989 return FALSE;
992 /***********************************************************************
993 * SetVolumeLabelA (KERNEL32.@)
995 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
997 WCHAR *rootW = NULL, *volnameW = NULL;
998 BOOL ret;
1000 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1001 if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
1002 ret = SetVolumeLabelW( rootW, volnameW );
1003 HeapFree( GetProcessHeap(), 0, volnameW );
1004 return ret;
1008 /***********************************************************************
1009 * GetVolumeNameForVolumeMountPointA (KERNEL32.@)
1011 BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
1013 BOOL ret;
1014 WCHAR volumeW[50], *pathW = NULL;
1015 DWORD len = min( sizeof(volumeW) / sizeof(WCHAR), size );
1017 TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
1019 if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
1020 return FALSE;
1022 if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
1023 FILE_name_WtoA( volumeW, -1, volume, len );
1025 HeapFree( GetProcessHeap(), 0, pathW );
1026 return ret;
1029 /***********************************************************************
1030 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
1032 BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR path, LPWSTR volume, DWORD size )
1034 static const WCHAR prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1035 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{'};
1036 static const WCHAR trailingW[] = {'\\',0};
1038 MOUNTMGR_MOUNT_POINT *input = NULL, *o1;
1039 MOUNTMGR_MOUNT_POINTS *output = NULL;
1040 WCHAR *p;
1041 char *r;
1042 DWORD i, i_size = 1024, o_size = 1024;
1043 WCHAR *nonpersist_name;
1044 WCHAR symlink_name[MAX_PATH];
1045 NTSTATUS status;
1046 HANDLE mgr = INVALID_HANDLE_VALUE;
1047 BOOL ret = FALSE;
1048 DWORD br;
1050 TRACE("(%s, %p, %x)\n", debugstr_w(path), volume, size);
1051 if (path[lstrlenW(path)-1] != '\\')
1053 SetLastError( ERROR_INVALID_NAME );
1054 return FALSE;
1057 if (size < 50)
1059 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1060 return FALSE;
1062 /* if length of input is > 3 then it must be a mounted folder */
1063 if (lstrlenW(path) > 3)
1065 FIXME("Mounted Folders are not yet supported\n");
1066 SetLastError( ERROR_NOT_A_REPARSE_POINT );
1067 return FALSE;
1070 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ,
1071 NULL, OPEN_EXISTING, 0, 0 );
1072 if (mgr == INVALID_HANDLE_VALUE) return FALSE;
1074 if (!(input = HeapAlloc( GetProcessHeap(), 0, i_size )))
1076 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1077 goto err_ret;
1080 if (!(output = HeapAlloc( GetProcessHeap(), 0, o_size )))
1082 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1083 goto err_ret;
1086 /* construct the symlink name as "\DosDevices\C:" */
1087 lstrcpyW( symlink_name, prefixW );
1088 lstrcatW( symlink_name, path );
1089 symlink_name[lstrlenW(symlink_name)-1] = 0;
1091 /* Take the mount point and get the "nonpersistent name" */
1092 /* We will then take that and get the volume name */
1093 nonpersist_name = (WCHAR *)(input + 1);
1094 status = read_nt_symlink( symlink_name, nonpersist_name, i_size - sizeof(*input) );
1095 TRACE("read_nt_symlink got stat=%x, for %s, got <%s>\n", status,
1096 debugstr_w(symlink_name), debugstr_w(nonpersist_name));
1097 if (status != STATUS_SUCCESS)
1099 SetLastError( ERROR_FILE_NOT_FOUND );
1100 goto err_ret;
1103 /* Now take the "nonpersistent name" and ask the mountmgr */
1104 /* to give us all the mount points. One of them will be */
1105 /* the volume name (format of \??\Volume{). */
1106 memset( input, 0, sizeof(*input) ); /* clear all input parameters */
1107 input->DeviceNameOffset = sizeof(*input);
1108 input->DeviceNameLength = lstrlenW( nonpersist_name) * sizeof(WCHAR);
1109 i_size = input->DeviceNameOffset + input->DeviceNameLength;
1111 output->Size = o_size;
1113 /* now get the true volume name from the mountmgr */
1114 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, i_size,
1115 output, o_size, &br, NULL ))
1116 goto err_ret;
1118 /* Verify and return the data, note string is not null terminated */
1119 TRACE("found %d matching mount points\n", output->NumberOfMountPoints);
1120 if (output->NumberOfMountPoints < 1)
1122 SetLastError( ERROR_NO_VOLUME_ID );
1123 goto err_ret;
1125 o1 = &output->MountPoints[0];
1127 /* look for the volume name in returned values */
1128 for(i=0;i<output->NumberOfMountPoints;i++)
1130 p = (WCHAR*)((char *)output + o1->SymbolicLinkNameOffset);
1131 r = (char *)output + o1->UniqueIdOffset;
1132 TRACE("found symlink=%s, unique=%s, devname=%s\n",
1133 debugstr_wn(p, o1->SymbolicLinkNameLength/sizeof(WCHAR)),
1134 debugstr_an(r, o1->UniqueIdLength),
1135 debugstr_wn((WCHAR*)((char *)output + o1->DeviceNameOffset),
1136 o1->DeviceNameLength/sizeof(WCHAR)));
1138 if (!strncmpW( p, volumeW, sizeof(volumeW)/sizeof(WCHAR) ))
1140 /* is there space in the return variable ?? */
1141 if ((o1->SymbolicLinkNameLength/sizeof(WCHAR))+2 > size)
1143 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1144 goto err_ret;
1146 memcpy( volume, p, o1->SymbolicLinkNameLength );
1147 volume[o1->SymbolicLinkNameLength / sizeof(WCHAR)] = 0;
1148 lstrcatW( volume, trailingW );
1149 /* change second char from '?' to '\' */
1150 volume[1] = '\\';
1151 ret = TRUE;
1152 break;
1154 o1++;
1157 err_ret:
1158 HeapFree( GetProcessHeap(), 0, input );
1159 HeapFree( GetProcessHeap(), 0, output );
1160 CloseHandle( mgr );
1161 return ret;
1164 /***********************************************************************
1165 * DefineDosDeviceW (KERNEL32.@)
1167 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
1169 DWORD len, dosdev;
1170 BOOL ret = FALSE;
1171 char *path = NULL, *target, *p;
1173 TRACE("%x, %s, %s\n", flags, debugstr_w(devname), debugstr_w(targetpath));
1175 if (!(flags & DDD_REMOVE_DEFINITION))
1177 if (!(flags & DDD_RAW_TARGET_PATH))
1179 FIXME( "(0x%08x,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
1180 flags, debugstr_w(devname), debugstr_w(targetpath) );
1181 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1182 return FALSE;
1185 len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
1186 if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
1188 WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
1189 for (p = target; *p; p++) if (*p == '\\') *p = '/';
1191 else
1193 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1194 return FALSE;
1197 else target = NULL;
1199 /* first check for a DOS device */
1201 if ((dosdev = RtlIsDosDeviceName_U( devname )))
1203 WCHAR name[5];
1205 memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
1206 name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
1207 path = get_dos_device_path( name );
1209 else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2]) /* drive mapping */
1211 path = get_dos_device_path( devname );
1213 else SetLastError( ERROR_FILE_NOT_FOUND );
1215 if (path)
1217 if (target)
1219 TRACE( "creating symlink %s -> %s\n", path, target );
1220 unlink( path );
1221 if (!symlink( target, path )) ret = TRUE;
1222 else FILE_SetDosError();
1224 else
1226 TRACE( "removing symlink %s\n", path );
1227 if (!unlink( path )) ret = TRUE;
1228 else FILE_SetDosError();
1230 HeapFree( GetProcessHeap(), 0, path );
1232 HeapFree( GetProcessHeap(), 0, target );
1233 return ret;
1237 /***********************************************************************
1238 * DefineDosDeviceA (KERNEL32.@)
1240 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
1242 WCHAR *devW, *targetW = NULL;
1243 BOOL ret;
1245 if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
1246 if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
1247 ret = DefineDosDeviceW(flags, devW, targetW);
1248 HeapFree( GetProcessHeap(), 0, targetW );
1249 return ret;
1253 /***********************************************************************
1254 * QueryDosDeviceW (KERNEL32.@)
1256 * returns array of strings terminated by \0, terminated by \0
1258 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
1260 static const WCHAR auxW[] = {'A','U','X',0};
1261 static const WCHAR prnW[] = {'P','R','N',0};
1262 static const WCHAR comW[] = {'C','O','M',0};
1263 static const WCHAR lptW[] = {'L','P','T',0};
1264 static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
1265 static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
1266 static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
1267 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1269 UNICODE_STRING nt_name;
1270 ANSI_STRING unix_name;
1271 WCHAR nt_buffer[10];
1272 NTSTATUS status;
1274 if (!bufsize)
1276 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1277 return 0;
1280 if (devname)
1282 WCHAR *p, name[5];
1283 char *path, *link;
1284 WCHAR *buffer;
1285 DWORD dosdev, ret = 0;
1287 if ((dosdev = RtlIsDosDeviceName_U( devname )))
1289 memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
1290 name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
1291 devname = name;
1294 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(dosdevW) + strlenW(devname)*sizeof(WCHAR) )))
1296 SetLastError( ERROR_OUTOFMEMORY );
1297 return 0;
1299 memcpy( buffer, dosdevW, sizeof(dosdevW) );
1300 strcatW( buffer, devname );
1301 status = read_nt_symlink( buffer, target, bufsize );
1302 HeapFree( GetProcessHeap(), 0, buffer );
1303 if (!status)
1305 ret = strlenW( target ) + 1;
1306 goto done;
1308 if (!dosdev) /* not a special DOS device */
1310 SetLastError( RtlNtStatusToDosError(status) );
1311 return 0;
1314 /* FIXME: should read NT symlink for all devices */
1316 if (!(path = get_dos_device_path( name ))) return 0;
1317 link = read_symlink( path );
1318 HeapFree( GetProcessHeap(), 0, path );
1320 if (link)
1322 ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
1323 HeapFree( GetProcessHeap(), 0, link );
1325 else /* look for device defaults */
1327 if (!strcmpiW( name, auxW ))
1329 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
1331 memcpy( target, com1W, sizeof(com1W) );
1332 ret = sizeof(com1W)/sizeof(WCHAR);
1334 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1335 return ret;
1337 if (!strcmpiW( name, prnW ))
1339 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
1341 memcpy( target, lpt1W, sizeof(lpt1W) );
1342 ret = sizeof(lpt1W)/sizeof(WCHAR);
1344 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1345 return ret;
1348 nt_buffer[0] = '\\';
1349 nt_buffer[1] = '?';
1350 nt_buffer[2] = '?';
1351 nt_buffer[3] = '\\';
1352 strcpyW( nt_buffer + 4, name );
1353 RtlInitUnicodeString( &nt_name, nt_buffer );
1354 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE );
1355 if (status) SetLastError( RtlNtStatusToDosError(status) );
1356 else
1358 ret = MultiByteToWideChar( CP_UNIXCP, 0, unix_name.Buffer, -1, target, bufsize );
1359 RtlFreeAnsiString( &unix_name );
1362 done:
1363 if (ret)
1365 if (ret < bufsize) target[ret++] = 0; /* add an extra null */
1366 for (p = target; *p; p++) if (*p == '/') *p = '\\';
1369 return ret;
1371 else /* return a list of all devices */
1373 OBJECT_ATTRIBUTES attr;
1374 HANDLE handle;
1375 WCHAR *p = target;
1376 int i;
1378 if (bufsize <= (sizeof(auxW)+sizeof(prnW))/sizeof(WCHAR))
1380 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1381 return 0;
1384 /* FIXME: these should be NT symlinks too */
1386 memcpy( p, auxW, sizeof(auxW) );
1387 p += sizeof(auxW) / sizeof(WCHAR);
1388 memcpy( p, prnW, sizeof(prnW) );
1389 p += sizeof(prnW) / sizeof(WCHAR);
1391 strcpyW( nt_buffer, com0W );
1392 RtlInitUnicodeString( &nt_name, nt_buffer );
1394 for (i = 1; i <= 9; i++)
1396 nt_buffer[7] = '0' + i;
1397 if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1399 RtlFreeAnsiString( &unix_name );
1400 if (p + 5 >= target + bufsize)
1402 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1403 return 0;
1405 strcpyW( p, comW );
1406 p[3] = '0' + i;
1407 p[4] = 0;
1408 p += 5;
1411 strcpyW( nt_buffer + 4, lptW );
1412 for (i = 1; i <= 9; i++)
1414 nt_buffer[7] = '0' + i;
1415 if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1417 RtlFreeAnsiString( &unix_name );
1418 if (p + 5 >= target + bufsize)
1420 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1421 return 0;
1423 strcpyW( p, lptW );
1424 p[3] = '0' + i;
1425 p[4] = 0;
1426 p += 5;
1430 RtlInitUnicodeString( &nt_name, dosdevW );
1431 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1432 attr.Length = sizeof(attr);
1433 attr.RootDirectory = 0;
1434 attr.ObjectName = &nt_name;
1435 attr.Attributes = OBJ_CASE_INSENSITIVE;
1436 attr.SecurityDescriptor = NULL;
1437 attr.SecurityQualityOfService = NULL;
1438 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1439 if (!status)
1441 char data[1024];
1442 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1443 ULONG ctx = 0, len;
1445 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1447 if (p + info->ObjectName.Length/sizeof(WCHAR) + 1 >= target + bufsize)
1449 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1450 NtClose( handle );
1451 return 0;
1453 memcpy( p, info->ObjectName.Buffer, info->ObjectName.Length );
1454 p += info->ObjectName.Length/sizeof(WCHAR);
1455 *p++ = 0;
1457 NtClose( handle );
1460 *p++ = 0; /* terminating null */
1461 return p - target;
1466 /***********************************************************************
1467 * QueryDosDeviceA (KERNEL32.@)
1469 * returns array of strings terminated by \0, terminated by \0
1471 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1473 DWORD ret = 0, retW;
1474 WCHAR *devnameW = NULL;
1475 LPWSTR targetW;
1477 if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1479 targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1480 if (!targetW)
1482 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1483 return 0;
1486 retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1488 ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1490 HeapFree(GetProcessHeap(), 0, targetW);
1491 return ret;
1495 /***********************************************************************
1496 * GetLogicalDrives (KERNEL32.@)
1498 DWORD WINAPI GetLogicalDrives(void)
1500 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1501 OBJECT_ATTRIBUTES attr;
1502 UNICODE_STRING nt_name;
1503 DWORD bitmask = 0;
1504 NTSTATUS status;
1505 HANDLE handle;
1507 RtlInitUnicodeString( &nt_name, dosdevW );
1508 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1509 attr.Length = sizeof(attr);
1510 attr.RootDirectory = 0;
1511 attr.ObjectName = &nt_name;
1512 attr.Attributes = OBJ_CASE_INSENSITIVE;
1513 attr.SecurityDescriptor = NULL;
1514 attr.SecurityQualityOfService = NULL;
1515 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1516 if (!status)
1518 char data[1024];
1519 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1520 ULONG ctx = 0, len;
1522 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1523 if(info->ObjectName.Length == 2*sizeof(WCHAR) && info->ObjectName.Buffer[1] == ':')
1524 bitmask |= 1 << (info->ObjectName.Buffer[0] - 'A');
1526 NtClose( handle );
1529 return bitmask;
1533 /***********************************************************************
1534 * GetLogicalDriveStringsA (KERNEL32.@)
1536 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1538 DWORD drives = GetLogicalDrives();
1539 UINT drive, count;
1541 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1542 if ((count * 4) + 1 > len) return count * 4 + 1;
1544 for (drive = 0; drive < 26; drive++)
1546 if (drives & (1 << drive))
1548 *buffer++ = 'A' + drive;
1549 *buffer++ = ':';
1550 *buffer++ = '\\';
1551 *buffer++ = 0;
1554 *buffer = 0;
1555 return count * 4;
1559 /***********************************************************************
1560 * GetLogicalDriveStringsW (KERNEL32.@)
1562 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1564 DWORD drives = GetLogicalDrives();
1565 UINT drive, count;
1567 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1568 if ((count * 4) + 1 > len) return count * 4 + 1;
1570 for (drive = 0; drive < 26; drive++)
1572 if (drives & (1 << drive))
1574 *buffer++ = 'A' + drive;
1575 *buffer++ = ':';
1576 *buffer++ = '\\';
1577 *buffer++ = 0;
1580 *buffer = 0;
1581 return count * 4;
1585 /***********************************************************************
1586 * GetDriveTypeW (KERNEL32.@)
1588 * Returns the type of the disk drive specified. If root is NULL the
1589 * root of the current directory is used.
1591 * RETURNS
1593 * Type of drive (from Win32 SDK):
1595 * DRIVE_UNKNOWN unable to find out anything about the drive
1596 * DRIVE_NO_ROOT_DIR nonexistent root dir
1597 * DRIVE_REMOVABLE the disk can be removed from the machine
1598 * DRIVE_FIXED the disk cannot be removed from the machine
1599 * DRIVE_REMOTE network disk
1600 * DRIVE_CDROM CDROM drive
1601 * DRIVE_RAMDISK virtual disk in RAM
1603 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1605 FILE_FS_DEVICE_INFORMATION info;
1606 IO_STATUS_BLOCK io;
1607 NTSTATUS status;
1608 HANDLE handle;
1609 UINT ret;
1611 if (!open_device_root( root, &handle ))
1613 /* CD ROM devices do not necessarily have a volume, but a drive type */
1614 ret = get_mountmgr_drive_type( root );
1615 if (ret == DRIVE_CDROM || ret == DRIVE_REMOVABLE)
1616 return ret;
1618 return DRIVE_NO_ROOT_DIR;
1621 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1622 NtClose( handle );
1623 if (status != STATUS_SUCCESS)
1625 SetLastError( RtlNtStatusToDosError(status) );
1626 ret = DRIVE_UNKNOWN;
1628 else
1630 switch (info.DeviceType)
1632 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: ret = DRIVE_CDROM; break;
1633 case FILE_DEVICE_VIRTUAL_DISK: ret = DRIVE_RAMDISK; break;
1634 case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1635 case FILE_DEVICE_DISK_FILE_SYSTEM:
1636 if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1637 else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1638 else if ((ret = get_mountmgr_drive_type( root )) == DRIVE_UNKNOWN) ret = DRIVE_FIXED;
1639 break;
1640 default:
1641 ret = DRIVE_UNKNOWN;
1642 break;
1645 TRACE( "%s -> %d\n", debugstr_w(root), ret );
1646 return ret;
1650 /***********************************************************************
1651 * GetDriveTypeA (KERNEL32.@)
1653 * See GetDriveTypeW.
1655 UINT WINAPI GetDriveTypeA( LPCSTR root )
1657 WCHAR *rootW = NULL;
1659 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1660 return GetDriveTypeW( rootW );
1664 /***********************************************************************
1665 * GetDiskFreeSpaceExW (KERNEL32.@)
1667 * This function is used to acquire the size of the available and
1668 * total space on a logical volume.
1670 * RETURNS
1672 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1673 * detailed error information.
1676 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1677 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1679 FILE_FS_SIZE_INFORMATION info;
1680 IO_STATUS_BLOCK io;
1681 NTSTATUS status;
1682 HANDLE handle;
1683 UINT units;
1685 TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1687 if (!open_device_root( root, &handle )) return FALSE;
1689 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1690 NtClose( handle );
1691 if (status != STATUS_SUCCESS)
1693 SetLastError( RtlNtStatusToDosError(status) );
1694 return FALSE;
1697 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1698 if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1699 if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1700 /* FIXME: this one should take quotas into account */
1701 if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1702 return TRUE;
1706 /***********************************************************************
1707 * GetDiskFreeSpaceExA (KERNEL32.@)
1709 * See GetDiskFreeSpaceExW.
1711 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1712 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1714 WCHAR *rootW = NULL;
1716 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1717 return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1721 /***********************************************************************
1722 * GetDiskFreeSpaceW (KERNEL32.@)
1724 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1725 LPDWORD sector_bytes, LPDWORD free_clusters,
1726 LPDWORD total_clusters )
1728 FILE_FS_SIZE_INFORMATION info;
1729 IO_STATUS_BLOCK io;
1730 NTSTATUS status;
1731 HANDLE handle;
1732 UINT units;
1734 TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1735 cluster_sectors, sector_bytes, free_clusters, total_clusters );
1737 if (!open_device_root( root, &handle )) return FALSE;
1739 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1740 NtClose( handle );
1741 if (status != STATUS_SUCCESS)
1743 SetLastError( RtlNtStatusToDosError(status) );
1744 return FALSE;
1747 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1749 if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */
1750 /* cap the size and available at 2GB as per specs */
1751 if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1752 info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1753 if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1754 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1756 /* nr. of clusters is always <= 65335 */
1757 while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1758 info.TotalAllocationUnits.QuadPart /= 2;
1759 info.AvailableAllocationUnits.QuadPart /= 2;
1760 info.SectorsPerAllocationUnit *= 2;
1764 if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1765 if (sector_bytes) *sector_bytes = info.BytesPerSector;
1766 if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1767 if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1768 TRACE("%#08x, %#08x, %#08x, %#08x\n", info.SectorsPerAllocationUnit, info.BytesPerSector,
1769 info.AvailableAllocationUnits.u.LowPart, info.TotalAllocationUnits.u.LowPart);
1770 return TRUE;
1774 /***********************************************************************
1775 * GetDiskFreeSpaceA (KERNEL32.@)
1777 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1778 LPDWORD sector_bytes, LPDWORD free_clusters,
1779 LPDWORD total_clusters )
1781 WCHAR *rootW = NULL;
1783 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1784 return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1787 /***********************************************************************
1788 * GetVolumePathNameA (KERNEL32.@)
1790 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1792 BOOL ret;
1793 WCHAR *filenameW = NULL, *volumeW = NULL;
1795 TRACE("(%s, %p, %d)\n", debugstr_a(filename), volumepathname, buflen);
1797 if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
1798 return FALSE;
1799 if (volumepathname && !(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1800 return FALSE;
1802 if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
1803 FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
1805 HeapFree( GetProcessHeap(), 0, volumeW );
1806 return ret;
1809 /***********************************************************************
1810 * GetVolumePathNameW (KERNEL32.@)
1812 * This routine is intended to find the most basic path on the same filesystem
1813 * for any particular path name. Since we can have very complicated drive/path
1814 * relationships on Unix systems, due to symbolic links, the safest way to
1815 * handle this is to start with the full path and work our way back folder by
1816 * folder unil we find a folder on a different drive (or run out of folders).
1818 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1820 static const WCHAR deviceprefixW[] = { '\\','?','?','\\',0 };
1821 static const WCHAR ntprefixW[] = { '\\','\\','?','\\',0 };
1822 WCHAR fallbackpathW[] = { 'C',':','\\',0 };
1823 NTSTATUS status = STATUS_SUCCESS;
1824 WCHAR *volumenameW = NULL, *c;
1825 int pos, last_pos, stop_pos;
1826 UNICODE_STRING nt_name;
1827 ANSI_STRING unix_name;
1828 BOOL first_run = TRUE;
1829 dev_t search_dev = 0;
1830 struct stat st;
1832 TRACE("(%s, %p, %d)\n", debugstr_w(filename), volumepathname, buflen);
1834 if (!filename || !volumepathname || !buflen)
1836 SetLastError(ERROR_INVALID_PARAMETER);
1837 return FALSE;
1840 last_pos = pos = strlenW( filename );
1841 /* allocate enough memory for searching the path (need room for a slash and a NULL terminator) */
1842 if (!(volumenameW = HeapAlloc( GetProcessHeap(), 0, (pos + 2) * sizeof(WCHAR) )))
1844 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1845 return FALSE;
1847 strcpyW( volumenameW, filename );
1848 stop_pos = 0;
1849 /* stop searching slashes early for NT-type and nearly NT-type paths */
1850 if (strncmpW(ntprefixW, filename, strlenW(ntprefixW)) == 0)
1851 stop_pos = strlenW(ntprefixW)-1;
1852 else if (strncmpW(ntprefixW, filename, 2) == 0)
1853 stop_pos = 2;
1857 volumenameW[pos+0] = '\\';
1858 volumenameW[pos+1] = '\0';
1859 if (!RtlDosPathNameToNtPathName_U( volumenameW, &nt_name, NULL, NULL ))
1860 goto cleanup;
1861 volumenameW[pos] = '\0';
1862 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
1863 RtlFreeUnicodeString( &nt_name );
1864 if (status == STATUS_SUCCESS)
1866 if (stat( unix_name.Buffer, &st ) != 0)
1868 RtlFreeAnsiString( &unix_name );
1869 status = STATUS_OBJECT_NAME_INVALID;
1870 goto cleanup;
1872 if (first_run)
1874 first_run = FALSE;
1875 search_dev = st.st_dev;
1877 else if (st.st_dev != search_dev)
1879 /* folder is on a new filesystem, return the last folder */
1880 RtlFreeAnsiString( &unix_name );
1881 break;
1884 RtlFreeAnsiString( &unix_name );
1885 last_pos = pos;
1886 c = strrchrW( volumenameW, '\\' );
1887 if (c != NULL)
1888 pos = c-volumenameW;
1889 } while (c != NULL && pos > stop_pos);
1891 if (status != STATUS_SUCCESS)
1893 WCHAR cwdW[MAX_PATH];
1895 /* the path was completely invalid */
1896 if (filename[0] == '\\' && strncmpW(deviceprefixW, filename, strlenW(deviceprefixW)) != 0)
1898 /* NT-style paths (that are not device paths) fail */
1899 status = STATUS_OBJECT_NAME_INVALID;
1900 goto cleanup;
1903 /* DOS-style paths (anything not beginning with a slash) have fallback replies */
1904 if (filename[1] == ':')
1906 /* if the path is semi-sane (X:) then use the given drive letter (if it is mounted) */
1907 fallbackpathW[0] = filename[0];
1908 if (!isalphaW(filename[0]) || GetDriveTypeW( fallbackpathW ) == DRIVE_NO_ROOT_DIR)
1910 status = STATUS_OBJECT_NAME_NOT_FOUND;
1911 goto cleanup;
1914 else if (GetCurrentDirectoryW( sizeof(cwdW)/sizeof(cwdW[0]), cwdW ))
1916 /* if the path is completely bogus then revert to the drive of the working directory */
1917 fallbackpathW[0] = cwdW[0];
1919 else
1921 status = STATUS_OBJECT_NAME_INVALID;
1922 goto cleanup;
1924 last_pos = strlenW(fallbackpathW) - 1; /* points to \\ */
1925 filename = fallbackpathW;
1926 status = STATUS_SUCCESS;
1929 if (last_pos + 1 <= buflen)
1931 WCHAR *p;
1932 memcpy(volumepathname, filename, last_pos * sizeof(WCHAR));
1933 if (last_pos + 2 <= buflen) volumepathname[last_pos++] = '\\';
1934 volumepathname[last_pos] = '\0';
1936 /* Normalize path */
1937 for (p = volumepathname; *p; p++) if (*p == '/') *p = '\\';
1939 /* DOS-style paths always return upper-case drive letters */
1940 if (volumepathname[1] == ':')
1941 volumepathname[0] = toupperW(volumepathname[0]);
1943 TRACE("Successfully translated path %s to mount-point %s\n",
1944 debugstr_w(filename), debugstr_w(volumepathname));
1946 else
1947 status = STATUS_NAME_TOO_LONG;
1949 cleanup:
1950 HeapFree( GetProcessHeap(), 0, volumenameW );
1952 if (status != STATUS_SUCCESS)
1953 SetLastError( RtlNtStatusToDosError(status) );
1954 return (status == STATUS_SUCCESS);
1958 /***********************************************************************
1959 * GetVolumePathNamesForVolumeNameA (KERNEL32.@)
1961 BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen)
1963 BOOL ret;
1964 WCHAR *volumenameW = NULL, *volumepathnameW;
1966 if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE;
1967 if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1969 HeapFree( GetProcessHeap(), 0, volumenameW );
1970 return FALSE;
1972 if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen )))
1974 char *path = volumepathname;
1975 const WCHAR *pathW = volumepathnameW;
1977 while (*pathW)
1979 int len = strlenW( pathW ) + 1;
1980 FILE_name_WtoA( pathW, len, path, buflen );
1981 buflen -= len;
1982 pathW += len;
1983 path += len;
1985 path[0] = 0;
1987 HeapFree( GetProcessHeap(), 0, volumenameW );
1988 HeapFree( GetProcessHeap(), 0, volumepathnameW );
1989 return ret;
1992 static MOUNTMGR_MOUNT_POINTS *query_mount_points( HANDLE mgr, MOUNTMGR_MOUNT_POINT *input, DWORD insize )
1994 MOUNTMGR_MOUNT_POINTS *output;
1995 DWORD outsize = 1024;
1996 DWORD br;
1998 for (;;)
2000 if (!(output = HeapAlloc( GetProcessHeap(), 0, outsize )))
2002 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
2003 return NULL;
2005 if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, insize, output, outsize, &br, NULL )) break;
2006 outsize = output->Size;
2007 HeapFree( GetProcessHeap(), 0, output );
2008 if (GetLastError() != ERROR_MORE_DATA) return NULL;
2010 return output;
2012 /***********************************************************************
2013 * GetVolumePathNamesForVolumeNameW (KERNEL32.@)
2015 BOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR volumename, LPWSTR volumepathname, DWORD buflen, PDWORD returnlen)
2017 static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
2018 HANDLE mgr;
2019 DWORD len, size;
2020 MOUNTMGR_MOUNT_POINT *spec;
2021 MOUNTMGR_MOUNT_POINTS *link, *target = NULL;
2022 WCHAR *name, *path;
2023 BOOL ret = FALSE;
2024 UINT i, j;
2026 TRACE("%s, %p, %u, %p\n", debugstr_w(volumename), volumepathname, buflen, returnlen);
2028 if (!volumename || (len = strlenW( volumename )) != 49)
2030 SetLastError( ERROR_INVALID_NAME );
2031 return FALSE;
2033 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
2034 if (mgr == INVALID_HANDLE_VALUE) return FALSE;
2036 size = sizeof(*spec) + sizeof(WCHAR) * (len - 1); /* remove trailing backslash */
2037 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
2038 spec->SymbolicLinkNameOffset = sizeof(*spec);
2039 spec->SymbolicLinkNameLength = size - sizeof(*spec);
2040 name = (WCHAR *)((char *)spec + spec->SymbolicLinkNameOffset);
2041 memcpy( name, volumename, size - sizeof(*spec) );
2042 name[1] = '?'; /* map \\?\ to \??\ */
2044 target = query_mount_points( mgr, spec, size );
2045 HeapFree( GetProcessHeap(), 0, spec );
2046 if (!target)
2048 goto done;
2050 if (!target->NumberOfMountPoints)
2052 SetLastError( ERROR_FILE_NOT_FOUND );
2053 goto done;
2055 len = 0;
2056 path = volumepathname;
2057 for (i = 0; i < target->NumberOfMountPoints; i++)
2059 link = NULL;
2060 if (target->MountPoints[i].DeviceNameOffset)
2062 const WCHAR *device = (const WCHAR *)((const char *)target + target->MountPoints[i].DeviceNameOffset);
2063 USHORT device_len = target->MountPoints[i].DeviceNameLength;
2065 size = sizeof(*spec) + device_len;
2066 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
2067 spec->DeviceNameOffset = sizeof(*spec);
2068 spec->DeviceNameLength = device_len;
2069 memcpy( (char *)spec + spec->DeviceNameOffset, device, device_len );
2071 link = query_mount_points( mgr, spec, size );
2072 HeapFree( GetProcessHeap(), 0, spec );
2074 else if (target->MountPoints[i].UniqueIdOffset)
2076 const WCHAR *id = (const WCHAR *)((const char *)target + target->MountPoints[i].UniqueIdOffset);
2077 USHORT id_len = target->MountPoints[i].UniqueIdLength;
2079 size = sizeof(*spec) + id_len;
2080 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
2081 spec->UniqueIdOffset = sizeof(*spec);
2082 spec->UniqueIdLength = id_len;
2083 memcpy( (char *)spec + spec->UniqueIdOffset, id, id_len );
2085 link = query_mount_points( mgr, spec, size );
2086 HeapFree( GetProcessHeap(), 0, spec );
2088 if (!link) continue;
2089 for (j = 0; j < link->NumberOfMountPoints; j++)
2091 const WCHAR *linkname;
2093 if (!link->MountPoints[j].SymbolicLinkNameOffset) continue;
2094 linkname = (const WCHAR *)((const char *)link + link->MountPoints[j].SymbolicLinkNameOffset);
2096 if (link->MountPoints[j].SymbolicLinkNameLength == sizeof(dosdevicesW) + 2 * sizeof(WCHAR) &&
2097 !memicmpW( linkname, dosdevicesW, sizeof(dosdevicesW) / sizeof(WCHAR) ))
2099 len += 4;
2100 if (volumepathname && len < buflen)
2102 path[0] = linkname[sizeof(dosdevicesW) / sizeof(WCHAR)];
2103 path[1] = ':';
2104 path[2] = '\\';
2105 path[3] = 0;
2106 path += 4;
2110 HeapFree( GetProcessHeap(), 0, link );
2112 if (buflen <= len) SetLastError( ERROR_MORE_DATA );
2113 else if (volumepathname)
2115 volumepathname[len] = 0;
2116 ret = TRUE;
2118 if (returnlen) *returnlen = len + 1;
2120 done:
2121 HeapFree( GetProcessHeap(), 0, target );
2122 CloseHandle( mgr );
2123 return ret;
2126 /***********************************************************************
2127 * FindFirstVolumeA (KERNEL32.@)
2129 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
2131 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2132 HANDLE handle = FindFirstVolumeW( buffer, len );
2134 if (handle != INVALID_HANDLE_VALUE)
2136 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
2138 FindVolumeClose( handle );
2139 handle = INVALID_HANDLE_VALUE;
2142 HeapFree( GetProcessHeap(), 0, buffer );
2143 return handle;
2146 /***********************************************************************
2147 * FindFirstVolumeW (KERNEL32.@)
2149 HANDLE WINAPI FindFirstVolumeW( LPWSTR volume, DWORD len )
2151 DWORD size = 1024;
2152 DWORD br;
2153 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
2154 NULL, OPEN_EXISTING, 0, 0 );
2155 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
2157 for (;;)
2159 MOUNTMGR_MOUNT_POINT input;
2160 MOUNTMGR_MOUNT_POINTS *output;
2162 if (!(output = HeapAlloc( GetProcessHeap(), 0, size )))
2164 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
2165 break;
2167 memset( &input, 0, sizeof(input) );
2169 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
2170 output, size, &br, NULL ))
2172 if (GetLastError() != ERROR_MORE_DATA) break;
2173 size = output->Size;
2174 HeapFree( GetProcessHeap(), 0, output );
2175 continue;
2177 CloseHandle( mgr );
2178 /* abuse the Size field to store the current index */
2179 output->Size = 0;
2180 if (!FindNextVolumeW( output, volume, len ))
2182 HeapFree( GetProcessHeap(), 0, output );
2183 return INVALID_HANDLE_VALUE;
2185 return output;
2187 CloseHandle( mgr );
2188 return INVALID_HANDLE_VALUE;
2191 /***********************************************************************
2192 * FindNextVolumeA (KERNEL32.@)
2194 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
2196 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2197 BOOL ret;
2199 if ((ret = FindNextVolumeW( handle, buffer, len )))
2201 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
2203 HeapFree( GetProcessHeap(), 0, buffer );
2204 return ret;
2207 /***********************************************************************
2208 * FindNextVolumeW (KERNEL32.@)
2210 BOOL WINAPI FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len )
2212 MOUNTMGR_MOUNT_POINTS *data = handle;
2214 while (data->Size < data->NumberOfMountPoints)
2216 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
2217 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
2218 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
2219 data->Size++;
2220 /* skip non-volumes */
2221 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
2222 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
2224 SetLastError( ERROR_FILENAME_EXCED_RANGE );
2225 return FALSE;
2227 memcpy( volume, link, size );
2228 volume[1] = '\\'; /* map \??\ to \\?\ */
2229 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
2230 volume[size / sizeof(WCHAR) + 1] = 0;
2231 TRACE( "returning entry %u %s\n", data->Size - 1, debugstr_w(volume) );
2232 return TRUE;
2234 SetLastError( ERROR_NO_MORE_FILES );
2235 return FALSE;
2238 /***********************************************************************
2239 * FindVolumeClose (KERNEL32.@)
2241 BOOL WINAPI FindVolumeClose(HANDLE handle)
2243 return HeapFree( GetProcessHeap(), 0, handle );
2246 /***********************************************************************
2247 * FindFirstVolumeMountPointA (KERNEL32.@)
2249 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
2251 FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
2252 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2253 return INVALID_HANDLE_VALUE;
2256 /***********************************************************************
2257 * FindFirstVolumeMountPointW (KERNEL32.@)
2259 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
2261 FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
2262 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2263 return INVALID_HANDLE_VALUE;
2266 /***********************************************************************
2267 * FindVolumeMountPointClose (KERNEL32.@)
2269 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
2271 FIXME("(%p), stub!\n", h);
2272 return FALSE;
2275 /***********************************************************************
2276 * DeleteVolumeMountPointA (KERNEL32.@)
2278 BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint)
2280 FIXME("(%s), stub!\n", debugstr_a(mountpoint));
2281 return FALSE;
2284 /***********************************************************************
2285 * DeleteVolumeMountPointW (KERNEL32.@)
2287 BOOL WINAPI DeleteVolumeMountPointW(LPCWSTR mountpoint)
2289 FIXME("(%s), stub!\n", debugstr_w(mountpoint));
2290 return FALSE;
2293 /***********************************************************************
2294 * SetVolumeMountPointA (KERNEL32.@)
2296 BOOL WINAPI SetVolumeMountPointA(LPCSTR path, LPCSTR volume)
2298 FIXME("(%s, %s), stub!\n", debugstr_a(path), debugstr_a(volume));
2299 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2300 return FALSE;
2303 /***********************************************************************
2304 * SetVolumeMountPointW (KERNEL32.@)
2306 BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume)
2308 FIXME("(%s, %s), stub!\n", debugstr_w(path), debugstr_w(volume));
2309 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2310 return FALSE;