/* File: win32.c Copyright (C) 2008 Christophe GRENIER This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) #include "types.h" #include "common.h" #ifdef HAVE_STDLIB_H #include /* free */ #endif #ifdef HAVE_WINDEF_H #include #endif #ifdef HAVE_WINBASE_H #include #include #endif #include /* isspace */ #ifdef HAVE_WINIOCTL_H #include #endif #if defined(__CYGWIN__) #include #include #include #endif #include "fnctdsk.h" #include "log.h" #include "win32.h" #include "hdwin32.h" #include "hdaccess.h" #include "alignio.h" extern const arch_fnct_t arch_none; static unsigned int file_win32_compute_sector_size(HANDLE handle); static uint64_t filewin32_getfilesize(HANDLE handle, const char *device); static const char *file_win32_description(disk_t *disk_car); static const char *file_win32_description_short(disk_t *disk_car); static void file_win32_clean(disk_t *disk_car); static int file_win32_pread(disk_t *disk_car, void *buf, const unsigned int count, const uint64_t offset); static int file_win32_pwrite(disk_t *disk_car, const void *buf, const unsigned int count, const uint64_t offset); static int file_win32_nopwrite(disk_t *disk_car, const void *buf, const unsigned int count, const uint64_t offset); static int file_win32_sync(disk_t *disk_car); static uint64_t filewin32_setfilepointer(HANDLE handle, const char *device); unsigned int disk_get_sector_size_win32(HANDLE handle, const char *device, const int verbose) { unsigned int sector_size; DWORD gotbytes; DISK_GEOMETRY geometry; DISK_GEOMETRY_EX geometry_ex; if (DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geometry_ex, sizeof(geometry_ex), &gotbytes, NULL)) { if(geometry_ex.Geometry.BytesPerSector <= (1<<24)) return geometry_ex.Geometry.BytesPerSector; } if (DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof(geometry), &gotbytes, NULL)) { if(geometry.BytesPerSector <= (1<<24)) return geometry.BytesPerSector; } sector_size=file_win32_compute_sector_size(handle); if(sector_size==0) sector_size=DEFAULT_SECTOR_SIZE; return sector_size; } uint64_t disk_get_size_win32(HANDLE handle, const char *device, const int verbose) { uint64_t disk_size=0; { GET_LENGTH_INFORMATION buf; DWORD i; if (DeviceIoControl(handle, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) { disk_size=(uint64_t)buf.Length.QuadPart; log_info("disk_get_size_win32 IOCTL_DISK_GET_LENGTH_INFO(%s)=%llu\n", device, (long long unsigned)disk_size); return disk_size; } } disk_size=filewin32_getfilesize(handle, device); if(disk_size!=0) return disk_size; if(device[0]!='\0' && device[1]!='\0' && device[2]!='\0' && device[3]!='\0' && device[4]!='\0') { uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes; if(GetDiskFreeSpaceEx (&device[4], (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes, (PULARGE_INTEGER)&i64FreeBytes)!=0) { if(verbose>1) log_info("disk_get_size_win32 GetDiskFreeSpaceEx %s: %llu\n", device, (long long unsigned)i64TotalBytes); return i64TotalBytes; } } { DWORD gotbytes; DISK_GEOMETRY_EX geometry_ex; if (DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geometry_ex, sizeof(geometry_ex), &gotbytes, NULL)) { disk_size=(uint64_t)geometry_ex.DiskSize.QuadPart; if(verbose>1) log_info("disk_get_size_win32 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX %s: %llu\n", device, (long long unsigned)disk_size); } } if(disk_size!=0) return disk_size; return filewin32_setfilepointer(handle, device); } void disk_get_geometry_win32(CHSgeometry_t *geom, HANDLE handle, const char *device, const int verbose) { if(geom->sectors_per_head!=0) return; { DWORD gotbytes; DISK_GEOMETRY_EX geometry_ex; if (DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geometry_ex, sizeof(geometry_ex), &gotbytes, NULL)) { geom->cylinders= geometry_ex.Geometry.Cylinders.QuadPart; geom->heads_per_cylinder=geometry_ex.Geometry.TracksPerCylinder; geom->sectors_per_head= geometry_ex.Geometry.SectorsPerTrack; if(geom->sectors_per_head!=0) { if(verbose>1) log_verbose("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX %s Ok (%lu, %u, %u)\n", device, geom->cylinders, geom->heads_per_cylinder, geom->sectors_per_head); return ; } } } { DWORD gotbytes; DISK_GEOMETRY geometry; if (DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof(geometry), &gotbytes, NULL)) { geom->cylinders= geometry.Cylinders.QuadPart; geom->heads_per_cylinder=geometry.TracksPerCylinder; geom->sectors_per_head= geometry.SectorsPerTrack; if(geom->sectors_per_head!=0) { if(verbose>1) log_verbose("IOCTL_DISK_GET_DRIVE_GEOMETRY %s Ok (%lu, %u, %u)\n", device, geom->cylinders, geom->heads_per_cylinder, geom->sectors_per_head); return ; } } } geom->cylinders=0; geom->heads_per_cylinder=1; geom->sectors_per_head=1; } // Try to handle cdrom struct info_file_win32_struct { HANDLE handle; char file_name[DISKNAME_MAX]; int mode; }; static uint64_t filewin32_getfilesize(HANDLE handle, const char *device) { uint64_t disk_size; DWORD lpFileSizeLow; DWORD lpFileSizeHigh; lpFileSizeLow=GetFileSize(handle,&lpFileSizeHigh); if(lpFileSizeLow==INVALID_FILE_SIZE && GetLastError() != NO_ERROR) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error("filewin32_getfilesize(%s) GetFileSize err %s\n", device, (char*)lpMsgBuf); LocalFree(lpMsgBuf); return 0; } disk_size=lpFileSizeLow+((uint64_t)lpFileSizeHigh>>32); log_verbose("filewin32_getfilesize(%s)=%llu\n", device, (long long unsigned)disk_size ); return disk_size; } static uint64_t filewin32_setfilepointer(HANDLE handle, const char *device) { uint64_t disk_size; LARGE_INTEGER li; li.QuadPart = 0; li.LowPart = SetFilePointer(handle, li.LowPart, &li.HighPart, FILE_END); if(li.LowPart==INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error("filewin32_setfilepointer(%s) SetFilePointer err %s\n", device, (char*)lpMsgBuf); LocalFree(lpMsgBuf); return 0; } disk_size=li.LowPart+((uint64_t)li.HighPart>>32); log_verbose("filewin32_setfilepointer(%s)=%llu\n", device, (long long unsigned)disk_size ); return disk_size; } disk_t *file_test_availability_win32(const char *device, const int verbose, int testdisk_mode) { disk_t *disk_car=NULL; HANDLE handle=INVALID_HANDLE_VALUE; int mode=0; int try_readonly=1; if((testdisk_mode&TESTDISK_O_RDWR)==TESTDISK_O_RDWR) { mode = FILE_READ_DATA | FILE_WRITE_DATA; handle = CreateFile(device,mode, (FILE_SHARE_WRITE | FILE_SHARE_READ), NULL, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) { if(verbose>1) { #ifdef __MINGW32__ log_error("file_test_availability_win32 RW failed %s\n", device); #else LPVOID buf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM , NULL , GetLastError() , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) , (LPTSTR)&buf , 0 , NULL ); log_error("file_test_availability_win32 RW failed: %s: %s\n", device,(const char*)buf); LocalFree(buf); #endif } try_readonly=0; } } if(handle==INVALID_HANDLE_VALUE && try_readonly>0) { testdisk_mode&=~TESTDISK_O_RDWR; mode = FILE_READ_DATA; handle = CreateFile(device,mode, (FILE_SHARE_WRITE | FILE_SHARE_READ), NULL, OPEN_EXISTING, 0, NULL); if (handle == INVALID_HANDLE_VALUE) { if(verbose>1) { #ifdef __MINGW32__ log_error("file_test_availability_win32 RO %s error\n", device); #else LPVOID buf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM , NULL , GetLastError() , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) , (LPTSTR)&buf , 0 , NULL ); log_error("file_test_availability_win32 RO failed: %s: %s\n", device,(const char*)buf); LocalFree(buf); #endif } } } if(handle==INVALID_HANDLE_VALUE) return NULL; { struct info_file_win32_struct *data; disk_car=(disk_t *)MALLOC(sizeof(*disk_car)); init_disk(disk_car); disk_car->arch=&arch_none; disk_car->device=strdup(device); data=(struct info_file_win32_struct *)MALLOC(sizeof(*data)); data->handle=handle; data->mode=mode; disk_car->data=data; disk_car->description=file_win32_description; disk_car->description_short=file_win32_description_short; disk_car->pread=file_win32_pread; disk_car->pwrite=((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?file_win32_pwrite:file_win32_nopwrite); disk_car->sync=file_win32_sync; disk_car->access_mode=testdisk_mode; disk_car->clean=file_win32_clean; disk_car->sector_size=disk_get_sector_size_win32(handle, device, verbose); disk_get_geometry_win32(&disk_car->geom, handle, device, verbose); disk_car->disk_real_size=disk_get_size_win32(handle, device, verbose); file_win32_disk_get_model(handle, disk_car, verbose); update_disk_car_fields(disk_car); if(disk_car->disk_real_size!=0) return disk_car; log_warning("Warning: can't get size for %s\n",device); file_win32_clean(disk_car); } return NULL; } static const char *file_win32_description(disk_t *disk_car) { struct info_file_win32_struct *data=(struct info_file_win32_struct *)disk_car->data; char buffer_disk_size[100]; size_to_unit(disk_car->disk_size, buffer_disk_size); if(disk_car->device[0]=='\\' && disk_car->device[1]=='\\' && disk_car->device[2]=='.' && disk_car->device[3]=='\\' && disk_car->device[5]==':') snprintf(disk_car->description_txt, sizeof(disk_car->description_txt),"Drive %c: - %s - CHS %lu %u %u%s", disk_car->device[4], buffer_disk_size, disk_car->geom.cylinders, disk_car->geom.heads_per_cylinder, disk_car->geom.sectors_per_head, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)")); else snprintf(disk_car->description_txt, sizeof(disk_car->description_txt),"Disk %s - %s - CHS %lu %u %u%s", disk_car->device, buffer_disk_size, disk_car->geom.cylinders, disk_car->geom.heads_per_cylinder, disk_car->geom.sectors_per_head, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)")); return disk_car->description_txt; } static const char *file_win32_description_short(disk_t *disk_car) { struct info_file_win32_struct *data=(struct info_file_win32_struct *)disk_car->data; char buffer_disk_size[100]; size_to_unit(disk_car->disk_size, buffer_disk_size); if(disk_car->device[0]=='\\' && disk_car->device[1]=='\\' && disk_car->device[2]=='.' && disk_car->device[3]=='\\' && disk_car->device[5]==':') { if(disk_car->model==NULL) snprintf(disk_car->description_short_txt, sizeof(disk_car->description_txt), "Drive %c: - %s%s", disk_car->device[4], buffer_disk_size, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)")); else snprintf(disk_car->description_short_txt, sizeof(disk_car->description_txt), "Drive %c: - %s%s - %s", disk_car->device[4], buffer_disk_size, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)"), disk_car->model); } else { if(disk_car->model==NULL) snprintf(disk_car->description_short_txt, sizeof(disk_car->description_txt), "Disk %s - %s%s", disk_car->device, buffer_disk_size, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)")); else snprintf(disk_car->description_short_txt, sizeof(disk_car->description_txt), "Disk %s - %s%s - %s", disk_car->device, buffer_disk_size, ((data->mode&FILE_WRITE_DATA)==FILE_WRITE_DATA?"":" (RO)"), disk_car->model); } return disk_car->description_short_txt; } static void file_win32_clean(disk_t *disk) { if(disk->data!=NULL) { struct info_file_win32_struct *data=(struct info_file_win32_struct *)disk->data; CloseHandle(data->handle); } generic_clean(disk); } static unsigned int file_win32_compute_sector_size(HANDLE handle) { char *buffer=(char *)MALLOC(4096); unsigned int sector_size; for(sector_size=512;sector_size<=4096;sector_size*=2) { long int ret; DWORD dwByteRead; ret=ReadFile(handle, buffer,sector_size,&dwByteRead,NULL); if(ret && dwByteRead==sector_size) { free(buffer); return sector_size; } } free(buffer); return 0; } static int file_win32_pread_aux(const disk_t *disk_car, void *buf, const unsigned int count, const uint64_t offset) { long int ret; HANDLE fd=((const struct info_file_win32_struct *)disk_car->data)->handle; LARGE_INTEGER li; li.QuadPart = offset; li.LowPart = SetFilePointer(fd, li.LowPart, &li.HighPart, FILE_BEGIN); if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error("file_win32_pread(%d,%u,buffer,%lu(%u/%u/%u)) seek err %s\n", (int)fd, (unsigned)(count/disk_car->sector_size), (long unsigned int)(offset/disk_car->sector_size), offset2cylinder(disk_car,offset), offset2head(disk_car,offset), offset2sector(disk_car,offset), (char*)lpMsgBuf); LocalFree(lpMsgBuf); return -1; } { DWORD dwByteRead; ret=ReadFile(fd, buf,count,&dwByteRead,NULL); if(ret) ret=dwByteRead; } if(ret!=(signed)count) { if(ret>0 || offsetdisk_size) { log_error("file_win32_pread(%d,%u,buffer,%lu(%u/%u/%u)) read err: ", (int)fd, (unsigned)(count/disk_car->sector_size), (long unsigned)(offset/disk_car->sector_size), offset2cylinder(disk_car,offset), offset2head(disk_car,offset), offset2sector(disk_car,offset)); if(ret<0) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error("%s\n", (char*)lpMsgBuf); LocalFree(lpMsgBuf); } else if(ret==0) log_error("read after end of file\n"); else log_error("Partial read\n"); } if(ret>0) memset((char*)buf+ret,0,count-ret); } return ret; } static int file_win32_pread(disk_t *disk_car, void *buf, const unsigned int count, const uint64_t offset) { return align_pread(&file_win32_pread_aux, disk_car, buf, count, offset); } static int file_win32_pwrite_aux(disk_t *disk_car, const void *buf, const unsigned int count, const uint64_t offset) { long int ret; HANDLE fd=((const struct info_file_win32_struct *)disk_car->data)->handle; LARGE_INTEGER li; li.QuadPart = offset; li.LowPart = SetFilePointer(fd, li.LowPart, &li.HighPart, FILE_BEGIN); if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error("file_win32_pwrite(%d,%u,buffer,%lu(%u/%u/%u)) seek err %s\n", (int)fd, (unsigned)(count/disk_car->sector_size), (long unsigned int)(offset/disk_car->sector_size), offset2cylinder(disk_car,offset), offset2head(disk_car,offset), offset2sector(disk_car,offset), (char*)lpMsgBuf); LocalFree(lpMsgBuf); return -1; } { DWORD dwByteRead; ret=WriteFile(fd, buf,count,&dwByteRead,NULL); if(ret) ret=dwByteRead; } disk_car->write_used=1; if(ret!=(signed)count) { log_error("file_win32_pwrite(%u,%u,buffer,%lu(%u/%u/%u)) write err\n", (int)fd, (unsigned)(count/disk_car->sector_size), (long unsigned)(offset/disk_car->sector_size), offset2cylinder(disk_car,offset),offset2head(disk_car,offset),offset2sector(disk_car,offset)); } return ret; } static int file_win32_pwrite(disk_t *disk_car, const void *buf, const unsigned int count, const uint64_t offset) { return align_pwrite(&file_win32_pread_aux, &file_win32_pwrite_aux, disk_car, buf, count, offset); } static int file_win32_nopwrite(disk_t *disk_car, const void *buf, const unsigned int count, const uint64_t offset) { const struct info_file_win32_struct *data=(const struct info_file_win32_struct *)disk_car->data; log_warning("file_win32_nopwrite(%d,%u,buffer,%lu(%u/%u/%u)) write refused\n", (unsigned int)data->handle, (unsigned)(count/disk_car->sector_size),(long unsigned)(offset/disk_car->sector_size), offset2cylinder(disk_car,offset),offset2head(disk_car,offset),offset2sector(disk_car,offset)); return -1; } static int file_win32_sync(disk_t *disk_car) { const struct info_file_win32_struct *data=(const struct info_file_win32_struct *)disk_car->data; if(FlushFileBuffers(data->handle)==0) { errno=EINVAL; return -1; } errno=0; return 0; } #endif