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