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
30 #define WIN32_NO_STATUS
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))
51 FS_ERROR
, /* error accessing the device */
52 FS_UNKNOWN
, /* unknown file system */
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;
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 */
81 if (cur_vd_type
> max_vd_type
)
83 max_vd_type
= cur_vd_type
;
84 best_offs
= offs
+ extra_offs
;
91 /***********************************************************************
92 * VOLUME_ReadFATSuperblock
94 static enum fs_type
VOLUME_ReadFATSuperblock( HANDLE handle
, BYTE
*buff
)
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 */
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 */
115 unsigned int sectors
,
124 sect_per_fat
= GETWORD(buff
, 0x16);
125 if (!sect_per_fat
) sect_per_fat
= GETLONG(buff
, 0x24);
126 total_sectors
= GETWORD(buff
, 0x13);
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
&&
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
;
148 if ((buff
[0x26] == 0x28 || buff
[0x26] == 0x29) &&
149 !memcmp(buff
+0x36, "FAT", 3))
157 /***********************************************************************
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
)
173 /***********************************************************************
174 * VOLUME_ReadCDSuperblock
176 static enum fs_type
VOLUME_ReadCDSuperblock( HANDLE handle
, BYTE
*buff
)
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
))
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
))
202 /* check for the iso9660 identifier */
203 if (!memcmp(&buff
[1], "CD001", 5)) return FS_ISO9660
;
208 /***********************************************************************
209 * SetVolumeLabelW (KERNEL32.@)
211 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR label
)
213 WCHAR device
[] = L
"\\\\.\\A:";
215 enum fs_type type
= FS_UNKNOWN
;
219 WCHAR path
[MAX_PATH
];
220 GetCurrentDirectoryW( MAX_PATH
, path
);
225 if (!root
[0] || root
[1] != ':')
227 SetLastError( ERROR_INVALID_NAME
);
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
);
254 TRACE( "cannot open device %s: err %ld\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
))
263 case DRIVE_NO_ROOT_DIR
:
264 SetLastError( ERROR_NOT_READY
);
266 case DRIVE_REMOVABLE
:
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
)
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
);
295 SetLastError( ERROR_ACCESS_DENIED
);
301 /***********************************************************************
302 * SetVolumeLabelA (KERNEL32.@)
304 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
306 WCHAR
*rootW
= NULL
, *volnameW
= NULL
;
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
);
317 /***********************************************************************
318 * GetVolumeNameForVolumeMountPointA (KERNEL32.@)
320 BOOL WINAPI
GetVolumeNameForVolumeMountPointA( LPCSTR path
, LPSTR volume
, DWORD size
)
323 WCHAR volumeW
[50], *pathW
= NULL
;
324 DWORD len
= min(ARRAY_SIZE(volumeW
), size
);
326 TRACE("(%s, %p, %lx)\n", debugstr_a(path
), volume
, size
);
328 if (!path
|| !(pathW
= FILE_name_AtoW( path
, TRUE
)))
331 if ((ret
= GetVolumeNameForVolumeMountPointW( pathW
, volumeW
, len
)))
332 FILE_name_WtoA( volumeW
, -1, volume
, len
);
334 HeapFree( GetProcessHeap(), 0, pathW
);
339 /***********************************************************************
340 * DefineDosDeviceA (KERNEL32.@)
342 BOOL WINAPI
DefineDosDeviceA(DWORD flags
, LPCSTR devname
, LPCSTR targetpath
)
344 WCHAR
*devW
, *targetW
= NULL
;
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
);
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
)
363 WCHAR
*devnameW
= NULL
;
366 if (devname
&& !(devnameW
= FILE_name_AtoW( devname
, FALSE
))) return 0;
368 targetW
= HeapAlloc( GetProcessHeap(),0, bufsize
* sizeof(WCHAR
) );
371 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
375 retW
= QueryDosDeviceW(devnameW
, targetW
, bufsize
);
377 ret
= FILE_name_WtoA( targetW
, retW
, target
, bufsize
);
379 HeapFree(GetProcessHeap(), 0, targetW
);
384 /***********************************************************************
385 * GetLogicalDriveStringsA (KERNEL32.@)
387 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
389 DWORD drives
= GetLogicalDrives();
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
;
410 /***********************************************************************
411 * GetVolumePathNameA (KERNEL32.@)
413 BOOL WINAPI
GetVolumePathNameA(LPCSTR filename
, LPSTR volumepathname
, DWORD buflen
)
416 WCHAR
*filenameW
= NULL
, *volumeW
= NULL
;
418 TRACE("(%s, %p, %ld)\n", debugstr_a(filename
), volumepathname
, buflen
);
420 if (filename
&& !(filenameW
= FILE_name_AtoW( filename
, FALSE
)))
422 if (volumepathname
&& !(volumeW
= HeapAlloc( GetProcessHeap(), 0, buflen
* sizeof(WCHAR
) )))
425 if ((ret
= GetVolumePathNameW( filenameW
, volumeW
, buflen
)))
426 FILE_name_WtoA( volumeW
, -1, volumepathname
, buflen
);
428 HeapFree( GetProcessHeap(), 0, volumeW
);
433 /***********************************************************************
434 * GetVolumePathNamesForVolumeNameA (KERNEL32.@)
436 BOOL WINAPI
GetVolumePathNamesForVolumeNameA(LPCSTR volumename
, LPSTR volumepathname
, DWORD buflen
, PDWORD returnlen
)
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
);
447 if ((ret
= GetVolumePathNamesForVolumeNameW( volumenameW
, volumepathnameW
, buflen
, returnlen
)))
449 char *path
= volumepathname
;
450 const WCHAR
*pathW
= volumepathnameW
;
454 int len
= lstrlenW( pathW
) + 1;
455 FILE_name_WtoA( pathW
, len
, path
, buflen
);
462 HeapFree( GetProcessHeap(), 0, volumenameW
);
463 HeapFree( GetProcessHeap(), 0, volumepathnameW
);
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
);
488 /***********************************************************************
489 * FindNextVolumeA (KERNEL32.@)
491 BOOL WINAPI
FindNextVolumeA( HANDLE handle
, LPSTR volume
, DWORD len
)
493 WCHAR
*buffer
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
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
);
504 /***********************************************************************
505 * FindFirstVolumeMountPointA (KERNEL32.@)
507 HANDLE WINAPI
FindFirstVolumeMountPointA(LPCSTR root
, LPSTR mount_point
, DWORD len
)
509 FIXME("(%s, %p, %ld), 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, %ld), 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
);
533 /***********************************************************************
534 * DeleteVolumeMountPointA (KERNEL32.@)
536 BOOL WINAPI
DeleteVolumeMountPointA(LPCSTR mountpoint
)
538 FIXME("(%s), stub!\n", debugstr_a(mountpoint
));
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
);
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
);