blob: 1bb0178d313d4a224ac796f6209cb899448eff89 [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
srs56940a697312010-01-28 21:10:52 -050018#ifdef _WIN32
srs5694add79a62010-01-26 15:59:58 -050019#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;
srs5694add79a62010-01-26 15:59:58 -050046} // constructor
47
48DiskIO::~DiskIO(void) {
49 Close();
srs5694add79a62010-01-26 15:59:58 -050050} // destructor
51
52// Open a disk device for reading. Returns 1 on success, 0 on failure.
srs56940a697312010-01-28 21:10:52 -050053int DiskIO::OpenForRead(const string & filename) {
srs5694add79a62010-01-26 15:59:58 -050054 int shouldOpen = 1;
55
56 if (isOpen) { // file is already open
57 if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) {
58 Close();
59 } else {
60 shouldOpen = 0;
61 } // if/else
62 } // if
63
64 if (shouldOpen) {
65 userFilename = filename;
66 MakeRealName();
67 OpenForRead();
68 } // if
69
70 return isOpen;
71} // DiskIO::OpenForRead(string filename)
72
73// Open a disk for reading and writing by filename.
74// Returns 1 on success, 0 on failure.
srs56940a697312010-01-28 21:10:52 -050075int DiskIO::OpenForWrite(const string & filename) {
srs5694add79a62010-01-26 15:59:58 -050076 int retval = 0;
77
78 if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) {
79 retval = 1;
80 } else {
81 userFilename = filename;
82 MakeRealName();
83 retval = OpenForWrite();
84 if (retval == 0) {
85 realFilename = userFilename = "";
86 } // if
87 } // if/else
88 return retval;
89} // DiskIO::OpenForWrite(string filename)
90
91// My original FindAlignment() function (after this one) isn't working, since
92// the BLKPBSZGET ioctl() isn't doing what I expected (it returns 512 even on
93// a WD Advanced Format drive). Therefore, I'm using a simpler function that
94// returns 1-sector alignment for unusual sector sizes and drives smaller than
95// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for
96// larger drives with 512-byte sectors.
97int DiskIO::FindAlignment(void) {
98 int err, result;
99
100 if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) {
101 result = 8; // play it safe; align for 4096-byte sectors
102 } else {
103 result = 1; // unusual sector size; assume it's the real physical size
104 } // if/else
105 return result;
106} // DiskIO::FindAlignment
107
108// Return the partition alignment value in sectors. Right now this works
109// only for Linux 2.6.32 and later, since I can't find equivalent ioctl()s
110// for OS X or FreeBSD, and the Linux ioctl is new
111/* int DiskIO::FindAlignment(int fd) {
112 int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096;
113 uint64_t diskSize;
114
srs5694fed16d02010-01-27 23:03:40 -0500115 cout << "Entering FindAlignment()\n";
srs5694add79a62010-01-26 15:59:58 -0500116#if defined (__linux__) && defined (BLKPBSZGET)
117 err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
srs5694fed16d02010-01-27 23:03:40 -0500118 cout << "In FindAlignment(), physicalSectorSize = " << physicalSectorSize
119 << ", err = " << err << "\n";
srs5694add79a62010-01-26 15:59:58 -0500120#else
121 err = -1;
122#endif
123
124 if (err < 0) { // ioctl didn't work; have to guess....
125 if (GetBlockSize(fd) == 512) {
126 result = 8; // play it safe; align for 4096-byte sectors
127} else {
128 result = 1; // unusual sector size; assume it's the real physical size
129} // if/else
130} else { // ioctl worked; compute alignment
131 result = physicalSectorSize / GetBlockSize(fd);
132 // Disks with larger physical than logical sectors must theoretically
133 // have a total disk size that's a multiple of the physical sector
134 // size; however, some such disks have compatibility jumper settings
135 // meant for one-partition MBR setups, and these reduce the total
136 // number of sectors by 1. If such a setting is used, it'll result
137 // in improper alignment, so look for this condition and warn the
138 // user if it's found....
139 diskSize = disksize(fd, &errnum);
140 if ((diskSize % (uint64_t) result) != 0) {
141 fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n"
142 "size (%d), but it should be! Check disk manual and jumper settings!\n",
143 (unsigned long long) diskSize, result);
144} // if
145} // if/else
146 if (result <= 0) // can happen if physical sector size < logical sector size
147 result = 1;
148 return result;
149} // DiskIO::FindAlignment(int) */
150
151// The same as FindAlignment(int), but opens and closes a device by filename
srs56940a697312010-01-28 21:10:52 -0500152int DiskIO::FindAlignment(const string & filename) {
srs5694add79a62010-01-26 15:59:58 -0500153 int retval = 1;
154
155 if (!isOpen)
156 OpenForRead(filename);
157 if (isOpen) {
158 retval = FindAlignment();
159 } // if
160 return retval;
161} // DiskIO::FindAlignment(char)