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