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
26 #include "wine/port.h"
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(volume);
45 #define SUPERBLOCK_SIZE 2048
47 #define CDFRAMES_PERSEC 75
48 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
49 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
50 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
52 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
53 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
57 FS_ERROR, /* error accessing the device */
58 FS_UNKNOWN, /* unknown file system */
65 /******************************************************************
66 * VOLUME_FindCdRomDataBestVoldesc
68 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
70 BYTE cur_vd_type, max_vd_type = 0;
72 DWORD size, offs, best_offs = 0, extra_offs = 0;
74 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
76 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
77 * the volume label is displaced forward by 8
79 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
80 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
81 if (size != sizeof(buffer)) break;
82 /* check for non-ISO9660 signature */
83 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
84 cur_vd_type = buffer[extra_offs];
85 if (cur_vd_type == 0xff) /* voldesc set terminator */
87 if (cur_vd_type > max_vd_type)
89 max_vd_type = cur_vd_type;
90 best_offs = offs + extra_offs;
97 /***********************************************************************
98 * VOLUME_ReadFATSuperblock
100 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
104 /* try a fixed disk, with a FAT partition */
105 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
106 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
107 size != SUPERBLOCK_SIZE)
110 if (buff[0] == 0xE9 || (buff[0] == 0xEB && buff[2] == 0x90))
112 /* guess which type of FAT we have */
113 unsigned int sz, nsect, nclust;
114 sz = GETWORD(buff, 0x16);
115 if (!sz) sz = GETLONG(buff, 0x24);
116 nsect = GETWORD(buff, 0x13);
117 if (!nsect) nsect = GETLONG(buff, 0x20);
118 nsect -= GETWORD(buff, 0x0e) + buff[0x10] * sz +
119 (GETWORD(buff, 0x11) * 32 + (GETWORD(buff, 0x0b) - 1)) / GETWORD(buff, 0x0b);
120 nclust = nsect / buff[0x0d];
124 if (buff[0x26] == 0x29 && !memcmp(buff+0x36, "FAT", 3))
126 /* FIXME: do really all FAT have their name beginning with
127 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
132 else if (!memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
138 /***********************************************************************
139 * VOLUME_ReadCDSuperblock
141 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
143 DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
145 if (!offs) return FS_UNKNOWN;
147 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
148 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
149 size != SUPERBLOCK_SIZE)
152 /* check for iso9660 present */
153 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
158 /**************************************************************************
159 * VOLUME_GetSuperblockLabel
161 static void VOLUME_GetSuperblockLabel( enum fs_type type, const BYTE *superblock,
162 WCHAR *label, DWORD len )
164 const BYTE *label_ptr = NULL;
174 label_ptr = superblock + 0x2b;
178 label_ptr = superblock + 0x47;
183 BYTE ver = superblock[0x5a];
185 if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */
186 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
187 { /* yippee, unicode */
190 if (len > 17) len = 17;
191 for (i = 0; i < len-1; i++)
192 label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
194 while (i && label[i-1] == ' ') label[--i] = 0;
197 label_ptr = superblock + 40;
202 if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
203 &label_len, label_ptr, label_len );
204 label_len /= sizeof(WCHAR);
205 label[label_len] = 0;
206 while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
210 /**************************************************************************
211 * VOLUME_SetSuperblockLabel
213 static BOOL VOLUME_SetSuperblockLabel( enum fs_type type, HANDLE handle, const WCHAR *label )
227 SetLastError( ERROR_ACCESS_DENIED );
230 RtlUnicodeToMultiByteN( label_data, sizeof(label_data), &len,
231 label, strlenW(label) * sizeof(WCHAR) );
232 if (len < sizeof(label_data))
233 memset( label_data + len, ' ', sizeof(label_data) - len );
235 return (SetFilePointer( handle, offset, NULL, FILE_BEGIN ) == offset &&
236 WriteFile( handle, label_data, sizeof(label_data), &len, NULL ));
240 /**************************************************************************
241 * VOLUME_GetSuperblockSerial
243 static DWORD VOLUME_GetSuperblockSerial( enum fs_type type, const BYTE *superblock )
251 return GETLONG( superblock, 0x27 );
253 return GETLONG( superblock, 0x33 );
259 sum[0] = sum[1] = sum[2] = sum[3] = 0;
260 for (i = 0; i < 2048; i += 4)
262 /* DON'T optimize this into DWORD !! (breaks overflow) */
263 sum[0] += superblock[i+0];
264 sum[1] += superblock[i+1];
265 sum[2] += superblock[i+2];
266 sum[3] += superblock[i+3];
269 * OK, another braindead one... argh. Just believe it.
270 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
271 * It's true and nobody will ever be able to change it.
273 if (GetVersion() & 0x80000000)
274 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
276 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
283 /**************************************************************************
284 * VOLUME_GetAudioCDSerial
286 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
291 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
292 serial += ((toc->TrackData[i].Address[1] << 16) |
293 (toc->TrackData[i].Address[2] << 8) |
294 toc->TrackData[i].Address[3]);
297 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
299 * There it is collected for correcting the serial when there are less than
302 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
304 DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
305 DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
306 serial += dwEnd - dwStart;
312 /***********************************************************************
313 * GetVolumeInformationW (KERNEL32.@)
315 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
316 DWORD *serial, DWORD *filename_len, DWORD *flags,
317 LPWSTR fsname, DWORD fsname_len )
319 static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
320 static const WCHAR fatW[] = {'F','A','T',0};
321 static const WCHAR cdfsW[] = {'C','D','F','S',0};
323 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
325 enum fs_type type = FS_UNKNOWN;
329 WCHAR path[MAX_PATH];
330 GetCurrentDirectoryW( MAX_PATH, path );
335 if (!root[0] || root[1] != ':')
337 SetLastError( ERROR_INVALID_NAME );
343 /* try to open the device */
345 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
346 NULL, OPEN_EXISTING, 0, 0 );
347 if (handle != INVALID_HANDLE_VALUE)
349 BYTE superblock[SUPERBLOCK_SIZE];
353 /* check for audio CD */
354 /* FIXME: we only check the first track for now */
355 if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
357 if (!(toc.TrackData[0].Control & 0x04)) /* audio track */
359 TRACE( "%s: found audio CD\n", debugstr_w(device) );
360 if (label) lstrcpynW( label, audiocdW, label_len );
361 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
362 CloseHandle( handle );
366 type = VOLUME_ReadCDSuperblock( handle, superblock );
370 type = VOLUME_ReadFATSuperblock( handle, superblock );
371 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
373 CloseHandle( handle );
374 TRACE( "%s: found fs type %d\n", debugstr_w(device), type );
375 if (type == FS_ERROR) return FALSE;
377 if (label && label_len) VOLUME_GetSuperblockLabel( type, superblock, label, label_len );
378 if (serial) *serial = VOLUME_GetSuperblockSerial( type, superblock );
383 TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
384 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
387 /* we couldn't open the device, fallback to default strategy */
389 switch(GetDriveTypeW( root ))
392 case DRIVE_NO_ROOT_DIR:
393 SetLastError( ERROR_NOT_READY );
395 case DRIVE_REMOVABLE:
406 if (label && label_len)
408 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
410 labelW[0] = device[4];
411 handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
412 OPEN_EXISTING, 0, 0 );
413 if (handle != INVALID_HANDLE_VALUE)
415 char buffer[256], *p;
418 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
419 CloseHandle( handle );
421 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
423 if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, label_len ))
424 label[label_len-1] = 0;
430 WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
432 serialW[0] = device[4];
433 handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
434 OPEN_EXISTING, 0, 0 );
435 if (handle != INVALID_HANDLE_VALUE)
440 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
441 CloseHandle( handle );
443 *serial = strtoul( buffer, NULL, 16 );
448 fill_fs_info: /* now fill in the information that depends on the file system type */
453 if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
454 if (filename_len) *filename_len = 221;
455 if (flags) *flags = FILE_READ_ONLY_VOLUME;
459 default: /* default to FAT file system (FIXME) */
460 if (fsname) lstrcpynW( fsname, fatW, fsname_len );
461 if (filename_len) *filename_len = 255;
462 if (flags) *flags = FILE_CASE_PRESERVED_NAMES; /* FIXME */
469 /***********************************************************************
470 * GetVolumeInformationA (KERNEL32.@)
472 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
473 DWORD label_len, DWORD *serial,
474 DWORD *filename_len, DWORD *flags,
475 LPSTR fsname, DWORD fsname_len )
477 UNICODE_STRING rootW;
478 LPWSTR labelW, fsnameW;
481 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
482 else rootW.Buffer = NULL;
483 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
484 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
486 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
487 filename_len, flags, fsnameW, fsname_len)))
489 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
490 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
493 RtlFreeUnicodeString(&rootW);
494 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
495 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
501 /***********************************************************************
502 * SetVolumeLabelW (KERNEL32.@)
504 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
506 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
508 enum fs_type type = FS_UNKNOWN;
512 WCHAR path[MAX_PATH];
513 GetCurrentDirectoryW( MAX_PATH, path );
518 if (!root[0] || root[1] != ':')
520 SetLastError( ERROR_INVALID_NAME );
526 /* try to open the device */
528 handle = CreateFileW( device, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
529 NULL, OPEN_EXISTING, 0, 0 );
530 if (handle == INVALID_HANDLE_VALUE)
533 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
534 NULL, OPEN_EXISTING, 0, 0 );
535 if (handle != INVALID_HANDLE_VALUE)
537 /* device can be read but not written, return error */
538 CloseHandle( handle );
539 SetLastError( ERROR_ACCESS_DENIED );
544 if (handle != INVALID_HANDLE_VALUE)
546 BYTE superblock[SUPERBLOCK_SIZE];
549 type = VOLUME_ReadFATSuperblock( handle, superblock );
550 ret = VOLUME_SetSuperblockLabel( type, handle, label );
551 CloseHandle( handle );
556 TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
557 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
560 /* we couldn't open the device, fallback to default strategy */
562 switch(GetDriveTypeW( root ))
565 case DRIVE_NO_ROOT_DIR:
566 SetLastError( ERROR_NOT_READY );
568 case DRIVE_REMOVABLE:
571 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
573 labelW[0] = device[4];
574 handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
575 CREATE_ALWAYS, 0, 0 );
576 if (handle != INVALID_HANDLE_VALUE)
581 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer), NULL, NULL ))
582 buffer[sizeof(buffer)-1] = 0;
583 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
584 CloseHandle( handle );
592 SetLastError( ERROR_ACCESS_DENIED );
598 /***********************************************************************
599 * SetVolumeLabelA (KERNEL32.@)
601 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
603 UNICODE_STRING rootW, volnameW;
606 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
607 else rootW.Buffer = NULL;
608 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
609 else volnameW.Buffer = NULL;
611 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
613 RtlFreeUnicodeString(&rootW);
614 RtlFreeUnicodeString(&volnameW);
619 /***********************************************************************
620 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
622 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
624 FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);