blob: d1d0c3c91f4b3c6a8132f76586a3e6e278d4fc20 [file] [log] [blame]
srs5694add79a62010-01-26 15:59:58 -05001//
2// C++ Interface: diskio (platform-independent components)
3//
4// Description: Class to handle low-level disk I/O for GPT fdisk
5//
6//
7// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
8//
9// Copyright: See COPYING file that comes with this distribution
10//
11//
12// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
13// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
14
15#define __STDC_LIMIT_MACROS
16#define __STDC_CONSTANT_MACROS
17
18#ifdef MINGW
19#include <windows.h>
20#include <winioctl.h>
21#define fstat64 fstat
22#define stat64 stat
23#define S_IRGRP 0
24#define S_IROTH 0
25#else
26#include <sys/ioctl.h>
27#endif
28#include <stdio.h>
29#include <string>
30#include <stdint.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <sys/stat.h>
34#include <iostream>
35
36#include "support.h"
37#include "diskio.h"
38
39using namespace std;
40
41DiskIO::DiskIO(void) {
42 userFilename = "";
43 realFilename = "";
44 isOpen = 0;
45 openForWrite = 0;
46 sectorData = NULL;
47} // constructor
48
49DiskIO::~DiskIO(void) {
50 Close();
51 free(sectorData);
52} // destructor
53
54// Open a disk device for reading. Returns 1 on success, 0 on failure.
55int DiskIO::OpenForRead(string filename) {
56 int shouldOpen = 1;
57
58 if (isOpen) { // file is already open
59 if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) {
60 Close();
61 } else {
62 shouldOpen = 0;
63 } // if/else
64 } // if
65
66 if (shouldOpen) {
67 userFilename = filename;
68 MakeRealName();
69 OpenForRead();
70 } // if
71
72 return isOpen;
73} // DiskIO::OpenForRead(string filename)
74
75// Open a disk for reading and writing by filename.
76// Returns 1 on success, 0 on failure.
77int DiskIO::OpenForWrite(string filename) {
78 int retval = 0;
79
80 if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) {
81 retval = 1;
82 } else {
83 userFilename = filename;
84 MakeRealName();
85 retval = OpenForWrite();
86 if (retval == 0) {
87 realFilename = userFilename = "";
88 } // if
89 } // if/else
90 return retval;
91} // DiskIO::OpenForWrite(string filename)
92
93// My original FindAlignment() function (after this one) isn't working, since
94// the BLKPBSZGET ioctl() isn't doing what I expected (it returns 512 even on
95// a WD Advanced Format drive). Therefore, I'm using a simpler function that
96// returns 1-sector alignment for unusual sector sizes and drives smaller than
97// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for
98// larger drives with 512-byte sectors.
99int DiskIO::FindAlignment(void) {
100 int err, result;
101
102 if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) {
103 result = 8; // play it safe; align for 4096-byte sectors
104 } else {
105 result = 1; // unusual sector size; assume it's the real physical size
106 } // if/else
107 return result;
108} // DiskIO::FindAlignment
109
110// Return the partition alignment value in sectors. Right now this works
111// only for Linux 2.6.32 and later, since I can't find equivalent ioctl()s
112// for OS X or FreeBSD, and the Linux ioctl is new
113/* int DiskIO::FindAlignment(int fd) {
114 int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096;
115 uint64_t diskSize;
116
srs5694fed16d02010-01-27 23:03:40 -0500117 cout << "Entering FindAlignment()\n";
srs5694add79a62010-01-26 15:59:58 -0500118#if defined (__linux__) && defined (BLKPBSZGET)
119 err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
srs5694fed16d02010-01-27 23:03:40 -0500120 cout << "In FindAlignment(), physicalSectorSize = " << physicalSectorSize
121 << ", err = " << err << "\n";
srs5694add79a62010-01-26 15:59:58 -0500122#else
123 err = -1;
124#endif
125
126 if (err < 0) { // ioctl didn't work; have to guess....
127 if (GetBlockSize(fd) == 512) {
128 result = 8; // play it safe; align for 4096-byte sectors
129} else {
130 result = 1; // unusual sector size; assume it's the real physical size
131} // if/else
132} else { // ioctl worked; compute alignment
133 result = physicalSectorSize / GetBlockSize(fd);
134 // Disks with larger physical than logical sectors must theoretically
135 // have a total disk size that's a multiple of the physical sector
136 // size; however, some such disks have compatibility jumper settings
137 // meant for one-partition MBR setups, and these reduce the total
138 // number of sectors by 1. If such a setting is used, it'll result
139 // in improper alignment, so look for this condition and warn the
140 // user if it's found....
141 diskSize = disksize(fd, &errnum);
142 if ((diskSize % (uint64_t) result) != 0) {
143 fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n"
144 "size (%d), but it should be! Check disk manual and jumper settings!\n",
145 (unsigned long long) diskSize, result);
146} // if
147} // if/else
148 if (result <= 0) // can happen if physical sector size < logical sector size
149 result = 1;
150 return result;
151} // DiskIO::FindAlignment(int) */
152
153// The same as FindAlignment(int), but opens and closes a device by filename
154int DiskIO::FindAlignment(string filename) {
155 int fd;
156 int retval = 1;
157
158 if (!isOpen)
159 OpenForRead(filename);
160 if (isOpen) {
161 retval = FindAlignment();
162 } // if
163 return retval;
164} // DiskIO::FindAlignment(char)