diff --git a/Makefile.mingw b/Makefile.mingw new file mode 100644 index 0000000000000000000000000000000000000000..0976a5a74e76fd3490b3a80e644aac8d9290f103 --- /dev/null +++ b/Makefile.mingw @@ -0,0 +1,37 @@ +CC=/usr/bin/i586-mingw32msvc-gcc +CXX=/usr/bin/i586-mingw32msvc-g++ +CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g +CXXFLAGS=-O2 -DMINGW -Wuninitialized -Wreturn-type -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g +LIB_NAMES=gptpart bsd parttypes attributes crc32 mbr gpt support diskio diskio-windows +LIB_SRCS=$(NAMES:=.cc) +LIB_OBJS=$(LIB_NAMES:=.o) +LIB_HEADERS=$(LIB_NAMES:=.h) +DEPEND= makedepend $(CFLAGS) + +#$(APPNAME): $(MBR2GPT_OBJS) +# $(CC) $(MBR2GPT_OBJS) -o $@ + +all: gdisk + +gdisk: $(LIB_OBJS) gdisk.o + $(CXX) $(LIB_OBJS) gdisk.o -o gdisk.exe + +sgdisk: $(LIB_OBJS) sgdisk.o + $(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -L/usr/local/lib -lpopt -o sgdisk + +wipegpt: $(LIB_OBJS) wipegpt.o + $(CXX) $(LIB_OBJS) wipegpt.o -o wipegpt + +lint: #no pre-reqs + lint $(SRCS) + +clean: #no pre-reqs + rm -f core *.o *~ gdisk sgdisk + +# what are the source dependencies +depend: $(SRCS) + $(DEPEND) $(SRCS) + +$(OBJS): + +# DO NOT DELETE diff --git a/diskio-unix.cc b/diskio-unix.cc new file mode 100644 index 0000000000000000000000000000000000000000..573af5638f98c3520e3f182920e6aa21c9410c66 --- /dev/null +++ b/diskio-unix.cc @@ -0,0 +1,366 @@ +// +// C++ Interface: diskio (Unix components [Linux, FreeBSD, Mac OS X]) +// +// Description: Class to handle low-level disk I/O for GPT fdisk +// +// +// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed +// under the terms of the GNU GPL version 2, as detailed in the COPYING file. + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <sys/ioctl.h> +#include <stdio.h> +#include <string> +#include <stdint.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <iostream> + +#include "support.h" +#include "diskio.h" + +using namespace std; + +// Returns the official "real" name for a shortened version of same. +// Trivial here; more important in Windows +void DiskIO::MakeRealName(void) { + realFilename = userFilename; +} // DiskIO::MakeRealName() + +// Open the currently on-record file for reading +int DiskIO::OpenForRead(void) { + int shouldOpen = 1; + + if (isOpen) { // file is already open + if (openForWrite) { + Close(); + } else { + shouldOpen = 0; + } // if/else + } // if + + if (shouldOpen) { + fd = open(realFilename.c_str(), O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Problem opening %s for reading! Error is %d\n", + realFilename.c_str(), errno); + if (errno == EACCES) { // User is probably not running as root + fprintf(stderr, "You must run this program as root or use sudo!\n"); + } // if + realFilename = ""; + userFilename = ""; + isOpen = 0; + openForWrite = 0; + } else { + isOpen = 1; + openForWrite = 0; + } // if/else + } // if + + return isOpen; +} // DiskIO::OpenForRead(void) + +// An extended file-open function. This includes some system-specific checks. +// Returns 1 if the file is open, 0 otherwise.... +int DiskIO::OpenForWrite(void) { + if ((isOpen) && (openForWrite)) + return 1; + + // Close the disk, in case it's already open for reading only.... + Close(); + + // try to open the device; may fail.... + fd = open(realFilename.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); +#ifdef __APPLE__ + // MacOS X requires a shared lock under some circumstances.... + if (fd < 0) { + fd = open(realFilename.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH | O_SHLOCK); + } // if +#endif + if (fd >= 0) { + isOpen = 1; + openForWrite = 1; + } else { + isOpen = 0; + openForWrite = 0; + } // if/else + return isOpen; +} // DiskIO::OpenForWrite(void) + +// Close the disk device. Note that this does NOT erase the stored filenames, +// so the file can be re-opened without specifying the filename. +void DiskIO::Close(void) { + if (isOpen) + close(fd); + isOpen = 0; + openForWrite = 0; +} // DiskIO::Close() + +// Returns block size of device pointed to by fd file descriptor. If the ioctl +// returns an error condition, print a warning but return a value of SECTOR_SIZE +// (512).. +int DiskIO::GetBlockSize(void) { + int err = -1, blockSize = 0; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { +#ifdef __APPLE__ + err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize); +#endif +#ifdef __FreeBSD__ + err = ioctl(fd, DIOCGSECTORSIZE, &blockSize); +#endif +#ifdef __linux__ + err = ioctl(fd, BLKSSZGET, &blockSize); +#endif + + if (err == -1) { + blockSize = SECTOR_SIZE; + // ENOTTY = inappropriate ioctl; probably being called on a disk image + // file, so don't display the warning message.... + // 32-bit code returns EINVAL, I don't know why. I know I'm treading on + // thin ice here, but it should be OK in all but very weird cases.... + if ((errno != ENOTTY) && (errno != EINVAL)) { + printf("\aError %d when determining sector size! Setting sector size to %d\n", + errno, SECTOR_SIZE); + } // if + } // if (err == -1) + } // if (isOpen) + + return (blockSize); +} // DiskIO::GetBlockSize() + +// Resync disk caches so the OS uses the new partition table. This code varies +// a lot from one OS to another. +void DiskIO::DiskSync(void) { + int i, platformFound = 0; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { + sync(); +#ifdef __APPLE__ + printf("Warning: The kernel may continue to use old or deleted partitions.\n" + "You should reboot or remove the drive.\n"); + /* don't know if this helps + * it definitely will get things on disk though: + * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */ + i = ioctl(fd, DKIOCSYNCHRONIZECACHE); + platformFound++; +#endif +#ifdef __FreeBSD__ + sleep(2); + i = ioctl(fd, DIOCGFLUSH); + printf("Warning: The kernel may continue to use old or deleted partitions.\n" + "You should reboot or remove the drive.\n"); + platformFound++; +#endif +#ifdef __linux__ + sleep(2); + i = ioctl(fd, BLKRRPART); + if (i) + printf("Warning: The kernel is still using the old partition table.\n" + "The new table will be used at the next reboot.\n"); + platformFound++; +#endif + if (platformFound == 0) + fprintf(stderr, "Warning: Platform not recognized!\n"); + if (platformFound > 1) + fprintf(stderr, "\nWarning: We seem to be running on multiple platforms!\n"); + } // if (isOpen) +} // DiskIO::DiskSync() + +// Seek to the specified sector. Returns 1 on success, 0 on failure. +int DiskIO::Seek(uint64_t sector) { + int retval = 1; + off_t seekTo, sought; + + // If disk isn't open, try to open it.... + if (!isOpen) { + retval = OpenForRead(); + } // if + + if (isOpen) { + seekTo = sector * (uint64_t) GetBlockSize(); + sought = lseek64(fd, seekTo, SEEK_SET); + if (sought != seekTo) { + retval = 0; + } // if + } // if + return retval; +} // DiskIO::Seek() + +// A variant on the standard read() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read. +// Returns the number of bytes read into buffer. +int DiskIO::Read(void* buffer, int numBytes) { + int blockSize = 512, i, numBlocks, retval = 0; + char* tempSpace; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { + // Compute required space and allocate memory + blockSize = GetBlockSize(); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Read the data into temporary space, then copy it to buffer + retval = read(fd, tempSpace, numBlocks * blockSize); + memcpy(buffer, tempSpace, numBytes); +/* for (i = 0; i < numBytes; i++) { + ((char*) buffer)[i] = tempSpace[i]; + } // for */ + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + } // if (isOpen) + return retval; +} // DiskIO::Read() + +// A variant on the standard write() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read. +// Returns the number of bytes written. +int DiskIO::Write(void* buffer, int numBytes) { + int blockSize = 512, i, numBlocks, retval = 0; + char* tempSpace; + + // If disk isn't open, try to open it.... + if ((!isOpen) || (!openForWrite)) { + OpenForWrite(); + } // if + + if (isOpen) { + // Compute required space and allocate memory + blockSize = GetBlockSize(); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Copy the data to my own buffer, then write it +/* for (i = 0; i < numBytes; i++) { + tempSpace[i] = ((char*) buffer)[i]; + } // for */ + memcpy(tempSpace, buffer, numBytes); + for (i = numBytes; i < numBlocks * blockSize; i++) { + tempSpace[i] = 0; + } // for + retval = write(fd, tempSpace, numBlocks * blockSize); + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + } // if (isOpen) + return retval; +} // DiskIO:Write() + +/************************************************************************************** + * * + * Below functions are lifted from various sources, as documented in comments before * + * each one. * + * * + **************************************************************************************/ + +// The disksize function is taken from the Linux fdisk code and modified +// greatly since then to enable FreeBSD and MacOS support, as well as to +// return correct values for disk image files. +uint64_t DiskIO::DiskSize(int *err) { + long sz; // Do not delete; needed for Linux + long long b; // Do not delete; needed for Linux + uint64_t sectors = 0; // size in sectors + off_t bytes = 0; // size in bytes + struct stat64 st; + int platformFound = 0; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { + // Note to self: I recall testing a simplified version of + // this code, similar to what's in the __APPLE__ block, + // on Linux, but I had some problems. IIRC, it ran OK on 32-bit + // systems but not on 64-bit. Keep this in mind in case of + // 32/64-bit issues on MacOS.... +#ifdef __APPLE__ + *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §ors); + platformFound++; +#endif +#ifdef __FreeBSD__ + *err = ioctl(fd, DIOCGMEDIASIZE, &bytes); + b = GetBlockSize(); + sectors = bytes / b; + platformFound++; +#endif +#ifdef __linux__ + *err = ioctl(fd, BLKGETSIZE, &sz); + if (*err) { + sectors = sz = 0; + } // if + if ((errno == EFBIG) || (!*err)) { + *err = ioctl(fd, BLKGETSIZE64, &b); + if (*err || b == 0 || b == sz) + sectors = sz; + else + sectors = (b >> 9); + } // if + // Unintuitively, the above returns values in 512-byte blocks, no + // matter what the underlying device's block size. Correct for this.... + sectors /= (GetBlockSize() / 512); + platformFound++; +#endif + if (platformFound != 1) + fprintf(stderr, "Warning! We seem to be running on no known platform!\n"); + + // The above methods have failed, so let's assume it's a regular + // file (a QEMU image, dd backup, or what have you) and see what + // fstat() gives us.... + if ((sectors == 0) || (*err == -1)) { + if (fstat64(fd, &st) == 0) { + bytes = (off_t) st.st_size; + if ((bytes % UINT64_C(512)) != 0) + fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!" + " Misbehavior is likely!\n\a"); + sectors = bytes / UINT64_C(512); + } // if + } // if + } // if (isOpen) + return sectors; +} // DiskIO::DiskSize() diff --git a/diskio-windows.cc b/diskio-windows.cc new file mode 100644 index 0000000000000000000000000000000000000000..84b77eeed6e79534a57b5042f980c4362451c2ef --- /dev/null +++ b/diskio-windows.cc @@ -0,0 +1,345 @@ +// +// C++ Interface: diskio (Windows-specific components) +// +// Description: Class to handle low-level disk I/O for GPT fdisk +// +// +// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +// This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed +// under the terms of the GNU GPL version 2, as detailed in the COPYING file. + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <windows.h> +#include <winioctl.h> +#define fstat64 fstat +#define stat64 stat +#define S_IRGRP 0 +#define S_IROTH 0 +#include <stdio.h> +#include <string> +#include <stdint.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <iostream> + +#include "support.h" +#include "diskio.h" + +using namespace std; + +// Returns the official Windows name for a shortened version of same. +void DiskIO::MakeRealName(void) { + int colonPos; + + colonPos = userFilename.find(':', 0); + if ((colonPos != string::npos) && (colonPos <= 3)) { + realFilename = "\\\\.\\physicaldrive"; + realFilename += userFilename.substr(0, colonPos); + } // if/else + printf("Exiting DiskIO::MakeRealName(); translated '%s' ", userFilename.c_str()); + printf("to '%s'\n", realFilename.c_str()); +} // DiskIO::MakeRealName() + +// Open the currently on-record file for reading +int DiskIO::OpenForRead(void) { + int shouldOpen = 1; + + if (isOpen) { // file is already open + if (openForWrite) { + Close(); + } else { + shouldOpen = 0; + } // if/else + } // if + + if (shouldOpen) { + printf("Opening '%s' for reading.\n", realFilename.c_str()); + fd = CreateFile(realFilename.c_str(),GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) { + CloseHandle(fd); + fprintf(stderr, "Problem opening %s for reading!\n", realFilename.c_str()); + realFilename = ""; + userFilename = ""; + isOpen = 0; + openForWrite = 0; + } else { + isOpen = 1; + openForWrite = 0; + } // if/else + } // if + + return isOpen; +} // DiskIO::OpenForRead(void) + +// An extended file-open function. This includes some system-specific checks. +// Returns 1 if the file is open, 0 otherwise.... +int DiskIO::OpenForWrite(void) { + if ((isOpen) && (openForWrite)) + return 1; + + // Close the disk, in case it's already open for reading only.... + Close(); + + // try to open the device; may fail.... + fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) { + CloseHandle(fd); + isOpen = 1; + openForWrite = 1; + } else { + isOpen = 0; + openForWrite = 0; + } // if/else + return isOpen; +} // DiskIO::OpenForWrite(void) + +// Close the disk device. Note that this does NOT erase the stored filenames, +// so the file can be re-opened without specifying the filename. +void DiskIO::Close(void) { + if (isOpen) + CloseHandle(fd); + isOpen = 0; + openForWrite = 0; +} // DiskIO::Close() + +// Returns block size of device pointed to by fd file descriptor. If the ioctl +// returns an error condition, print a warning but return a value of SECTOR_SIZE +// (512).. +int DiskIO::GetBlockSize(void) { + int err; + DWORD blockSize, junk1, junk2, junk3; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { +/* BOOL WINAPI GetDiskFreeSpace( + __in LPCTSTR lpRootPathName, + __out LPDWORD lpSectorsPerCluster, + __out LPDWORD lpBytesPerSector, + __out LPDWORD lpNumberOfFreeClusters, + __out LPDWORD lpTotalNumberOfClusters + ); */ +// err = GetDiskFreeSpace(realFilename.c_str(), &junk1, &blockSize, &junk2, &junk3); + err = 1; + blockSize = 512; + + if (err == 0) { + blockSize = SECTOR_SIZE; + // ENOTTY = inappropriate ioctl; probably being called on a disk image + // file, so don't display the warning message.... + // 32-bit code returns EINVAL, I don't know why. I know I'm treading on + // thin ice here, but it should be OK in all but very weird cases.... + if ((errno != ENOTTY) && (errno != EINVAL)) { + printf("\aError %d when determining sector size! Setting sector size to %d\n", + GetLastError(), SECTOR_SIZE); + } // if + } // if (err == -1) + } // if (isOpen) + + return (blockSize); +} // DiskIO::GetBlockSize() + +// Resync disk caches so the OS uses the new partition table. This code varies +// a lot from one OS to another. +void DiskIO::DiskSync(void) { + int i; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { +#ifndef MINGW + sync(); +#endif +#ifdef MINGW + printf("Warning: I don't know how to sync disks in Windows! The old partition table is\n" + "probably still in use!\n"); +#endif + } // if (isOpen) +} // DiskIO::DiskSync() + +// Seek to the specified sector. Returns 1 on success, 0 on failure. +int DiskIO::Seek(uint64_t sector) { + int retval = 1; + LARGE_INTEGER seekTo; + uint32_t lowBits, highBits; + uint64_t bytePos; + + // If disk isn't open, try to open it.... + if (!isOpen) { + retval = OpenForRead(); + } // if + + if (isOpen) { + bytePos = sector * (uint64_t) GetBlockSize(); + lowBits = (uint32_t) (bytePos / UINT64_C(4294967296)); + highBits = (uint32_t) (bytePos % UINT64_C(4294967296)); + seekTo.LowPart = lowBits; + seekTo.HighPart = highBits; +// seekTo.QuadPart = (LONGLONG) (sector * (uint64_t) GetBlockSize()); +/* printf("In DiskIO::Seek(), sector = %llu, ", sector); + printf("block size = %d, ", GetBlockSize()); + printf("seekTo.QuadPart = %lld\n", seekTo.QuadPart); + printf(" seekTo.LowPart = %lu, ", seekTo.LowPart); + printf("seekTo.HighPart = %lu\n", seekTo.HighPart); */ + retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN); + if (retval == 0) { + errno = GetLastError(); + fprintf(stderr, "Error when seeking to %lld! Error is %d\n", + seekTo.QuadPart, errno); + retval = 0; + } // if + } // if + return retval; +} // DiskIO::Seek() + +// A variant on the standard read() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read. +// Returns the number of bytes read into buffer. +int DiskIO::Read(void* buffer, int numBytes) { + int blockSize = 512, i, numBlocks; + char* tempSpace; + DWORD retval = 0; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { + // Compute required space and allocate memory + blockSize = GetBlockSize(); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Read the data into temporary space, then copy it to buffer +// retval = read(fd, tempSpace, numBlocks * blockSize); + ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL); + printf("In DiskIO::Read(), have read %d bytes.\n", (int) retval); + for (i = 0; i < numBytes; i++) { + ((char*) buffer)[i] = tempSpace[i]; + } // for + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + } // if (isOpen) + return retval; +} // DiskIO::Read() + +// A variant on the standard write() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read. +// Returns the number of bytes written. +int DiskIO::Write(void* buffer, int numBytes) { + int blockSize = 512, i, numBlocks, retval = 0; + char* tempSpace; + DWORD numWritten; + + // If disk isn't open, try to open it.... + if ((!isOpen) || (!openForWrite)) { + OpenForWrite(); + } // if + + if (isOpen) { + // Compute required space and allocate memory + blockSize = GetBlockSize(); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Copy the data to my own buffer, then write it + for (i = 0; i < numBytes; i++) { + tempSpace[i] = ((char*) buffer)[i]; + } // for + for (i = numBytes; i < numBlocks * blockSize; i++) { + tempSpace[i] = 0; + } // for +// retval = write(fd, tempSpace, numBlocks * blockSize); + WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL); + retval = (int) numWritten; + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + } // if (isOpen) + return retval; +} // DiskIO:Write() + +// Returns the size of the disk in blocks. +uint64_t DiskIO::DiskSize(int *err) { + uint64_t sectors = 0; // size in sectors + off_t bytes = 0; // size in bytes + struct stat64 st; + GET_LENGTH_INFORMATION buf; + DWORD i; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { + // Note to self: I recall testing a simplified version of + // this code, similar to what's in the __APPLE__ block, + // on Linux, but I had some problems. IIRC, it ran OK on 32-bit + // systems but not on 64-bit. Keep this in mind in case of + // 32/64-bit issues on MacOS.... +/* HANDLE fin; + fin = CreateFile(realFilename.c_str(), GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); */ + if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) { + sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize(); +// printf("disk_get_size_win32 IOCTL_DISK_GET_LENGTH_INFO = %llu\n", +// (long long unsigned) sectors); + } else { + fprintf(stderr, "Couldn't determine disk size!\n"); + } + +/* // The above methods have failed, so let's assume it's a regular + // file (a QEMU image, dd backup, or what have you) and see what + // fstat() gives us.... + if ((sectors == 0) || (*err == -1)) { + if (fstat64(fd, &st) == 0) { + bytes = (off_t) st.st_size; + if ((bytes % UINT64_C(512)) != 0) + fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!" + " Misbehavior is likely!\n\a"); + sectors = bytes / UINT64_C(512); + } // if + } // if */ + } // if (isOpen) + return sectors; +} // DiskIO::DiskSize() diff --git a/diskio.cc b/diskio.cc new file mode 100644 index 0000000000000000000000000000000000000000..3fc3726b53968125c98726548dc2af8463a43e3e --- /dev/null +++ b/diskio.cc @@ -0,0 +1,164 @@ +// +// C++ Interface: diskio (platform-independent components) +// +// Description: Class to handle low-level disk I/O for GPT fdisk +// +// +// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed +// under the terms of the GNU GPL version 2, as detailed in the COPYING file. + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#ifdef MINGW +#include <windows.h> +#include <winioctl.h> +#define fstat64 fstat +#define stat64 stat +#define S_IRGRP 0 +#define S_IROTH 0 +#else +#include <sys/ioctl.h> +#endif +#include <stdio.h> +#include <string> +#include <stdint.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <iostream> + +#include "support.h" +#include "diskio.h" + +using namespace std; + +DiskIO::DiskIO(void) { + userFilename = ""; + realFilename = ""; + isOpen = 0; + openForWrite = 0; + sectorData = NULL; +} // constructor + +DiskIO::~DiskIO(void) { + Close(); + free(sectorData); +} // destructor + +// Open a disk device for reading. Returns 1 on success, 0 on failure. +int DiskIO::OpenForRead(string filename) { + int shouldOpen = 1; + + if (isOpen) { // file is already open + if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) { + Close(); + } else { + shouldOpen = 0; + } // if/else + } // if + + if (shouldOpen) { + userFilename = filename; + MakeRealName(); + OpenForRead(); + } // if + + return isOpen; +} // DiskIO::OpenForRead(string filename) + +// Open a disk for reading and writing by filename. +// Returns 1 on success, 0 on failure. +int DiskIO::OpenForWrite(string filename) { + int retval = 0; + + if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) { + retval = 1; + } else { + userFilename = filename; + MakeRealName(); + retval = OpenForWrite(); + if (retval == 0) { + realFilename = userFilename = ""; + } // if + } // if/else + return retval; +} // DiskIO::OpenForWrite(string filename) + +// My original FindAlignment() function (after this one) isn't working, since +// the BLKPBSZGET ioctl() isn't doing what I expected (it returns 512 even on +// a WD Advanced Format drive). Therefore, I'm using a simpler function that +// returns 1-sector alignment for unusual sector sizes and drives smaller than +// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for +// larger drives with 512-byte sectors. +int DiskIO::FindAlignment(void) { + int err, result; + + if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) { + result = 8; // play it safe; align for 4096-byte sectors + } else { + result = 1; // unusual sector size; assume it's the real physical size + } // if/else + return result; +} // DiskIO::FindAlignment + +// Return the partition alignment value in sectors. Right now this works +// only for Linux 2.6.32 and later, since I can't find equivalent ioctl()s +// for OS X or FreeBSD, and the Linux ioctl is new +/* int DiskIO::FindAlignment(int fd) { + int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096; + uint64_t diskSize; + + printf("Entering FindAlignment()\n"); +#if defined (__linux__) && defined (BLKPBSZGET) + err = ioctl(fd, BLKPBSZGET, &physicalSectorSize); + printf("In FindAlignment(), physicalSectorSize = %d, err = %d\n", physicalSectorSize, err); +// printf("Tried to get hardware alignment; err is %d, sector size is %d\n", err, physicalSectorSize); +#else + err = -1; +#endif + + if (err < 0) { // ioctl didn't work; have to guess.... + if (GetBlockSize(fd) == 512) { + result = 8; // play it safe; align for 4096-byte sectors +} else { + result = 1; // unusual sector size; assume it's the real physical size +} // if/else +} else { // ioctl worked; compute alignment + result = physicalSectorSize / GetBlockSize(fd); + // Disks with larger physical than logical sectors must theoretically + // have a total disk size that's a multiple of the physical sector + // size; however, some such disks have compatibility jumper settings + // meant for one-partition MBR setups, and these reduce the total + // number of sectors by 1. If such a setting is used, it'll result + // in improper alignment, so look for this condition and warn the + // user if it's found.... + diskSize = disksize(fd, &errnum); + if ((diskSize % (uint64_t) result) != 0) { + fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n" + "size (%d), but it should be! Check disk manual and jumper settings!\n", + (unsigned long long) diskSize, result); +} // if +} // if/else + if (result <= 0) // can happen if physical sector size < logical sector size + result = 1; + return result; +} // DiskIO::FindAlignment(int) */ + +// The same as FindAlignment(int), but opens and closes a device by filename +int DiskIO::FindAlignment(string filename) { + int fd; + int retval = 1; + + if (!isOpen) + OpenForRead(filename); + if (isOpen) { + retval = FindAlignment(); + } // if + return retval; +} // DiskIO::FindAlignment(char) diff --git a/diskio.h b/diskio.h new file mode 100644 index 0000000000000000000000000000000000000000..2435cc542069390c3d059cae00c5e35915c0fd46 --- /dev/null +++ b/diskio.h @@ -0,0 +1,85 @@ +// +// C++ Interface: diskio +// +// Description: Class to handle low-level disk I/O for GPT fdisk +// +// +// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed +// under the terms of the GNU GPL version 2, as detailed in the COPYING file. + +#ifndef __DISKIO_H +#define __DISKIO_H + +#include <string> +#include <stdint.h> +#include <sys/types.h> +#ifdef MINGW +#include <windows.h> +#include <winioctl.h> +#else +#include <sys/ioctl.h> +#endif + +#if defined (__FreeBSD__) || defined (__APPLE__) +#define fstat64 fstat +#define stat64 stat +#endif + +#include "support.h" +#include "parttypes.h" + +using namespace std; + +// Below constant corresponds to an 800GB disk -- a somewhat arbitrary +// cutoff +#define SMALLEST_ADVANCED_FORMAT UINT64_C(1677721600) + +/*************************************** + * * + * DiskIO class and related structures * + * * + ***************************************/ + +class DiskIO { + protected: + string userFilename; + string realFilename; + int isOpen; + int openForWrite; + uint8_t *sectorData; +#ifdef MINGW + HANDLE fd; +#else + int fd; +#endif + public: + DiskIO(void); +// DiskIO(const DiskIO & orig); + ~DiskIO(void); + +// DiskIO & operator=(const DiskIO & orig); + void MakeRealName(void); + int OpenForRead(string filename); + int OpenForRead(void); + int OpenForWrite(string filename); + int OpenForWrite(void); + void Close(); + int Seek(uint64_t sector); + int Read(void* buffer, int numBytes); + int Write(void* buffer, int numBytes); + void DiskSync(void); // resync disk caches to use new partitions + int GetBlockSize(void); + int FindAlignment(void); + int FindAlignment(string filename); + int IsOpen(void) {return isOpen;} + int IsOpenForWrite(void) {return openForWrite;} + + uint64_t DiskSize(int* err); +}; // struct GPTPart + +#endif