/*
 * CDVTOC - a utility to create a valid Solaris SPARC vtoc in an ISO image
 * for booting systems off CDROM
 *
 * Author  : Marty Lee (marty@maui.co.uk)
 * Version : 1.7
 * Date    : 12/06/05
 */

#define _LARGEFILE64_SOURCE 1

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/vtoc.h>
#include <sys/dklabel.h>
#include <sys/types.h>
#include <sys/stat.h>

#if !defined(_SUNOS_VTOC_8) && !defined(_SUNOS_VTOC_16)
#if NDKMAP = 8
#define _SUNOS_VTOC_8
#elif NDKMAP = 16
#define _SUNOS_VTOC_16
#else
#error Neither _SUNOS_VTOC_8 nor _SUNOS_VTOC_16 defined
#endif
#endif

#define SECTORSIZE 512

void usage(char *argv[]) {
	printf("Usage: %s [-l label] [-r rpm] [-p pcyl] [-i intrlv] [-c ncyl] [-h head] [-s sects] [-o outputfile] [-C] <slice0> [slice1]....\n", argv[0]);
	exit(0);
}

int main(int argc, char *argv[]) {

	long padbytes[V_NUMPAR];
	int create_flag;
	char * output_file;
	uint64_t c,i,fd;
	daddr_t cyl;
	extern char *optarg;
	extern int optind;
	register u_short *ptr;

	struct dk_label label;

#ifdef _LP64
	printf("The data model is LP64 in this environment\n");
#else
#ifdef _ILP32
	printf("The data model is ILP32 in this environment\n");
#else
#error    "Unknown data model!"
#endif
#endif

	create_flag = 0;
	output_file = NULL;

	/* Initial Label */
	label.dkl_vtoc.v_version = V_VERSION;
	label.dkl_vtoc.v_nparts = NDKMAP;
	for(i=0;i<NDKMAP;i++) {
		label.dkl_vtoc.v_part[i].p_tag=V_UNASSIGNED;
		label.dkl_vtoc.v_part[i].p_flag=0;
		label.dkl_map[i].dkl_cylno=0L;
		label.dkl_map[i].dkl_nblk=0L;
		padbytes[i]=0L;
	}
	label.dkl_vtoc.v_part[0].p_tag=V_USR;
	label.dkl_vtoc.v_part[0].p_flag=V_RONLY;
	label.dkl_vtoc.v_part[1].p_tag=V_ROOT;
	label.dkl_vtoc.v_part[1].p_flag=V_RONLY;
	label.dkl_rpm=350;
	label.dkl_pcyl=2048;
	label.dkl_apc=0;
	label.dkl_obs1=0;
	label.dkl_obs2=0;
	label.dkl_intrlv=1;
	label.dkl_ncyl=2048;
	label.dkl_acyl=0;
	label.dkl_nhead=1;
	label.dkl_nsect=640;
	label.dkl_obs3=0;
	label.dkl_obs4=0;
	label.dkl_vtoc.v_sanity = VTOC_SANE;
	label.dkl_magic = DKL_MAGIC;

	while ((c = getopt(argc, argv, "l:r:p:i:c:h:s:o:C")) != EOF) {
		switch(c) {
			case 'l':
				strncpy(label.dkl_asciilabel,optarg,128);
				break;
			case 'r':
				sscanf(optarg,"%d",&label.dkl_rpm);
				break;
			case 'p':
				sscanf(optarg,"%d",&label.dkl_pcyl);
				break;
			case 'i':
				sscanf(optarg,"%d",&label.dkl_intrlv);
				break;
			case 'c':
				sscanf(optarg,"%d",&label.dkl_ncyl);
				break;
			case 'h':
				sscanf(optarg,"%d",&label.dkl_nhead);
				break;
			case 's':
				sscanf(optarg,"%d",&label.dkl_nsect);
				break;
			case 'o':
				output_file=optarg;
				break;
			case 'C':
				create_flag=1;
				break;

			default:
				usage(argv);
				break;
		}
	}

	if((argc - optind) == 0) usage(argv);
	if(!output_file) usage(argv);

	cyl=0;

	for(i=optind; i<argc;i++) {
		struct stat64 buf;

		if(stat64(argv[i], &buf)!=0) {
			perror("stat");
			exit(1);
		}

		label.dkl_map[i-optind].dkl_cylno=cyl;

		padbytes[i-optind]=((label.dkl_nsect*SECTORSIZE)-
			(buf.st_size%(label.dkl_nsect*SECTORSIZE))) %
			(label.dkl_nsect*SECTORSIZE);

		label.dkl_map[i-optind].dkl_nblk=
			(buf.st_size+padbytes[i-optind])/SECTORSIZE;

		printf("Slice %d: Start %lld, size %lld, nsectors %ld, ncyls %ld, padding %ld\n",
			i-optind, cyl, buf.st_size,
			label.dkl_map[i-optind].dkl_nblk,
			label.dkl_map[i-optind].dkl_nblk/label.dkl_nsect,
			padbytes[i-optind]);

		cyl+=(label.dkl_map[i-optind].dkl_nblk)/label.dkl_nsect;
	}
		

	label.dkl_cksum = 0;
	for (ptr = (u_short *)((char *)&label + sizeof(label) - sizeof(u_short)); ptr >= (u_short *)&label; ptr--) {
		label.dkl_cksum ^= *ptr;
	}

	fd=open64(output_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
	if(fd < 0) {
		perror("Creating output file");
		exit(1);
	}

	if(write(fd, &label, sizeof(label)) != sizeof(label)) {
		perror("write");
		exit(1);
	}

	if(create_flag) {
		int i;
		int fd2;
		char blocks[8192];

		printf("Creating ISO\n");
	
		for(i=optind; i<argc;i++) {
			uint64_t len;

			printf("Append %s\n", argv[i]);
			if((fd2 = open64(argv[i], O_RDONLY)) < 0) {
				perror("open");
				exit(1);
			}

			if((i-optind) == 0) {
				ssize_t skip=0;
				printf("Skip first sector\n");
				/* Skip first SECTOR */
				while(skip < SECTORSIZE) {
					skip+=read(fd2, blocks, SECTORSIZE-skip);
				}
			}

			while((len = read(fd2, blocks, sizeof(blocks))) > 0) {
				uint64_t len2 = 0;
				while(len2 < len) {
					len2 = write(fd, &blocks[len2], len-len2);
				}
			}
			
			close(fd2);

			if(padbytes[i-optind]>0) {
				size_t len;
				memset(blocks, 0, sizeof(blocks));

				printf("Append %ld padding bytes\n",
					padbytes[i-optind]);

				len=padbytes[i-optind];
				while(len > 0) {
					len-=write(fd, blocks,
						(len > sizeof(blocks))?
							sizeof(blocks):len);
				}
			}
		}
	}

	close(fd);

	exit(0);
}
