New files to support I/O restructuring and (currently broken) Windows

version.
This commit is contained in:
srs5694
2010-01-26 15:59:58 -05:00
parent 7dbb932233
commit add79a6e1b
5 changed files with 997 additions and 0 deletions

37
Makefile.mingw Normal file
View File

@@ -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

366
diskio-unix.cc Normal file
View File

@@ -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, &sectors);
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()

345
diskio-windows.cc Normal file
View File

@@ -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()

164
diskio.cc Normal file
View File

@@ -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)

85
diskio.h Normal file
View File

@@ -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