#include <errno.h>
#include <fcntl.h>
+#include "hfdisk.h"
#include "io.h"
#include "errors.h"
#include "partition_map.h"
//
// Types
//
-typedef struct names {
- char *abbr;
- char *full;
-} NAMES;
//
// Global Constants
//
-NAMES plist[] = {
- {"Drvr", "Apple_Driver"},
- {"Dr43", "Apple_Driver43"},
- {"Free", "Apple_Free"},
- {" HFS", "Apple_HFS"},
- {" MFS", "Apple_MFS"},
- {"PDOS", "Apple_PRODOS"},
- {"junk", "Apple_Scratch"},
- {"unix", "Apple_UNIX_SVR2"},
- {" map", "Apple_partition_map"},
- {0, 0}
-};
const char * kStringEmpty = "";
const char * kStringNot = " not";
-.TH PDISK 8 "20 December 1996" "MkLinux DR2" "Linux Programmer's Manual"
+.TH HFDISK 8 "20 December 1996" "MkLinux DR2" "Linux Programmer's Manual"
.SH NAME
-pdisk \- Apple partition table editor for Linux
+hfdisk \- Apple partition table editor
.SH SYNOPSIS
-.B pdisk
+.B hfdisk
.B "[\-h|\--help] [\-v|\--version] [\-l|\--list [name ...]]"
.br
-.B pdisk
+.B hfdisk
.B "[\-r|\--readonly]"
device ...
.SH DESCRIPTION
-.B pdisk
+.B hfdisk
is a menu driven program which partitions disks using the standard Apple
disk partitioning scheme described in "Inside Macintosh: Devices".
It does not support the intel/dos partitioning scheme supported by
.TP
.B \-v | \--version
Prints version number of the
-.B pdisk
+.B hfdisk
program.
.TP
.B \-h | \--help
Prints a rather lame set of help messages for the
-.B pdisk
+.B hfdisk
program.
.TP
.B \-l | \--list
.TP
.B \-r | \--readonly
Prevents
-.B pdisk
+.B hfdisk
from writing to the device.
.SH "Editing Partition Tables"
An argument which is simply the name of a
.I device
indicates that
-.B pdisk
+.B hfdisk
should edit the partition table of that device.
The current top level editing commands are:
.B c
(create new partition) command is the only one with complicated arguments.
The first argument is the base address (in blocks) of the partition.
-Besides a raw number, you can also specify a partition number followed
-by the letter 'p' to indicate that the first block of the new partition should
-be the same as the first block of that existing free space partition.
The second argument is the length of the partition in blocks.
-This can be a raw number or can be a partition number followed by the
-letter 'p' to use the size of that partition or can be a number followed
+This can be a raw number or can be a number followed
by 'k', 'm', or 'g' to indicate the size in kilobytes, megabytes, or gigabytes
respectively.
(These are powers of 1024, of course, not powers of 1000.)
.SH BUGS
Some people believe there should really be just one disk partitioning utility.
.br
-.B pdisk
+.B hfdisk
should be able to create HFS partitions that work.
.br
Even more help should be available during user input.
#include <errno.h>
#include <stdlib.h>
#include <string.h>
+#include <inttypes.h>
#include <sys/ioctl.h>
kListOption = 1001
};
+const NAMES plist[] = {
+ {"Drvr", "Apple_Driver"},
+ {"Dr43", "Apple_Driver43"},
+ {"Free", "Apple_Free"},
+ {" HFS", "Apple_HFS"},
+ {" MFS", "Apple_MFS"},
+ {"PDOS", "Apple_PRODOS"},
+ {"junk", "Apple_Scratch"},
+ {"unix", "Apple_UNIX_SVR2"},
+ {" map", "Apple_partition_map"},
+ {0, 0}
+};
+
//
// Global Variables
void do_write_partition_map(partition_map_header *map);
void edit(char *name);
int get_base_argument(long *number, partition_map_header *map);
-int get_command_line(int *argc, char ***argv);
-int get_size_argument(long *number, partition_map_header *map);
+int get_size_argument(uint32_t base, long *number, partition_map_header *map);
int get_options(int argc, char **argv);
void print_notes();
break;
case 'Q':
case 'q':
- flush_to_newline(1);
goto finis;
break;
case 'I':
if (!dflag) {
goto do_error;
} else if (do_expert(map)) {
- flush_to_newline(0);
goto finis;
}
break;
if (get_base_argument(&base, map) == 0) {
return;
}
- if (get_size_argument(&length, map) == 0) {
+ if (get_size_argument(base, &length, map) == 0) {
return;
}
if (get_type == 0) {
add_partition_to_map(name, kUnixType, base, length, map);
- } else if (get_string_argument("Type of partition: ", &type_name, 1) == 0) {
- bad_input("Bad type");
- return;
} else {
- if (strncmp(type_name, kFreeType, DPISTRLEN) == 0) {
- bad_input("Can't create a partition with the Free type");
- return;
- }
- if (strncmp(type_name, kMapType, DPISTRLEN) == 0) {
- bad_input("Can't create a partition with the Map type");
- return;
+ while (1) {
+ if (get_string_argument("Type of partition (L for known types): ", &type_name, 1) == 0) {
+ bad_input("Bad type");
+ return;
+ } else if (strncmp(type_name, kFreeType, DPISTRLEN) == 0) {
+ bad_input("Can't create a partition with the Free type");
+ } else if (strncmp(type_name, kMapType, DPISTRLEN) == 0) {
+ bad_input("Can't create a partition with the Map type");
+ } else if (strncmp(type_name, "L", DPISTRLEN) == 0) {
+ int i = 0;
+ while (plist[i].full) {
+ printf("%s\n", plist[i].full);
+ ++i;
+ }
+ printf("\n");
+ }
+ else
+ {
+ break;
+ }
}
add_partition_to_map(name, type_name, base, length, map);
}
int
get_base_argument(long *number, partition_map_header *map)
{
- partition_map * entry;
- int c;
int result = 0;
- if (get_number_argument("First block: ", number, kDefault) == 0) {
+ uint32_t defaultFirstBlock = find_free_space(map);
+ char prompt[32];
+ sprintf(prompt, "First block [%"PRIu32"]: ", defaultFirstBlock);
+ if (get_number_argument(prompt, number, defaultFirstBlock) == 0) {
bad_input("Bad block number");
} else {
result = 1;
- c = getch();
-
- if (c == 'p' || c == 'P') {
- entry = find_entry_by_disk_address(*number, map);
- if (entry == NULL) {
- bad_input("Bad partition number");
- result = 0;
- } else {
- *number = entry->data->dpme_pblock_start;
- }
- } else if (c > 0) {
- ungetch(c);
- }
}
return result;
}
int
-get_size_argument(long *number, partition_map_header *map)
+get_size_argument(uint32_t base, long *number, partition_map_header *map)
{
- partition_map * entry;
- int c;
int result = 0;
- long multiple;
- if (get_number_argument("Length (in blocks, kB (k), MB (M) or GB (G)): ", number, kDefault) == 0) {
+ uint32_t defaultSize = 20480; // 10MB
+
+ // Work out how much free-space is available.
+ partition_map* part = find_entry_by_sector(base, map);
+ if (part)
+ {
+ size_t partEnd =
+ part->data->dpme_pblock_start +
+ part->data->dpme_pblocks;
+ defaultSize = partEnd - base;
+ }
+
+ char prompt[80];
+ sprintf(
+ prompt,
+ "Length (in blocks, kB (k), MB (M) or GB (G)) [%"PRIu32"]: ",
+ defaultSize);
+
+ if (get_number_argument(prompt, number, defaultSize) == 0) {
bad_input("Bad length");
} else {
result = 1;
- multiple = get_multiplier(PBLOCK_SIZE);
- if (multiple != 1) {
- *number *= multiple;
- } else {
- c = getch();
-
- if (c == 'p' || c == 'P') {
- entry = find_entry_by_disk_address(*number, map);
- if (entry == NULL) {
- bad_input("Bad partition number");
- result = 0;
- } else {
- *number = entry->data->dpme_pblocks;
- }
- } else if (c > 0) {
- ungetch(c);
- }
- }
}
return result;
}
printf("the map causes all data on that partition to be LOST FOREVER. \n");
printf("Make sure you have a backup of any data on such partitions you \n");
printf("want to keep before answering 'yes' to the question below! \n\n");
- if (get_okay("Write partition map? [n/y]: ", 0) != 1) {
+ if (get_okay("Write partition map? [N/y]: ") != 1) {
return;
}
break;
case 'X':
case 'x':
- flush_to_newline(1);
goto finis;
break;
case 'Q':
//
// Types
//
+typedef struct names {
+ char *abbr;
+ char *full;
+} NAMES;
//
// Global Constants
//
+extern const NAMES plist[];
//
//
// Global Variables
//
-short unget_buf[UNGET_MAX_COUNT+1];
-int unget_count;
//
// Forward declarations
//
-long get_number(int first_char);
-char* get_string(int eos);
//
// Routines
//
-int
-getch()
-{
- if (unget_count > 0) {
- return (unget_buf[--unget_count]);
- } else {
- return (getc(stdin));
- }
-}
-
-
-void
-ungetch(int c)
-{
- // In practice there is never more than one character in
- // the unget_buf, but what's a little overkill among friends?
-
- if (unget_count < UNGET_MAX_COUNT) {
- unget_buf[unget_count++] = c;
- } else {
- fatal(-1, "Programmer error in ungetch().");
- }
-}
-
-
-void
-flush_to_newline(int keep_newline)
-{
- int c;
-
- for (;;) {
- c = getch();
-
- if (c <= 0) {
- break;
- } else if (c == '\n') {
- if (keep_newline) {
- ungetch(c);
- }
- break;
- } else {
- // skip
- }
- }
- return;
-}
int
-get_okay(char *prompt, int default_value)
+get_okay(char *prompt)
{
- int c;
-
- flush_to_newline(0);
- printf("%s", prompt);
-
- for (;;) {
- c = getch();
-
- if (c <= 0) {
- break;
- } else if (c == ' ' || c == '\t') {
- // skip blanks and tabs
- } else if (c == '\n') {
- ungetch(c);
- return default_value;
- } else if (c == 'y' || c == 'Y') {
- return 1;
- } else if (c == 'n' || c == 'N') {
- return 0;
- } else {
- flush_to_newline(0);
- printf("%s", prompt);
+ int result = 0;
+ char* string = NULL;
+ if (get_string_argument(prompt, &string, 1))
+ {
+ if (string[0] == 'Y' || string[0] == 'y')
+ {
+ result = 1;
}
}
- return -1;
+ free(string);
+ return result;
}
int
get_command(char *prompt, int promptBeforeGet, int *command)
{
- int c;
-
- if (promptBeforeGet) {
- printf("%s", prompt);
- }
- for (;;) {
- c = getch();
-
- if (c <= 0) {
- break;
- } else if (c == ' ' || c == '\t') {
- // skip blanks and tabs
- } else if (c == '\n') {
- printf("%s", prompt);
- } else {
- *command = c;
- return 1;
- }
+ int result = 0;
+ char* string = NULL;
+ if (get_string_argument(prompt, &string, 1))
+ {
+ *command = string[0];
+ result = 1;
}
- return 0;
+ free(string);
+ return result;
+
}
int
get_number_argument(char *prompt, long *number, long default_value)
{
- int c;
int result = 0;
- for (;;) {
- c = getch();
+ char* buf = NULL;
+ size_t buflen = 0;
+ char multiplier;
+ int matched;
- if (c <= 0) {
+ while (result == 0) {
+ printf("%s", prompt);
+
+ if (getline(&buf, &buflen, stdin) == -1)
+ {
+ // EOF
break;
- } else if (c == ' ' || c == '\t') {
- // skip blanks and tabs
- } else if (c == '\n') {
- if (default_value < 0) {
- printf("%s", prompt);
- } else {
- ungetch(c);
- *number = default_value;
- result = 1;
- break;
- }
- } else if ('0' <= c && c <= '9') {
- *number = get_number(c);
+ }
+ else if ((default_value > 0) && (strncmp(buf, "\n", 1) == 0))
+ {
+ *number = default_value;
result = 1;
break;
- } else {
- ungetch(c);
- *number = 0;
- break;
+ }
+ else if ((matched = sscanf(buf, "%ld%c", number, &multiplier)) >= 1)
+ {
+ result = 1;
+ if (matched == 2) {
+ if (multiplier == 'g' || multiplier == 'G') {
+ *number *= (1024*1024*1024 / PBLOCK_SIZE);
+ } else if (multiplier == 'm' || multiplier == 'M') {
+ *number *= (1024*1024 / PBLOCK_SIZE);
+ } else if (multiplier == 'k' || multiplier == 'K') {
+ *number *= (1024 / PBLOCK_SIZE);
+ } else if (multiplier != '\n') {
+ result = 0;
+ }
+ }
}
}
+ free(buf);
return result;
}
-long
-get_number(int first_char)
-{
- register int c;
- int base;
- int digit;
- int ret_value;
-
- if (first_char != '0') {
- c = first_char;
- base = 10;
- digit = BAD_DIGIT;
- } else if ((c=getch()) == 'x' || c == 'X') {
- c = getch();
- base = 16;
- digit = BAD_DIGIT;
- } else {
- c = first_char;
- base = 8;
- digit = 0;
- }
- ret_value = 0;
- for (ret_value = 0; ; c = getch()) {
- if (c >= '0' && c <= '9') {
- digit = c - '0';
- } else if (c >='A' && c <= 'F') {
- digit = 10 + (c - 'A');
- } else if (c >='a' && c <= 'f') {
- digit = 10 + (c - 'a');
- } else {
- digit = BAD_DIGIT;
- }
- if (digit >= base) {
- break;
- }
- ret_value = ret_value * base + digit;
- }
- ungetch(c);
- return(ret_value);
-}
-
-
int
get_string_argument(char *prompt, char **string, int reprompt)
{
- int c;
int result = 0;
+ size_t buflen = 0;
- for (;;) {
- c = getch();
+ while (result == 0) {
+ printf("%s", prompt);
- if (c <= 0) {
- break;
- } else if (c == ' ' || c == '\t') {
- // skip blanks and tabs
- } else if (c == '\n') {
- if (reprompt) {
- printf("%s", prompt);
- } else {
- ungetch(c);
- *string = NULL;
- break;
- }
- } else if (c == '"' || c == '\'') {
- *string = get_string(c);
- result = 1;
- break;
- } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
- || (c == '-' || c == '/')) {
- ungetch(c);
- *string = get_string(' ');
- result = 1;
- break;
- } else {
- ungetch(c);
- *string = NULL;
+ if (getline(string, &buflen, stdin) == -1)
+ {
+ // EOF
break;
}
- }
- return result;
-}
-
-
-char *
-get_string(int eos)
-{
- int c;
- char *s;
- char *ret_value;
- char *limit;
- int length;
-
- ret_value = (char *) malloc(STRING_CHUNK);
- if (ret_value == NULL) {
- error(errno, "can't allocate memory for string buffer");
- return NULL;
- }
- length = STRING_CHUNK;
- limit = ret_value + length;
-
- c = getch();
- for (s = ret_value; ; c = getch()) {
- if (s >= limit) {
- // expand string
- limit = (char *) malloc(length+STRING_CHUNK);
- if (limit == NULL) {
- error(errno, "can't allocate memory for string buffer");
- ret_value[length-1] = 0;
- break;
- }
- strncpy(limit, ret_value, length);
- free(ret_value);
- s = limit + (s - ret_value);
- ret_value = limit;
- length += STRING_CHUNK;
- limit = ret_value + length;
- }
- if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
- *s++ = 0;
+ else if ((strncmp(*string, "\n", 1) == 0) && !reprompt)
+ {
+ result = 0;
break;
- } else if (c == '\n') {
- *s++ = 0;
- ungetch(c);
- break;
- } else {
- *s++ = c;
}
- }
- return(ret_value);
-}
-
-
-long
-get_multiplier(long divisor)
-{
- int c;
- int result;
-
- c = getch();
-
- if (c <= 0 || divisor <= 0) {
- result = 0;
- } else if (c == 'g' || c == 'G') {
- result = 1024*1024*1024;
- } else if (c == 'm' || c == 'M') {
- result = 1024*1024;
- } else if (c == 'k' || c == 'K') {
- result = 1024;
- } else {
- ungetch(c);
- result = 1;
- }
- if (result > 1) {
- if (result >= divisor) {
- result /= divisor;
- } else {
+ else
+ {
+ size_t len = strnlen(*string, buflen);
+ if ((*string)[len - 1] == '\n') {
+ (*string)[len - 1] = '\0';
+ }
result = 1;
}
}
return result;
}
-
int
number_of_digits(unsigned long value)
{
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
- flush_to_newline(1);
}
//
void bad_input(char *fmt, ...);
int close_device(int fildes);
-void flush_to_newline(int keep_newline);
int get_command(char *prompt, int promptBeforeGet, int *command);
-long get_multiplier(long divisor);
int get_number_argument(char *prompt, long *number, long default_value);
-int get_okay(char *prompt, int default_value);
+int get_okay(char *prompt);
int get_string_argument(char *prompt, char **string, int reprompt);
-int getch();
int number_of_digits(unsigned long value);
int open_device(const char *path, int oflag);
int read_block(int fd, unsigned long num, char *buf, int quiet);
-void ungetch(int c);
int write_block(int fd, unsigned long num, char *buf);
if (oldmap != NULL) {
printf("map already exists\n");
- if (get_okay("do you want to reinit? [n/y]: ", 0) != 1) {
+ if (get_okay("do you want to reinit? [N/y]: ") != 1) {
return oldmap;
}
}
map->maximum_in_map = -1;
number = compute_device_size(fd);
- printf("size of 'device' is %lu blocks: ", number);
- flush_to_newline(0);
- get_number_argument("what should be the size? ", (long *)&number, number);
+ char prompt[64];
+ sprintf(prompt, "Device block size [%lu]: ", number);
+ get_number_argument(prompt, (long *)&number, number);
if (number < 4) {
number = 4;
}
if (valid != 0) {
x = x + 1;
}
- // printf("size in blocks = %d\n", x);
free(data);
}
delete_partition_from_map(entry);
add_partition_to_map("Apple", kMapType, 1, new_size, map);
}
+
+uint32_t
+find_free_space(partition_map_header *map)
+{
+ partition_map * cur;
+ uint32_t result = -1;
+
+ // find a block that starts includes base and length
+ cur = map->base_order;
+ while (cur != NULL) {
+ if (strncmp(cur->data->dpme_type, kFreeType, DPISTRLEN) == 0) {
+ result = cur->data->dpme_pblock_start;
+ break;
+ } else {
+ cur = cur->next_by_base;
+ }
+ }
+ return result;
+}
+
+partition_map*
+find_entry_by_sector(uint32_t lba, partition_map_header *map)
+{
+ partition_map* cur = map->base_order;
+ while (cur != NULL) {
+ if ((cur->data->dpme_pblock_start <= lba) &&
+ (lba < cur->data->dpme_pblock_start + cur->data->dpme_pblocks)) {
+ break;
+ } else {
+ cur = cur->next_by_base;
+ }
+ }
+ return cur;
+}
+
void close_partition_map(partition_map_header *map);
void delete_partition_from_map(partition_map *entry);
partition_map* find_entry_by_disk_address(long index, partition_map_header *map);
+partition_map* find_entry_by_sector(uint32_t lba, partition_map_header *map);
partition_map_header* init_partition_map(char *name, partition_map_header* oldmap);
void move_entry_in_map(long old_index, long index, partition_map_header *map);
partition_map_header* open_partition_map(char *name, int *valid_file);
void resize_map(long new_size, partition_map_header *map);
void write_partition_map(partition_map_header *map);
+uint32_t find_free_space(partition_map_header *map);
#endif