Subject: Reworked sys/arch/sgimips/stand/sgivol/sgivol.c
To: None <port-sgimips@netbsd.org>
From: sgimips NetBSD list <sgimips@mrynet.com>
List: port-sgimips
Date: 02/09/2002 10:26:40
Could someone please review and consider my modified sgivol.c
for checkin into the source tree?

The changes are basically as follows...

CURRENT:
mod80 (605)# /ousr/mdec/sgivol
Usage:  sgivol [-i] device
        sgivol [-r vhfilename diskfilename] device
        sgivol [-w vhfilename diskfilename] device
        sgivol [-d vhfilename] device

MODIFIED:
mod80 (606)# /usr/mdec/sgivol
Usage: sgivol device
        sgivol (to view volume header)
or sgivol -i [-qf] device
        sgivol (to initialize volumeheader)
or sgivol -r [-q] vhfilename diskfilename device
        sgivol (to read a disk file from the volume header)
or sgivol -w [-qf] vhfilename diskfilename device
        sgivol (to write a disk file to the volume header)
or sgivol -d [-qf] vhfilename device
        sgivol (to delete a volume header file)
or sgivol -p [-q] partition first blocks type device
        sgivol (to modify a volume header partition entry)

I have modified sgivol to accept the -q and -f flags.

The -f flag (force) bypasses any confirmation prompting.
This is necessary for release-generation and install-time
procedures.

The -q flag (quiet) mutes normal processing output.  This
cleans up the install-time screen.

Thanks for the consideration.

-scott
--------------------------
/*
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Michael Hitch and Hubert Feyrer.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/ioctl.h> 
#include <sys/disklabel.h>
#include <sys/stat.h> 
 
#include <errno.h>
#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <util.h>

/*
 * Some IRIX man pages refer to the size being a multiple of whole cylinders.
 * Later ones only refer to the size being "typically" 2MB.  IRIX fx(1)
 * uses a default drive geometry if one can't be determined, suggesting
 * that "whole cylinder" multiples are not required.
 */

#define SGI_SIZE_VOLHDR	3135	/* XXX This is really arbitrary */

int	fd;
int	n;
int	opt_i = 0;			/* Initialize volume header */
int	opt_r = 0;			/* Read a file from volume header */
int	opt_w = 0;			/* Write a file to volume header */
int	opt_d = 0;			/* Delete a file from volume header */
int	opt_p = 0;			/* Modify a partition */
int	opt_f = 0;			/* Force actions */
int	opt_q = 0;			/* quiet mode */
char	*vfilename = "";
char	*ufilename = "";
int	partno, partfirst, partblocks, parttype;
struct	sgilabel *volhdr;
int32_t	checksum;

struct disklabel lbl;

unsigned char buf[512];

char *sgi_types[] = {
	"Volume Header",
	"Repl Trks",
	"Repl Secs",
	"Raw",
	"BSD4.2",
	"SysV",
	"Volume",
	"EFS",
	"LVol",
	"RLVol",
	"XFS",
	"XSFLog",
	"XLV",
	"XVM"
};

void    display_vol(void);
void    init_volhdr(void);
void    read_file(void);
void    write_file(void);
void    delete_file(void);
void    modify_partition(void);
void    write_volhdr(void);
int     allocate_space(int);
void    checksum_vol(void);
void    usage(void);

int
main(int argc, char **argv)
{
	int ch;
	while ((ch = getopt(argc, argv, "irwpdqf")) != -1)
		switch (ch) {
		/* -i, -r, -w, -d and -p override each other */
		/* -q implies -f */
		case 'q':
			++opt_q;
			++opt_f;
			break;
		case 'f':
			++opt_f;
			break;
		case 'i':
			++opt_i;
			opt_r = opt_w = opt_d = opt_p = 0;
			break;
		case 'r':
			++opt_r;
			opt_i = opt_w = opt_d = opt_p = 0;
			break;
		case 'w':
			++opt_w;
			opt_i = opt_r = opt_d = opt_p = 0;
			break;
		case 'd':
			++opt_d;
			opt_i = opt_r = opt_w = opt_p = 0;
			break;
		case 'p':
			++opt_p;
			opt_i = opt_r = opt_w = opt_d = 0;
			partno = atoi(argv[0]);
			partfirst = atoi(argv[1]);
			partblocks = atoi(argv[2]);
			parttype = atoi(argv[3]);
			break;
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (opt_r || opt_w) {
		if (argc != 3)
			usage();
		vfilename = argv[0];
		ufilename = argv[1];
		argc -= 2;
		argv += 2;
	}
	if (opt_d) {
		if (argc != 2)
			usage();
		vfilename = argv[0];
		argc--;
		argv++;
	}

	if (opt_p) {
		if (argc != 5)
			usage();
		partno = atoi(argv[0]);
		partfirst = atoi(argv[1]);
		partblocks = atoi(argv[2]);
		parttype = atoi(argv[3]);
		argc -= 4;
		argv += 4;
	}
	if (argc != 1)
		usage();
	
	fd = open(argv[0], (opt_i | opt_w | opt_d | opt_p) ? O_RDWR : O_RDONLY);
	if (fd < 0) {
		sprintf(buf, "/dev/r%s%c", argv[0], 'a' + getrawpartition());
		fd = open(buf, (opt_i | opt_w | opt_d | opt_p) 
				? O_RDWR : O_RDONLY);
		if (fd < 0) {
			printf("Error opening device %s: %s\n",
				argv[0], strerror(errno));
			exit(1);
		}
	}
	n = read(fd, buf, sizeof(buf));
	if (n != sizeof(buf)) {
		perror("read volhdr");
		exit(1);
	}
	n = ioctl(fd, DIOCGDINFO, &lbl);
	if (n < 0) {
		perror("DIOCGDINFO");
		exit(1);
	}
	volhdr = (struct sgilabel *)buf;
	if (opt_i) {
		init_volhdr();
		exit(0);
	}
	if (volhdr->magic != SGILABEL_MAGIC) {
		printf("No Volume Header found, magic=%x.  Use -i first.\n", 
			volhdr->magic);
		exit(1);
	}
	if (opt_r) {
		read_file();
		exit(0);
	}
	if (opt_w) {
		write_file();
		exit(0);
	}
	if (opt_d) {
		delete_file();
		exit(0);
	}
	if (opt_p) {
		modify_partition();
		exit(0);
	}
	display_vol();

	exit(0);
}

void
display_vol(void)
{
	int32_t	*l;

	l = (int32_t *)buf;
	checksum = 0;
	for (n = 0; n < 512 / 4; ++n)
		checksum += l[n];
	printf("disklabel shows %d sectors\n", lbl.d_secperunit);
	printf("checksum:  %08x%s\n", checksum, checksum == 0 
		? "" : " *ERROR*");
	printf("root part: %d\n", volhdr->root);
	printf("swap part: %d\n", volhdr->swap);
	printf("bootfile:  %s\n", volhdr->bootfile);
	/* volhdr->devparams[0..47] */
	printf("\nVolume header files:\n");
	for (n = 0; n < 15; ++n)
		if (volhdr->voldir[n].name[0])
			printf("%-8s offset %4d blocks, length %8d bytes (%d blocks)\n",
			    volhdr->voldir[n].name, volhdr->voldir[n].block,
			    volhdr->voldir[n].bytes, (volhdr->voldir[n].bytes + 511 ) / 512);
	printf("\nSGI partitions:\n");
	for (n = 0; n < MAXPARTITIONS; ++n)
		if (volhdr->partitions[n].blocks)
			printf("%2d:%c blocks %8d first %8d type %2d (%s)\n", 
			    n, n + 'a',
			    volhdr->partitions[n].blocks, 
			    volhdr->partitions[n].first,
			    volhdr->partitions[n].type,
			    volhdr->partitions[n].type > 13 ? "???" :
			    sgi_types[volhdr->partitions[n].type]);
}

void
init_volhdr(void)
{
	memset(buf, 0, sizeof(buf));
	volhdr->magic = SGILABEL_MAGIC;
	volhdr->root = 0;
	volhdr->swap = 1;
	strcpy(volhdr->bootfile, "netbsd");
	volhdr->partitions[10].blocks = lbl.d_secperunit;
	volhdr->partitions[10].first = 0;
	volhdr->partitions[10].type = SGI_PTYPE_VOLUME;
	volhdr->partitions[8].blocks = SGI_SIZE_VOLHDR;
	volhdr->partitions[8].first = 0;
	volhdr->partitions[8].type = SGI_PTYPE_VOLHDR;
	volhdr->partitions[0].blocks = lbl.d_secperunit - SGI_SIZE_VOLHDR;
	volhdr->partitions[0].first = SGI_SIZE_VOLHDR;
	volhdr->partitions[0].type = SGI_PTYPE_EFS;
	write_volhdr();
}

void
read_file(void)
{
	FILE *fp;

	if (!opt_q)
		printf("Reading file %s\n", vfilename);
	for (n = 0; n < 15; ++n) {
		if (strcmp(vfilename, volhdr->voldir[n].name) == NULL)
			break;
	}
	if (n >= 15) {
		printf("file %s not found\n", vfilename);
		exit(1);
	}
	lseek(fd, volhdr->voldir[n].block * 512, SEEK_SET);
	fp = fopen(ufilename, "w");
	if (fp == NULL) {
		perror("open write");
		exit(1);
	}
	n = volhdr->voldir[n].bytes;
	while (n > 0) {
		if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
			perror("read file");
			exit(1);
		}
		fwrite(buf, 1, n > sizeof(buf) ? sizeof(buf) : n, fp);
		n -= n > sizeof(buf) ? sizeof(buf) : n;
	}
	fclose(fp);
}

void
write_file(void)
{
	FILE *fp;
	int fno;
	int block;
	struct stat st;
	char fbuf[512];

	if (!opt_q)
		printf("Writing file %s\n", ufilename);
	if (stat(ufilename, &st) < 0) {
		perror("stat");
		exit(1);
	}
	if (!opt_q)
		printf("File %s has %lld bytes\n", ufilename, st.st_size);
	fno = -1;
	for (n = 0; n < 15; ++n) {
		if (volhdr->voldir[n].name[0] == '\0' && fno < 0)
			fno = n;
		if (strcmp(vfilename, volhdr->voldir[n].name) == 0) {
			fno = n;
			break;
		}
	}
	if (fno == -1) {
		printf("No directory space for file %s\n", vfilename);
		exit(1);
	}
	/* -w can overwrite, -a won't overwrite */
	if (volhdr->voldir[fno].block > 0) {
		if (!opt_q)
			printf("File %s exists, removing old file\n", 
				vfilename);
		volhdr->voldir[fno].name[0] = 0;
		volhdr->voldir[fno].block = volhdr->voldir[fno].bytes = 0;
	}
	if (st.st_size == 0) {
		printf("bad file size\n");
		exit(1);
	}
	block = allocate_space((int)st.st_size);
	if (block < 0) {
		printf("No space for file\n");
		exit(1);
	}

	/* Warn if the filename exceeds 8 chars -- Terminating NULL */
	/* not required for 8-character filenames */
	if (strlen(vfilename) > sizeof(volhdr->voldir[fno].name) - 1
			&& !opt_q) {
		printf("Warning: '%s' will be truncated to 8 characters.\n",
		       		vfilename);
	}
	
	strncpy(volhdr->voldir[fno].name, vfilename, 8);

	volhdr->voldir[fno].block = block;
	volhdr->voldir[fno].bytes = st.st_size;

	write_volhdr();

	/* write the file itself */
	n = lseek(fd, block * 512, SEEK_SET);
	if (n < 0) {
		perror("lseek write");
		exit(1);
	}
	n = st.st_size;
	fp = fopen(ufilename, "r");
	while (n > 0) {
		fread(fbuf, 1, n > 512 ? 512 : n, fp);
		if (write(fd, fbuf, 512) != 512) {
			perror("write file");
			exit(1);
		}
		n -= n > 512 ? 512 : n;
	}
}

void
delete_file(void)
{
	for (n = 0; n < 15; ++n) {
		if (strcmp(vfilename, volhdr->voldir[n].name) == NULL) {
			break;
		}
	}
	if (n >= 15) {
		if (!opt_q)
			printf("File %s not found\n", vfilename);
		exit(1);
	}
	volhdr->voldir[n].name[0] = '\0';
	volhdr->voldir[n].block = volhdr->voldir[n].bytes = 0;
	write_volhdr();
}

void
modify_partition(void)
{
	if (!opt_p)
		printf("Modify partition %d start %d length %d\n", 
			partno, partfirst, partblocks);
	if (partno < 0 || partno > 15) {
		printf("Invalid partition number: %d\n", partno);
		exit(1);
	}
	volhdr->partitions[partno].blocks = partblocks;
	volhdr->partitions[partno].first = partfirst;
	volhdr->partitions[partno].type = parttype;
	write_volhdr();
}

void
write_volhdr(void)
{
	checksum_vol();
	if (!opt_q)
		display_vol();
	if (!opt_f) {
		printf("\nDo you want to update volume (y/n)? ");
		n = getchar();
		if (n != 'Y' && n != 'y')
			exit(1);
	}
	n = lseek(fd, 0 , SEEK_SET);
	if (n < 0) {
		perror("lseek 0");
		exit(1);
	}
	n = write(fd, buf, 512);
	if (n < 0)
		perror("write volhdr");
}

/*
 * HF: There's still a bug in there which allows things like:
 * 
 *   Volume header files:
 *   sgiboot  offset    2 blocks, length   260730 bytes (510 blocks)
 *   sash     offset    2 blocks, length   283648 bytes (554 blocks)
 *                     ^^^
 */
int
allocate_space(int size)
{
	int n2, blocks;
	int first;

	blocks = (size + 511) / 512;
	first = 2;
	n2 = 0;
	while (n2 < 15) {
		if (volhdr->voldir[n2].name[0]) {
			if (first < (volhdr->voldir[n2].block +
			    (volhdr->voldir[n2].bytes + 511) / 512) &&
			    (first + blocks) >= volhdr->voldir[n2].block) {
				first = volhdr->voldir[n2].block +
				    (volhdr->voldir[n2].bytes + 511) / 512;
#ifdef DEBUG
printf("allocate: n=%d first=%d blocks=%d size=%d\n", n2, first, blocks, size);
printf("%s %d %d\n", volhdr->voldir[n2].name, volhdr->voldir[n2].block, 
		volhdr->voldir[n2].bytes);
printf("first=%d block=%d last=%d end=%d\n", first, volhdr->voldir[n2].block,
		first + blocks - 1, 
		volhdr->voldir[n2].block + 
				(volhdr->voldir[n2].bytes + 511)/512);
#endif
				n2 = 0;
				continue;
			}
		}
		++n2;
	}
	if (first + blocks > lbl.d_secperunit)
		first = -1;
	return(first);
}

void
checksum_vol(void)
{
	int32_t	*l;

	volhdr->checksum = checksum = 0;
	l = (int32_t *)buf;
	for (n = 0; n < 512 / 4; ++n)
		checksum += l[n];
	volhdr->checksum = -checksum;
}

void
usage(void)
{
	int i;
	static const struct {
		const char *name;
		const char *expn;
	} usages[] = {
	{ "device",
	    "(to view volume header)" },
	{ "-i [-qf] device",
	    "(to initialize volumeheader)" },
	{ "-r [-q] vhfilename diskfilename device",
	    "(to read a disk file from the volume header)" },
	{ "-w [-qf] vhfilename diskfilename device",
	    "(to write a disk file to the volume header)" },
	{ "-d [-qf] vhfilename device",
	    "(to delete a volume header file)" },
	{ "-p [-q] partition first blocks type device",
	    "(to modify a volume header partition entry)" },
	{ NULL,
	    NULL }
	};

	if (!opt_q) 
            for (i = 0; usages[i].name; i++) {
                (void) fputs(i ? "or " : "Usage: ", stderr);
                (void) fprintf(stderr, "%s %s", getprogname(), usages[i].name);
                (void) fputs("\n\t", stderr);
                (void) fprintf(stderr, "%s %s", getprogname(), usages[i].expn);
                (void) fputs("\n", stderr);
            }
	exit(1);
}