summaryrefslogtreecommitdiff
path: root/diskio.cc
blob: bb5bea74fc1758274433ce7cdc5e8567acc2f23c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//
// C++ Interface: diskio (platform-independent components)
//
// Description: Class to handle low-level disk I/O for GPT fdisk
//
//
// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
//
// Copyright: See COPYING file that comes with this distribution
//
//
// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
// under the terms of the GNU GPL version 2, as detailed in the COPYING file.

#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
#include <windows.h>
#include <winioctl.h>
#define fstat64 fstat
#define stat64 stat
#define S_IRGRP 0
#define S_IROTH 0
#else
#include <sys/ioctl.h>
#endif
#include <string>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>

#include "support.h"
#include "diskio.h"

using namespace std;

DiskIO::DiskIO(void) {
   userFilename = "";
   realFilename = "";
   isOpen = 0;
   openForWrite = 0;
} // constructor

DiskIO::~DiskIO(void) {
   Close();
} // destructor

// Open a disk device for reading. Returns 1 on success, 0 on failure.
int DiskIO::OpenForRead(const string & filename) {
   int shouldOpen = 1;

   if (isOpen) { // file is already open
      if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) {
         Close();
      } else {
         shouldOpen = 0;
      } // if/else
   } // if

   if (shouldOpen) {
      userFilename = filename;
      MakeRealName();
      OpenForRead();
   } // if

   return isOpen;
} // DiskIO::OpenForRead(string filename)

// Open a disk for reading and writing by filename.
// Returns 1 on success, 0 on failure.
int DiskIO::OpenForWrite(const string & filename) {
   int retval = 0;

   if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) {
      retval = 1;
   } else {
      userFilename = filename;
      MakeRealName();
      retval = OpenForWrite();
      if (retval == 0) {
         realFilename = userFilename = "";
      } // if
   } // if/else
   return retval;
} // DiskIO::OpenForWrite(string filename)

// My original FindAlignment() function (after this one) isn't working, since
// the BLKPBSZGET ioctl() isn't doing what I expected (it returns 512 even on
// a WD Advanced Format drive). Therefore, I'm using a simpler function that
// returns 1-sector alignment for unusual sector sizes and drives smaller than
// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for
// larger drives with 512-byte sectors.
int DiskIO::FindAlignment(void) {
   int err, result;

   if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) {
      result = 8; // play it safe; align for 4096-byte sectors
   } else {
      result = 1; // unusual sector size; assume it's the real physical size
   } // if/else
   return result;
} // DiskIO::FindAlignment

// Return the partition alignment value in sectors. Right now this works
// only for Linux 2.6.32 and later, since I can't find equivalent ioctl()s
// for OS X or FreeBSD, and the Linux ioctl is new
/* int DiskIO::FindAlignment(int fd) {
   int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096;
   uint64_t diskSize;

   cout << "Entering FindAlignment()\n";
#if defined (__linux__) && defined (BLKPBSZGET)
   err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
   cout << "In FindAlignment(), physicalSectorSize = " << physicalSectorSize
        << ", err = " << err << "\n";
#else
   err = -1;
#endif

   if (err < 0) { // ioctl didn't work; have to guess....
      if (GetBlockSize(fd) == 512) {
         result = 8; // play it safe; align for 4096-byte sectors
} else {
         result = 1; // unusual sector size; assume it's the real physical size
} // if/else
} else { // ioctl worked; compute alignment
      result = physicalSectorSize / GetBlockSize(fd);
      // Disks with larger physical than logical sectors must theoretically
      // have a total disk size that's a multiple of the physical sector
      // size; however, some such disks have compatibility jumper settings
      // meant for one-partition MBR setups, and these reduce the total
      // number of sectors by 1. If such a setting is used, it'll result
      // in improper alignment, so look for this condition and warn the
      // user if it's found....
      diskSize = disksize(fd, &errnum);
      if ((diskSize % (uint64_t) result) != 0) {
         fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n"
                         "size (%d), but it should be! Check disk manual and jumper settings!\n",
                         (unsigned long long) diskSize, result);
} // if
} // if/else
   if (result <= 0) // can happen if physical sector size < logical sector size
      result = 1;
   return result;
} // DiskIO::FindAlignment(int) */

// The same as FindAlignment(int), but opens and closes a device by filename
int DiskIO::FindAlignment(const string & filename) {
   int retval = 1;

   if (!isOpen)
      OpenForRead(filename);
   if (isOpen) {
      retval = FindAlignment();
   } // if
   return retval;
} // DiskIO::FindAlignment(char)