src/sbin/pdisk/pdisk.c

521 lines
11 KiB
C

/* $OpenBSD: pdisk.c,v 1.87 2016/05/28 22:26:13 tb Exp $ */
/*
* pdisk - an editor for Apple format partition tables
*
* Written by Eryk Vershen
*
* Still under development (as of 15 January 1998)
*/
/*
* Copyright 1996,1997,1998 by Apple Computer, Inc.
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appears in all copies and
* that both the copyright notice and this permission notice appear in
* supporting documentation.
*
* APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h> /* DEV_BSIZE */
#include <sys/dkio.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#include "partition_map.h"
#include "io.h"
#include "dump.h"
int lflag; /* list the device */
int rflag; /* open device read Only */
static int first_get = 1;
void do_dump_map(struct partition_map *, int);
void do_change_map_size(struct partition_map *);
void do_create_partition(struct partition_map *, int);
void do_delete_partition(struct partition_map *);
void do_display_entry(struct partition_map *);
void do_rename_partition(struct partition_map *);
void do_change_type(struct partition_map *);
void do_reorder(struct partition_map *);
void do_write_partition_map(struct partition_map *);
void edit(struct partition_map **);
int get_base_argument(long *, struct partition_map *);
int get_size_argument(long *, struct partition_map *);
__dead static void usage(void);
int
main(int argc, char **argv)
{
struct disklabel dl;
struct stat st;
struct partition_map *map;
int c, fd, oflags;
oflags = O_RDWR;
while ((c = getopt(argc, argv, "lr")) != -1) {
switch (c) {
case 'l':
lflag = 1;
oflags = O_RDONLY;
break;
case 'r':
rflag = 1;
oflags = O_RDONLY;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
fd = opendev(*argv, oflags, OPENDEV_PART, NULL);
if (fd == -1)
err(1, "can't open file '%s'", *argv);
if (fstat(fd, &st) == -1)
err(1, "can't fstat %s", *argv);
if (!S_ISCHR(st.st_mode))
errx(1, "%s is not a character device", *argv);
if (ioctl(fd, DIOCGPDINFO, &dl) == -1)
err(1, "can't get disklabel for %s", *argv);
if (dl.d_secsize != DEV_BSIZE)
errx(1, "disk sector size (%d) != 512\n", dl.d_secsize);
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
map = open_partition_map(fd, *argv, DL_GETDSIZE(&dl), dl.d_secsize);
if (map != NULL) {
if (lflag)
dump_partition_map(map);
else
edit(&map);
}
free_partition_map(map);
close(fd);
return 0;
}
/*
* Edit the file
*/
void
edit(struct partition_map **mapp)
{
struct partition_map *map = *mapp;
struct partition_map *oldmap;
int command;
printf("Edit %s -\n", map->name);
while (get_command("Command (? for help): ", first_get, &command)) {
first_get = 0;
switch (command) {
case '?':
printf("Notes:\n"
" Base and length fields are blocks, which "
"vary in size between media.\n"
" The base field can be <nth>p; i.e. the "
"base of the nth partition.\n"
" The length field can be a length followed "
"by k, m, g or t to indicate\n"
" kilo, mega, giga, or tera bytes.\n"
" The length field can also be <nth>p; i.e. "
"the length of the nth partition.\n"
" The name of a partition is descriptive "
"text.\n\n");
/* fall through */
case 'h':
printf("Commands are:\n"
" ? verbose command help\n"
" C create a partition of a specified type\n"
" c create an OpenBSD partition\n"
" d delete a partition\n"
" f full display of a partition\n"
" h command help\n"
" i (re)initialize the partition map\n"
" n (re)name a partition\n"
" P show the partition map's data structures\n"
" p print the partition map\n"
" q quit editing\n"
" r reorder (swap) disk positions of two "
"entries in the partition map\n"
" s change the size of the partition map\n"
" t change the type of a partition\n"
" w write the partition map to disk\n");
break;
case 'P':
do_dump_map(map, 1);
break;
case 'p':
do_dump_map(map, 0);
break;
case 'q':
if (map->changed) {
if (get_okay("Discard changes? [n/y]: ", 0) !=
1) {
break;
}
}
flush_to_newline(1);
return;
case 'i':
if (get_okay("Discard current map? [n/y]: ", 0) == 1) {
oldmap = map;
map = create_partition_map(oldmap->fd,
oldmap->name, oldmap->media_size,
oldmap->sbBlkSize);
if (map == NULL)
break;
*mapp = map;
free_partition_map(oldmap);
}
break;
case 'C':
do_create_partition(map, 1);
break;
case 'c':
do_create_partition(map, 0);
break;
case 'n':
do_rename_partition(map);
break;
case 'd':
do_delete_partition(map);
break;
case 'r':
do_reorder(map);
break;
case 's':
do_change_map_size(map);
break;
case 't':
do_change_type(map);
break;
case 'w':
do_write_partition_map(map);
break;
case 'f':
do_display_entry(map);
break;
default:
bad_input("No such command (%c)", command);
break;
}
}
}
void
do_create_partition(struct partition_map *map, int get_type)
{
long base, length;
char *name = NULL;
char *type = NULL;
if (get_base_argument(&base, map) == 0)
return;
if (get_size_argument(&length, map) == 0)
return;
name = get_dpistr_argument("Name of partition: ");
if (name == NULL) {
bad_input("Bad name");
goto out;
}
if (get_type == 0)
type = strdup(kUnixType);
else
type = get_dpistr_argument("Type of partition: ");
if (type == NULL) {
bad_input("Bad type");
goto out;
}
if (strncasecmp(type, kFreeType, DPISTRLEN) == 0) {
bad_input("Can't create a partition with the Free type");
goto out;
}
if (strncasecmp(type, kMapType, DPISTRLEN) == 0) {
bad_input("Can't create a partition with the Map type");
goto out;
}
add_partition_to_map(name, type, base, length, map);
out:
free(type);
free(name);
return;
}
int
get_base_argument(long *number, struct partition_map *map)
{
struct entry *entry;
int result = 0;
if (get_number_argument("First block: ", number) == 0) {
bad_input("Bad block number");
} else {
result = 1;
if (get_partition_modifier()) {
entry = find_entry_by_disk_address(*number, map);
if (entry == NULL) {
bad_input("Bad partition number");
result = 0;
} else {
*number = entry->dpme_pblock_start;
}
}
}
return result;
}
int
get_size_argument(long *number, struct partition_map *map)
{
struct entry *entry;
unsigned long multiple;
int result = 0;
if (get_number_argument("Length in blocks: ", number) == 0) {
bad_input("Bad length");
} else {
multiple = get_multiplier(map->sbBlkSize);
if (multiple == 0) {
bad_input("Bad multiplier");
} else if (multiple != 1) {
*number *= multiple;
result = 1;
} else if (get_partition_modifier()) {
entry = find_entry_by_disk_address(*number, map);
if (entry == NULL) {
bad_input("Bad partition number");
} else {
*number = entry->dpme_pblocks;
result = 1;
}
} else {
result = 1;
}
}
return result;
}
void
do_rename_partition(struct partition_map *map)
{
struct entry *entry;
char *name;
long ix;
if (get_number_argument("Partition number: ", &ix) == 0) {
bad_input("Bad partition number");
return;
}
entry = find_entry_by_disk_address(ix, map);
if (entry == NULL) {
printf("No such partition\n");
return;
}
printf("Existing partition name ``%s''.\n", entry->dpme_name);
name = get_dpistr_argument("New name of partition: ");
if (name == NULL) {
bad_input("Bad name");
return;
}
/*
* Since dpme_name is supposed to be NUL-filled, make sure
* current contents are zapped before copying in new name!
*/
memset(entry->dpme_name, 0, sizeof(entry->dpme_name));
strlcpy(entry->dpme_name, name, sizeof(entry->dpme_name));
map->changed = 1;
free(name);
return;
}
void
do_change_type(struct partition_map *map)
{
struct entry *entry;
char *type;
long ix;
if (get_number_argument("Partition number: ", &ix) == 0) {
bad_input("Bad partition number");
return;
}
entry = find_entry_by_disk_address(ix, map);
if (entry == NULL) {
printf("No such partition\n");
return;
}
printf("Existing partition type ``%s''.\n", entry->dpme_type);
type = get_dpistr_argument("New type of partition: ");
if (type == NULL) {
bad_input("Bad type");
return;
}
/*
* Since dpme_type is supposed to be NUL-filled, make sure
* current contents are zapped before copying in new type!
*/
memset(entry->dpme_type, 0, sizeof(entry->dpme_type));
strncpy(entry->dpme_type, type, sizeof(entry->dpme_type));
map->changed = 1;
free(type);
return;
}
void
do_delete_partition(struct partition_map *map)
{
struct entry *cur;
long ix;
if (get_number_argument("Partition number: ", &ix) == 0) {
bad_input("Bad partition number");
return;
}
cur = find_entry_by_disk_address(ix, map);
if (cur == NULL)
printf("No such partition\n");
else
delete_partition_from_map(cur);
}
void
do_reorder(struct partition_map *map)
{
long ix, old_index;
if (get_number_argument("Partition number: ", &old_index) == 0) {
bad_input("Bad partition number");
return;
}
if (get_number_argument("New number: ", &ix) == 0) {
bad_input("Bad partition number");
return;
}
move_entry_in_map(old_index, ix, map);
}
void
do_write_partition_map(struct partition_map *map)
{
if (map->changed == 0) {
bad_input("The map has not been changed.");
return;
}
if (rflag) {
bad_input("The map is not writable.");
return;
}
printf("Writing the map destroys what was there before. ");
if (get_okay("Is that okay? [n/y]: ", 0) != 1) {
return;
}
write_partition_map(map);
map->changed = 0;
}
void
do_change_map_size(struct partition_map *map)
{
long size;
if (get_number_argument("New size: ", &size) == 0) {
bad_input("Bad size");
return;
}
resize_map(size, map);
}
void
do_display_entry(struct partition_map *map)
{
long number;
if (get_number_argument("Partition number: ", &number) == 0) {
bad_input("Bad partition number");
return;
}
if (number == 0)
full_dump_block_zero(map);
else
full_dump_partition_entry(map, number);
}
void
do_dump_map(struct partition_map *map, int verbose)
{
if (verbose)
show_data_structures(map);
else
dump_partition_map(map);
}
__dead static void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-lr] disk\n", __progname);
exit(1);
}