reg: Print the full key path without a trailing backslash.
[wine.git] / dlls / kernel32 / volume.c
blob93f50c080ba7db8f358b1e91400e0c11d20b01a8
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 <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winnls.h"
34 #include "winternl.h"
35 #include "winioctl.h"
36 #include "ntddcdrm.h"
37 #include "ddk/wdm.h"
38 #include "kernel_private.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(volume);
43 #define BLOCK_SIZE 2048
44 #define SUPERBLOCK_SIZE BLOCK_SIZE
46 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
47 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
49 enum fs_type
51 FS_ERROR, /* error accessing the device */
52 FS_UNKNOWN, /* unknown file system */
53 FS_FAT1216,
54 FS_FAT32,
55 FS_ISO9660,
56 FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
59 /******************************************************************
60 * VOLUME_FindCdRomDataBestVoldesc
62 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
64 BYTE cur_vd_type, max_vd_type = 0;
65 BYTE buffer[0x800];
66 DWORD size, offs, best_offs = 0, extra_offs = 0;
68 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
70 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
71 * the volume label is displaced forward by 8
73 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
74 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
75 if (size != sizeof(buffer)) break;
76 /* check for non-ISO9660 signature */
77 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
78 cur_vd_type = buffer[extra_offs];
79 if (cur_vd_type == 0xff) /* voldesc set terminator */
80 break;
81 if (cur_vd_type > max_vd_type)
83 max_vd_type = cur_vd_type;
84 best_offs = offs + extra_offs;
87 return best_offs;
91 /***********************************************************************
92 * VOLUME_ReadFATSuperblock
94 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
96 DWORD size;
98 /* try a fixed disk, with a FAT partition */
99 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
100 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
102 if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN; /* not a real device */
103 return FS_ERROR;
106 if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
108 /* FIXME: do really all FAT have their name beginning with
109 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
111 if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
113 /* guess which type of FAT we have */
114 int reasonable;
115 unsigned int sectors,
116 sect_per_fat,
117 total_sectors,
118 num_boot_sectors,
119 num_fats,
120 num_root_dir_ents,
121 bytes_per_sector,
122 sectors_per_cluster,
123 nclust;
124 sect_per_fat = GETWORD(buff, 0x16);
125 if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
126 total_sectors = GETWORD(buff, 0x13);
127 if (!total_sectors)
128 total_sectors = GETLONG(buff, 0x20);
129 num_boot_sectors = GETWORD(buff, 0x0e);
130 num_fats = buff[0x10];
131 num_root_dir_ents = GETWORD(buff, 0x11);
132 bytes_per_sector = GETWORD(buff, 0x0b);
133 sectors_per_cluster = buff[0x0d];
134 /* check if the parameters are reasonable and will not cause
135 * arithmetic errors in the calculation */
136 reasonable = num_boot_sectors < total_sectors &&
137 num_fats < 16 &&
138 bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
139 sectors_per_cluster >= 1;
140 if (!reasonable) return FS_UNKNOWN;
141 sectors = total_sectors - num_boot_sectors - num_fats * sect_per_fat -
142 (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
143 nclust = sectors / sectors_per_cluster;
144 if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
145 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
146 if (nclust < 65525)
148 if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
149 !memcmp(buff+0x36, "FAT", 3))
150 return FS_FAT1216;
153 return FS_UNKNOWN;
157 /***********************************************************************
158 * VOLUME_ReadCDBlock
160 static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
162 DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
164 if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
165 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
166 size != SUPERBLOCK_SIZE)
167 return FALSE;
169 return TRUE;
173 /***********************************************************************
174 * VOLUME_ReadCDSuperblock
176 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
178 int i;
179 DWORD offs;
181 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
182 * Starting from sector 16, we may find :
183 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
184 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
185 * There is no explicit end so read 16 sectors and then give up */
186 for( i=16; i<16+16; i++)
188 if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
189 continue;
191 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
192 * but we assume the volume is well-formatted */
193 if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
196 offs = VOLUME_FindCdRomDataBestVoldesc( handle );
197 if (!offs) return FS_UNKNOWN;
199 if (!VOLUME_ReadCDBlock(handle, buff, offs))
200 return FS_ERROR;
202 /* check for the iso9660 identifier */
203 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
204 return FS_UNKNOWN;
208 /***********************************************************************
209 * SetVolumeLabelW (KERNEL32.@)
211 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
213 WCHAR device[] = L"\\\\.\\A:";
214 HANDLE handle;
215 enum fs_type type = FS_UNKNOWN;
217 if (!root)
219 WCHAR path[MAX_PATH];
220 GetCurrentDirectoryW( MAX_PATH, path );
221 device[4] = path[0];
223 else
225 if (!root[0] || root[1] != ':')
227 SetLastError( ERROR_INVALID_NAME );
228 return FALSE;
230 device[4] = root[0];
233 /* try to open the device */
235 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
236 NULL, OPEN_EXISTING, 0, 0 );
237 if (handle != INVALID_HANDLE_VALUE)
239 BYTE superblock[SUPERBLOCK_SIZE];
241 type = VOLUME_ReadFATSuperblock( handle, superblock );
242 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
243 CloseHandle( handle );
244 if (type != FS_UNKNOWN)
246 /* we can't set the label on FAT or CDROM file systems */
247 TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
248 SetLastError( ERROR_ACCESS_DENIED );
249 return FALSE;
252 else
254 TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
255 if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
258 /* we couldn't open the device, fallback to default strategy */
260 switch(GetDriveTypeW( root ))
262 case DRIVE_UNKNOWN:
263 case DRIVE_NO_ROOT_DIR:
264 SetLastError( ERROR_NOT_READY );
265 break;
266 case DRIVE_REMOVABLE:
267 case DRIVE_FIXED:
269 WCHAR labelW[] = L"A:\\.windows-label";
271 labelW[0] = device[4];
273 if (!label[0]) /* delete label file when setting an empty label */
274 return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
276 handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
277 CREATE_ALWAYS, 0, 0 );
278 if (handle != INVALID_HANDLE_VALUE)
280 char buffer[64];
281 DWORD size;
283 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
284 buffer[sizeof(buffer)-2] = 0;
285 strcat( buffer, "\n" );
286 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
287 CloseHandle( handle );
288 return TRUE;
290 break;
292 case DRIVE_REMOTE:
293 case DRIVE_RAMDISK:
294 case DRIVE_CDROM:
295 SetLastError( ERROR_ACCESS_DENIED );
296 break;
298 return FALSE;
301 /***********************************************************************
302 * SetVolumeLabelA (KERNEL32.@)
304 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
306 WCHAR *rootW = NULL, *volnameW = NULL;
307 BOOL ret;
309 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
310 if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
311 ret = SetVolumeLabelW( rootW, volnameW );
312 HeapFree( GetProcessHeap(), 0, volnameW );
313 return ret;
317 /***********************************************************************
318 * GetVolumeNameForVolumeMountPointA (KERNEL32.@)
320 BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
322 BOOL ret;
323 WCHAR volumeW[50], *pathW = NULL;
324 DWORD len = min(ARRAY_SIZE(volumeW), size );
326 TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
328 if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
329 return FALSE;
331 if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
332 FILE_name_WtoA( volumeW, -1, volume, len );
334 HeapFree( GetProcessHeap(), 0, pathW );
335 return ret;
339 /***********************************************************************
340 * DefineDosDeviceA (KERNEL32.@)
342 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
344 WCHAR *devW, *targetW = NULL;
345 BOOL ret;
347 if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
348 if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
349 ret = DefineDosDeviceW(flags, devW, targetW);
350 HeapFree( GetProcessHeap(), 0, targetW );
351 return ret;
355 /***********************************************************************
356 * QueryDosDeviceA (KERNEL32.@)
358 * returns array of strings terminated by \0, terminated by \0
360 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
362 DWORD ret = 0, retW;
363 WCHAR *devnameW = NULL;
364 LPWSTR targetW;
366 if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
368 targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
369 if (!targetW)
371 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
372 return 0;
375 retW = QueryDosDeviceW(devnameW, targetW, bufsize);
377 ret = FILE_name_WtoA( targetW, retW, target, bufsize );
379 HeapFree(GetProcessHeap(), 0, targetW);
380 return ret;
384 /***********************************************************************
385 * GetLogicalDriveStringsA (KERNEL32.@)
387 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
389 DWORD drives = GetLogicalDrives();
390 UINT drive, count;
392 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
393 if ((count * 4) + 1 > len) return count * 4 + 1;
395 for (drive = 0; drive < 26; drive++)
397 if (drives & (1 << drive))
399 *buffer++ = 'A' + drive;
400 *buffer++ = ':';
401 *buffer++ = '\\';
402 *buffer++ = 0;
405 *buffer = 0;
406 return count * 4;
410 /***********************************************************************
411 * GetVolumePathNameA (KERNEL32.@)
413 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
415 BOOL ret;
416 WCHAR *filenameW = NULL, *volumeW = NULL;
418 TRACE("(%s, %p, %d)\n", debugstr_a(filename), volumepathname, buflen);
420 if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
421 return FALSE;
422 if (volumepathname && !(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
423 return FALSE;
425 if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
426 FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
428 HeapFree( GetProcessHeap(), 0, volumeW );
429 return ret;
433 /***********************************************************************
434 * GetVolumePathNamesForVolumeNameA (KERNEL32.@)
436 BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen)
438 BOOL ret;
439 WCHAR *volumenameW = NULL, *volumepathnameW;
441 if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE;
442 if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
444 HeapFree( GetProcessHeap(), 0, volumenameW );
445 return FALSE;
447 if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen )))
449 char *path = volumepathname;
450 const WCHAR *pathW = volumepathnameW;
452 while (*pathW)
454 int len = lstrlenW( pathW ) + 1;
455 FILE_name_WtoA( pathW, len, path, buflen );
456 buflen -= len;
457 pathW += len;
458 path += len;
460 path[0] = 0;
462 HeapFree( GetProcessHeap(), 0, volumenameW );
463 HeapFree( GetProcessHeap(), 0, volumepathnameW );
464 return ret;
468 /***********************************************************************
469 * FindFirstVolumeA (KERNEL32.@)
471 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
473 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
474 HANDLE handle = FindFirstVolumeW( buffer, len );
476 if (handle != INVALID_HANDLE_VALUE)
478 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
480 FindVolumeClose( handle );
481 handle = INVALID_HANDLE_VALUE;
484 HeapFree( GetProcessHeap(), 0, buffer );
485 return handle;
488 /***********************************************************************
489 * FindNextVolumeA (KERNEL32.@)
491 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
493 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
494 BOOL ret;
496 if ((ret = FindNextVolumeW( handle, buffer, len )))
498 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
500 HeapFree( GetProcessHeap(), 0, buffer );
501 return ret;
504 /***********************************************************************
505 * FindFirstVolumeMountPointA (KERNEL32.@)
507 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
509 FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
510 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
511 return INVALID_HANDLE_VALUE;
514 /***********************************************************************
515 * FindFirstVolumeMountPointW (KERNEL32.@)
517 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
519 FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
520 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
521 return INVALID_HANDLE_VALUE;
524 /***********************************************************************
525 * FindVolumeMountPointClose (KERNEL32.@)
527 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
529 FIXME("(%p), stub!\n", h);
530 return FALSE;
533 /***********************************************************************
534 * DeleteVolumeMountPointA (KERNEL32.@)
536 BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint)
538 FIXME("(%s), stub!\n", debugstr_a(mountpoint));
539 return FALSE;
542 /***********************************************************************
543 * SetVolumeMountPointA (KERNEL32.@)
545 BOOL WINAPI SetVolumeMountPointA(LPCSTR path, LPCSTR volume)
547 FIXME("(%s, %s), stub!\n", debugstr_a(path), debugstr_a(volume));
548 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
549 return FALSE;
552 /***********************************************************************
553 * SetVolumeMountPointW (KERNEL32.@)
555 BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume)
557 FIXME("(%s, %s), stub!\n", debugstr_w(path), debugstr_w(volume));
558 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
559 return FALSE;