Copied with minimal adaptation from kernel32/volume.c
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v3: fix test failures...
I *think* this is the best approach to implementing FileFsVolumeInformation and FileFsAttributeInformation, but given the (necessarily) different nature of Wine to Windows and the difficulty of using host libraries from ntdll directly, I'm not as confident as I might otherwise be.
Part of the goal here is to reimplement is_same_file() using Win32 API functions. My original idea was to use FileIdInformation, which seems quite similar: we return a volume and file ID pair. However, to do that, we need to return the volume's actual serial number [1].
mountmgr *seems* like the right place to store this information, from a purely semantic sense. It does mean we have to add more fields to an internal ioctl, but given aed088df11 I'm led to believe this is nevertheless the preferred approach. Moreover, storing this information in mountmgr means we have the ability to derive the serial or label from disk management libraries (udisks2, Disk Arbitration, etc.) in the case that we can't calculate it ourselves (e.g. for an ext4 host partition).
Note that for the proposed implementation of is_same_file() to work, we need a different serial number to be returned for each Unix drive—including those that aren't mapped to a DOS device. This necessitates patch 3/6, or something similar. On the other hand, since we treat volumes like C: as separate volumes, i.e. ordinary directories linked as DOS devices, we need to be able to identify the correct volume by path, hence find_dos_device() in patch 4/6.
The other way I can think of to do this is to centralize the logic in ntdll (i.e. move it from kernel32), and forgo relying on mountmgr. The problem with this is that we need to retrieve the Unix device file from the file, which as far as I can tell is not really possible without relying on some complex platform-specific trickery.
Alternatively, we could ask mountmgr for the Unix device and then do the calculations in ntdll. It's not clear to me that's any worse than the current approach, except that we lose the ability to derive the label or serial from disk management libraries.
The code in kernel32 is not particularly pretty, but I figured it was a better use of my time not to obsessively clean it up...
This series does not actually include the code that assigns a default serial, but my plan is to derive it from the UUID in the case that we don't have a true 4-byte serial. I'm not sure of the best way to do this, though, so suggestions are welcome...
[1] https://www.winehq.org/pipermail/wine-devel/2020-March/161123.html
dlls/mountmgr.sys/device.c | 598 +++++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+)
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 0ca2d42517a..1459386f06c 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -71,6 +71,16 @@ static const WCHAR target_id_keyW[] = {'T','a','r','g','e','t',' ','I','d',' ',' static const WCHAR lun_keyW[] = {'L','o','g','i','c','a','l',' ','U','n','i','t',' ','I','d',' ','%','d',0}; static const WCHAR devnameW[] = {'D','e','v','i','c','e','N','a','m','e',0};
+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 */ +}; + struct disk_device { enum device_type type; /* drive type */ @@ -90,6 +100,9 @@ struct volume unsigned int ref; /* ref count */ GUID guid; /* volume uuid */ struct mount_point *mount; /* Volume{xxx} mount point */ + WCHAR label[256]; /* volume label */ + DWORD serial; /* volume serial number */ + enum fs_type fs_type; /* file system type */ };
struct dos_drive @@ -204,6 +217,510 @@ static void send_notify( int drive, int code ) WM_DEVICECHANGE, code, (LPARAM)&info ); }
+#define BLOCK_SIZE 2048 +#define SUPERBLOCK_SIZE BLOCK_SIZE + +#define CDFRAMES_PERSEC 75 +#define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60) +#define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3]) +#define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address) + +#define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)]) +#define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2)) + +static int open_volume_file( const struct volume *volume, const char *file ) +{ + const char *unix_mount = volume->device->unix_mount; + char *path; + int fd; + + if (!unix_mount) return -1; + + if (unix_mount[0] == '/') + { + if (!(path = HeapAlloc( GetProcessHeap(), 0, strlen( unix_mount ) + 1 + strlen( file ) + 1 ))) + return -1; + + strcpy( path, unix_mount ); + } + else + { + const char *config_dir = wine_get_config_dir(); + + if (!(path = HeapAlloc( GetProcessHeap(), 0, strlen( config_dir ) + + strlen("/dosdevices/") + strlen(unix_mount) + 1 + strlen( file ) + 1 ))) + return -1; + + strcpy( path, config_dir ); + strcat( path, "/dosdevices/" ); + strcat( path, unix_mount ); + } + strcat( path, "/" ); + strcat( path, file ); + + fd = open( path, O_RDONLY ); + HeapFree( GetProcessHeap(), 0, path ); + return fd; +} + +/* get the label by reading it from a file at the root of the filesystem */ +static void get_filesystem_label( struct volume *volume ) +{ + int fd; + ssize_t size; + char buffer[256], *p; + + volume->label[0] = 0; + + if ((fd = open_volume_file( volume, ".windows-label" )) == -1) + return; + size = read( fd, buffer, sizeof(buffer) ); + close( fd ); + + p = buffer + size; + while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--; + *p = 0; + if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, volume->label, ARRAY_SIZE(volume->label) )) + volume->label[ARRAY_SIZE(volume->label) - 1] = 0; +} + +/* get the serial number by reading it from a file at the root of the filesystem */ +static void get_filesystem_serial( struct volume *volume ) +{ + int fd; + ssize_t size; + char buffer[32]; + + volume->serial = 0; + + if ((fd = open_volume_file( volume, ".windows-serial" )) == -1) + return; + size = read( fd, buffer, sizeof(buffer) ); + close( fd ); + + if (size < 0) return; + buffer[size] = 0; + volume->serial = strtoul( buffer, NULL, 16 ); +} + + +/****************************************************************** + * 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; +} + + +/************************************************************************** + * UDF_Find_PVD + * Find the Primary Volume Descriptor + */ +static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] ) +{ + unsigned int i; + DWORD offset; + INT locations[] = { 256, -1, -257, 512 }; + + for(i=0; i<ARRAY_SIZE(locations); i++) + { + if (!VOLUME_ReadCDBlock(handle, pvd, locations[i]*BLOCK_SIZE)) + return FALSE; + + /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */ + if (pvd[0]==2 && pvd[1]==0) + { + /* Tag location (Uint32) at offset 12, little-endian */ + offset = pvd[20 + 0]; + offset |= pvd[20 + 1] << 8; + offset |= pvd[20 + 2] << 16; + offset |= pvd[20 + 3] << 24; + offset *= BLOCK_SIZE; + + if (!VOLUME_ReadCDBlock(handle, pvd, offset)) + return FALSE; + + /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */ + if (pvd[0]!=1 || pvd[1]!=0) + return FALSE; + + /* 8 or 16 bits per character -- [U] 2.1.1 */ + if (!(pvd[24]==8 || pvd[24]==16)) + return FALSE; + + return TRUE; + } + } + + return FALSE; +} + + +/************************************************************************** + * VOLUME_GetSuperblockLabel + */ +static void VOLUME_GetSuperblockLabel( struct volume *volume, HANDLE handle, const BYTE *superblock ) +{ + const BYTE *label_ptr = NULL; + DWORD label_len; + + switch (volume->fs_type) + { + case FS_ERROR: + label_len = 0; + break; + case FS_UNKNOWN: + get_filesystem_label( volume ); + return; + case FS_FAT1216: + label_ptr = superblock + 0x2b; + label_len = 11; + break; + case FS_FAT32: + label_ptr = superblock + 0x47; + label_len = 11; + break; + case FS_ISO9660: + { + BYTE ver = superblock[0x5a]; + + if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */ + ((ver == 0x40) || (ver == 0x43) || (ver == 0x45))) + { /* yippee, unicode */ + unsigned int i; + + for (i = 0; i < 16; i++) + volume->label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i]; + volume->label[i] = 0; + while (i && volume->label[i-1] == ' ') volume->label[--i] = 0; + return; + } + label_ptr = superblock + 40; + label_len = 32; + break; + } + case FS_UDF: + { + BYTE pvd[BLOCK_SIZE]; + + if(!UDF_Find_PVD(handle, pvd)) + { + label_len = 0; + break; + } + + /* [E] 3/10.1.4 and [U] 2.1.1 */ + if(pvd[24]==8) + { + label_ptr = pvd + 24 + 1; + label_len = pvd[24+32-1]; + break; + } + else + { + unsigned int i; + + label_len = 1 + pvd[24+32-1]; + for (i = 0; i < label_len; i += 2) + volume->label[i/2] = (pvd[24+1+i] << 8) | pvd[24+1+i+1]; + volume->label[label_len] = 0; + return; + } + } + } + if (label_len) RtlMultiByteToUnicodeN( volume->label, sizeof(volume->label) - sizeof(WCHAR), + &label_len, (const char *)label_ptr, label_len ); + label_len /= sizeof(WCHAR); + volume->label[label_len] = 0; + while (label_len && volume->label[label_len-1] == ' ') volume->label[--label_len] = 0; +} + + +/************************************************************************** + * UDF_Find_FSD_Sector + * Find the File Set Descriptor used to compute the serial of a UDF volume + */ +static int UDF_Find_FSD_Sector( HANDLE handle, BYTE block[] ) +{ + int i, PVD_sector, PD_sector, PD_length; + + if(!UDF_Find_PVD(handle,block)) + goto default_sector; + + /* Retrieve the tag location of the PVD -- [E] 3/7.2 */ + PVD_sector = block[12 + 0]; + PVD_sector |= block[12 + 1] << 8; + PVD_sector |= block[12 + 2] << 16; + PVD_sector |= block[12 + 3] << 24; + + /* Find the Partition Descriptor */ + for(i=PVD_sector+1; ; i++) + { + if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE)) + goto default_sector; + + /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */ + if(block[0]==5 && block[1]==0) + break; + + /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */ + if(block[0]==8 && block[1]==0) + goto default_sector; + } + + /* Find the partition starting location -- [E] 3/10.5.8 */ + PD_sector = block[188 + 0]; + PD_sector |= block[188 + 1] << 8; + PD_sector |= block[188 + 2] << 16; + PD_sector |= block[188 + 3] << 24; + + /* Find the partition length -- [E] 3/10.5.9 */ + PD_length = block[192 + 0]; + PD_length |= block[192 + 1] << 8; + PD_length |= block[192 + 2] << 16; + PD_length |= block[192 + 3] << 24; + + for(i=PD_sector; i<PD_sector+PD_length; i++) + { + if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE)) + goto default_sector; + + /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */ + if(block[0]==0 && block[1]==1) + return i; + } + +default_sector: + WARN("FSD sector not found, serial may be incorrect\n"); + return 257; +} + + +/************************************************************************** + * VOLUME_GetSuperblockSerial + */ +static void VOLUME_GetSuperblockSerial( struct volume *volume, HANDLE handle, const BYTE *superblock ) +{ + int FSD_sector; + BYTE block[BLOCK_SIZE]; + + switch (volume->fs_type) + { + case FS_ERROR: + break; + case FS_UNKNOWN: + get_filesystem_serial( volume ); + break; + case FS_FAT1216: + volume->serial = GETLONG( superblock, 0x27 ); + break; + case FS_FAT32: + volume->serial = GETLONG( superblock, 0x43 ); + break; + case FS_UDF: + FSD_sector = UDF_Find_FSD_Sector(handle, block); + if (!VOLUME_ReadCDBlock(handle, block, FSD_sector*BLOCK_SIZE)) + break; + superblock = block; + /* fallthrough */ + case FS_ISO9660: + { + BYTE sum[4]; + int i; + + sum[0] = sum[1] = sum[2] = sum[3] = 0; + for (i = 0; i < 2048; i += 4) + { + /* DON'T optimize this into DWORD !! (breaks overflow) */ + sum[0] += superblock[i+0]; + sum[1] += superblock[i+1]; + sum[2] += superblock[i+2]; + sum[3] += superblock[i+3]; + } + /* + * OK, another braindead one... argh. Just believe it. + * Me$$ysoft chose to reverse the serial number in NT4/W2K. + * It's true and nobody will ever be able to change it. + */ + if ((GetVersion() & 0x80000000) || volume->fs_type == FS_UDF) + volume->serial = (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0]; + else + volume->serial = (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3]; + } + } +} + + +/************************************************************************** + * VOLUME_GetAudioCDSerial + */ +static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc ) +{ + DWORD serial = 0; + int i; + + for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++) + serial += ((toc->TrackData[i].Address[1] << 16) | + (toc->TrackData[i].Address[2] << 8) | + toc->TrackData[i].Address[3]); + + /* + * dwStart, dwEnd collect the beginning and end of the disc respectively, in + * frames. + * There it is collected for correcting the serial when there are less than + * 3 tracks. + */ + if (toc->LastTrack - toc->FirstTrack + 1 < 3) + { + DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack); + DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1); + serial += dwEnd - dwStart; + } + return serial; +} + + /* create the disk device for a given volume */ static NTSTATUS create_disk_device( enum device_type type, struct disk_device **device_ret ) { @@ -473,6 +990,72 @@ static struct volume *find_matching_volume( const char *udi, const char *device, return NULL; }
+static BOOL get_volume_device_info( struct volume *volume ) +{ + const char *unix_device = volume->device->unix_device; + ANSI_STRING unix_name; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + HANDLE handle; + NTSTATUS ret; + CDROM_TOC toc; + DWORD size; + BYTE superblock[SUPERBLOCK_SIZE]; + IO_STATUS_BLOCK io; + + if (!unix_device) + return FALSE; + + RtlInitAnsiString( &unix_name, unix_device ); + if ((ret = wine_unix_to_nt_file_name( &unix_name, &nt_name ))) + { + ERR("Failed to convert %s to NT, status %#x\n", debugstr_a(unix_device), ret); + return FALSE; + } + + InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL ); + if ((ret = NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) + { + WARN("Failed to open %s, status %#x\n", debugstr_a(unix_device), ret); + RtlFreeUnicodeString( &nt_name ); + return FALSE; + } + + if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &size, 0 )) + { + if (!(toc.TrackData[0].Control & 0x04)) /* audio track */ + { + static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0}; + TRACE( "%s: found audio CD\n", debugstr_a(unix_device) ); + lstrcpynW( volume->label, audiocdW, ARRAY_SIZE(volume->label) ); + volume->serial = VOLUME_GetAudioCDSerial( &toc ); + volume->fs_type = FS_ISO9660; + CloseHandle( handle ); + return TRUE; + } + volume->fs_type = VOLUME_ReadCDSuperblock( handle, superblock ); + } + else + { + volume->fs_type = VOLUME_ReadFATSuperblock( handle, superblock ); + if (volume->fs_type == FS_UNKNOWN) volume->fs_type = VOLUME_ReadCDSuperblock( handle, superblock ); + } + + TRACE( "%s: found fs type %d\n", debugstr_a(unix_device), volume->fs_type ); + if (volume->fs_type == FS_ERROR) + { + CloseHandle( handle ); + return FALSE; + } + + VOLUME_GetSuperblockLabel( volume, handle, superblock ); + VOLUME_GetSuperblockSerial( volume, handle, superblock ); + + CloseHandle( handle ); + return TRUE; +} + /* change the information for an existing volume */ static NTSTATUS set_volume_info( struct volume *volume, struct dos_drive *drive, const char *device, const char *mount_point, enum device_type type, const GUID *guid ) @@ -506,6 +1089,21 @@ static NTSTATUS set_volume_info( struct volume *volume, struct dos_drive *drive, disk_device->unix_device = strdupA( device ); disk_device->unix_mount = strdupA( mount_point );
+ if (!get_volume_device_info( volume )) + { + if (volume->device->type == DEVICE_CDROM) + volume->fs_type = FS_ISO9660; + else if (volume->device->type == DEVICE_DVD) + volume->fs_type = FS_UDF; + else + volume->fs_type = FS_UNKNOWN; + + get_filesystem_label( volume ); + get_filesystem_serial( volume ); + } + + TRACE("fs_type %#x, label %s, serial %08x\n", volume->fs_type, debugstr_w(volume->label), volume->serial); + if (guid && memcmp( &volume->guid, guid, sizeof(volume->guid) )) { volume->guid = *guid;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/mountmgr.sys/device.c | 15 ++++++++++++++- dlls/mountmgr.sys/mountmgr.c | 4 +++- dlls/mountmgr.sys/mountmgr.h | 3 ++- include/ddk/mountmgr.h | 10 ++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 1459386f06c..89171363367 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1618,7 +1618,8 @@ NTSTATUS remove_dos_device( int letter, const char *udi ) }
/* query information about an existing dos drive, by letter or udi */ -NTSTATUS query_dos_device( int letter, enum device_type *type, char **device, char **mount_point ) +NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, + char **device, char **mount_point ) { NTSTATUS status = STATUS_NO_SUCH_DEVICE; struct dos_drive *drive; @@ -1630,6 +1631,18 @@ NTSTATUS query_dos_device( int letter, enum device_type *type, char **device, ch if (drive->drive != letter) continue; disk_device = drive->volume->device; if (type) *type = disk_device->type; + if (fs_type) + { + switch (drive->volume->fs_type) + { + case FS_ISO9660: *fs_type = MOUNTMGR_FS_TYPE_ISO9660; break; + case FS_UDF: *fs_type = MOUNTMGR_FS_TYPE_UDF; break; + case FS_FAT1216: *fs_type = MOUNTMGR_FS_TYPE_FAT; break; + case FS_FAT32: *fs_type = MOUNTMGR_FS_TYPE_FAT32; break; + default: *fs_type = MOUNTMGR_FS_TYPE_NTFS; break; + } + *fs_type = drive->volume->fs_type; + } if (device) *device = strdupA( disk_device->unix_device ); if (mount_point) *mount_point = strdupA( disk_device->unix_mount ); status = STATUS_SUCCESS; diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c index 222395fadf9..be72b0540d0 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -287,12 +287,13 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, int letter = tolowerW( input->letter ); NTSTATUS status; DWORD size, type = DEVICE_UNKNOWN; + enum mountmgr_fs_type fs_type; enum device_type device_type; char *ptr;
if (letter < 'a' || letter > 'z') return STATUS_INVALID_PARAMETER;
- if ((status = query_dos_device( letter - 'a', &device_type, &device, &mount_point ))) return status; + if ((status = query_dos_device( letter - 'a', &device_type, &fs_type, &device, &mount_point ))) return status; switch (device_type) { case DEVICE_UNKNOWN: type = DRIVE_UNKNOWN; break; @@ -314,6 +315,7 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, output->size = size; output->letter = letter; output->type = type; + output->fs_type = fs_type; output->mount_point_offset = 0; output->device_offset = 0;
diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h index b7fb7d86586..26901499ac3 100644 --- a/dlls/mountmgr.sys/mountmgr.h +++ b/dlls/mountmgr.sys/mountmgr.h @@ -57,7 +57,8 @@ extern NTSTATUS add_dos_device( int letter, const char *udi, const char *device, const char *mount_point, enum device_type type, const GUID *guid, UNICODE_STRING *devname ) DECLSPEC_HIDDEN; extern NTSTATUS remove_dos_device( int letter, const char *udi ) DECLSPEC_HIDDEN; -extern NTSTATUS query_dos_device( int letter, enum device_type *type, char **device, char **mount_point ) DECLSPEC_HIDDEN; +extern NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, + char **device, char **mount_point ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI parallel_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; diff --git a/include/ddk/mountmgr.h b/include/ddk/mountmgr.h index cb5822bf3d6..8ddf1582a67 100644 --- a/include/ddk/mountmgr.h +++ b/include/ddk/mountmgr.h @@ -52,10 +52,20 @@ static const WCHAR MOUNTMGR_DOS_DEVICE_NAME[] = {'\','\','.','\','M','o','u', #define IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE CTL_CODE(MOUNTMGRCONTROLTYPE, 32, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE CTL_CODE(MOUNTMGRCONTROLTYPE, 33, METHOD_BUFFERED, FILE_READ_ACCESS)
+enum mountmgr_fs_type +{ + MOUNTMGR_FS_TYPE_NTFS, + MOUNTMGR_FS_TYPE_FAT, + MOUNTMGR_FS_TYPE_FAT32, + MOUNTMGR_FS_TYPE_ISO9660, + MOUNTMGR_FS_TYPE_UDF, +}; + struct mountmgr_unix_drive { ULONG size; ULONG type; + ULONG fs_type; WCHAR letter; USHORT mount_point_offset; USHORT device_offset;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/mountmgr.sys/device.c | 60 ++++++++++++++++++++++++++++-------- dlls/mountmgr.sys/mountmgr.c | 14 +++++++-- dlls/mountmgr.sys/mountmgr.h | 2 ++ include/ddk/mountmgr.h | 1 + 4 files changed, 63 insertions(+), 14 deletions(-)
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 89171363367..5aac45c6508 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -30,6 +30,11 @@ #ifdef HAVE_SYS_IOCTL_H # include <sys/ioctl.h> #endif +#ifdef MAJOR_IN_MKDEV +# include <sys/mkdev.h> +#elif defined(MAJOR_IN_SYSMACROS) +# include <sys/sysmacros.h> +#endif
#define NONAMELESSUNION
@@ -1617,6 +1622,18 @@ NTSTATUS remove_dos_device( int letter, const char *udi ) return status; }
+enum mountmgr_fs_type get_mountmgr_fs_type(enum fs_type fs_type) +{ + switch (fs_type) + { + case FS_ISO9660: return MOUNTMGR_FS_TYPE_ISO9660; + case FS_UDF: return MOUNTMGR_FS_TYPE_UDF; + case FS_FAT1216: return MOUNTMGR_FS_TYPE_FAT; + case FS_FAT32: return MOUNTMGR_FS_TYPE_FAT32; + default: return MOUNTMGR_FS_TYPE_NTFS; + } +} + /* query information about an existing dos drive, by letter or udi */ NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) @@ -1631,18 +1648,37 @@ NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_ if (drive->drive != letter) continue; disk_device = drive->volume->device; if (type) *type = disk_device->type; - if (fs_type) - { - switch (drive->volume->fs_type) - { - case FS_ISO9660: *fs_type = MOUNTMGR_FS_TYPE_ISO9660; break; - case FS_UDF: *fs_type = MOUNTMGR_FS_TYPE_UDF; break; - case FS_FAT1216: *fs_type = MOUNTMGR_FS_TYPE_FAT; break; - case FS_FAT32: *fs_type = MOUNTMGR_FS_TYPE_FAT32; break; - default: *fs_type = MOUNTMGR_FS_TYPE_NTFS; break; - } - *fs_type = drive->volume->fs_type; - } + if (fs_type) *fs_type = get_mountmgr_fs_type( drive->volume->fs_type ); + if (device) *device = strdupA( disk_device->unix_device ); + if (mount_point) *mount_point = strdupA( disk_device->unix_mount ); + status = STATUS_SUCCESS; + break; + } + LeaveCriticalSection( &device_section ); + return status; +} + +/* query information about an existing unix device, by dev_t */ +NTSTATUS query_unix_device( unsigned int major, unsigned int minor, enum device_type *type, + enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) +{ + NTSTATUS status = STATUS_NO_SUCH_DEVICE; + struct volume *volume; + struct disk_device *disk_device; + struct stat st; + + EnterCriticalSection( &device_section ); + LIST_FOR_EACH_ENTRY( volume, &volumes_list, struct volume, entry ) + { + disk_device = volume->device; + + if (!disk_device->unix_device + || stat( disk_device->unix_device, &st ) < 0 + || st.st_rdev != makedev( major, minor )) + continue; + + if (type) *type = disk_device->type; + if (fs_type) *fs_type = get_mountmgr_fs_type( volume->fs_type ); if (device) *device = strdupA( disk_device->unix_device ); if (mount_point) *mount_point = strdupA( disk_device->unix_mount ); status = STATUS_SUCCESS; diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c index be72b0540d0..1ba66de7cbc 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -291,9 +291,19 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, enum device_type device_type; char *ptr;
- if (letter < 'a' || letter > 'z') return STATUS_INVALID_PARAMETER; + if (!letter) + { + if ((status = query_unix_device( input->major, input->minor, &device_type, + &fs_type, &device, &mount_point ))) + return status; + } + else + { + if (letter < 'a' || letter > 'z') return STATUS_INVALID_PARAMETER; + + if ((status = query_dos_device( letter - 'a', &device_type, &fs_type, &device, &mount_point ))) return status; + }
- if ((status = query_dos_device( letter - 'a', &device_type, &fs_type, &device, &mount_point ))) return status; switch (device_type) { case DEVICE_UNKNOWN: type = DRIVE_UNKNOWN; break; diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h index 26901499ac3..15893592976 100644 --- a/dlls/mountmgr.sys/mountmgr.h +++ b/dlls/mountmgr.sys/mountmgr.h @@ -59,6 +59,8 @@ extern NTSTATUS add_dos_device( int letter, const char *udi, const char *device, extern NTSTATUS remove_dos_device( int letter, const char *udi ) DECLSPEC_HIDDEN; extern NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) DECLSPEC_HIDDEN; +extern NTSTATUS query_unix_device( unsigned int major, unsigned int minor, enum device_type *type, + enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI parallel_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; diff --git a/include/ddk/mountmgr.h b/include/ddk/mountmgr.h index 8ddf1582a67..4271333204e 100644 --- a/include/ddk/mountmgr.h +++ b/include/ddk/mountmgr.h @@ -66,6 +66,7 @@ struct mountmgr_unix_drive ULONG size; ULONG type; ULONG fs_type; + ULONG major, minor; WCHAR letter; USHORT mount_point_offset; USHORT device_offset;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/file.c | 219 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 205 insertions(+), 14 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 7481db42b5b..d8576caa18c 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -108,6 +108,8 @@ #include "winioctl.h" #include "ddk/ntddk.h" #include "ddk/ntddser.h" +#define WINE_MOUNTMGR_EXTENSIONS +#include "ddk/mountmgr.h"
WINE_DEFAULT_DEBUG_CHANNEL(ntdll); WINE_DECLARE_DEBUG_CHANNEL(winediag); @@ -120,8 +122,6 @@ mode_t FILE_umask = 0; #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) #define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2)
-static const WCHAR ntfsW[] = {'N','T','F','S'}; - /* fetch the attributes of a file */ static inline ULONG get_file_attributes( const struct stat *st ) { @@ -3121,6 +3121,125 @@ static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info ) return STATUS_SUCCESS; }
+/* Find a DOS device which can act as the root of "path". + * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */ +static int find_dos_device( const char *path ) +{ + int len = strlen(path); + int drive; + char *buffer; + struct stat st; + struct drive_info info[MAX_DOS_DRIVES]; + dev_t dev_id; + + if (!DIR_get_drives_info( info )) return -1; + + if (stat( path, &st ) < 0) return -1; + dev_id = st.st_dev; + + /* strip off trailing slashes */ + while (len > 1 && path[len - 1] == '/') len--; + + /* make a copy of the path */ + if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) return -1; + memcpy( buffer, path, len ); + buffer[len] = 0; + + for (;;) + { + if (!stat( buffer, &st ) && S_ISDIR( st.st_mode )) + { + if (st.st_dev != dev_id) break; + + for (drive = 0; drive < MAX_DOS_DRIVES; drive++) + { + if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino)) + { + if (len == 1) len = 0; /* preserve root slash in returned path */ + TRACE( "%s -> drive %c:, root=%s, name=%s\n", + debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len)); + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + return drive; + } + } + } + if (len <= 1) break; /* reached root */ + while (path[len - 1] != '/') len--; + while (path[len - 1] == '/') len--; + buffer[len] = 0; + } + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + return -1; +} + +static struct mountmgr_unix_drive *get_mountmgr_fs_info( HANDLE handle, int fd ) +{ + struct mountmgr_unix_drive *drive; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + ANSI_STRING unix_name; + IO_STATUS_BLOCK io; + HANDLE mountmgr; + NTSTATUS status; + int letter; + + if (server_get_unix_name( handle, &unix_name )) + return NULL; + + letter = find_dos_device( unix_name.Buffer ); + RtlFreeAnsiString( &unix_name ); + + if (!(drive = RtlAllocateHeap( GetProcessHeap(), 0, 1024 ))) + return NULL; + + if (letter == -1) + { + struct stat st; + + if (fstat( fd, &st ) == -1) + { + RtlFreeHeap( GetProcessHeap(), 0, drive ); + return NULL; + } + + drive->major = major( st.st_dev ); + drive->minor = minor( st.st_dev ); + drive->letter = 0; + } + else + drive->letter = 'a' + letter; + + RtlInitUnicodeString( &string, MOUNTMGR_DEVICE_NAME ); + InitializeObjectAttributes( &attr, &string, 0, NULL, NULL ); + if (NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT )) + return NULL; + + status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, + drive, sizeof(*drive), drive, 1024 ); + if (status == STATUS_BUFFER_OVERFLOW) + { + if (!(drive = RtlReAllocateHeap( GetProcessHeap(), 0, drive, drive->size ))) + { + RtlFreeHeap( GetProcessHeap(), 0, drive ); + NtClose( mountmgr ); + return NULL; + } + status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, + drive, sizeof(*drive), drive, drive->size ); + } + NtClose( mountmgr ); + + if (status) + { + WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status); + RtlFreeHeap( GetProcessHeap(), 0, drive ); + return NULL; + } + + return drive; +} +
/****************************************************************************** * NtQueryVolumeInformationFile [NTDLL.@] @@ -3241,24 +3360,96 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io } break; case FileFsAttributeInformation: - if (length < offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[ARRAY_SIZE( ntfsW )] )) - io->u.Status = STATUS_BUFFER_TOO_SMALL; + { + static const WCHAR fatW[] = {'F','A','T'}; + static const WCHAR fat32W[] = {'F','A','T','3','2'}; + static const WCHAR ntfsW[] = {'N','T','F','S'}; + static const WCHAR cdfsW[] = {'C','D','F','S'}; + static const WCHAR udfW[] = {'U','D','F'}; + + FILE_FS_ATTRIBUTE_INFORMATION *info = buffer; + struct mountmgr_unix_drive *drive; + enum mountmgr_fs_type fs_type = MOUNTMGR_FS_TYPE_NTFS; + + if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION)) + { + io->u.Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if ((drive = get_mountmgr_fs_info( handle, fd ))) + { + fs_type = drive->fs_type; + RtlFreeHeap( GetProcessHeap(), 0, drive ); + } else { - FILE_FS_ATTRIBUTE_INFORMATION *info = buffer; + struct statfs stfs;
- FIXME( "%p: faking attribute info\n", handle ); - info->FileSystemAttributes = FILE_SUPPORTS_ENCRYPTION | FILE_FILE_COMPRESSION | - FILE_PERSISTENT_ACLS | FILE_UNICODE_ON_DISK | - FILE_CASE_PRESERVED_NAMES | FILE_CASE_SENSITIVE_SEARCH; - info->MaximumComponentNameLength = MAXIMUM_FILENAME_LENGTH - 1; - info->FileSystemNameLength = sizeof(ntfsW); - memcpy(info->FileSystemName, ntfsW, sizeof(ntfsW)); + if (!fstatfs( fd, &stfs )) + { +#if defined(linux) && defined(HAVE_FSTATFS) + switch (stfs.f_type) + { + case 0x9660: + fs_type = MOUNTMGR_FS_TYPE_ISO9660; + break; + case 0x15013346: + fs_type = MOUNTMGR_FS_TYPE_UDF; + break; + case 0x4d44: + fs_type = MOUNTMGR_FS_TYPE_FAT32; + break; + } +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) + if (!strcmp( stfs.f_fstypename, "cd9660" )) + fs_type = MOUNTMGR_FS_TYPE_ISO9660; + else if (!strcmp( stfs.f_fstypename, "udf" )) + fs_type = MOUNTMGR_FS_TYPE_UDF; + else if (!strcmp( stfs.f_fstypename, "msdos" )) + fs_type = MOUNTMGR_FS_TYPE_FAT32; +#endif + } + }
- io->Information = sizeof(*info); - io->u.Status = STATUS_SUCCESS; + switch (fs_type) + { + case MOUNTMGR_FS_TYPE_ISO9660: + info->FileSystemAttributes = FILE_READ_ONLY_VOLUME; + info->MaximumComponentNameLength = 221; + info->FileSystemNameLength = min( sizeof(cdfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) ); + memcpy(info->FileSystemName, cdfsW, info->FileSystemNameLength); + break; + case MOUNTMGR_FS_TYPE_UDF: + info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH; + info->MaximumComponentNameLength = 255; + info->FileSystemNameLength = min( sizeof(udfW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) ); + memcpy(info->FileSystemName, udfW, info->FileSystemNameLength); + break; + case MOUNTMGR_FS_TYPE_FAT: + info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */ + info->MaximumComponentNameLength = 255; + info->FileSystemNameLength = min( sizeof(fatW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) ); + memcpy(info->FileSystemName, fatW, info->FileSystemNameLength); + break; + case MOUNTMGR_FS_TYPE_FAT32: + info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */ + info->MaximumComponentNameLength = 255; + info->FileSystemNameLength = min( sizeof(fat32W), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) ); + memcpy(info->FileSystemName, fat32W, info->FileSystemNameLength); + break; + default: + info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS; + info->MaximumComponentNameLength = 255; + info->FileSystemNameLength = min( sizeof(ntfsW), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) ); + memcpy(info->FileSystemName, ntfsW, info->FileSystemNameLength); + break; } + + io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength; + io->u.Status = STATUS_SUCCESS; break; + } case FileFsControlInformation: FIXME( "%p: control info not supported\n", handle ); break;
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/mountmgr.sys/device.c | 18 ++++++++++++++++-- dlls/mountmgr.sys/mountmgr.c | 20 +++++++++++++++++--- dlls/mountmgr.sys/mountmgr.h | 7 ++++--- include/ddk/mountmgr.h | 2 ++ 4 files changed, 39 insertions(+), 8 deletions(-)
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 5aac45c6508..b99548aad27 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -157,6 +157,15 @@ static char *strdupA( const char *str ) return ret; }
+static WCHAR *strdupW( const WCHAR *str ) +{ + WCHAR *ret; + + if (!str) return NULL; + if ((ret = RtlAllocateHeap( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) ))) strcpyW( ret, str ); + return ret; +} + static const GUID *get_default_uuid( int letter ) { static GUID guid; @@ -1636,7 +1645,7 @@ enum mountmgr_fs_type get_mountmgr_fs_type(enum fs_type fs_type)
/* query information about an existing dos drive, by letter or udi */ NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, - char **device, char **mount_point ) + DWORD *serial, char **device, char **mount_point, WCHAR **label ) { NTSTATUS status = STATUS_NO_SUCH_DEVICE; struct dos_drive *drive; @@ -1649,8 +1658,10 @@ NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_ disk_device = drive->volume->device; if (type) *type = disk_device->type; if (fs_type) *fs_type = get_mountmgr_fs_type( drive->volume->fs_type ); + if (serial) *serial = drive->volume->serial; if (device) *device = strdupA( disk_device->unix_device ); if (mount_point) *mount_point = strdupA( disk_device->unix_mount ); + if (label) *label = strdupW( drive->volume->label ); status = STATUS_SUCCESS; break; } @@ -1660,7 +1671,8 @@ NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_
/* query information about an existing unix device, by dev_t */ NTSTATUS query_unix_device( unsigned int major, unsigned int minor, enum device_type *type, - enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) + enum mountmgr_fs_type *fs_type, DWORD *serial, char **device, + char **mount_point, WCHAR **label ) { NTSTATUS status = STATUS_NO_SUCH_DEVICE; struct volume *volume; @@ -1679,8 +1691,10 @@ NTSTATUS query_unix_device( unsigned int major, unsigned int minor, enum device_
if (type) *type = disk_device->type; if (fs_type) *fs_type = get_mountmgr_fs_type( volume->fs_type ); + if (serial) *serial = volume->serial; if (device) *device = strdupA( disk_device->unix_device ); if (mount_point) *mount_point = strdupA( disk_device->unix_mount ); + if (label) *label = strdupW( volume->label ); status = STATUS_SUCCESS; break; } diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c index 1ba66de7cbc..d9d09160202 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -286,22 +286,25 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, char *device, *mount_point; int letter = tolowerW( input->letter ); NTSTATUS status; - DWORD size, type = DEVICE_UNKNOWN; + DWORD size, type = DEVICE_UNKNOWN, serial; enum mountmgr_fs_type fs_type; enum device_type device_type; char *ptr; + WCHAR *label;
if (!letter) { if ((status = query_unix_device( input->major, input->minor, &device_type, - &fs_type, &device, &mount_point ))) + &fs_type, &serial, &device, &mount_point, &label ))) return status; } else { if (letter < 'a' || letter > 'z') return STATUS_INVALID_PARAMETER;
- if ((status = query_dos_device( letter - 'a', &device_type, &fs_type, &device, &mount_point ))) return status; + if ((status = query_dos_device( letter - 'a', &device_type, &fs_type, &serial, &device, + &mount_point, &label ))) + return status; }
switch (device_type) @@ -326,8 +329,10 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, output->letter = letter; output->type = type; output->fs_type = fs_type; + output->serial = serial; output->mount_point_offset = 0; output->device_offset = 0; + output->label_offset = 0;
if (size > outsize) { @@ -364,6 +369,14 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, } else output->device_offset = 0;
+ if (label) + { + output->label_offset = ptr - (char *)output; + strcpyW( (WCHAR *)ptr, label ); + ptr += (strlenW(label) + 1) * sizeof(WCHAR); + } + else output->label_offset = 0; + TRACE( "returning %c: dev %s mount %s type %u\n", letter, debugstr_a(device), debugstr_a(mount_point), type );
@@ -371,6 +384,7 @@ static NTSTATUS query_unix_drive( void *buff, SIZE_T insize, done: RtlFreeHeap( GetProcessHeap(), 0, device ); RtlFreeHeap( GetProcessHeap(), 0, mount_point ); + RtlFreeHeap( GetProcessHeap(), 0, label ); return status; }
diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h index 15893592976..469d83335b9 100644 --- a/dlls/mountmgr.sys/mountmgr.h +++ b/dlls/mountmgr.sys/mountmgr.h @@ -57,10 +57,11 @@ extern NTSTATUS add_dos_device( int letter, const char *udi, const char *device, const char *mount_point, enum device_type type, const GUID *guid, UNICODE_STRING *devname ) DECLSPEC_HIDDEN; extern NTSTATUS remove_dos_device( int letter, const char *udi ) DECLSPEC_HIDDEN; -extern NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, - char **device, char **mount_point ) DECLSPEC_HIDDEN; +extern NTSTATUS query_dos_device( int letter, enum device_type *type, enum mountmgr_fs_type *fs_type, DWORD *serial, + char **device, char **mount_point, WCHAR **label ) DECLSPEC_HIDDEN; extern NTSTATUS query_unix_device( unsigned int major, unsigned int minor, enum device_type *type, - enum mountmgr_fs_type *fs_type, char **device, char **mount_point ) DECLSPEC_HIDDEN; + DWORD *serial, enum mountmgr_fs_type *fs_type, char **device, + char **mount_point, WCHAR **label ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; extern NTSTATUS WINAPI parallel_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) DECLSPEC_HIDDEN; diff --git a/include/ddk/mountmgr.h b/include/ddk/mountmgr.h index 4271333204e..3ef74a84970 100644 --- a/include/ddk/mountmgr.h +++ b/include/ddk/mountmgr.h @@ -67,9 +67,11 @@ struct mountmgr_unix_drive ULONG type; ULONG fs_type; ULONG major, minor; + DWORD serial; WCHAR letter; USHORT mount_point_offset; USHORT device_offset; + USHORT label_offset; };
#define IOCTL_MOUNTMGR_QUERY_DHCP_REQUEST_PARAMS CTL_CODE(MOUNTMGRCONTROLTYPE, 64, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/file.c | 36 ++++++++++++++++++++++++++++++++---- dlls/ntdll/tests/file.c | 7 ++----- 2 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index d8576caa18c..f665d1f669a 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -3264,7 +3264,6 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io { int fd, needs_close; struct stat st; - static int once;
io->u.Status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ); if (io->u.Status == STATUS_BAD_DEVICE_TYPE) @@ -3287,9 +3286,6 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io
switch( info_class ) { - case FileFsVolumeInformation: - if (!once++) FIXME( "%p: volume info not supported\n", handle ); - break; case FileFsLabelInformation: FIXME( "%p: label info not supported\n", handle ); break; @@ -3450,6 +3446,38 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io io->u.Status = STATUS_SUCCESS; break; } + case FileFsVolumeInformation: + { + FILE_FS_VOLUME_INFORMATION *info = buffer; + struct mountmgr_unix_drive *drive; + const WCHAR *label; + + if (length < sizeof(FILE_FS_VOLUME_INFORMATION)) + { + io->u.Status = STATUS_INFO_LENGTH_MISMATCH; + break; + } + + if (!(drive = get_mountmgr_fs_info( handle, fd ))) + { + ERR_(winediag)("Failed to query volume information from mountmgr.\n"); + io->u.Status = STATUS_NOT_IMPLEMENTED; + break; + } + + label = (WCHAR *)((char *)drive + drive->label_offset); + info->VolumeCreationTime.QuadPart = 0; /* FIXME */ + info->VolumeSerialNumber = drive->serial; + info->VolumeLabelLength = min( strlenW( label ) * sizeof(WCHAR), + length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) ); + info->SupportsObjects = (drive->fs_type == MOUNTMGR_FS_TYPE_NTFS); + memcpy( info->VolumeLabel, label, info->VolumeLabelLength ); + RtlFreeHeap( GetProcessHeap(), 0, drive ); + + io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength; + io->u.Status = STATUS_SUCCESS; + break; + } case FileFsControlInformation: FIXME( "%p: control info not supported\n", handle ); break; diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 88be357fe8b..7b833fc9e86 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3866,8 +3866,6 @@ static void test_query_volume_information_file(void)
ffvi = (FILE_FS_VOLUME_INFORMATION *)buf;
-todo_wine -{ ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", status); ok(U(io).Status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %d\n", U(io).Status);
@@ -3875,10 +3873,9 @@ todo_wine "expected %d, got %lu\n", (FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + ffvi->VolumeLabelLength), io.Information);
- ok(ffvi->VolumeCreationTime.QuadPart != 0, "Missing VolumeCreationTime\n"); - ok(ffvi->VolumeSerialNumber != 0, "Missing VolumeSerialNumber\n"); + todo_wine ok(ffvi->VolumeCreationTime.QuadPart != 0, "Missing VolumeCreationTime\n"); + todo_wine ok(ffvi->VolumeSerialNumber != 0, "Missing VolumeSerialNumber\n"); ok(ffvi->SupportsObjects == 1,"expected 1, got %d\n", ffvi->SupportsObjects); -} ok(ffvi->VolumeLabelLength == lstrlenW(ffvi->VolumeLabel) * sizeof(WCHAR), "got %d\n", ffvi->VolumeLabelLength);
trace("VolumeSerialNumber: %x VolumeLabelName: %s\n", ffvi->VolumeSerialNumber, wine_dbgstr_w(ffvi->VolumeLabel));