2
* lphdisk - Hibernation Parititon Prep Utility for laptops running Phoenix
5
* Copyright 2000-2001 Patrick D. Ashmore <pda@procyon.com> and
6
* Alex Stewart <alex@foogod.com>
7
* This software is released under the Artistic License
13
- Partition numbers are always passed and used base-one (i.e. the first
14
partition is partition 1, not 0). This ensures consistency both
15
throughout the program and with convention elsewhere, reducing the risk
16
of something using a different partition than it thought it was. This
17
does mean, however, that if you ever see something like "pi[partnum]",
18
it is probably _wrong_ (it probably should be "pi[partnum-1]").
35
/* General Program Defines: */
37
#define SECTOR_SIZE 512 /* Bytes per sector */
38
#define PP_BLOCK_SIZE 2 /* Sectors per "block" in /proc/partitions */
39
#define MBR_SIZE 512 /* Boot record is 512-bytes long */
40
#define TABLE_START 0x1BE /* Start of partition table in MBR */
41
#define TABLE_MAGIC 0x1FE /* Start of "magic number" in MBR */
42
#define TABLE_ENTRY 16 /* Length of a single partition table entry */
43
#define PART_TYPE 4 /* Position of partition type byte */
44
#define PART_START 8 /* Position of start sector (32-bit value) */
45
#define PART_SIZE 12 /* Position of partition size (32-bit value) */
46
#define MAGIC_ID 0xAA55 /* "magic number" (little-endian reversed) */
47
#define HIBERNATION_ID 0xA0 /* NoteBIOS Hibernation partition ID */
48
#define EXTENDED_ID 0x05 /* ID for an "extended partition" area */
49
#define PROBE_PADDING 2048 /* Add 2MB "padding" to probed requirements */
51
/* Error Exit Codes: */
53
#define ERR_USAGE 1 /* User requested usage message */
54
#define ERR_BADARGS 2 /* User entered bad command arguments */
55
#define ERR_OPEN 3 /* Open of device failed */
56
#define ERR_STAT 4 /* Statting open device failed */
57
#define ERR_BADFILE 5 /* Bad file type */
58
#define ERR_READ 6 /* Read error */
59
#define ERR_TABLE 7 /* Error reading/checking partition table */
60
#define ERR_FINDPART 8 /* Problem determining which partition to use */
61
#define ERR_WRITE 9 /* Problem formatting hibernate partition */
62
#define ERR_CANTPROBE 10 /* Asked to probe only, but can't obtain info */
64
/* Structs and Type Declarations: */
67
int type; /* Partition type ID */
68
size_t start; /* Start sector of partition */
69
size_t size; /* Length of partition in sectors */
72
/* Global Variables: */
74
const char *version_string =
75
"lphdisk 0.9, by Patrick D. Ashmore and Alex Stewart";
77
const char *pp_filename = "/proc/partitions";
78
const char *mtrr_filename = "/proc/mtrr";
79
const char *meminfo_filename = "/proc/meminfo";
81
int force_flag = 0; /* "force" is off by default */
82
int quiet_flag = 0; /* Be noisy by default */
83
int debug_flag = 0; /* Debugging messages off by default */
84
int write_flag = 1; /* Do actually write things, though */
85
int probeonly_flag = 0; /* Continue after probing by default */
87
char phasers[] = "stun"; /* We come in peace! (Shoot to kill!) */
89
/* The header, with sector and checksum values set to 0, looks like this: */
90
/* __ __ __ __ __ __ */
91
/* 54 69 6D 4F 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 */
92
/* Checksum Size in Sectors */
94
const unsigned char header_base[] = { /* first 16 bytes of an empty header */
95
0x54, 0x69, 0x6D, 0x4F, 0x00, 0x00, 0x00, 0x00,
96
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
99
/*****************************************************************************/
100
/* General Purpose Utility Routines */
101
/*****************************************************************************/
103
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
104
/* The following is a hack to take advantage of the ext2 "_llseek" system */
105
/* call to do seeks to "long long" offsets under linux (this is needed to */
106
/* seek to sectors beyond 4194303 (2GB)). This isn't directly supported by */
107
/* glibc, so we need to make our own interface function for it. We should */
108
/* be able to get the _NR__llseek define from linux/unistd.h. From this we */
109
/* can construct a wrapper to perform the right system call. */
111
#include <linux/unistd.h> /* for __NR__llseek */
113
typedef long long lloff_t;
117
static _syscall5(int,_llseek, unsigned int,fd, unsigned long,offset_high,
118
unsigned long,offset_low, lloff_t *,result,
121
lloff_t llseek (unsigned int fd, lloff_t offset, unsigned int origin) {
125
retval = _llseek (fd, ((unsigned long long) offset) >> 32,
126
((unsigned long long) offset) & 0xffffffff,
128
return (retval == -1 ? (lloff_t) retval : result);
131
#else /* __NR__llseek */
133
/* Somehow, __NR__llseek wasn't in linux/unistd.h. This shouldn't ever */
134
/* happen, but better safe than sorry.. The best we can do is emulate it */
135
/* with lseek, and hope we don't get an offset that's too large (throw an */
136
/* error if we do) */
138
lloff_t llseek (unsigned int fd, lloff_t offset, unsigned int origin) {
139
off_t offt_offset = (off_t) offset;
141
if ((lloff_t)offt_offset != offset) {
142
/* converting to off_t and back yields different result, indicating an */
147
return lseek(fd, offt_offset, origin);
151
#endif /* __NR__llseek */
153
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
155
#define get16(p) get_int_le(p,2)
156
#define get32(p) get_int_le(p,4)
158
/* get_int_le(): Pull an integer in little-endian format from 'buf'. */
159
/* 'numbytes' should be 2 or 4, for an 16-bit or 32-bit value. Returns the */
160
/* value read. Generally called using the get16 and get32 macros above. */
162
int get_int_le (const unsigned char *buf, int numbytes) {
166
for (i=numbytes-1; i>=0; i--) {
167
value = (value << 8) + buf[i];
172
#define put16(p,v) put_int_le(p,2,v)
173
#define put32(p,v) put_int_le(p,4,v)
175
/* put_int_le(): Insert 'value' into 'buf' in little-endian format. */
176
/* 'numbytes' should be 2 or 4, for an 16-bit or 32-bit value. */
177
/* Generally called using the put16 and put32 macros above. */
179
void put_int_le (unsigned char *buf, int numbytes, int value) {
182
for (i=numbytes-1; i>=0; i--) {
183
buf[i] = value & 0xFF;
188
/* seek_sector(): Seek to a particular sector on the disk. Returns zero on */
189
/* success, nonzero on error. */
191
int seek_sector (int fd, size_t secno) {
192
lloff_t offset = (lloff_t) secno * SECTOR_SIZE;
194
if (llseek(fd, offset, SEEK_SET) == (lloff_t) -1)
200
/*****************************************************************************/
201
/* lphdisk-specific Functions */
202
/*****************************************************************************/
204
/* debug(): Write a debugging message (if debug_flag is set). Arguments are */
205
/* the same as for printf. */
207
void debug (char *fmt, ...) {
211
if (debug_flag) vprintf(fmt, ap);
215
/* do_write(): Write 'count' bytes from 'buf' to file 'fd'. Returns number */
216
/* of bytes written. (this is a wrapper for the write() system call which */
217
/* simply checks write_flag first, to support the -n command line option) */
219
ssize_t do_write (int fd, const void *buf, size_t count) {
220
//static FILE *f = 0;
223
//if (f == 0) f = fdopen(fd, "a");
224
//return fwrite(buf, 1, count, f);
225
return write(fd, buf, count);
231
/* read_mbr(): Read the Master Boot Record from sector 0 of 'fd' and store */
232
/* it in 'buf'. 'buf' must point to a buffer at least MBR_SIZE. Returns */
233
/* nonzero on error. */
235
int read_mbr (int fd, unsigned char *buf) {
236
if (seek_sector(fd, 0) ||
237
(read(fd, buf, MBR_SIZE) != MBR_SIZE)) {
241
fprintf(stderr, "read_mbr: short read\n");
249
/* parse_table(): Take an MBR in 'buf', parse the important bits of the */
250
/* primary partition table, and store them in 'pi' (an array of 4 partinfo */
251
/* structs, one for each primary partition). Performs basic sanity checking */
252
/* on what it finds. Returns nonzero on error. */
254
int parse_table (const unsigned char *buf, partinfo *pi) {
255
int i, i_start, i_end;
256
int j, j_start, j_end;
257
const unsigned char *pos;
259
/* Do some minimal sanity checking by verifying that the sector we've */
260
/* read has the right (two-byte) magic number at the end of it for a */
261
/* partition table */
263
if (get16(buf + TABLE_MAGIC) != MAGIC_ID) {
264
debug("magic number=%04X\n", get16(buf + TABLE_MAGIC));
265
fprintf(stderr, "parse_table: Invalid partition table magic number!\n");
269
/* Parse the important bits of the 4 primary partitions in the MBR */
271
debug("Parsing Partition table:\n");
272
for (i=1; i<5; i++) {
273
pos = buf + TABLE_START + ((i-1) * TABLE_ENTRY);
274
pi[i-1].type = *(pos + PART_TYPE);
275
pi[i-1].start = get32(pos + PART_START);
276
pi[i-1].size = get32(pos + PART_SIZE);
277
debug(" %d: type=%02x, start=%u, size=%u\n", i, pi[i-1].type,
278
pi[i-1].start, pi[i-1].size);
280
/* Also sanity-check the partition's allocation on the disk.. */
283
i_start = pi[i-1].start;
284
i_end = i_start + pi[i-1].size;
285
for (j=0; j<i; j++) {
286
j_start = pi[j-1].start;
287
j_end = j_start + pi[j-1].size;
289
(((i_start >= j_start) && (i_start < j_end)) ||
290
((j_start >= i_start) && (j_start < i_end)))) {
291
fprintf(stderr, "parse_table: Partition %d overlaps with"
292
" partition %d!\n", i, j);
302
/* check_proc_partitions(): Take parsed partition information for a device */
303
/* (in 'pi' array) and attempt to correlate it with what the kernel reports */
304
/* in /proc/partitions for that device. 'dev' indicates the major/minor */
305
/* numbers for the device that 'pi' contains info for. Returns nonzero on */
308
int check_proc_partitions (dev_t dev, const partinfo *pi) {
310
int devmajor = major(dev);
311
int devminor = minor(dev);
312
int checked[4] = {0,0,0,0};
313
int result, major, minor, size, partition;
316
if (!(f = fopen(pp_filename, "r"))) {
317
fprintf(stderr, "Unable to open %s: %s\n", pp_filename, strerror(errno));
321
fscanf(f, "%*[^\n]\n"); /* Read the header line and discard it */
323
while (result != EOF) {
324
result = fscanf(f, "%d %d %d %*[^\n]\n", &major, &minor, &size);
325
if (result == 3 && major == devmajor) {
326
if (minor > devminor && minor < devminor+5) {
328
/* Found one within the first four partitions of our disk.. */
330
partition = minor - devminor;
332
if (!(pi[partition-1].type)) {
333
debug("Warning: Partition %d is listed in %s, but is not present in "
334
"partition table!\n", partition, pp_filename);
338
if (size != (pi[partition-1].size / PP_BLOCK_SIZE)) {
340
/* Sizes don't match! */
342
if (pi[partition-1].type == EXTENDED_ID) {
344
/* It's ok, the extended partition doesn't have an accurate size */
345
/* listed in /proc/partitions, so we can't expect it to match. */
348
debug("Warning: Size of partition %d in partition table (%u) does"
349
" not match size listed in %s (%u)!\n", partition,
350
pi[partition-1].size, pp_filename, size * PP_BLOCK_SIZE);
355
/* Everything checks out for this partition.. */
357
checked[partition-1] = 1;
362
for (i=1; i<5; i++) {
363
if (pi[i-1].type && !checked[i-1]) {
365
/* Found a partition in the table that isn't in /proc/partitions! */
367
if (pi[i-1].type == HIBERNATION_ID) {
369
/* We'll make an exception if the (apparently newly created) */
370
/* partition is the hibernation partition and there's nothing else */
371
/* odd about the disk (if the user just created it, and we're not in */
372
/* danger of clobbering some other partition, it'll save on reboots */
373
/* if they can run this immediately afterward, and it should be */
376
debug("Hibernation partition %d is not listed in %s.\n", i,
379
debug("Partition %d is listed in partition table, but is not"
380
" present in %s!\n", i, pp_filename);
386
/* All systems are go! */
390
/* seek_a0(): Given a partinfo array ('pi'), find the partition with the */
391
/* right ID for a hibernation partition. Returns the partition number if */
392
/* found, 0 if not found, or -1 if more than one partition matches. */
394
int seek_a0 (const partinfo *pi) { /* FUNCTION - find a0 partition */
398
for (i=1; i<5; i++) {
399
if (pi[i-1].type && (pi[i-1].type == HIBERNATION_ID)) {
400
debug("Hibernation partition found on partition %d\n", i+1);
401
if (partition == 0) {
412
/* create_header(): Given a buffer of size SECTOR_SIZE in 'buf', create a */
413
/* NoteBIOS hibernation header sector appropriate for a hibernation */
414
/* partition of size 'partsize'. */
416
void create_header (unsigned char *buf, size_t partsize) {
417
unsigned int word; /* byte pair for checksum addition */
418
unsigned int checksum; /* running checksum total */
419
int i; /* general counter variable */
421
memcpy(buf, header_base, 16); /* Start off with the base header */
423
buf[16] = partsize & 0xFF; /* first byte in sector size */
424
buf[17] = (partsize >> 8) & 0xFF; /* second byte in sector size */
425
buf[18] = (partsize >> 16) & 0xFF; /* third byte in sector size */
426
buf[19] = (partsize >> 24) & 0xFF; /* fourth byte in sector size */
428
debug("create_header: read_sz_b0: %02X\n", buf[16]);
429
debug("create_header: read_sz_b1: %02X\n", buf[17]);
430
debug("create_header: read_sz_b2: %02X\n", buf[18]);
431
debug("create_header: read_sz_b3: %02X\n", buf[19]);
432
debug("create_header: Bytes for total sectors: %08X\n", partsize);
434
for (i = 20; i < SECTOR_SIZE; i++) buf[i] = 0xFF; /* header filler with FFs */
437
for (i = SECTOR_SIZE-2; i > 6; i -= 2) { /* compute the checksum */
440
checksum = checksum + word; /* add word to checksum */
442
debug("create_header: i=%03d W=%04X CS=%08X %08X %08X\n",
443
i, word, checksum, -checksum, ~checksum);
447
checksum = ~checksum; /* invert checksum */
448
buf[6] = checksum & 0xFF; /* significant high byte of checksum */
449
buf[7] = (checksum >> 8) & 0xFF; /* significant low byte of checksum */
451
debug("create_header: Bytes (in order) to insert in checksum spot:");
452
debug(" %02X %02X\n", buf[6], buf[7]);
455
/* do_format(): Actually do the formatting of a hibernate partition. 'fd' */
456
/* is a descriptor for the "master" disk device (_not_ the partition */
457
/* device), and 'pinfo' contains the partinfo struct for the appropriate */
458
/* partition to use. Returns nonzero on failure. */
460
int do_format (int fd, partinfo pinfo) {
463
unsigned int start_sector = pinfo.start;
464
unsigned int partsize = pinfo.size;
466
buf = malloc(SECTOR_SIZE);
468
fprintf(stderr, "do_format: memory allocation failed!\n");
472
create_header(buf, partsize);
476
char *spacer; /* debug output beautifier :) */
477
debug("do_format: Partition header:\ndo_format: ");
478
for (i=0; i<512; i++) {
480
if ((i+1) % 16 == 0) {
481
spacer = "\ndo_format: ";
483
if ((i+1) % 8 == 0) {
485
} else if ((i+1) % 4 == 0) {
492
debug("%02X%s", buf[i], spacer);
494
debug("End of header...\n");
496
/* end debug section */
498
debug("seeking to sector %u...\n", start_sector);
499
seek_sector(fd, start_sector); /* position to beginning of partition */
501
/* Write two copies of the header sector */
502
if ((do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE) ||
503
(do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE)) {
504
perror("do_format(header)");
508
for (i = 0; i < SECTOR_SIZE; i++) buf[i] = 0x50;
510
/* Write partsize-2 "blank" sectors */
511
for (i = 3; i <= partsize; i++) {
512
if (do_write(fd, buf, SECTOR_SIZE) != SECTOR_SIZE) {
516
/* only update output every 50 sectors */
517
if (((i % 50) == 0) || (i == partsize)) {
518
if (quiet_flag == 0) {
519
printf("Formatting sector %d of %u.", i, partsize);
520
printf(" (sectors of %d bytes)\r", SECTOR_SIZE);
528
/* mtrr_physmem(): Use /proc/mtrr to attempt to determine the amount of */
529
/* physical RAM in the system. Returns the size of RAM (in KB) indicated */
530
/* by /proc/mtrr, or zero if it could not determine an appropriate value. */
532
int mtrr_physmem(void) {
536
if (!(f = fopen(mtrr_filename, "r"))) {
537
debug("Unable to open %s: %s\n", mtrr_filename, strerror(errno));
540
if ((fscanf(f, "reg%*d: base=0x%*x (%dMB), size=%dMB", &base, &size) != 2) ||
542
debug("Parse of %s failed.\n", mtrr_filename);
548
debug("%s reports main RAM as %d KB\n", mtrr_filename, size);
553
/* meminfo_physmem(): Use /proc/meminfo to attempt to determine the amount */
554
/* of physical RAM in the system. Returns the size of RAM (in KB) indicated */
555
/* by /proc/meminfo, or zero if it could not determine an appropriate value. */
557
int meminfo_physmem(void) {
562
if (!(f = fopen(meminfo_filename, "r"))) {
563
debug("Unable to open %s: %s\n", meminfo_filename, strerror(errno));
566
fscanf(f, "%*[^\n]\n"); /* Read the header line and discard it */
568
if (fscanf(f, "Mem: %u", &size) != 1) {
569
debug("Parse of %s failed.\n", meminfo_filename);
574
/* convert to KB and then round up to the next power of 2 (since RAM */
575
/* sizes don't come in anything else, so this should correct for the */
576
/* kernel size, etc) */
578
debug("%s reports memory size of %d KB", meminfo_filename, size);
579
for (ramsize = 1; size; size >>= 1) ramsize <<= 1;
581
debug(" (adjusted=%d)\n", ramsize);
586
/* get_physmem(): Use all available methods to get a best guess of the */
587
/* amount of physical RAM in the system. Returns the size of RAM (in KB), */
588
/* or zero if it could not determine an appropriate value. */
590
int get_physmem(void) {
591
int mtrr_size, meminfo_size;
593
/* First try /proc/mtrr, as this gives us actual physical memory on most */
594
/* systems. This info won't exist on earlier kernels or if the kernel */
595
/* wasn't compiled with it enabled, though. (this can also give rather */
596
/* wonky info on non-IA32 systems, or possibly really odd IA32 ones, but */
597
/* it's very unlikely we'll find a laptop with one of these */
598
/* configurations, especially one with a NoteBIOS in it) */
600
mtrr_size = mtrr_physmem();
602
/* Now try /proc/meminfo. This is the total usable memory reported by the */
603
/* kernel, and should always be available, but this can be reduced by the */
604
/* kernel's size and internal allocations, as well as being overridable by */
605
/* boot parameters, etc. The meminfo_physmem() routine attempts to */
606
/* compensate for the space used by the kernel, but can't do anything */
607
/* about boot parameters or some other things that can change this value. */
609
meminfo_size = meminfo_physmem();
611
if (mtrr_size >= meminfo_size) {
612
debug("get_physmem: RAM size is %d KB\n", mtrr_size);
615
debug("get_physmem: RAM size is %d KB\n", meminfo_size);
620
/* vesa_videomem(): Use VESA BIOS Extension calls to attempt to query the */
621
/* amount of RAM on the video card (this should work on almost all modern */
622
/* models). This routine uses a copy of Josh Vanderhoof's LRMI library */
623
/* taken from svgalib to perform the appropriate BIOS interrupts. Returns */
624
/* the amount of video RAM detected (in KB) or zero if it was unable to */
625
/* obtain a number. */
627
int vesa_videomem(void) {
629
struct vbe_info_block *info;
632
/* (we do this first so we can catch the case of the user not being root */
633
/* (and quietly handle it with a debug message) before LRMI_init fails to */
634
/* open /dev/mem and prints an error message (since the user not being */
635
/* root is not technically an error, it just reduces some of the */
636
/* functionality we can provide) */
637
/* Allow read/write to all IO ports: */
640
debug("Can't set privilege level for VESA probe: %s\n", strerror(errno));
645
debug("LRMI initialization failed.\n");
650
info = LRMI_alloc_real(sizeof(struct vbe_info_block));
653
debug("vesa_videomem: can't alloc real mode memory.\n");
658
memcpy(info->vbe_signature, "VBE2", 4);
660
memset(&r, 0, sizeof(r));
662
r.es = (unsigned int)info >> 4;
665
if (!LRMI_int(0x10, &r)) {
666
debug("Can't get VESA info (vm86 failure).\n");
671
/* Set privilege level back to normal now that we're done with it */
674
if ((r.eax & 0xffff) != 0x4f ||
675
strncmp(info->vbe_signature, "VESA", 4) != 0) {
676
debug("No VESA bios.\n");
680
debug("VBE Version %x.%x\n", (int)(info->vbe_version >> 8) & 0xff,
681
(int)info->vbe_version & 0xff);
683
debug("Video card identified as: %s\n",
684
(char *)(info->oem_string_seg * 16 + info->oem_string_off));
686
/* The VESA BIOS call returns memory in 64KB units. Multiply to get KB.. */
688
video_mem = (int)(info->total_memory) * 64;
690
debug("Total video memory: %u KB\n", video_mem);
692
LRMI_free_real(info);
697
/* get_videomem(): Use all available methods to get a best guess of the */
698
/* amount of RAM in the video card. Returns the size of RAM (in KB), */
699
/* or zero if it could not determine an appropriate value. */
700
/* (currently vesa_videomem() is the only method, so this is just a wrapper */
703
#define get_videomem() vesa_videomem()
705
/*****************************************************************************/
707
/*****************************************************************************/
711
const char short_opts[] = "hpqdnf";
712
const struct option long_opts[] = {
714
{"probeonly", 0, 0, 'p'},
715
{"quiet", 0, 0, 'q'},
716
{"debug", 0, 0, 'd'},
717
{"nowrite", 0, 0, 'n'},
718
{"force", 0, 0, 'f'},
721
const char usage_string[] = "\
722
Usage: %1$s [options] [device]
723
Prepare a hibernation partition for APM suspend-to-disk.
726
-h, --help Display brief usage and option information (this screen)
727
-p, --probeonly Only calculate and display required size, do not format
728
-q, --quiet Turn off informational messages, useful for scripts
729
-d, --debug Turn on (verbose) debugging messages
730
-n, --nowrite Do not actually write to the disk
731
-f, --force **DANGEROUS** Format without regard to potential problems
733
'device' should be a raw disk device (not a partition). The default device
738
void print_usage (void) {
739
char *progname = rindex(argv0, '/');
740
progname = progname ? progname+1 : argv0;
741
printf(usage_string, progname, version_string);
744
int main (int argc, char **argv) { /* MAIN FUNCTION - the beast */
745
char drive[FILENAME_MAX] = "/dev/hda";/* Default to /dev/hda */
748
unsigned char mbr_buf[MBR_SIZE];
752
int ramsize, vramsize, required_size;
753
size_t required_sectors;
757
while (1) { /* loop to parse command line args */
758
int c; /* current getopt option */
760
c = getopt_long (argc, argv, short_opts, long_opts, 0);
761
if (c == -1) break; /* break if error */
763
switch (c) { /* case switch for options */
764
case 'h': /* case h - help */
765
print_usage(); /* print help information */
768
case 'p': /* case p - probeonly */
772
case 'q': /* case q - be quiet! */
773
quiet_flag = 1; /* set quiet flag */
776
case 'd': /* case d - debug on */
780
case 'n': /* case n - no write */
784
case 'f': /* case f - force */
785
force_flag = 1; /* use the force */
796
if (optind < argc) strcpy(drive, argv[optind++]);
798
if (optind < argc) { /* extra argument routine */
799
printf("Unexpected arguments: "); /* print error */
800
while (optind < argc) {
801
printf ("%s ", argv[optind++]); /* print bad args */
808
/* That takes care of argument parsing, now on with the actual work... */
810
if (!(ramsize = get_physmem())) {
811
if (!quiet_flag) printf("Warning: Cannot determine physical RAM.\n");
813
} else if (!(vramsize = get_videomem())) {
814
if (!quiet_flag) printf("Warning: Unable to determine the amount of video"
817
required_sectors = 0;
819
required_size = ramsize + vramsize + PROBE_PADDING;
820
required_sectors = (((size_t)required_size * 1024) / SECTOR_SIZE) + 2;
823
if (!required_size) {
824
if (!quiet_flag) printf("Reccomended partition size is unknown.\n");
826
if (!quiet_flag) printf("Reccomended partition size is %d MB"
827
" (%d sectors)\n", ((required_size+1023) >> 10),
831
if (probeonly_flag) {
832
return (required_size ? 0 : ERR_CANTPROBE);
837
if ((fd = open(drive, O_RDWR)) == -1) {
838
fprintf(stderr, "Error: cannot open %s: %s\n", drive, strerror(errno));
840
/* This error pops up a lot if someone runs lphdisk with no arguments */
841
/* and isn't root, which can happen if they've just */
842
/* installed/encountered it and don't know what it does or how it works. */
843
/* Give 'em a hint if this is the case. */
845
if (argc == 1) printf("(Try '%s --help' for help)\n", argv0);
850
/* Are we looking at a block device? */
852
if (fstat(fd, &st)) {
856
if (S_ISBLK(st.st_mode)) {
858
debug("%s is a block device (major=%d, minor=%d)\n", drive, major(dev),
862
if (!quiet_flag) fprintf(stderr, "Warning: %s is not a block device.\n",
866
fprintf(stderr, "Error: %s is not a block device (override with -f)\n",
872
/* Read the MBR and parse the partition table. */
874
if (read_mbr(fd, mbr_buf)) {
875
fprintf(stderr, "Unable to read master boot record.\n");
879
if (parse_table(mbr_buf, pi)) {
880
fprintf(stderr, "Unable to parse partition table.\n");
884
/* If we're using a block device, then verify it against /proc/partitions */
886
if (dev && check_proc_partitions(dev, pi)) {
888
if (!quiet_flag) fprintf(stderr, "Warning: /proc/partitions does not"
889
" match partition table.\n");
891
fprintf(stderr, "Error: /proc/partitions does not match partition table"
892
" (override with -f).\n");
897
/* Find the right partition. */
899
partition = seek_a0(pi);
901
fprintf(stderr, "Error: Unable to find partition of type %02X.\n",
904
} else if (partition < 0) {
905
fprintf(stderr, "Error: More than one partition is of type %02X. Unable"
906
" to determine which to use.\n", HIBERNATION_ID);
911
/* So far so good.. now it's time to actually _do_ it. */
913
if (!quiet_flag) printf("Creating hibernate area on %s, partition %d...\n",
916
/* Check for a couple of things to warn people about.. */
918
if ((dev != makedev(3, 0)) && !quiet_flag) {
919
fprintf(stderr, "Warning: The BIOS will probably be unable to use this"
920
" hibernate partition\n because it is not on the first IDE"
924
if ((pi[partition-1].size < required_sectors) && !quiet_flag) {
925
fprintf(stderr, "Warning: hibernate partition size (%d) is smaller than"
926
" reccomended size (%d).\n", pi[partition-1].size,
932
if (do_format(fd, pi[partition-1])) {
933
if (!quiet_flag) printf("\n");
934
fprintf(stderr, "Format failed.\n");
937
if (!quiet_flag) printf("\nFormat complete.\n");
940
/* All done.. Clean up and exit. */