1cf200d32Sopenharmony_ci// 2cf200d32Sopenharmony_ci// C++ Interface: diskio (Unix components [Linux, FreeBSD, Mac OS X]) 3cf200d32Sopenharmony_ci// 4cf200d32Sopenharmony_ci// Description: Class to handle low-level disk I/O for GPT fdisk 5cf200d32Sopenharmony_ci// 6cf200d32Sopenharmony_ci// 7cf200d32Sopenharmony_ci// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 8cf200d32Sopenharmony_ci// 9cf200d32Sopenharmony_ci// Copyright: See COPYING file that comes with this distribution 10cf200d32Sopenharmony_ci// 11cf200d32Sopenharmony_ci// 12cf200d32Sopenharmony_ci// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed 13cf200d32Sopenharmony_ci// under the terms of the GNU GPL version 2, as detailed in the COPYING file. 14cf200d32Sopenharmony_ci 15cf200d32Sopenharmony_ci#define __STDC_LIMIT_MACROS 16cf200d32Sopenharmony_ci#ifndef __STDC_CONSTANT_MACROS 17cf200d32Sopenharmony_ci#define __STDC_CONSTANT_MACROS 18cf200d32Sopenharmony_ci#endif 19cf200d32Sopenharmony_ci 20cf200d32Sopenharmony_ci#include <sys/ioctl.h> 21cf200d32Sopenharmony_ci#include <string.h> 22cf200d32Sopenharmony_ci#include <string> 23cf200d32Sopenharmony_ci#include <stdint.h> 24cf200d32Sopenharmony_ci#include <unistd.h> 25cf200d32Sopenharmony_ci#include <errno.h> 26cf200d32Sopenharmony_ci#include <fcntl.h> 27cf200d32Sopenharmony_ci#include <sys/stat.h> 28cf200d32Sopenharmony_ci#include <unistd.h> 29cf200d32Sopenharmony_ci 30cf200d32Sopenharmony_ci#ifdef __linux__ 31cf200d32Sopenharmony_ci#include "linux/hdreg.h" 32cf200d32Sopenharmony_ci#endif 33cf200d32Sopenharmony_ci 34cf200d32Sopenharmony_ci#include <iostream> 35cf200d32Sopenharmony_ci#include <fstream> 36cf200d32Sopenharmony_ci#include <sstream> 37cf200d32Sopenharmony_ci 38cf200d32Sopenharmony_ci#include "diskio.h" 39cf200d32Sopenharmony_ci 40cf200d32Sopenharmony_ciusing namespace std; 41cf200d32Sopenharmony_ci 42cf200d32Sopenharmony_ci#if defined(__APPLE__) || defined(__linux__) 43cf200d32Sopenharmony_ci#define off64_t off_t 44cf200d32Sopenharmony_ci#define stat64 stat 45cf200d32Sopenharmony_ci#define fstat64 fstat 46cf200d32Sopenharmony_ci#define lstat64 lstat 47cf200d32Sopenharmony_ci#define lseek64 lseek 48cf200d32Sopenharmony_ci#endif 49cf200d32Sopenharmony_ci 50cf200d32Sopenharmony_ci// Returns the official "real" name for a shortened version of same. 51cf200d32Sopenharmony_ci// Trivial here; more important in Windows 52cf200d32Sopenharmony_civoid DiskIO::MakeRealName(void) { 53cf200d32Sopenharmony_ci realFilename = userFilename; 54cf200d32Sopenharmony_ci} // DiskIO::MakeRealName() 55cf200d32Sopenharmony_ci 56cf200d32Sopenharmony_ci// Open the currently on-record file for reading. Returns 1 if the file is 57cf200d32Sopenharmony_ci// already open or is opened by this call, 0 if opening the file doesn't 58cf200d32Sopenharmony_ci// work. 59cf200d32Sopenharmony_ciint DiskIO::OpenForRead(void) { 60cf200d32Sopenharmony_ci int shouldOpen = 1; 61cf200d32Sopenharmony_ci struct stat64 st; 62cf200d32Sopenharmony_ci 63cf200d32Sopenharmony_ci if (isOpen) { // file is already open 64cf200d32Sopenharmony_ci if (openForWrite) { 65cf200d32Sopenharmony_ci Close(); 66cf200d32Sopenharmony_ci } else { 67cf200d32Sopenharmony_ci shouldOpen = 0; 68cf200d32Sopenharmony_ci } // if/else 69cf200d32Sopenharmony_ci } // if 70cf200d32Sopenharmony_ci 71cf200d32Sopenharmony_ci if (shouldOpen) { 72cf200d32Sopenharmony_ci fd = open(realFilename.c_str(), O_RDONLY); 73cf200d32Sopenharmony_ci if (fd == -1) { 74cf200d32Sopenharmony_ci cerr << "Problem opening " << realFilename << " for reading! Error is " << errno << ".\n"; 75cf200d32Sopenharmony_ci if (errno == EACCES) // User is probably not running as root 76cf200d32Sopenharmony_ci cerr << "You must run this program as root or use sudo!\n"; 77cf200d32Sopenharmony_ci if (errno == ENOENT) 78cf200d32Sopenharmony_ci cerr << "The specified file does not exist!\n"; 79cf200d32Sopenharmony_ci realFilename = ""; 80cf200d32Sopenharmony_ci userFilename = ""; 81cf200d32Sopenharmony_ci modelName = ""; 82cf200d32Sopenharmony_ci isOpen = 0; 83cf200d32Sopenharmony_ci openForWrite = 0; 84cf200d32Sopenharmony_ci } else { 85cf200d32Sopenharmony_ci isOpen = 0; 86cf200d32Sopenharmony_ci openForWrite = 0; 87cf200d32Sopenharmony_ci if (fstat64(fd, &st) == 0) { 88cf200d32Sopenharmony_ci if (S_ISDIR(st.st_mode)) 89cf200d32Sopenharmony_ci cerr << "The specified path is a directory!\n"; 90cf200d32Sopenharmony_ci#if !(defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) \ 91cf200d32Sopenharmony_ci && !defined(__APPLE__) 92cf200d32Sopenharmony_ci else if (S_ISCHR(st.st_mode)) 93cf200d32Sopenharmony_ci cerr << "The specified path is a character device!\n"; 94cf200d32Sopenharmony_ci#endif 95cf200d32Sopenharmony_ci else if (S_ISFIFO(st.st_mode)) 96cf200d32Sopenharmony_ci cerr << "The specified path is a FIFO!\n"; 97cf200d32Sopenharmony_ci else if (S_ISSOCK(st.st_mode)) 98cf200d32Sopenharmony_ci cerr << "The specified path is a socket!\n"; 99cf200d32Sopenharmony_ci else 100cf200d32Sopenharmony_ci isOpen = 1; 101cf200d32Sopenharmony_ci } // if (fstat64()...) 102cf200d32Sopenharmony_ci#if defined(__linux__) && !defined(EFI) 103cf200d32Sopenharmony_ci if (isOpen && realFilename.substr(0,4) == "/dev") { 104cf200d32Sopenharmony_ci ostringstream modelNameFilename; 105cf200d32Sopenharmony_ci modelNameFilename << "/sys/block" << realFilename.substr(4,512) << "/device/model"; 106cf200d32Sopenharmony_ci ifstream modelNameFile(modelNameFilename.str().c_str()); 107cf200d32Sopenharmony_ci if (modelNameFile.is_open()) { 108cf200d32Sopenharmony_ci getline(modelNameFile, modelName); 109cf200d32Sopenharmony_ci } // if 110cf200d32Sopenharmony_ci } // if 111cf200d32Sopenharmony_ci#endif 112cf200d32Sopenharmony_ci } // if/else 113cf200d32Sopenharmony_ci } // if 114cf200d32Sopenharmony_ci 115cf200d32Sopenharmony_ci return isOpen; 116cf200d32Sopenharmony_ci} // DiskIO::OpenForRead(void) 117cf200d32Sopenharmony_ci 118cf200d32Sopenharmony_ci// An extended file-open function. This includes some system-specific checks. 119cf200d32Sopenharmony_ci// Returns 1 if the file is open, 0 otherwise.... 120cf200d32Sopenharmony_ciint DiskIO::OpenForWrite(void) { 121cf200d32Sopenharmony_ci if ((isOpen) && (openForWrite)) 122cf200d32Sopenharmony_ci return 1; 123cf200d32Sopenharmony_ci 124cf200d32Sopenharmony_ci // Close the disk, in case it's already open for reading only.... 125cf200d32Sopenharmony_ci Close(); 126cf200d32Sopenharmony_ci 127cf200d32Sopenharmony_ci // try to open the device; may fail.... 128cf200d32Sopenharmony_ci fd = open(realFilename.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); 129cf200d32Sopenharmony_ci#ifdef __APPLE__ 130cf200d32Sopenharmony_ci // MacOS X requires a shared lock under some circumstances.... 131cf200d32Sopenharmony_ci if (fd < 0) { 132cf200d32Sopenharmony_ci cerr << "Warning: Devices opened with shared lock will not have their\npartition table automatically reloaded!\n"; 133cf200d32Sopenharmony_ci fd = open(realFilename.c_str(), O_WRONLY | O_SHLOCK); 134cf200d32Sopenharmony_ci } // if 135cf200d32Sopenharmony_ci#endif 136cf200d32Sopenharmony_ci if (fd >= 0) { 137cf200d32Sopenharmony_ci isOpen = 1; 138cf200d32Sopenharmony_ci openForWrite = 1; 139cf200d32Sopenharmony_ci } else { 140cf200d32Sopenharmony_ci isOpen = 0; 141cf200d32Sopenharmony_ci openForWrite = 0; 142cf200d32Sopenharmony_ci } // if/else 143cf200d32Sopenharmony_ci return isOpen; 144cf200d32Sopenharmony_ci} // DiskIO::OpenForWrite(void) 145cf200d32Sopenharmony_ci 146cf200d32Sopenharmony_ci// Close the disk device. Note that this does NOT erase the stored filenames, 147cf200d32Sopenharmony_ci// so the file can be re-opened without specifying the filename. 148cf200d32Sopenharmony_civoid DiskIO::Close(void) { 149cf200d32Sopenharmony_ci if (isOpen) 150cf200d32Sopenharmony_ci if (close(fd) < 0) 151cf200d32Sopenharmony_ci cerr << "Warning! Problem closing file!\n"; 152cf200d32Sopenharmony_ci isOpen = 0; 153cf200d32Sopenharmony_ci openForWrite = 0; 154cf200d32Sopenharmony_ci} // DiskIO::Close() 155cf200d32Sopenharmony_ci 156cf200d32Sopenharmony_ci// Returns block size of device pointed to by fd file descriptor. If the ioctl 157cf200d32Sopenharmony_ci// returns an error condition, print a warning but return a value of SECTOR_SIZE 158cf200d32Sopenharmony_ci// (512). If the disk can't be opened at all, return a value of 0. 159cf200d32Sopenharmony_ciint DiskIO::GetBlockSize(void) { 160cf200d32Sopenharmony_ci int err = -1, blockSize = 0; 161cf200d32Sopenharmony_ci#ifdef __sun__ 162cf200d32Sopenharmony_ci struct dk_minfo minfo; 163cf200d32Sopenharmony_ci#endif 164cf200d32Sopenharmony_ci 165cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 166cf200d32Sopenharmony_ci if (!isOpen) { 167cf200d32Sopenharmony_ci OpenForRead(); 168cf200d32Sopenharmony_ci } // if 169cf200d32Sopenharmony_ci 170cf200d32Sopenharmony_ci if (isOpen) { 171cf200d32Sopenharmony_ci#ifdef __APPLE__ 172cf200d32Sopenharmony_ci err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize); 173cf200d32Sopenharmony_ci#endif 174cf200d32Sopenharmony_ci#ifdef __sun__ 175cf200d32Sopenharmony_ci err = ioctl(fd, DKIOCGMEDIAINFO, &minfo); 176cf200d32Sopenharmony_ci if (err == 0) 177cf200d32Sopenharmony_ci blockSize = minfo.dki_lbsize; 178cf200d32Sopenharmony_ci#endif 179cf200d32Sopenharmony_ci#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) 180cf200d32Sopenharmony_ci err = ioctl(fd, DIOCGSECTORSIZE, &blockSize); 181cf200d32Sopenharmony_ci#endif 182cf200d32Sopenharmony_ci#ifdef __linux__ 183cf200d32Sopenharmony_ci err = ioctl(fd, BLKSSZGET, &blockSize); 184cf200d32Sopenharmony_ci#endif 185cf200d32Sopenharmony_ci 186cf200d32Sopenharmony_ci if (err == -1) { 187cf200d32Sopenharmony_ci blockSize = SECTOR_SIZE; 188cf200d32Sopenharmony_ci // ENOTTY = inappropriate ioctl; probably being called on a disk image 189cf200d32Sopenharmony_ci // file, so don't display the warning message.... 190cf200d32Sopenharmony_ci // 32-bit code returns EINVAL, I don't know why. I know I'm treading on 191cf200d32Sopenharmony_ci // thin ice here, but it should be OK in all but very weird cases.... 192cf200d32Sopenharmony_ci if ((errno != ENOTTY) && (errno != EINVAL)) { 193cf200d32Sopenharmony_ci cerr << "\aError " << errno << " when determining sector size! Setting sector size to " 194cf200d32Sopenharmony_ci << SECTOR_SIZE << "\n"; 195cf200d32Sopenharmony_ci cout << "Disk device is " << realFilename << "\n"; 196cf200d32Sopenharmony_ci } // if 197cf200d32Sopenharmony_ci } // if (err == -1) 198cf200d32Sopenharmony_ci } // if (isOpen) 199cf200d32Sopenharmony_ci 200cf200d32Sopenharmony_ci return (blockSize); 201cf200d32Sopenharmony_ci} // DiskIO::GetBlockSize() 202cf200d32Sopenharmony_ci 203cf200d32Sopenharmony_ci// Returns the physical block size of the device, if possible. If this is 204cf200d32Sopenharmony_ci// not supported, or if an error occurs, this function returns 0. 205cf200d32Sopenharmony_ci// TODO: Get this working in more OSes than Linux. 206cf200d32Sopenharmony_ciint DiskIO::GetPhysBlockSize(void) { 207cf200d32Sopenharmony_ci int err = -1, physBlockSize = 0; 208cf200d32Sopenharmony_ci 209cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 210cf200d32Sopenharmony_ci if (!isOpen) { 211cf200d32Sopenharmony_ci OpenForRead(); 212cf200d32Sopenharmony_ci } // if 213cf200d32Sopenharmony_ci 214cf200d32Sopenharmony_ci if (isOpen) { 215cf200d32Sopenharmony_ci#if defined __linux__ && !defined(EFI) 216cf200d32Sopenharmony_ci err = ioctl(fd, BLKPBSZGET, &physBlockSize); 217cf200d32Sopenharmony_ci#endif 218cf200d32Sopenharmony_ci } // if (isOpen) 219cf200d32Sopenharmony_ci if (err == -1) 220cf200d32Sopenharmony_ci physBlockSize = 0; 221cf200d32Sopenharmony_ci return (physBlockSize); 222cf200d32Sopenharmony_ci} // DiskIO::GetPhysBlockSize(void) 223cf200d32Sopenharmony_ci 224cf200d32Sopenharmony_ci// Returns the number of heads, according to the kernel, or 255 if the 225cf200d32Sopenharmony_ci// correct value can't be determined. 226cf200d32Sopenharmony_ciuint32_t DiskIO::GetNumHeads(void) { 227cf200d32Sopenharmony_ci uint32_t numHeads = 255; 228cf200d32Sopenharmony_ci 229cf200d32Sopenharmony_ci#ifdef HDIO_GETGEO 230cf200d32Sopenharmony_ci struct hd_geometry geometry; 231cf200d32Sopenharmony_ci 232cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 233cf200d32Sopenharmony_ci if (!isOpen) 234cf200d32Sopenharmony_ci OpenForRead(); 235cf200d32Sopenharmony_ci 236cf200d32Sopenharmony_ci if (!ioctl(fd, HDIO_GETGEO, &geometry)) 237cf200d32Sopenharmony_ci numHeads = (uint32_t) geometry.heads; 238cf200d32Sopenharmony_ci#endif 239cf200d32Sopenharmony_ci return numHeads; 240cf200d32Sopenharmony_ci} // DiskIO::GetNumHeads(); 241cf200d32Sopenharmony_ci 242cf200d32Sopenharmony_ci// Returns the number of sectors per track, according to the kernel, or 63 243cf200d32Sopenharmony_ci// if the correct value can't be determined. 244cf200d32Sopenharmony_ciuint32_t DiskIO::GetNumSecsPerTrack(void) { 245cf200d32Sopenharmony_ci uint32_t numSecs = 63; 246cf200d32Sopenharmony_ci 247cf200d32Sopenharmony_ci #ifdef HDIO_GETGEO 248cf200d32Sopenharmony_ci struct hd_geometry geometry; 249cf200d32Sopenharmony_ci 250cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 251cf200d32Sopenharmony_ci if (!isOpen) 252cf200d32Sopenharmony_ci OpenForRead(); 253cf200d32Sopenharmony_ci 254cf200d32Sopenharmony_ci if (!ioctl(fd, HDIO_GETGEO, &geometry)) 255cf200d32Sopenharmony_ci numSecs = (uint32_t) geometry.sectors; 256cf200d32Sopenharmony_ci #endif 257cf200d32Sopenharmony_ci return numSecs; 258cf200d32Sopenharmony_ci} // DiskIO::GetNumSecsPerTrack() 259cf200d32Sopenharmony_ci 260cf200d32Sopenharmony_ci// Resync disk caches so the OS uses the new partition table. This code varies 261cf200d32Sopenharmony_ci// a lot from one OS to another. 262cf200d32Sopenharmony_ci// Returns 1 on success, 0 if the kernel continues to use the old partition table. 263cf200d32Sopenharmony_ci// (Note that for most OSes, the default of 0 is returned because I've not yet 264cf200d32Sopenharmony_ci// looked into how to test for success in the underlying system calls...) 265cf200d32Sopenharmony_ciint DiskIO::DiskSync(void) { 266cf200d32Sopenharmony_ci int i, retval = 0, platformFound = 0; 267cf200d32Sopenharmony_ci 268cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 269cf200d32Sopenharmony_ci if (!isOpen) { 270cf200d32Sopenharmony_ci OpenForRead(); 271cf200d32Sopenharmony_ci } // if 272cf200d32Sopenharmony_ci 273cf200d32Sopenharmony_ci if (isOpen) { 274cf200d32Sopenharmony_ci sync(); 275cf200d32Sopenharmony_ci#if defined(__APPLE__) || defined(__sun__) 276cf200d32Sopenharmony_ci cout << "Warning: The kernel may continue to use old or deleted partitions.\n" 277cf200d32Sopenharmony_ci << "You should reboot or remove the drive.\n"; 278cf200d32Sopenharmony_ci /* don't know if this helps 279cf200d32Sopenharmony_ci * it definitely will get things on disk though: 280cf200d32Sopenharmony_ci * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */ 281cf200d32Sopenharmony_ci#ifdef __sun__ 282cf200d32Sopenharmony_ci i = ioctl(fd, DKIOCFLUSHWRITECACHE); 283cf200d32Sopenharmony_ci#else 284cf200d32Sopenharmony_ci i = ioctl(fd, DKIOCSYNCHRONIZECACHE); 285cf200d32Sopenharmony_ci#endif 286cf200d32Sopenharmony_ci platformFound++; 287cf200d32Sopenharmony_ci#endif 288cf200d32Sopenharmony_ci#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) 289cf200d32Sopenharmony_ci sleep(2); 290cf200d32Sopenharmony_ci i = ioctl(fd, DIOCGFLUSH); 291cf200d32Sopenharmony_ci cout << "Warning: The kernel may continue to use old or deleted partitions.\n" 292cf200d32Sopenharmony_ci << "You should reboot or remove the drive.\n"; 293cf200d32Sopenharmony_ci platformFound++; 294cf200d32Sopenharmony_ci#endif 295cf200d32Sopenharmony_ci#ifdef __linux__ 296cf200d32Sopenharmony_ci sleep(1); // Theoretically unnecessary, but ioctl() fails sometimes if omitted.... 297cf200d32Sopenharmony_ci fsync(fd); 298cf200d32Sopenharmony_ci i = ioctl(fd, BLKRRPART); 299cf200d32Sopenharmony_ci if (i) { 300cf200d32Sopenharmony_ci cout << "Warning: The kernel is still using the old partition table.\n" 301cf200d32Sopenharmony_ci << "The new table will be used at the next reboot or after you\n" 302cf200d32Sopenharmony_ci << "run partprobe(8) or kpartx(8)\n"; 303cf200d32Sopenharmony_ci } else { 304cf200d32Sopenharmony_ci retval = 1; 305cf200d32Sopenharmony_ci } // if/else 306cf200d32Sopenharmony_ci platformFound++; 307cf200d32Sopenharmony_ci#endif 308cf200d32Sopenharmony_ci if (platformFound == 0) 309cf200d32Sopenharmony_ci cerr << "Warning: Platform not recognized!\n"; 310cf200d32Sopenharmony_ci if (platformFound > 1) 311cf200d32Sopenharmony_ci cerr << "\nWarning: We seem to be running on multiple platforms!\n"; 312cf200d32Sopenharmony_ci } // if (isOpen) 313cf200d32Sopenharmony_ci return retval; 314cf200d32Sopenharmony_ci} // DiskIO::DiskSync() 315cf200d32Sopenharmony_ci 316cf200d32Sopenharmony_ci// Seek to the specified sector. Returns 1 on success, 0 on failure. 317cf200d32Sopenharmony_ci// Note that seeking beyond the end of the file is NOT detected as a failure! 318cf200d32Sopenharmony_ciint DiskIO::Seek(uint64_t sector) { 319cf200d32Sopenharmony_ci int retval = 1; 320cf200d32Sopenharmony_ci off64_t seekTo, sought; 321cf200d32Sopenharmony_ci 322cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 323cf200d32Sopenharmony_ci if (!isOpen) { 324cf200d32Sopenharmony_ci retval = OpenForRead(); 325cf200d32Sopenharmony_ci } // if 326cf200d32Sopenharmony_ci 327cf200d32Sopenharmony_ci if (isOpen) { 328cf200d32Sopenharmony_ci seekTo = sector * (uint64_t) GetBlockSize(); 329cf200d32Sopenharmony_ci sought = lseek64(fd, seekTo, SEEK_SET); 330cf200d32Sopenharmony_ci if (sought != seekTo) { 331cf200d32Sopenharmony_ci retval = 0; 332cf200d32Sopenharmony_ci } // if 333cf200d32Sopenharmony_ci } // if 334cf200d32Sopenharmony_ci return retval; 335cf200d32Sopenharmony_ci} // DiskIO::Seek() 336cf200d32Sopenharmony_ci 337cf200d32Sopenharmony_ci// A variant on the standard read() function. Done to work around 338cf200d32Sopenharmony_ci// limitations in FreeBSD concerning the matching of the sector 339cf200d32Sopenharmony_ci// size with the number of bytes read. 340cf200d32Sopenharmony_ci// Returns the number of bytes read into buffer. 341cf200d32Sopenharmony_ciint DiskIO::Read(void* buffer, int numBytes) { 342cf200d32Sopenharmony_ci int blockSize, numBlocks, retval = 0; 343cf200d32Sopenharmony_ci char* tempSpace; 344cf200d32Sopenharmony_ci 345cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 346cf200d32Sopenharmony_ci if (!isOpen) { 347cf200d32Sopenharmony_ci OpenForRead(); 348cf200d32Sopenharmony_ci } // if 349cf200d32Sopenharmony_ci 350cf200d32Sopenharmony_ci if (isOpen) { 351cf200d32Sopenharmony_ci // Compute required space and allocate memory 352cf200d32Sopenharmony_ci blockSize = GetBlockSize(); 353cf200d32Sopenharmony_ci if (numBytes <= blockSize) { 354cf200d32Sopenharmony_ci numBlocks = 1; 355cf200d32Sopenharmony_ci tempSpace = new char [blockSize]; 356cf200d32Sopenharmony_ci } else { 357cf200d32Sopenharmony_ci numBlocks = numBytes / blockSize; 358cf200d32Sopenharmony_ci if ((numBytes % blockSize) != 0) 359cf200d32Sopenharmony_ci numBlocks++; 360cf200d32Sopenharmony_ci tempSpace = new char [numBlocks * blockSize]; 361cf200d32Sopenharmony_ci } // if/else 362cf200d32Sopenharmony_ci if (tempSpace == NULL) { 363cf200d32Sopenharmony_ci cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n"; 364cf200d32Sopenharmony_ci exit(1); 365cf200d32Sopenharmony_ci } // if 366cf200d32Sopenharmony_ci 367cf200d32Sopenharmony_ci // Read the data into temporary space, then copy it to buffer 368cf200d32Sopenharmony_ci retval = read(fd, tempSpace, numBlocks * blockSize); 369cf200d32Sopenharmony_ci memcpy(buffer, tempSpace, numBytes); 370cf200d32Sopenharmony_ci 371cf200d32Sopenharmony_ci // Adjust the return value, if necessary.... 372cf200d32Sopenharmony_ci if (((numBlocks * blockSize) != numBytes) && (retval > 0)) 373cf200d32Sopenharmony_ci retval = numBytes; 374cf200d32Sopenharmony_ci 375cf200d32Sopenharmony_ci delete[] tempSpace; 376cf200d32Sopenharmony_ci } // if (isOpen) 377cf200d32Sopenharmony_ci return retval; 378cf200d32Sopenharmony_ci} // DiskIO::Read() 379cf200d32Sopenharmony_ci 380cf200d32Sopenharmony_ci// A variant on the standard write() function. Done to work around 381cf200d32Sopenharmony_ci// limitations in FreeBSD concerning the matching of the sector 382cf200d32Sopenharmony_ci// size with the number of bytes read. 383cf200d32Sopenharmony_ci// Returns the number of bytes written. 384cf200d32Sopenharmony_ciint DiskIO::Write(void* buffer, int numBytes) { 385cf200d32Sopenharmony_ci int blockSize, i, numBlocks, retval = 0; 386cf200d32Sopenharmony_ci char* tempSpace; 387cf200d32Sopenharmony_ci 388cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 389cf200d32Sopenharmony_ci if ((!isOpen) || (!openForWrite)) { 390cf200d32Sopenharmony_ci OpenForWrite(); 391cf200d32Sopenharmony_ci } // if 392cf200d32Sopenharmony_ci 393cf200d32Sopenharmony_ci if (isOpen) { 394cf200d32Sopenharmony_ci // Compute required space and allocate memory 395cf200d32Sopenharmony_ci blockSize = GetBlockSize(); 396cf200d32Sopenharmony_ci if (numBytes <= blockSize) { 397cf200d32Sopenharmony_ci numBlocks = 1; 398cf200d32Sopenharmony_ci tempSpace = new char [blockSize]; 399cf200d32Sopenharmony_ci } else { 400cf200d32Sopenharmony_ci numBlocks = numBytes / blockSize; 401cf200d32Sopenharmony_ci if ((numBytes % blockSize) != 0) numBlocks++; 402cf200d32Sopenharmony_ci tempSpace = new char [numBlocks * blockSize]; 403cf200d32Sopenharmony_ci } // if/else 404cf200d32Sopenharmony_ci if (tempSpace == NULL) { 405cf200d32Sopenharmony_ci cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n"; 406cf200d32Sopenharmony_ci exit(1); 407cf200d32Sopenharmony_ci } // if 408cf200d32Sopenharmony_ci 409cf200d32Sopenharmony_ci // Copy the data to my own buffer, then write it 410cf200d32Sopenharmony_ci memcpy(tempSpace, buffer, numBytes); 411cf200d32Sopenharmony_ci for (i = numBytes; i < numBlocks * blockSize; i++) { 412cf200d32Sopenharmony_ci tempSpace[i] = 0; 413cf200d32Sopenharmony_ci } // for 414cf200d32Sopenharmony_ci retval = write(fd, tempSpace, numBlocks * blockSize); 415cf200d32Sopenharmony_ci 416cf200d32Sopenharmony_ci // Adjust the return value, if necessary.... 417cf200d32Sopenharmony_ci if (((numBlocks * blockSize) != numBytes) && (retval > 0)) 418cf200d32Sopenharmony_ci retval = numBytes; 419cf200d32Sopenharmony_ci 420cf200d32Sopenharmony_ci delete[] tempSpace; 421cf200d32Sopenharmony_ci } // if (isOpen) 422cf200d32Sopenharmony_ci return retval; 423cf200d32Sopenharmony_ci} // DiskIO:Write() 424cf200d32Sopenharmony_ci 425cf200d32Sopenharmony_ci/************************************************************************************** 426cf200d32Sopenharmony_ci * * 427cf200d32Sopenharmony_ci * Below functions are lifted from various sources, as documented in comments before * 428cf200d32Sopenharmony_ci * each one. * 429cf200d32Sopenharmony_ci * * 430cf200d32Sopenharmony_ci **************************************************************************************/ 431cf200d32Sopenharmony_ci 432cf200d32Sopenharmony_ci// The disksize function is taken from the Linux fdisk code and modified 433cf200d32Sopenharmony_ci// greatly since then to enable FreeBSD and MacOS support, as well as to 434cf200d32Sopenharmony_ci// return correct values for disk image files. 435cf200d32Sopenharmony_ciuint64_t DiskIO::DiskSize(int *err) { 436cf200d32Sopenharmony_ci uint64_t sectors = 0; // size in sectors 437cf200d32Sopenharmony_ci off64_t bytes = 0; // size in bytes 438cf200d32Sopenharmony_ci struct stat64 st; 439cf200d32Sopenharmony_ci int platformFound = 0; 440cf200d32Sopenharmony_ci#ifdef __sun__ 441cf200d32Sopenharmony_ci struct dk_minfo minfo; 442cf200d32Sopenharmony_ci#endif 443cf200d32Sopenharmony_ci 444cf200d32Sopenharmony_ci // If disk isn't open, try to open it.... 445cf200d32Sopenharmony_ci if (!isOpen) { 446cf200d32Sopenharmony_ci OpenForRead(); 447cf200d32Sopenharmony_ci } // if 448cf200d32Sopenharmony_ci 449cf200d32Sopenharmony_ci if (isOpen) { 450cf200d32Sopenharmony_ci // Note to self: I recall testing a simplified version of 451cf200d32Sopenharmony_ci // this code, similar to what's in the __APPLE__ block, 452cf200d32Sopenharmony_ci // on Linux, but I had some problems. IIRC, it ran OK on 32-bit 453cf200d32Sopenharmony_ci // systems but not on 64-bit. Keep this in mind in case of 454cf200d32Sopenharmony_ci // 32/64-bit issues on MacOS.... 455cf200d32Sopenharmony_ci#ifdef __APPLE__ 456cf200d32Sopenharmony_ci *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §ors); 457cf200d32Sopenharmony_ci platformFound++; 458cf200d32Sopenharmony_ci#endif 459cf200d32Sopenharmony_ci#ifdef __sun__ 460cf200d32Sopenharmony_ci *err = ioctl(fd, DKIOCGMEDIAINFO, &minfo); 461cf200d32Sopenharmony_ci if (*err == 0) 462cf200d32Sopenharmony_ci sectors = minfo.dki_capacity; 463cf200d32Sopenharmony_ci platformFound++; 464cf200d32Sopenharmony_ci#endif 465cf200d32Sopenharmony_ci#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) 466cf200d32Sopenharmony_ci *err = ioctl(fd, DIOCGMEDIASIZE, &bytes); 467cf200d32Sopenharmony_ci long long b = GetBlockSize(); 468cf200d32Sopenharmony_ci sectors = bytes / b; 469cf200d32Sopenharmony_ci platformFound++; 470cf200d32Sopenharmony_ci#endif 471cf200d32Sopenharmony_ci#ifdef __linux__ 472cf200d32Sopenharmony_ci long sz; 473cf200d32Sopenharmony_ci long long b; 474cf200d32Sopenharmony_ci *err = ioctl(fd, BLKGETSIZE, &sz); 475cf200d32Sopenharmony_ci if (*err) { 476cf200d32Sopenharmony_ci sectors = sz = 0; 477cf200d32Sopenharmony_ci } // if 478cf200d32Sopenharmony_ci if ((!*err) || (errno == EFBIG)) { 479cf200d32Sopenharmony_ci *err = ioctl(fd, BLKGETSIZE64, &b); 480cf200d32Sopenharmony_ci if (*err || b == 0 || b == sz) 481cf200d32Sopenharmony_ci sectors = sz; 482cf200d32Sopenharmony_ci else 483cf200d32Sopenharmony_ci sectors = (b >> 9); 484cf200d32Sopenharmony_ci } // if 485cf200d32Sopenharmony_ci // Unintuitively, the above returns values in 512-byte blocks, no 486cf200d32Sopenharmony_ci // matter what the underlying device's block size. Correct for this.... 487cf200d32Sopenharmony_ci sectors /= (GetBlockSize() / 512); 488cf200d32Sopenharmony_ci platformFound++; 489cf200d32Sopenharmony_ci#endif 490cf200d32Sopenharmony_ci if (platformFound != 1) 491cf200d32Sopenharmony_ci cerr << "Warning! We seem to be running on no known platform!\n"; 492cf200d32Sopenharmony_ci 493cf200d32Sopenharmony_ci // The above methods have failed, so let's assume it's a regular 494cf200d32Sopenharmony_ci // file (a QEMU image, dd backup, or what have you) and see what 495cf200d32Sopenharmony_ci // fstat() gives us.... 496cf200d32Sopenharmony_ci if ((sectors == 0) || (*err == -1)) { 497cf200d32Sopenharmony_ci if (fstat64(fd, &st) == 0) { 498cf200d32Sopenharmony_ci bytes = st.st_size; 499cf200d32Sopenharmony_ci if ((bytes % UINT64_C(512)) != 0) 500cf200d32Sopenharmony_ci cerr << "Warning: File size is not a multiple of 512 bytes!" 501cf200d32Sopenharmony_ci << " Misbehavior is likely!\n\a"; 502cf200d32Sopenharmony_ci sectors = bytes / UINT64_C(512); 503cf200d32Sopenharmony_ci } // if 504cf200d32Sopenharmony_ci } // if 505cf200d32Sopenharmony_ci } // if (isOpen) 506cf200d32Sopenharmony_ci return sectors; 507cf200d32Sopenharmony_ci} // DiskIO::DiskSize() 508