dpnet/tests: Add a trailing '\n' to some ok() calls.
[wine.git] / dlls / kernel32 / volume.c
blobd3967649eb12b13df2034248692868e50875ba67
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, 0, &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, &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, &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 Extented 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, &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, 0, &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 nulW[] = {'N','U','L',0};
1262 static const WCHAR prnW[] = {'P','R','N',0};
1263 static const WCHAR comW[] = {'C','O','M',0};
1264 static const WCHAR lptW[] = {'L','P','T',0};
1265 static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
1266 static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
1267 static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
1268 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1270 UNICODE_STRING nt_name;
1271 ANSI_STRING unix_name;
1272 WCHAR nt_buffer[10];
1273 NTSTATUS status;
1275 if (!bufsize)
1277 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1278 return 0;
1281 if (devname)
1283 WCHAR *p, name[5];
1284 char *path, *link;
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;
1292 else
1294 WCHAR *buffer;
1296 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(dosdevW) + strlenW(devname)*sizeof(WCHAR) )))
1298 SetLastError( ERROR_OUTOFMEMORY );
1299 return 0;
1301 memcpy( buffer, dosdevW, sizeof(dosdevW) );
1302 strcatW( buffer, devname );
1303 status = read_nt_symlink( buffer, target, bufsize );
1304 HeapFree( GetProcessHeap(), 0, buffer );
1305 if (status)
1307 SetLastError( RtlNtStatusToDosError(status) );
1308 return 0;
1310 ret = strlenW( target ) + 1;
1311 goto done;
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 if (dosdev) /* 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(nulW)+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, nulW, sizeof(nulW) );
1389 p += sizeof(nulW) / sizeof(WCHAR);
1390 memcpy( p, prnW, sizeof(prnW) );
1391 p += sizeof(prnW) / sizeof(WCHAR);
1393 strcpyW( nt_buffer, com0W );
1394 RtlInitUnicodeString( &nt_name, nt_buffer );
1396 for (i = 1; i <= 9; i++)
1398 nt_buffer[7] = '0' + i;
1399 if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1401 RtlFreeAnsiString( &unix_name );
1402 if (p + 5 >= target + bufsize)
1404 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1405 return 0;
1407 strcpyW( p, comW );
1408 p[3] = '0' + i;
1409 p[4] = 0;
1410 p += 5;
1413 strcpyW( nt_buffer + 4, lptW );
1414 for (i = 1; i <= 9; i++)
1416 nt_buffer[7] = '0' + i;
1417 if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1419 RtlFreeAnsiString( &unix_name );
1420 if (p + 5 >= target + bufsize)
1422 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1423 return 0;
1425 strcpyW( p, lptW );
1426 p[3] = '0' + i;
1427 p[4] = 0;
1428 p += 5;
1432 RtlInitUnicodeString( &nt_name, dosdevW );
1433 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1434 attr.Length = sizeof(attr);
1435 attr.RootDirectory = 0;
1436 attr.ObjectName = &nt_name;
1437 attr.Attributes = OBJ_CASE_INSENSITIVE;
1438 attr.SecurityDescriptor = NULL;
1439 attr.SecurityQualityOfService = NULL;
1440 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1441 if (!status)
1443 char data[1024];
1444 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1445 ULONG ctx = 0, len;
1447 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1449 if (p + info->ObjectName.Length/sizeof(WCHAR) + 1 >= target + bufsize)
1451 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1452 NtClose( handle );
1453 return 0;
1455 memcpy( p, info->ObjectName.Buffer, info->ObjectName.Length );
1456 p += info->ObjectName.Length/sizeof(WCHAR);
1457 *p++ = 0;
1459 NtClose( handle );
1462 *p++ = 0; /* terminating null */
1463 return p - target;
1468 /***********************************************************************
1469 * QueryDosDeviceA (KERNEL32.@)
1471 * returns array of strings terminated by \0, terminated by \0
1473 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1475 DWORD ret = 0, retW;
1476 WCHAR *devnameW = NULL;
1477 LPWSTR targetW;
1479 if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1481 targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1482 if (!targetW)
1484 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1485 return 0;
1488 retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1490 ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1492 HeapFree(GetProcessHeap(), 0, targetW);
1493 return ret;
1497 /***********************************************************************
1498 * GetLogicalDrives (KERNEL32.@)
1500 DWORD WINAPI GetLogicalDrives(void)
1502 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1503 OBJECT_ATTRIBUTES attr;
1504 UNICODE_STRING nt_name;
1505 DWORD bitmask = 0;
1506 NTSTATUS status;
1507 HANDLE handle;
1509 RtlInitUnicodeString( &nt_name, dosdevW );
1510 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1511 attr.Length = sizeof(attr);
1512 attr.RootDirectory = 0;
1513 attr.ObjectName = &nt_name;
1514 attr.Attributes = OBJ_CASE_INSENSITIVE;
1515 attr.SecurityDescriptor = NULL;
1516 attr.SecurityQualityOfService = NULL;
1517 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1518 if (!status)
1520 char data[1024];
1521 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1522 ULONG ctx = 0, len;
1524 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1525 if(info->ObjectName.Length == 2*sizeof(WCHAR) && info->ObjectName.Buffer[1] == ':')
1526 bitmask |= 1 << (info->ObjectName.Buffer[0] - 'A');
1528 NtClose( handle );
1531 return bitmask;
1535 /***********************************************************************
1536 * GetLogicalDriveStringsA (KERNEL32.@)
1538 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1540 DWORD drives = GetLogicalDrives();
1541 UINT drive, count;
1543 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1544 if ((count * 4) + 1 > len) return count * 4 + 1;
1546 for (drive = 0; drive < 26; drive++)
1548 if (drives & (1 << drive))
1550 *buffer++ = 'A' + drive;
1551 *buffer++ = ':';
1552 *buffer++ = '\\';
1553 *buffer++ = 0;
1556 *buffer = 0;
1557 return count * 4;
1561 /***********************************************************************
1562 * GetLogicalDriveStringsW (KERNEL32.@)
1564 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1566 DWORD drives = GetLogicalDrives();
1567 UINT drive, count;
1569 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1570 if ((count * 4) + 1 > len) return count * 4 + 1;
1572 for (drive = 0; drive < 26; drive++)
1574 if (drives & (1 << drive))
1576 *buffer++ = 'A' + drive;
1577 *buffer++ = ':';
1578 *buffer++ = '\\';
1579 *buffer++ = 0;
1582 *buffer = 0;
1583 return count * 4;
1587 /***********************************************************************
1588 * GetDriveTypeW (KERNEL32.@)
1590 * Returns the type of the disk drive specified. If root is NULL the
1591 * root of the current directory is used.
1593 * RETURNS
1595 * Type of drive (from Win32 SDK):
1597 * DRIVE_UNKNOWN unable to find out anything about the drive
1598 * DRIVE_NO_ROOT_DIR nonexistent root dir
1599 * DRIVE_REMOVABLE the disk can be removed from the machine
1600 * DRIVE_FIXED the disk cannot be removed from the machine
1601 * DRIVE_REMOTE network disk
1602 * DRIVE_CDROM CDROM drive
1603 * DRIVE_RAMDISK virtual disk in RAM
1605 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1607 FILE_FS_DEVICE_INFORMATION info;
1608 IO_STATUS_BLOCK io;
1609 NTSTATUS status;
1610 HANDLE handle;
1611 UINT ret;
1613 if (!open_device_root( root, &handle )) return DRIVE_NO_ROOT_DIR;
1615 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1616 NtClose( handle );
1617 if (status != STATUS_SUCCESS)
1619 SetLastError( RtlNtStatusToDosError(status) );
1620 ret = DRIVE_UNKNOWN;
1622 else
1624 switch (info.DeviceType)
1626 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: ret = DRIVE_CDROM; break;
1627 case FILE_DEVICE_VIRTUAL_DISK: ret = DRIVE_RAMDISK; break;
1628 case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1629 case FILE_DEVICE_DISK_FILE_SYSTEM:
1630 if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1631 else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1632 else if ((ret = get_mountmgr_drive_type( root )) == DRIVE_UNKNOWN) ret = DRIVE_FIXED;
1633 break;
1634 default:
1635 ret = DRIVE_UNKNOWN;
1636 break;
1639 TRACE( "%s -> %d\n", debugstr_w(root), ret );
1640 return ret;
1644 /***********************************************************************
1645 * GetDriveTypeA (KERNEL32.@)
1647 * See GetDriveTypeW.
1649 UINT WINAPI GetDriveTypeA( LPCSTR root )
1651 WCHAR *rootW = NULL;
1653 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1654 return GetDriveTypeW( rootW );
1658 /***********************************************************************
1659 * GetDiskFreeSpaceExW (KERNEL32.@)
1661 * This function is used to acquire the size of the available and
1662 * total space on a logical volume.
1664 * RETURNS
1666 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1667 * detailed error information.
1670 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1671 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1673 FILE_FS_SIZE_INFORMATION info;
1674 IO_STATUS_BLOCK io;
1675 NTSTATUS status;
1676 HANDLE handle;
1677 UINT units;
1679 TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1681 if (!open_device_root( root, &handle )) return FALSE;
1683 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1684 NtClose( handle );
1685 if (status != STATUS_SUCCESS)
1687 SetLastError( RtlNtStatusToDosError(status) );
1688 return FALSE;
1691 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1692 if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1693 if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1694 /* FIXME: this one should take quotas into account */
1695 if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1696 return TRUE;
1700 /***********************************************************************
1701 * GetDiskFreeSpaceExA (KERNEL32.@)
1703 * See GetDiskFreeSpaceExW.
1705 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1706 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1708 WCHAR *rootW = NULL;
1710 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1711 return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1715 /***********************************************************************
1716 * GetDiskFreeSpaceW (KERNEL32.@)
1718 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1719 LPDWORD sector_bytes, LPDWORD free_clusters,
1720 LPDWORD total_clusters )
1722 FILE_FS_SIZE_INFORMATION info;
1723 IO_STATUS_BLOCK io;
1724 NTSTATUS status;
1725 HANDLE handle;
1726 UINT units;
1728 TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1729 cluster_sectors, sector_bytes, free_clusters, total_clusters );
1731 if (!open_device_root( root, &handle )) return FALSE;
1733 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1734 NtClose( handle );
1735 if (status != STATUS_SUCCESS)
1737 SetLastError( RtlNtStatusToDosError(status) );
1738 return FALSE;
1741 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1743 if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */
1744 /* cap the size and available at 2GB as per specs */
1745 if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1746 info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1747 if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1748 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1750 /* nr. of clusters is always <= 65335 */
1751 while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1752 info.TotalAllocationUnits.QuadPart /= 2;
1753 info.AvailableAllocationUnits.QuadPart /= 2;
1754 info.SectorsPerAllocationUnit *= 2;
1758 if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1759 if (sector_bytes) *sector_bytes = info.BytesPerSector;
1760 if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1761 if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1762 TRACE("%#08x, %#08x, %#08x, %#08x\n", info.SectorsPerAllocationUnit, info.BytesPerSector,
1763 info.AvailableAllocationUnits.u.LowPart, info.TotalAllocationUnits.u.LowPart);
1764 return TRUE;
1768 /***********************************************************************
1769 * GetDiskFreeSpaceA (KERNEL32.@)
1771 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1772 LPDWORD sector_bytes, LPDWORD free_clusters,
1773 LPDWORD total_clusters )
1775 WCHAR *rootW = NULL;
1777 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1778 return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1781 /***********************************************************************
1782 * GetVolumePathNameA (KERNEL32.@)
1784 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1786 BOOL ret;
1787 WCHAR *filenameW = NULL, *volumeW = NULL;
1789 FIXME("(%s, %p, %d), stub!\n", debugstr_a(filename), volumepathname, buflen);
1791 if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
1792 return FALSE;
1793 if (volumepathname && !(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1794 return FALSE;
1796 if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
1797 FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
1799 HeapFree( GetProcessHeap(), 0, volumeW );
1800 return ret;
1803 /***********************************************************************
1804 * GetVolumePathNameW (KERNEL32.@)
1806 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1808 const WCHAR *p = filename;
1810 FIXME("(%s, %p, %d), stub!\n", debugstr_w(filename), volumepathname, buflen);
1812 if (!filename || !volumepathname || !buflen)
1814 SetLastError(ERROR_INVALID_PARAMETER);
1815 return FALSE;
1818 if (p && tolowerW(p[0]) >= 'a' && tolowerW(p[0]) <= 'z' && p[1] ==':' && p[2] == '\\')
1820 if (buflen < 4)
1822 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1823 return FALSE;
1825 volumepathname[0] = p[0];
1826 volumepathname[1] = ':';
1827 volumepathname[2] = '\\';
1828 volumepathname[3] = 0;
1829 return TRUE;
1832 SetLastError(ERROR_INVALID_NAME);
1833 return FALSE;
1836 /***********************************************************************
1837 * GetVolumePathNamesForVolumeNameA (KERNEL32.@)
1839 BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen)
1841 BOOL ret;
1842 WCHAR *volumenameW = NULL, *volumepathnameW;
1844 if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE;
1845 if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1847 HeapFree( GetProcessHeap(), 0, volumenameW );
1848 return FALSE;
1850 if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen )))
1852 char *path = volumepathname;
1853 const WCHAR *pathW = volumepathnameW;
1855 while (*pathW)
1857 int len = strlenW( pathW ) + 1;
1858 FILE_name_WtoA( pathW, len, path, buflen );
1859 buflen -= len;
1860 pathW += len;
1861 path += len;
1863 path[0] = 0;
1865 HeapFree( GetProcessHeap(), 0, volumenameW );
1866 HeapFree( GetProcessHeap(), 0, volumepathnameW );
1867 return ret;
1870 static MOUNTMGR_MOUNT_POINTS *query_mount_points( HANDLE mgr, MOUNTMGR_MOUNT_POINT *input, DWORD insize )
1872 MOUNTMGR_MOUNT_POINTS *output;
1873 DWORD outsize = 1024;
1874 DWORD br;
1876 for (;;)
1878 if (!(output = HeapAlloc( GetProcessHeap(), 0, outsize )))
1880 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1881 return NULL;
1883 if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, insize, output, outsize, &br, NULL )) break;
1884 outsize = output->Size;
1885 HeapFree( GetProcessHeap(), 0, output );
1886 if (GetLastError() != ERROR_MORE_DATA) return NULL;
1888 return output;
1890 /***********************************************************************
1891 * GetVolumePathNamesForVolumeNameW (KERNEL32.@)
1893 BOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR volumename, LPWSTR volumepathname, DWORD buflen, PDWORD returnlen)
1895 static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
1896 HANDLE mgr;
1897 DWORD len, size;
1898 MOUNTMGR_MOUNT_POINT *spec;
1899 MOUNTMGR_MOUNT_POINTS *link, *target = NULL;
1900 WCHAR *name, *path;
1901 BOOL ret = FALSE;
1902 UINT i, j;
1904 TRACE("%s, %p, %u, %p\n", debugstr_w(volumename), volumepathname, buflen, returnlen);
1906 if (!volumename || (len = strlenW( volumename )) != 49)
1908 SetLastError( ERROR_INVALID_NAME );
1909 return FALSE;
1911 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
1912 if (mgr == INVALID_HANDLE_VALUE) return FALSE;
1914 size = sizeof(*spec) + sizeof(WCHAR) * (len - 1); /* remove trailing backslash */
1915 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1916 spec->SymbolicLinkNameOffset = sizeof(*spec);
1917 spec->SymbolicLinkNameLength = size - sizeof(*spec);
1918 name = (WCHAR *)((char *)spec + spec->SymbolicLinkNameOffset);
1919 memcpy( name, volumename, size - sizeof(*spec) );
1920 name[1] = '?'; /* map \\?\ to \??\ */
1922 target = query_mount_points( mgr, spec, size );
1923 HeapFree( GetProcessHeap(), 0, spec );
1924 if (!target)
1926 goto done;
1928 if (!target->NumberOfMountPoints)
1930 SetLastError( ERROR_FILE_NOT_FOUND );
1931 goto done;
1933 len = 0;
1934 path = volumepathname;
1935 for (i = 0; i < target->NumberOfMountPoints; i++)
1937 link = NULL;
1938 if (target->MountPoints[i].DeviceNameOffset)
1940 const WCHAR *device = (const WCHAR *)((const char *)target + target->MountPoints[i].DeviceNameOffset);
1941 USHORT device_len = target->MountPoints[i].DeviceNameLength;
1943 size = sizeof(*spec) + device_len;
1944 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1945 spec->DeviceNameOffset = sizeof(*spec);
1946 spec->DeviceNameLength = device_len;
1947 memcpy( (char *)spec + spec->DeviceNameOffset, device, device_len );
1949 link = query_mount_points( mgr, spec, size );
1950 HeapFree( GetProcessHeap(), 0, spec );
1952 else if (target->MountPoints[i].UniqueIdOffset)
1954 const WCHAR *id = (const WCHAR *)((const char *)target + target->MountPoints[i].UniqueIdOffset);
1955 USHORT id_len = target->MountPoints[i].UniqueIdLength;
1957 size = sizeof(*spec) + id_len;
1958 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1959 spec->UniqueIdOffset = sizeof(*spec);
1960 spec->UniqueIdLength = id_len;
1961 memcpy( (char *)spec + spec->UniqueIdOffset, id, id_len );
1963 link = query_mount_points( mgr, spec, size );
1964 HeapFree( GetProcessHeap(), 0, spec );
1966 if (!link) continue;
1967 for (j = 0; j < link->NumberOfMountPoints; j++)
1969 const WCHAR *linkname;
1971 if (!link->MountPoints[j].SymbolicLinkNameOffset) continue;
1972 linkname = (const WCHAR *)((const char *)link + link->MountPoints[j].SymbolicLinkNameOffset);
1974 if (link->MountPoints[j].SymbolicLinkNameLength == sizeof(dosdevicesW) + 2 * sizeof(WCHAR) &&
1975 !memicmpW( linkname, dosdevicesW, sizeof(dosdevicesW) / sizeof(WCHAR) ))
1977 len += 4;
1978 if (volumepathname && len < buflen)
1980 path[0] = linkname[sizeof(dosdevicesW) / sizeof(WCHAR)];
1981 path[1] = ':';
1982 path[2] = '\\';
1983 path[3] = 0;
1984 path += 4;
1988 HeapFree( GetProcessHeap(), 0, link );
1990 if (buflen <= len) SetLastError( ERROR_MORE_DATA );
1991 else if (volumepathname)
1993 volumepathname[len] = 0;
1994 ret = TRUE;
1996 if (returnlen) *returnlen = len + 1;
1998 done:
1999 HeapFree( GetProcessHeap(), 0, target );
2000 CloseHandle( mgr );
2001 return ret;
2004 /***********************************************************************
2005 * FindFirstVolumeA (KERNEL32.@)
2007 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
2009 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2010 HANDLE handle = FindFirstVolumeW( buffer, len );
2012 if (handle != INVALID_HANDLE_VALUE)
2014 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
2016 FindVolumeClose( handle );
2017 handle = INVALID_HANDLE_VALUE;
2020 HeapFree( GetProcessHeap(), 0, buffer );
2021 return handle;
2024 /***********************************************************************
2025 * FindFirstVolumeW (KERNEL32.@)
2027 HANDLE WINAPI FindFirstVolumeW( LPWSTR volume, DWORD len )
2029 DWORD size = 1024;
2030 DWORD br;
2031 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
2032 NULL, OPEN_EXISTING, 0, 0 );
2033 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
2035 for (;;)
2037 MOUNTMGR_MOUNT_POINT input;
2038 MOUNTMGR_MOUNT_POINTS *output;
2040 if (!(output = HeapAlloc( GetProcessHeap(), 0, size )))
2042 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
2043 break;
2045 memset( &input, 0, sizeof(input) );
2047 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
2048 output, size, &br, NULL ))
2050 if (GetLastError() != ERROR_MORE_DATA) break;
2051 size = output->Size;
2052 HeapFree( GetProcessHeap(), 0, output );
2053 continue;
2055 CloseHandle( mgr );
2056 /* abuse the Size field to store the current index */
2057 output->Size = 0;
2058 if (!FindNextVolumeW( output, volume, len ))
2060 HeapFree( GetProcessHeap(), 0, output );
2061 return INVALID_HANDLE_VALUE;
2063 return output;
2065 CloseHandle( mgr );
2066 return INVALID_HANDLE_VALUE;
2069 /***********************************************************************
2070 * FindNextVolumeA (KERNEL32.@)
2072 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
2074 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2075 BOOL ret;
2077 if ((ret = FindNextVolumeW( handle, buffer, len )))
2079 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
2081 HeapFree( GetProcessHeap(), 0, buffer );
2082 return ret;
2085 /***********************************************************************
2086 * FindNextVolumeW (KERNEL32.@)
2088 BOOL WINAPI FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len )
2090 MOUNTMGR_MOUNT_POINTS *data = handle;
2092 while (data->Size < data->NumberOfMountPoints)
2094 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
2095 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
2096 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
2097 data->Size++;
2098 /* skip non-volumes */
2099 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
2100 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
2102 SetLastError( ERROR_FILENAME_EXCED_RANGE );
2103 return FALSE;
2105 memcpy( volume, link, size );
2106 volume[1] = '\\'; /* map \??\ to \\?\ */
2107 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
2108 volume[size / sizeof(WCHAR) + 1] = 0;
2109 TRACE( "returning entry %u %s\n", data->Size - 1, debugstr_w(volume) );
2110 return TRUE;
2112 SetLastError( ERROR_NO_MORE_FILES );
2113 return FALSE;
2116 /***********************************************************************
2117 * FindVolumeClose (KERNEL32.@)
2119 BOOL WINAPI FindVolumeClose(HANDLE handle)
2121 return HeapFree( GetProcessHeap(), 0, handle );
2124 /***********************************************************************
2125 * FindFirstVolumeMountPointA (KERNEL32.@)
2127 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
2129 FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
2130 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2131 return INVALID_HANDLE_VALUE;
2134 /***********************************************************************
2135 * FindFirstVolumeMountPointW (KERNEL32.@)
2137 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
2139 FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
2140 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2141 return INVALID_HANDLE_VALUE;
2144 /***********************************************************************
2145 * FindVolumeMountPointClose (KERNEL32.@)
2147 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
2149 FIXME("(%p), stub!\n", h);
2150 return FALSE;
2153 /***********************************************************************
2154 * DeleteVolumeMountPointA (KERNEL32.@)
2156 BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint)
2158 FIXME("(%s), stub!\n", debugstr_a(mountpoint));
2159 return FALSE;
2162 /***********************************************************************
2163 * DeleteVolumeMountPointW (KERNEL32.@)
2165 BOOL WINAPI DeleteVolumeMountPointW(LPCWSTR mountpoint)
2167 FIXME("(%s), stub!\n", debugstr_w(mountpoint));
2168 return FALSE;