165 lines
5.1 KiB
C++
165 lines
5.1 KiB
C++
//
|
|
// 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;
|
|
|
|
cout << "Entering FindAlignment()\n";
|
|
#if defined (__linux__) && defined (BLKPBSZGET)
|
|
err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
|
|
cout << "In FindAlignment(), physicalSectorSize = " << physicalSectorSize
|
|
<< ", err = " << err << "\n";
|
|
#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)
|