Reimplemented DeleteFile by using CreateFile with
[wine.git] / dlls / kernel / volume.c
blob48cc2f130ff06e40cffbc63f6c4da72c1a3a2171
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "config.h"
26 #include "wine/port.h"
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <stdio.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winreg.h"
35 #include "winnls.h"
36 #include "winternl.h"
37 #include "ntstatus.h"
38 #include "winioctl.h"
39 #include "ntddstor.h"
40 #include "ntddcdrm.h"
41 #include "kernel_private.h"
42 #include "file.h"
43 #include "wine/library.h"
44 #include "wine/unicode.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(volume);
49 #define SUPERBLOCK_SIZE 2048
51 #define CDFRAMES_PERSEC 75
52 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
53 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
54 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
56 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
57 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
59 enum fs_type
61 FS_ERROR, /* error accessing the device */
62 FS_UNKNOWN, /* unknown file system */
63 FS_FAT1216,
64 FS_FAT32,
65 FS_ISO9660
69 /* return default device to use for serial ports */
70 /* result should not be more than 16 characters long */
71 static BOOL get_default_com_device( char *buffer, int num )
73 if (!num || num > 9) return FALSE;
74 #ifdef linux
75 sprintf( buffer, "/dev/ttyS%d", num - 1 );
76 return TRUE;
77 #else
78 FIXME( "no known default for device com%d\n", num );
79 return FALSE;
80 #endif
83 /* return default device to use for parallel ports */
84 /* result should not be more than 16 characters long */
85 static BOOL get_default_lpt_device( char *buffer, int num )
87 if (!num || num > 9) return FALSE;
88 #ifdef linux
89 sprintf( buffer, "/dev/lp%d", num - 1 );
90 return TRUE;
91 #else
92 FIXME( "no known default for device lpt%d\n", num );
93 return FALSE;
94 #endif
97 /* read a Unix symlink; returned buffer must be freed by caller */
98 static char *read_symlink( const char *path )
100 char *buffer;
101 int ret, size = 128;
103 for (;;)
105 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size )))
107 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
108 return 0;
110 ret = readlink( path, buffer, size );
111 if (ret == -1)
113 FILE_SetDosError();
114 HeapFree( GetProcessHeap(), 0, buffer );
115 return 0;
117 if (ret != size)
119 buffer[ret] = 0;
120 return buffer;
122 HeapFree( GetProcessHeap(), 0, buffer );
123 size *= 2;
127 /* get the path of a dos device symlink in the $WINEPREFIX/dosdevices directory */
128 static char *get_dos_device_path( LPCWSTR name )
130 const char *config_dir = wine_get_config_dir();
131 char *buffer, *dev;
132 int i;
134 if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
135 strlen(config_dir) + sizeof("/dosdevices/") + 5 )))
137 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
138 return NULL;
140 strcpy( buffer, config_dir );
141 strcat( buffer, "/dosdevices/" );
142 dev = buffer + strlen(buffer);
143 /* no codepage conversion, DOS device names are ASCII anyway */
144 for (i = 0; i < 5; i++)
145 if (!(dev[i] = (char)tolowerW(name[i]))) break;
146 dev[5] = 0;
147 return buffer;
151 /* create symlinks for the DOS drives; helper for VOLUME_CreateDevices */
152 static int create_drives(void)
154 WCHAR name[3], rootW[MAX_PATHNAME_LEN];
155 int i, count = 0;
157 for (i = 0; i < 26; i++)
159 const char *root = DRIVE_GetRoot( i );
160 if (!root) continue;
161 name[0] = 'a' + i;
162 name[1] = ':';
163 name[2] = 0;
164 if (MultiByteToWideChar( CP_UNIXCP, 0, root, -1, rootW, MAX_PATHNAME_LEN ) &&
165 DefineDosDeviceW( DDD_RAW_TARGET_PATH, name, rootW ))
167 MESSAGE( "Created symlink %s/dosdevices/%c: -> %s\n", wine_get_config_dir(), 'a' + i, root );
168 count++;
171 return count;
175 /***********************************************************************
176 * VOLUME_CreateDevices
178 * Create the device files for the new device naming scheme.
179 * Should go away after a transition period.
181 void VOLUME_CreateDevices(void)
183 const char *config_dir = wine_get_config_dir();
184 char *buffer;
185 int i, count = 0;
187 if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
188 strlen(config_dir) + sizeof("/dosdevices") + 3 )))
189 return;
191 strcpy( buffer, config_dir );
192 strcat( buffer, "/dosdevices" );
194 if (!mkdir( buffer, 0777 )) /* we created it, so now create the devices */
196 HKEY hkey;
197 DWORD dummy;
198 OBJECT_ATTRIBUTES attr;
199 UNICODE_STRING nameW;
200 WCHAR *p, *devnameW;
201 char tmp[128];
202 WCHAR com[5] = {'C','O','M','1',0};
203 WCHAR lpt[5] = {'L','P','T','1',0};
205 static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
206 'S','o','f','t','w','a','r','e','\\',
207 'W','i','n','e','\\','W','i','n','e','\\',
208 'C','o','n','f','i','g','\\',
209 'S','e','r','i','a','l','P','o','r','t','s',0};
210 static const WCHAR parallelportsW[] = {'M','a','c','h','i','n','e','\\',
211 'S','o','f','t','w','a','r','e','\\',
212 'W','i','n','e','\\','W','i','n','e','\\',
213 'C','o','n','f','i','g','\\',
214 'P','a','r','a','l','l','e','l','P','o','r','t','s',0};
216 attr.Length = sizeof(attr);
217 attr.RootDirectory = 0;
218 attr.ObjectName = &nameW;
219 attr.Attributes = 0;
220 attr.SecurityDescriptor = NULL;
221 attr.SecurityQualityOfService = NULL;
222 RtlInitUnicodeString( &nameW, serialportsW );
224 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
226 RtlInitUnicodeString( &nameW, com );
227 for (i = 1; i <= 9; i++)
229 com[3] = '0' + i;
230 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
231 tmp, sizeof(tmp), &dummy ))
233 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
234 if ((p = strchrW( devnameW, ',' ))) *p = 0;
235 if (DefineDosDeviceW( DDD_RAW_TARGET_PATH, com, devnameW ))
237 char devname[32];
238 WideCharToMultiByte(CP_UNIXCP, 0, devnameW, -1,
239 devname, sizeof(devname), NULL, NULL);
240 MESSAGE( "Created symlink %s/dosdevices/com%d -> %s\n", config_dir, i, devname );
241 count++;
245 NtClose( hkey );
248 RtlInitUnicodeString( &nameW, parallelportsW );
249 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
251 RtlInitUnicodeString( &nameW, lpt );
252 for (i = 1; i <= 9; i++)
254 lpt[3] = '0' + i;
255 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
256 tmp, sizeof(tmp), &dummy ))
258 devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
259 if ((p = strchrW( devnameW, ',' ))) *p = 0;
260 if (DefineDosDeviceW( DDD_RAW_TARGET_PATH, lpt, devnameW ))
262 char devname[32];
263 WideCharToMultiByte(CP_UNIXCP, 0, devnameW, -1,
264 devname, sizeof(devname), NULL, NULL);
265 MESSAGE( "Created symlink %s/dosdevices/lpt%d -> %s\n", config_dir, i, devname );
266 count++;
270 NtClose( hkey );
272 count += create_drives();
274 else
276 struct stat st;
277 int i;
279 /* it is possible that the serial/parallel devices have been created but */
280 /* not the drives; check for at least one drive symlink to catch that case */
281 strcat( buffer, "/a:" );
282 for (i = 0; i < 26; i++)
284 buffer[strlen(buffer)-2] = 'a' + i;
285 if (!lstat( buffer, &st )) break;
287 if (i == 26) count += create_drives();
290 if (count)
291 MESSAGE( "\nYou can now remove the [SerialPorts] and [ParallelPorts] sections\n"
292 "in your configuration file, as well as the \"Path=\" definitions in\n"
293 "the drive sections, they are replaced by the above symlinks.\n\n" );
295 HeapFree( GetProcessHeap(), 0, buffer );
299 /******************************************************************
300 * VOLUME_OpenDevice
302 HANDLE VOLUME_OpenDevice( LPCWSTR name, DWORD access, DWORD sharing,
303 LPSECURITY_ATTRIBUTES sa, DWORD attributes )
305 char *buffer, *dev;
306 HANDLE ret;
308 if (!(buffer = get_dos_device_path( name ))) return 0;
309 dev = strrchr( buffer, '/' ) + 1;
311 for (;;)
313 TRACE("trying %s\n", buffer );
315 ret = FILE_CreateFile( buffer, access, sharing, sa, OPEN_EXISTING,
316 attributes, 0, DRIVE_FIXED );
317 if (ret || GetLastError() != ERROR_FILE_NOT_FOUND) break;
318 if (!dev) break;
320 /* now try some defaults for it */
321 if (!strcmp( dev, "aux" ))
323 strcpy( dev, "com1" );
324 continue;
326 if (!strcmp( dev, "prn" ))
328 strcpy( dev, "lpt1" );
329 continue;
331 if (!strcmp( dev, "nul" ))
333 strcpy( buffer, "/dev/null" );
334 dev = NULL; /* last try */
335 continue;
337 if (!strncmp( dev, "com", 3 ) && get_default_com_device( buffer, dev[3] - '0' ))
339 dev = NULL; /* last try */
340 continue;
342 if (!strncmp( dev, "lpt", 3 ) && get_default_lpt_device( buffer, dev[3] - '0' ))
344 dev = NULL; /* last try */
345 continue;
347 break;
350 if (!ret) WARN( "could not open device %s err %ld\n", debugstr_w(name), GetLastError() );
351 HeapFree( GetProcessHeap(), 0, buffer );
352 return ret;
356 /******************************************************************
357 * VOLUME_FindCdRomDataBestVoldesc
359 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
361 BYTE cur_vd_type, max_vd_type = 0;
362 BYTE buffer[16];
363 DWORD size, offs, best_offs = 0, extra_offs = 0;
365 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
367 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
368 * the volume label is displaced forward by 8
370 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
371 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
372 if (size != sizeof(buffer)) break;
373 /* check for non-ISO9660 signature */
374 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
375 cur_vd_type = buffer[extra_offs];
376 if (cur_vd_type == 0xff) /* voldesc set terminator */
377 break;
378 if (cur_vd_type > max_vd_type)
380 max_vd_type = cur_vd_type;
381 best_offs = offs + extra_offs;
384 return best_offs;
388 /***********************************************************************
389 * VOLUME_ReadFATSuperblock
391 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
393 DWORD size;
395 /* try a fixed disk, with a FAT partition */
396 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
397 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
398 size != SUPERBLOCK_SIZE)
399 return FS_ERROR;
401 if (buff[0] == 0xE9 || (buff[0] == 0xEB && buff[2] == 0x90))
403 /* guess which type of FAT we have */
404 unsigned int sz, nsect, nclust;
405 sz = GETWORD(buff, 0x16);
406 if (!sz) sz = GETLONG(buff, 0x24);
407 nsect = GETWORD(buff, 0x13);
408 if (!nsect) nsect = GETLONG(buff, 0x20);
409 nsect -= GETWORD(buff, 0x0e) + buff[0x10] * sz +
410 (GETWORD(buff, 0x11) * 32 + (GETWORD(buff, 0x0b) - 1)) / GETWORD(buff, 0x0b);
411 nclust = nsect / buff[0x0d];
413 if (nclust < 65525)
415 if (buff[0x26] == 0x29 && !memcmp(buff+0x36, "FAT", 3))
417 /* FIXME: do really all FAT have their name beginning with
418 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
420 return FS_FAT1216;
423 else if (!memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
425 return FS_UNKNOWN;
429 /***********************************************************************
430 * VOLUME_ReadCDSuperblock
432 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
434 DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
436 if (!offs) return FS_UNKNOWN;
438 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
439 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
440 size != SUPERBLOCK_SIZE)
441 return FS_ERROR;
443 /* check for iso9660 present */
444 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
445 return FS_UNKNOWN;
449 /**************************************************************************
450 * VOLUME_GetSuperblockLabel
452 static void VOLUME_GetSuperblockLabel( enum fs_type type, const BYTE *superblock,
453 WCHAR *label, DWORD len )
455 const BYTE *label_ptr = NULL;
456 DWORD label_len;
458 switch(type)
460 case FS_ERROR:
461 case FS_UNKNOWN:
462 label_len = 0;
463 break;
464 case FS_FAT1216:
465 label_ptr = superblock + 0x2b;
466 label_len = 11;
467 break;
468 case FS_FAT32:
469 label_ptr = superblock + 0x47;
470 label_len = 11;
471 break;
472 case FS_ISO9660:
474 BYTE ver = superblock[0x5a];
476 if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */
477 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
478 { /* yippee, unicode */
479 int i;
481 if (len > 17) len = 17;
482 for (i = 0; i < len-1; i++)
483 label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
484 label[i] = 0;
485 while (i && label[i-1] == ' ') label[--i] = 0;
486 return;
488 label_ptr = superblock + 40;
489 label_len = 32;
490 break;
493 if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
494 &label_len, label_ptr, label_len );
495 label_len /= sizeof(WCHAR);
496 label[label_len] = 0;
497 while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
501 /**************************************************************************
502 * VOLUME_SetSuperblockLabel
504 static BOOL VOLUME_SetSuperblockLabel( enum fs_type type, HANDLE handle, const WCHAR *label )
506 BYTE label_data[11];
507 DWORD offset, len;
509 switch(type)
511 case FS_FAT1216:
512 offset = 0x2b;
513 break;
514 case FS_FAT32:
515 offset = 0x47;
516 break;
517 default:
518 SetLastError( ERROR_ACCESS_DENIED );
519 return FALSE;
521 RtlUnicodeToMultiByteN( label_data, sizeof(label_data), &len,
522 label, strlenW(label) * sizeof(WCHAR) );
523 if (len < sizeof(label_data))
524 memset( label_data + len, ' ', sizeof(label_data) - len );
526 return (SetFilePointer( handle, offset, NULL, FILE_BEGIN ) == offset &&
527 WriteFile( handle, label_data, sizeof(label_data), &len, NULL ));
531 /**************************************************************************
532 * VOLUME_GetSuperblockSerial
534 static DWORD VOLUME_GetSuperblockSerial( enum fs_type type, const BYTE *superblock )
536 switch(type)
538 case FS_ERROR:
539 case FS_UNKNOWN:
540 break;
541 case FS_FAT1216:
542 return GETLONG( superblock, 0x27 );
543 case FS_FAT32:
544 return GETLONG( superblock, 0x33 );
545 case FS_ISO9660:
547 BYTE sum[4];
548 int i;
550 sum[0] = sum[1] = sum[2] = sum[3] = 0;
551 for (i = 0; i < 2048; i += 4)
553 /* DON'T optimize this into DWORD !! (breaks overflow) */
554 sum[0] += superblock[i+0];
555 sum[1] += superblock[i+1];
556 sum[2] += superblock[i+2];
557 sum[3] += superblock[i+3];
560 * OK, another braindead one... argh. Just believe it.
561 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
562 * It's true and nobody will ever be able to change it.
564 if (GetVersion() & 0x80000000)
565 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
566 else
567 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
570 return 0;
574 /**************************************************************************
575 * VOLUME_GetAudioCDSerial
577 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
579 DWORD serial = 0;
580 int i;
582 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
583 serial += ((toc->TrackData[i].Address[1] << 16) |
584 (toc->TrackData[i].Address[2] << 8) |
585 toc->TrackData[i].Address[3]);
588 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
589 * frames.
590 * There it is collected for correcting the serial when there are less than
591 * 3 tracks.
593 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
595 DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
596 DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
597 serial += dwEnd - dwStart;
599 return serial;
603 /***********************************************************************
604 * GetVolumeInformationW (KERNEL32.@)
606 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
607 DWORD *serial, DWORD *filename_len, DWORD *flags,
608 LPWSTR fsname, DWORD fsname_len )
610 static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
611 static const WCHAR fatW[] = {'F','A','T',0};
612 static const WCHAR cdfsW[] = {'C','D','F','S',0};
614 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
615 HANDLE handle;
616 enum fs_type type = FS_UNKNOWN;
618 if (!root)
620 WCHAR path[MAX_PATH];
621 GetCurrentDirectoryW( MAX_PATH, path );
622 device[4] = path[0];
624 else
626 if (!root[0] || root[1] != ':')
628 SetLastError( ERROR_INVALID_NAME );
629 return FALSE;
631 device[4] = root[0];
634 /* try to open the device */
636 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
637 NULL, OPEN_EXISTING, 0, 0 );
638 if (handle != INVALID_HANDLE_VALUE)
640 BYTE superblock[SUPERBLOCK_SIZE];
641 CDROM_TOC toc;
642 DWORD br;
644 /* check for audio CD */
645 /* FIXME: we only check the first track for now */
646 if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
648 if (!(toc.TrackData[0].Control & 0x04)) /* audio track */
650 TRACE( "%s: found audio CD\n", debugstr_w(device) );
651 if (label) lstrcpynW( label, audiocdW, label_len );
652 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
653 CloseHandle( handle );
654 type = FS_ISO9660;
655 goto fill_fs_info;
657 type = VOLUME_ReadCDSuperblock( handle, superblock );
659 else
661 type = VOLUME_ReadFATSuperblock( handle, superblock );
662 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
664 CloseHandle( handle );
665 TRACE( "%s: found fs type %d\n", debugstr_w(device), type );
666 if (type == FS_ERROR) return FALSE;
668 if (label && label_len) VOLUME_GetSuperblockLabel( type, superblock, label, label_len );
669 if (serial) *serial = VOLUME_GetSuperblockSerial( type, superblock );
670 goto fill_fs_info;
672 else
674 TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
675 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
678 /* we couldn't open the device, fallback to default strategy */
680 switch(GetDriveTypeW( root ))
682 case DRIVE_UNKNOWN:
683 case DRIVE_NO_ROOT_DIR:
684 SetLastError( ERROR_NOT_READY );
685 return FALSE;
686 case DRIVE_REMOVABLE:
687 case DRIVE_FIXED:
688 case DRIVE_REMOTE:
689 case DRIVE_RAMDISK:
690 type = FS_UNKNOWN;
691 break;
692 case DRIVE_CDROM:
693 type = FS_ISO9660;
694 break;
697 if (label && label_len)
699 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
701 labelW[0] = device[4];
702 handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
703 OPEN_EXISTING, 0, 0 );
704 if (handle != INVALID_HANDLE_VALUE)
706 char buffer[256], *p;
707 DWORD size;
709 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
710 CloseHandle( handle );
711 p = buffer + size;
712 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
713 *p = 0;
714 if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, label_len ))
715 label[label_len-1] = 0;
717 else label[0] = 0;
719 if (serial)
721 WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
723 serialW[0] = device[4];
724 handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
725 OPEN_EXISTING, 0, 0 );
726 if (handle != INVALID_HANDLE_VALUE)
728 char buffer[32];
729 DWORD size;
731 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
732 CloseHandle( handle );
733 buffer[size] = 0;
734 *serial = strtoul( buffer, NULL, 16 );
736 else *serial = 0;
739 fill_fs_info: /* now fill in the information that depends on the file system type */
741 switch(type)
743 case FS_ISO9660:
744 if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
745 if (filename_len) *filename_len = 221;
746 if (flags) *flags = FILE_READ_ONLY_VOLUME;
747 break;
748 case FS_FAT1216:
749 case FS_FAT32:
750 default: /* default to FAT file system (FIXME) */
751 if (fsname) lstrcpynW( fsname, fatW, fsname_len );
752 if (filename_len) *filename_len = 255;
753 if (flags) *flags = FILE_CASE_PRESERVED_NAMES; /* FIXME */
754 break;
756 return TRUE;
760 /***********************************************************************
761 * GetVolumeInformationA (KERNEL32.@)
763 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
764 DWORD label_len, DWORD *serial,
765 DWORD *filename_len, DWORD *flags,
766 LPSTR fsname, DWORD fsname_len )
768 UNICODE_STRING rootW;
769 LPWSTR labelW, fsnameW;
770 BOOL ret;
772 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
773 else rootW.Buffer = NULL;
774 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
775 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
777 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
778 filename_len, flags, fsnameW, fsname_len)))
780 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
781 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
784 RtlFreeUnicodeString(&rootW);
785 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
786 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
787 return ret;
792 /***********************************************************************
793 * SetVolumeLabelW (KERNEL32.@)
795 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
797 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
798 HANDLE handle;
799 enum fs_type type = FS_UNKNOWN;
801 if (!root)
803 WCHAR path[MAX_PATH];
804 GetCurrentDirectoryW( MAX_PATH, path );
805 device[4] = path[0];
807 else
809 if (!root[0] || root[1] != ':')
811 SetLastError( ERROR_INVALID_NAME );
812 return FALSE;
814 device[4] = root[0];
817 /* try to open the device */
819 handle = CreateFileW( device, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
820 NULL, OPEN_EXISTING, 0, 0 );
821 if (handle == INVALID_HANDLE_VALUE)
823 /* try read-only */
824 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
825 NULL, OPEN_EXISTING, 0, 0 );
826 if (handle != INVALID_HANDLE_VALUE)
828 /* device can be read but not written, return error */
829 CloseHandle( handle );
830 SetLastError( ERROR_ACCESS_DENIED );
831 return FALSE;
835 if (handle != INVALID_HANDLE_VALUE)
837 BYTE superblock[SUPERBLOCK_SIZE];
838 BOOL ret;
840 type = VOLUME_ReadFATSuperblock( handle, superblock );
841 ret = VOLUME_SetSuperblockLabel( type, handle, label );
842 CloseHandle( handle );
843 return ret;
845 else
847 TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
848 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
851 /* we couldn't open the device, fallback to default strategy */
853 switch(GetDriveTypeW( root ))
855 case DRIVE_UNKNOWN:
856 case DRIVE_NO_ROOT_DIR:
857 SetLastError( ERROR_NOT_READY );
858 break;
859 case DRIVE_REMOVABLE:
860 case DRIVE_FIXED:
862 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
864 labelW[0] = device[4];
865 handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
866 CREATE_ALWAYS, 0, 0 );
867 if (handle != INVALID_HANDLE_VALUE)
869 char buffer[64];
870 DWORD size;
872 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer), NULL, NULL ))
873 buffer[sizeof(buffer)-1] = 0;
874 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
875 CloseHandle( handle );
876 return TRUE;
878 break;
880 case DRIVE_REMOTE:
881 case DRIVE_RAMDISK:
882 case DRIVE_CDROM:
883 SetLastError( ERROR_ACCESS_DENIED );
884 break;
886 return FALSE;
889 /***********************************************************************
890 * SetVolumeLabelA (KERNEL32.@)
892 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
894 UNICODE_STRING rootW, volnameW;
895 BOOL ret;
897 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
898 else rootW.Buffer = NULL;
899 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
900 else volnameW.Buffer = NULL;
902 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
904 RtlFreeUnicodeString(&rootW);
905 RtlFreeUnicodeString(&volnameW);
906 return ret;
910 /***********************************************************************
911 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
913 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
915 FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
916 return 0;
920 /***********************************************************************
921 * DefineDosDeviceW (KERNEL32.@)
923 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
925 DWORD len, dosdev;
926 BOOL ret = FALSE;
927 char *path = NULL, *target, *p;
929 if (!(flags & DDD_RAW_TARGET_PATH))
931 FIXME( "(0x%08lx,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
932 flags, debugstr_w(devname), debugstr_w(targetpath) );
933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
934 return FALSE;
937 len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
938 if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
940 WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
941 for (p = target; *p; p++) if (*p == '\\') *p = '/';
943 else
945 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
946 return FALSE;
949 /* first check for a DOS device */
951 if ((dosdev = RtlIsDosDeviceName_U( devname )))
953 WCHAR name[5];
955 memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
956 name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
957 path = get_dos_device_path( name );
959 else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2]) /* drive mapping */
961 path = get_dos_device_path( devname );
963 else SetLastError( ERROR_FILE_NOT_FOUND );
965 if (path)
967 TRACE( "creating symlink %s -> %s\n", path, target );
968 unlink( path );
969 if (!symlink( target, path )) ret = TRUE;
970 else FILE_SetDosError();
971 HeapFree( GetProcessHeap(), 0, path );
973 HeapFree( GetProcessHeap(), 0, target );
974 return ret;
978 /***********************************************************************
979 * DefineDosDeviceA (KERNEL32.@)
981 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
983 UNICODE_STRING d, t;
984 BOOL ret;
986 if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
988 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
989 return FALSE;
991 if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
993 RtlFreeUnicodeString(&d);
994 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
995 return FALSE;
997 ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
998 RtlFreeUnicodeString(&d);
999 RtlFreeUnicodeString(&t);
1000 return ret;
1004 /***********************************************************************
1005 * QueryDosDeviceW (KERNEL32.@)
1007 * returns array of strings terminated by \0, terminated by \0
1009 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
1011 static const WCHAR auxW[] = {'A','U','X',0};
1012 static const WCHAR nulW[] = {'N','U','L',0};
1013 static const WCHAR prnW[] = {'P','R','N',0};
1014 static const WCHAR comW[] = {'C','O','M',0};
1015 static const WCHAR lptW[] = {'L','P','T',0};
1016 static const WCHAR com0W[] = {'C','O','M','0',0};
1017 static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
1018 static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
1020 char buffer[16];
1021 struct stat st;
1023 if (!bufsize)
1025 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1026 return 0;
1029 if (devname)
1031 WCHAR *p, name[5];
1032 char *path, *link;
1033 DWORD dosdev, ret = 0;
1035 if ((dosdev = RtlIsDosDeviceName_U( devname )))
1037 memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
1038 name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
1040 else if (devname[0] && devname[1] == ':' && !devname[2])
1042 memcpy( name, devname, 3 * sizeof(WCHAR) );
1044 else
1046 SetLastError( ERROR_BAD_PATHNAME );
1047 return 0;
1050 if (!(path = get_dos_device_path( name ))) return 0;
1051 link = read_symlink( path );
1052 HeapFree( GetProcessHeap(), 0, path );
1054 if (link)
1056 ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
1057 HeapFree( GetProcessHeap(), 0, link );
1059 else if (dosdev) /* look for device defaults */
1061 if (!strcmpiW( name, auxW ))
1063 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
1065 memcpy( target, com1W, sizeof(com1W) );
1066 ret = sizeof(com1W)/sizeof(WCHAR);
1068 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1069 return ret;
1071 if (!strcmpiW( name, prnW ))
1073 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
1075 memcpy( target, lpt1W, sizeof(lpt1W) );
1076 ret = sizeof(lpt1W)/sizeof(WCHAR);
1078 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1079 return ret;
1082 buffer[0] = 0;
1084 if (!strcmpiW( name, nulW ))
1085 strcpy( buffer, "/dev/null" );
1086 else if (!strncmpiW( name, comW, 3 ))
1087 get_default_com_device( buffer, name[3] - '0' );
1088 else if (!strncmpiW( name, lptW, 3 ))
1089 get_default_lpt_device( buffer, name[3] - '0' );
1091 if (buffer[0] && !stat( buffer, &st ))
1092 ret = MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, target, bufsize );
1093 else
1094 SetLastError( ERROR_FILE_NOT_FOUND );
1097 if (ret)
1099 if (ret < bufsize) target[ret++] = 0; /* add an extra null */
1100 for (p = target; *p; p++) if (*p == '/') *p = '\\';
1103 return ret;
1105 else /* return a list of all devices */
1107 WCHAR *p = target;
1108 char *path, *dev, buffer[16];
1109 int i;
1111 if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
1113 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1114 return 0;
1117 memcpy( p, auxW, sizeof(auxW) );
1118 p += sizeof(auxW) / sizeof(WCHAR);
1119 memcpy( p, nulW, sizeof(nulW) );
1120 p += sizeof(nulW) / sizeof(WCHAR);
1121 memcpy( p, prnW, sizeof(prnW) );
1122 p += sizeof(prnW) / sizeof(WCHAR);
1124 if (!(path = get_dos_device_path( com0W ))) return 0;
1125 dev = strrchr( path, '/' ) + 1;
1127 for (i = 1; i <= 9; i++)
1129 sprintf( dev, "com%d", i );
1130 if (!stat( path, &st ) ||
1131 (get_default_com_device( buffer, i ) && !stat( buffer, &st )))
1133 if (p + 5 >= target + bufsize)
1135 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1136 return 0;
1138 strcpyW( p, comW );
1139 p[3] = '0' + i;
1140 p[4] = 0;
1141 p += 5;
1144 for (i = 1; i <= 9; i++)
1146 sprintf( dev, "lpt%d", i );
1147 if (!stat( path, &st ) ||
1148 (get_default_lpt_device( buffer, i ) && !stat( buffer, &st )))
1150 if (p + 5 >= target + bufsize)
1152 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1153 return 0;
1155 strcpyW( p, lptW );
1156 p[3] = '0' + i;
1157 p[4] = 0;
1158 p += 5;
1161 for (i = 0; i < 26; i++)
1163 sprintf( dev, "%c:", 'a' + i );
1164 if (!stat( path, &st ))
1166 if (p + 3 >= target + bufsize)
1168 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1169 return 0;
1171 *p++ = 'A' + i;
1172 *p++ = ':';
1173 *p++ = 0;
1176 *p++ = 0; /* terminating null */
1177 return p - target;
1182 /***********************************************************************
1183 * QueryDosDeviceA (KERNEL32.@)
1185 * returns array of strings terminated by \0, terminated by \0
1187 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1189 DWORD ret = 0, retW;
1190 UNICODE_STRING devnameW;
1191 LPWSTR targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1193 if (!targetW)
1195 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1196 return 0;
1199 if (devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1200 else devnameW.Buffer = NULL;
1202 retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1204 ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target, bufsize, NULL, NULL);
1206 RtlFreeUnicodeString(&devnameW);
1207 HeapFree(GetProcessHeap(), 0, targetW);
1208 return ret;