/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: newfs.c,v 1.23.1.1 92/06/24 13:29:21 marker Exp $"

/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */
/* @(#)newfs.c	1.4 88/06/21 4.0NFSSRC; from	5.2 (Berkeley) 9/11/85" */

/*
 * newfs: friendly front end to mkfs
 */
#include <sys/param.h>
#include <sys/stat.h>
#include <ufs/fs.h>
#include <sys/dir.h>
#include <stdio.h>
#include <disktab.h>

#ifdef RISCOS
#include <sysv/sys.s>		/* making SysV system call */
#include <sysv/sys/dkio.h>
#define ustat(_rdev, _ustatarea) syscall(SYS_utssys, _ustatarea, _rdev, 2)
#define DEFAULT_BLOCK_SIZE (8*1024)
#endif
#ifndef mips
#define	BOOTDIR	"/usr/mdec"	/* directory for boot blocks */
#endif
#include <fcntl.h>
#include <errno.h>
#include <sys/vdisk.h>

int	Nflag;			/* run mkfs without writing file system */
int	verbose;		/* show mkfs line before exec */
int	noboot;			/* do not fill boot blocks */
int	fssize;			/* file system size */
int	fsize;			/* fragment size */
int	bsize;			/* block size */
int	ntracks;		/* # tracks/cylinder */
int	nsectors;		/* # sectors/track */
int	sectorsize;		/* bytes/sector */
int	cpg;			/* cylinders/cylinder group */
int	minfree = -1;		/* free space threshold */
int	opt;			/* optimization preference (space or time) */
int	rpm;			/* revolutions/minute of drive */
int	density;		/* number of bytes per inode */

char	device[MAXPATHLEN];
char	cmd[BUFSIZ];

char	*index();
char	*rindex();
char	*sprintf();

main(argc, argv)
	int argc;
	char *argv[];
{
	char *cp, *special;
	struct volume_header vh;
	register struct partition_table *pp;
	struct stat sb;
	register int i;
	int status;
	int part;
	int getsize = 0;

#ifdef RISCOS
	struct {
		daddr_t tfree;
		ino_t tinode;
		char fname[6];
		char fpack[6];
	} ustatarea;
#endif

	argc--, argv++;
	while (argc > 0 && argv[0][0] == '-') {
		for (cp = &argv[0][1]; *cp; cp++)
			switch (*cp) {

			case 'v':
				verbose++;
				break;

			case 'N':
				Nflag++;
				/* fall through to */

			case 'n':
				noboot++;
				break;

			case 's':
				if (argc < 1)
					fatal("-s: missing file system size");
				argc--, argv++;
				fssize = atoi(*argv);
				if (fssize < 0)
					fatal("%s: bad file system size",
						*argv);
				goto next;

			case 't':
				if (argc < 1)
					fatal("-t: missing track total");
				argc--, argv++;
				ntracks = atoi(*argv);
				if (ntracks < 0)
					fatal("%s: bad total tracks", *argv);
				goto next;

			case 'o':
				if (argc < 1)
					fatal("-o: missing optimization preference");
				argc--, argv++;
				if (strcmp(*argv, "space") == 0)
					opt = FS_OPTSPACE;
				else if (strcmp(*argv, "time") == 0)
					opt = FS_OPTTIME;
				else
					fatal("%s: bad optimization preference %s",
					    *argv,
					    "(options are `space' or `time')");
				goto next;

			case 'b':
				if (argc < 1)
					fatal("-b: missing block size");
				argc--, argv++;
				bsize = atoi(*argv);
				if (bsize < 0 || bsize < MINBSIZE)
					fatal("%s: bad block size", *argv);
				goto next;

			case 'f':
				if (argc < 1)
					fatal("-f: missing frag size");
				argc--, argv++;
				fsize = atoi(*argv);
				if (fsize < 0)
					fatal("%s: bad frag size", *argv);
				goto next;

			case 'S':
				if (argc < 1)
					fatal("-S: missing sector size");
				argc--, argv++;
				sectorsize = atoi(*argv);
				if (sectorsize < 0)
					fatal("%s: bad sector size", *argv);
				goto next;

			case 'c':
				if (argc < 1)
					fatal("-c: missing cylinders/group");
				argc--, argv++;
				cpg = atoi(*argv);
				if (cpg < 0)
					fatal("%s: bad cylinders/group", *argv);
				goto next;

			case 'm':
				if (argc < 1)
					fatal("-m: missing free space %%\n");
				argc--, argv++;
				minfree = atoi(*argv);
				if (minfree < 0 || minfree > 99)
					fatal("%s: bad free space %%\n",
						*argv);
				goto next;

			case 'r':
				if (argc < 1)
					fatal("-r: missing revs/minute\n");
				argc--, argv++;
				rpm = atoi(*argv);
				if (rpm < 0)
					fatal("%s: bad revs/minute\n", *argv);
				goto next;

			case 'i':
				if (argc < 1)
					fatal("-i: missing bytes per inode\n");
				argc--, argv++;
				density = atoi(*argv);
				if (density < 0)
					fatal("%s: bad bytes per inode\n",
						*argv);
				goto next;

			default:
				fatal("-%c: unknown flag", cp);
			}
next:
		argc--, argv++;
	}
	if (argc < 1) {
		fprintf(stderr, "usage: newfs.ffs [ -v ] [ mkfs-options ] %s\n",
			"special-device ");
		fprintf(stderr, "where mkfs-options are:\n");
		fprintf(stderr, "\t-N do not create file system, %s\n",
			"just print out parameters");
		fprintf(stderr, "\t-s file system size (sectors)\n");
		fprintf(stderr, "\t-b block size\n");
		fprintf(stderr, "\t-f frag size\n");
		fprintf(stderr, "\t-t tracks/cylinder\n");
		fprintf(stderr, "\t-c cylinders/group\n");
		fprintf(stderr, "\t-m minimum free space %%\n");
		fprintf(stderr, "\t-o optimization preference %s\n",
			"(`space' or `time')");
		fprintf(stderr, "\t-r revolutions/minute\n");
		fprintf(stderr, "\t-S sector size\n");
		fprintf(stderr, "\t-i number of bytes per inode\n");
		exit(1);
	}
	special = argv[0];
#ifndef RISCOS
	/*
	 * Sys V doesn't follow the berkeley disk naming conventions.
	 * Make user specify the character device.
	 */
	cp = rindex(special, '/');
	if (cp != 0)
		special = cp + 1;
	if (*special == 'r' && special[1] != 'a' && special[1] != 'b')
		special++;
	special = sprintf(device, "/dev/r%s", special);
#endif
	if (stat(special, &sb) < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(special);
		exit(2);
	}
#ifdef RISCOS
	/*
	 * This is a hack to discourage users from newfs'ing a mounted
	 * file system. The first part checks if a newfs is being done
	 * on a character special device and looks for a corresponding
	 * block device.
	 */

	if ((sb.st_mode & S_IFMT) == S_IFCHR) {
		if ((cp=rindex(special, '/')) != NULL)
			sprintf(device, "/dev/dsk%s", cp);
		else {
			sprintf(device, "/dev/dsk/%s", special);
		}
		if (stat(device, &sb) < 0) {
			fprintf(stderr, "newfs.ffs: %s: no corresponding block device\n", special);
			exit(2);
		}
	}
	/*
	 * Check if block device is mounted.
	 */
	if((sb.st_mode & S_IFMT) == S_IFBLK)
		if(ustat(sb.st_rdev,&ustatarea) >= 0) {
			fprintf(stderr, "*** MOUNTED FILE SYSTEM\n");
			if (!Nflag)
				exit(1);
		}
#else
	/* We no longer need the below check because our closing of
	 * block devices is guaranteed to flush all blocks to disk before
	 * they are invalidated, unlike other unixes.
	 */
	if ((sb.st_mode & S_IFMT) != S_IFCHR)
		fatal("%s: not a character device", special);
#endif
	cp = index(argv[0], '\0') - 1;
#ifdef RISCOS
	/* this code is INSERTED here to get the volume header */
	/* I did it this way so it is ISOLATED */
	{
		int fd,atoi();
		struct io_arg	ia;
		dev_t physical_rdev , get_physical();
		char *ptr, *buf, *buf_ptr, *calloc(), *find_phys();
		buf = calloc(1,strlen(special)+12);	/* add slop */

		if((strncmp("/dev/dsk/",special,9) != 0) &&
		   (strncmp("/dev/rdsk/", special,10) != 0) &&
		   (strncmp("/dev/scsi/", special,10) != 0) &&
		   (strncmp("/dev/rscsi/", special,11) != 0)) {
		   if(!(buf_ptr = find_phys("/dev/dsk", sb.st_rdev)))
			if(!(buf_ptr = find_phys("/dev/scsi", sb.st_rdev)))
				fatal("%s:cannot find physical device",special);
		   strcpy(special, buf_ptr);
		}


		if (strncmp("/dev/r",special,6) != 0) {
			if (strncmp(special,"/dev/",5) == 0) {
				(void) strcpy(buf,"/dev/r");
				(void) strcat(buf,&special[5]);
			}
		}
		else {
			(void) strcpy(buf,special);
		}
		ptr = rindex(buf,'/');
		if (ptr == NULL)	/* no '/' */
			goto out;
		ptr++;ptr++;ptr++;
		if (*ptr != 'c')	/* no 'c' */
			goto out;
		ptr++;
		while (*ptr && *ptr >= '0' && *ptr <= '9') ptr++;
		if (*ptr != 'd')	/* no 'd' */
			goto out;
		ptr++;
		while (*ptr && *ptr >= '0' && *ptr <= '9') ptr++;
		if (*ptr != 's')	/* no 's' */
			goto out;
		ptr++;
		part = atoi(ptr);
		if (part < 0 || part > 15)
			goto out;
		if (stat(buf, &sb) < 0)
			fatal("%s: cannot stat `%s'", argv[1], buf);
		if ((fd = open(buf, 0)) < 0) 
			fatal("%s: cannot open `%s'", argv[1], buf);
	
		if (ioctl (fd, DIOCGETSIZE, &ia) >= 0) {
			getsize = ia.retval;
		}
		if ((physical_rdev = get_physical(sb.st_rdev)) != sb.st_rdev) {
			close (fd);
			if(!(buf_ptr = find_phys("/dev/dsk", physical_rdev)))
			    if(!(buf_ptr= find_phys("/dev/scsi",physical_rdev)))
				fatal("%s:cannot find physical device",special);
       	                (void) strcpy(buf,"/dev/r");
			(void) strcat(buf,&buf_ptr[5]);
			if (stat(buf, &sb) < 0)
				fatal("%s: cannot stat `%s'", argv[1], buf);
			if ((fd = open(buf, 0)) < 0) 
				fatal("%s: cannot open `%s'", argv[1], buf);
		}
		ia.retval = ia.sectst = 0;
		ia.memaddr = (unsigned long)&vh;
		ia.datasz = sizeof(vh);
		if (ioctl(fd, DIOCGETVH, &ia) < 0) {
			(void) close(fd);
			fatal("%s: cannot get volume header on `%s'", argv[1], buf);
		}
		(void) close(fd);
		if (vh.vh_magic != VHMAGIC) {
			fatal("%s: bad volume header on `%s'", 
				argv[1], buf);
			exit(1);
		}
out:
		part++;		/* placeholder for the goto's above */

	}
#endif
	part = minor(sb.st_rdev) % NPARTAB;
	if ((part >= NPARTAB) || (part < 0) || (part == 8) || (part == 9) || (part == 10))
		fatal("%s: can't figure out file system partition", argv[0]);
	pp = &vh.vh_pt[part];
	if (getsize != 0) {
		if (fssize == 0) {
			fssize = getsize;
		} else if (fssize > getsize) {
			fatal("specified filesystem size %d exceeds partition size %d",
				fssize, getsize);
		}
	} else if (fssize == 0) {
		fssize = pp->pt_nblks;
		if (fssize < 0)
			fatal("no defined size for `%c' partition %d",
				*cp,part);
	} else {
		if (fssize > pp->pt_nblks) {
			fatal("specified filesystem size %d exceeds partition size %d",
				fssize, pp->pt_nblks);
		}
	}
	if (nsectors == 0) {
		nsectors = vh.vh_dp.dp_secs;
		if (nsectors < 0)
			fatal("no defined #sectors/track");
	}
	if (ntracks == 0) {
		ntracks = vh.vh_dp.dp_trks0;
		if (ntracks < 0)
			fatal("no defined #tracks/cylinder");
	}
	if (sectorsize == 0) {
		sectorsize = vh.vh_dp.dp_secbytes;
		if (sectorsize < 0)
			fatal("no defined sector size");
	}
	if (bsize == 0) {
		bsize = DEFAULT_BLOCK_SIZE;
	}
	if (fsize == 0) {
		fsize = bsize/8;
	}
	if (rpm == 0) {
		rpm = 3600;	/* unused */
	}
	/* XXX - following defaults are both here and in mkfs */
	if (density <= 0)
		density = 2048;
	if (minfree < 0)
		minfree = 10;
	if (minfree < 10 && opt != FS_OPTSPACE) {
		fprintf(stderr, "setting optimization for space ");
		fprintf(stderr, "with minfree less than 10%\n");
		opt = FS_OPTSPACE;
	}
	if (cpg == 0)
		cpg = 16;
#ifdef RISCOS
	/*
	 * Round the partition size down to to an integral number of
	 * filesystem blocks.
	 */
	fssize = (fssize / (bsize / DEV_BSIZE)) * (bsize / DEV_BSIZE);
#endif
	sprintf(cmd, "/etc/mkfs.ffs %s%s %d %d %d %d %d %d %d %d %d %s",
		Nflag ? "-N " : "", special,
		fssize, nsectors, ntracks, bsize, fsize, cpg, minfree, rpm/60,
		density, opt == FS_OPTSPACE ? "s" : "t");
	if (verbose)
		printf("%s\n", cmd);
	if (status = system(cmd))
		exit(status >> 8);
	if (Nflag)
		exit(0);
#ifdef RISCOS
	sprintf(cmd, "/etc/fsirand.ffs %s", special);
#else
	sprintf(cmd, "fsirand %s", special);
#endif
	if (status = system(cmd))
		printf("%s: failed, status = %d\n", cmd, status);
#ifdef RISCOS
	sprintf(cmd, "/etc/tunefs.ffs -d 0 %s\n", special);
	if (status = system(cmd))
		printf("%s: failed, status = %d\n", cmd, status);
#endif RISCOS
#ifndef mips
	if (*cp == 'a' && !noboot) {
		char type[3];
		struct stat sb;

		cp = rindex(special, '/');
		if (cp == NULL)
			fatal("%s: can't figure out disk type from name",
				special);
		if (stat(special, &sb) >= 0 && (sb.st_mode & S_IFMT) == S_IFCHR)
			cp++;
		type[0] = *++cp;
		type[1] = *++cp;
		type[2] = '\0';
		installboot(special, type);
	}
#endif /* mips */
	exit(0);
}


#ifndef mips
installboot(dev, type)
	char *dev, *type;
{
	int fd;
	char bootblock[MAXPATHLEN], standalonecode[MAXPATHLEN];
	char bootimage[BBSIZE];

	sprintf(bootblock, "%s/%sboot", BOOTDIR, type);
	sprintf(standalonecode, "%s/boot%s", BOOTDIR, type);
	if (verbose) {
		printf("installing boot code\n");
		printf("sector 0 boot = %s\n", bootblock);
		printf("1st level boot = %s\n", standalonecode);
	}
	fd = open(bootblock, 0);
	if (fd < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(bootblock);
		exit(1);
	}
	if (read(fd, bootimage, DEV_BSIZE) < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(bootblock);
		exit(2);
	}
	close(fd);
	fd = open(standalonecode, 0);
	if (fd < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(standalonecode);
		exit(1);
	}
	if (read(fd, &bootimage[DEV_BSIZE], BBSIZE - DEV_BSIZE) < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(standalonecode);
		exit(2);
	}
	close(fd);
	fd = open(dev, 1);
	if (fd < 0) {
		fprintf(stderr, "newfs.ffs: "); perror(dev);
		exit(1);
	}
	if (write(fd, bootimage, BBSIZE) != BBSIZE) {
		fprintf(stderr, "newfs.ffs: "); perror(dev);
		exit(2);
	}
	close(fd);
}
#endif

/*VARARGS*/
fatal(fmt, arg1, arg2)
	char *fmt;
{

	fprintf(stderr, "newfs.ffs: ");
	fprintf(stderr, fmt, arg1, arg2);
	putc('\n', stderr);
	exit(10);
}



dev_t
get_physical (dev)
	dev_t dev;
{
	int fd, cdev;
	struct dktab dktab;
	struct io_arg io_arg;

	if ((fd = open ("/dev/rdsk/vdiskioctl", O_RDONLY)) < 0) {
		return (dev);
	}

	cdev = dev;
	io_arg.datasz = sizeof (struct dktab);
	io_arg.memaddr = (unsigned long)&dktab;
	while (1) {
		dktab.dk_dev = cdev;
		if (ioctl (fd, DIOCGETDKTAB, &io_arg) < 0) {
			return (dev);
		}
		while (dktab.dk_type == DK_EMPTY) {
			if ((dktab.dk_dev = dktab.dk_forw) == cdev) {
				return (dev);
			}
			if (ioctl (fd, DIOCGETDKTAB, &io_arg) < 0) {
				return (dev);
			}
		}
		if (dktab.dk_type == DK_SIMPLE) {
			return (dktab.dk_dev);
		}
		cdev = dktab.dk_first;
	}
}

char *
find_phys(name, rdev)
	char *name;
	dev_t rdev;
{
	DIR *dirp;
	struct direct *entry;
	struct stat buf;
	char path[MAXPATHLEN];
	int pathlen;


	strcpy (path, name);
	strcat (path, "/");
	pathlen = strlen (path);
	dirp = opendir (name);

	while ((entry = readdir(dirp)) != NULL) {
		strncpy (&path[pathlen], entry->d_name, entry->d_reclen);
		path[pathlen + entry->d_reclen] = NULL;
		if (stat (path, &buf) < 0) {
			continue;
		}
		if(buf.st_rdev == rdev){
			closedir(dirp);
			return(path);
		}
	}

	closedir (dirp);
	return(NULL);

}

