wine-wine/dlls/kernel32/volume.c

564 lines
18 KiB
C

/*
* Volume management functions
*
* Copyright 1993 Erik Bos
* Copyright 1996, 2004 Alexandre Julliard
* Copyright 1999 Petr Tomasek
* Copyright 2000 Andreas Mohr
* Copyright 2003 Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winternl.h"
#include "winioctl.h"
#include "ntddcdrm.h"
#include "ddk/wdm.h"
#include "kernel_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(volume);
#define BLOCK_SIZE 2048
#define SUPERBLOCK_SIZE BLOCK_SIZE
#define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
#define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
enum fs_type
{
FS_ERROR, /* error accessing the device */
FS_UNKNOWN, /* unknown file system */
FS_FAT1216,
FS_FAT32,
FS_ISO9660,
FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
};
/******************************************************************
* VOLUME_FindCdRomDataBestVoldesc
*/
static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
{
BYTE cur_vd_type, max_vd_type = 0;
BYTE buffer[0x800];
DWORD size, offs, best_offs = 0, extra_offs = 0;
for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
{
/* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
* the volume label is displaced forward by 8
*/
if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
if (size != sizeof(buffer)) break;
/* check for non-ISO9660 signature */
if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
cur_vd_type = buffer[extra_offs];
if (cur_vd_type == 0xff) /* voldesc set terminator */
break;
if (cur_vd_type > max_vd_type)
{
max_vd_type = cur_vd_type;
best_offs = offs + extra_offs;
}
}
return best_offs;
}
/***********************************************************************
* VOLUME_ReadFATSuperblock
*/
static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
{
DWORD size;
/* try a fixed disk, with a FAT partition */
if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
!ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
{
if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN; /* not a real device */
return FS_ERROR;
}
if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
/* FIXME: do really all FAT have their name beginning with
* "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
*/
if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
{
/* guess which type of FAT we have */
int reasonable;
unsigned int sectors,
sect_per_fat,
total_sectors,
num_boot_sectors,
num_fats,
num_root_dir_ents,
bytes_per_sector,
sectors_per_cluster,
nclust;
sect_per_fat = GETWORD(buff, 0x16);
if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
total_sectors = GETWORD(buff, 0x13);
if (!total_sectors)
total_sectors = GETLONG(buff, 0x20);
num_boot_sectors = GETWORD(buff, 0x0e);
num_fats = buff[0x10];
num_root_dir_ents = GETWORD(buff, 0x11);
bytes_per_sector = GETWORD(buff, 0x0b);
sectors_per_cluster = buff[0x0d];
/* check if the parameters are reasonable and will not cause
* arithmetic errors in the calculation */
reasonable = num_boot_sectors < total_sectors &&
num_fats < 16 &&
bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
sectors_per_cluster >= 1;
if (!reasonable) return FS_UNKNOWN;
sectors = total_sectors - num_boot_sectors - num_fats * sect_per_fat -
(num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
nclust = sectors / sectors_per_cluster;
if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
!memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
if (nclust < 65525)
{
if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
!memcmp(buff+0x36, "FAT", 3))
return FS_FAT1216;
}
}
return FS_UNKNOWN;
}
/***********************************************************************
* VOLUME_ReadCDBlock
*/
static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
{
DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
!ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
size != SUPERBLOCK_SIZE)
return FALSE;
return TRUE;
}
/***********************************************************************
* VOLUME_ReadCDSuperblock
*/
static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
{
int i;
DWORD offs;
/* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
* Starting from sector 16, we may find :
* - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
* - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
* There is no explicit end so read 16 sectors and then give up */
for( i=16; i<16+16; i++)
{
if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
continue;
/* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
* but we assume the volume is well-formatted */
if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
}
offs = VOLUME_FindCdRomDataBestVoldesc( handle );
if (!offs) return FS_UNKNOWN;
if (!VOLUME_ReadCDBlock(handle, buff, offs))
return FS_ERROR;
/* check for the iso9660 identifier */
if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
return FS_UNKNOWN;
}
/***********************************************************************
* SetVolumeLabelW (KERNEL32.@)
*/
BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
{
WCHAR device[] = {'\\','\\','.','\\','A',':',0};
HANDLE handle;
enum fs_type type = FS_UNKNOWN;
if (!root)
{
WCHAR path[MAX_PATH];
GetCurrentDirectoryW( MAX_PATH, path );
device[4] = path[0];
}
else
{
if (!root[0] || root[1] != ':')
{
SetLastError( ERROR_INVALID_NAME );
return FALSE;
}
device[4] = root[0];
}
/* try to open the device */
handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0 );
if (handle != INVALID_HANDLE_VALUE)
{
BYTE superblock[SUPERBLOCK_SIZE];
type = VOLUME_ReadFATSuperblock( handle, superblock );
if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
CloseHandle( handle );
if (type != FS_UNKNOWN)
{
/* we can't set the label on FAT or CDROM file systems */
TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
SetLastError( ERROR_ACCESS_DENIED );
return FALSE;
}
}
else
{
TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
}
/* we couldn't open the device, fallback to default strategy */
switch(GetDriveTypeW( root ))
{
case DRIVE_UNKNOWN:
case DRIVE_NO_ROOT_DIR:
SetLastError( ERROR_NOT_READY );
break;
case DRIVE_REMOVABLE:
case DRIVE_FIXED:
{
WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
labelW[0] = device[4];
if (!label[0]) /* delete label file when setting an empty label */
return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, 0, 0 );
if (handle != INVALID_HANDLE_VALUE)
{
char buffer[64];
DWORD size;
if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
buffer[sizeof(buffer)-2] = 0;
strcat( buffer, "\n" );
WriteFile( handle, buffer, strlen(buffer), &size, NULL );
CloseHandle( handle );
return TRUE;
}
break;
}
case DRIVE_REMOTE:
case DRIVE_RAMDISK:
case DRIVE_CDROM:
SetLastError( ERROR_ACCESS_DENIED );
break;
}
return FALSE;
}
/***********************************************************************
* SetVolumeLabelA (KERNEL32.@)
*/
BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
{
WCHAR *rootW = NULL, *volnameW = NULL;
BOOL ret;
if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
ret = SetVolumeLabelW( rootW, volnameW );
HeapFree( GetProcessHeap(), 0, volnameW );
return ret;
}
/***********************************************************************
* GetVolumeNameForVolumeMountPointA (KERNEL32.@)
*/
BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
{
BOOL ret;
WCHAR volumeW[50], *pathW = NULL;
DWORD len = min(ARRAY_SIZE(volumeW), size );
TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
return FALSE;
if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
FILE_name_WtoA( volumeW, -1, volume, len );
HeapFree( GetProcessHeap(), 0, pathW );
return ret;
}
/***********************************************************************
* DefineDosDeviceA (KERNEL32.@)
*/
BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
{
WCHAR *devW, *targetW = NULL;
BOOL ret;
if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
ret = DefineDosDeviceW(flags, devW, targetW);
HeapFree( GetProcessHeap(), 0, targetW );
return ret;
}
/***********************************************************************
* QueryDosDeviceA (KERNEL32.@)
*
* returns array of strings terminated by \0, terminated by \0
*/
DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
{
DWORD ret = 0, retW;
WCHAR *devnameW = NULL;
LPWSTR targetW;
if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
if (!targetW)
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return 0;
}
retW = QueryDosDeviceW(devnameW, targetW, bufsize);
ret = FILE_name_WtoA( targetW, retW, target, bufsize );
HeapFree(GetProcessHeap(), 0, targetW);
return ret;
}
/***********************************************************************
* GetLogicalDriveStringsA (KERNEL32.@)
*/
UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
{
DWORD drives = GetLogicalDrives();
UINT drive, count;
for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
if ((count * 4) + 1 > len) return count * 4 + 1;
for (drive = 0; drive < 26; drive++)
{
if (drives & (1 << drive))
{
*buffer++ = 'A' + drive;
*buffer++ = ':';
*buffer++ = '\\';
*buffer++ = 0;
}
}
*buffer = 0;
return count * 4;
}
/***********************************************************************
* GetVolumePathNameA (KERNEL32.@)
*/
BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
{
BOOL ret;
WCHAR *filenameW = NULL, *volumeW = NULL;
TRACE("(%s, %p, %d)\n", debugstr_a(filename), volumepathname, buflen);
if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
return FALSE;
if (volumepathname && !(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
return FALSE;
if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
HeapFree( GetProcessHeap(), 0, volumeW );
return ret;
}
/***********************************************************************
* GetVolumePathNamesForVolumeNameA (KERNEL32.@)
*/
BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen)
{
BOOL ret;
WCHAR *volumenameW = NULL, *volumepathnameW;
if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE;
if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
{
HeapFree( GetProcessHeap(), 0, volumenameW );
return FALSE;
}
if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen )))
{
char *path = volumepathname;
const WCHAR *pathW = volumepathnameW;
while (*pathW)
{
int len = lstrlenW( pathW ) + 1;
FILE_name_WtoA( pathW, len, path, buflen );
buflen -= len;
pathW += len;
path += len;
}
path[0] = 0;
}
HeapFree( GetProcessHeap(), 0, volumenameW );
HeapFree( GetProcessHeap(), 0, volumepathnameW );
return ret;
}
/***********************************************************************
* FindFirstVolumeA (KERNEL32.@)
*/
HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
{
WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
HANDLE handle = FindFirstVolumeW( buffer, len );
if (handle != INVALID_HANDLE_VALUE)
{
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
{
FindVolumeClose( handle );
handle = INVALID_HANDLE_VALUE;
}
}
HeapFree( GetProcessHeap(), 0, buffer );
return handle;
}
/***********************************************************************
* FindNextVolumeA (KERNEL32.@)
*/
BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
{
WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
BOOL ret;
if ((ret = FindNextVolumeW( handle, buffer, len )))
{
if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
}
HeapFree( GetProcessHeap(), 0, buffer );
return ret;
}
/***********************************************************************
* FindFirstVolumeMountPointA (KERNEL32.@)
*/
HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
{
FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return INVALID_HANDLE_VALUE;
}
/***********************************************************************
* FindFirstVolumeMountPointW (KERNEL32.@)
*/
HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
{
FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return INVALID_HANDLE_VALUE;
}
/***********************************************************************
* FindVolumeMountPointClose (KERNEL32.@)
*/
BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
{
FIXME("(%p), stub!\n", h);
return FALSE;
}
/***********************************************************************
* DeleteVolumeMountPointA (KERNEL32.@)
*/
BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint)
{
FIXME("(%s), stub!\n", debugstr_a(mountpoint));
return FALSE;
}
/***********************************************************************
* SetVolumeMountPointA (KERNEL32.@)
*/
BOOL WINAPI SetVolumeMountPointA(LPCSTR path, LPCSTR volume)
{
FIXME("(%s, %s), stub!\n", debugstr_a(path), debugstr_a(volume));
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/***********************************************************************
* SetVolumeMountPointW (KERNEL32.@)
*/
BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume)
{
FIXME("(%s, %s), stub!\n", debugstr_w(path), debugstr_w(volume));
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}