1/*	$NetBSD: svhlabel.c,v 1.5 2009/04/06 12:33:11 lukem Exp $	*/
2
3/*
4 * Copyright (C) 2007 Stephen M. Rumble.
5 * Copyright (C) 1998 Wolfgang Solfrank.
6 * Copyright (C) 1998 TooLs GmbH.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by TooLs GmbH.
20 * 4. The name of TooLs GmbH may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37__RCSID("$NetBSD: svhlabel.c,v 1.5 2009/04/06 12:33:11 lukem Exp $");
38#endif /* not lint */
39
40#include <stdio.h>
41#include <err.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <limits.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <util.h>
49
50#include <sys/param.h>
51#define FSTYPENAMES
52#include <sys/disklabel.h>
53#include <sys/bootblock.h>
54#include <sys/ioctl.h>
55
56#include <fs/efs/efs.h>
57#include <fs/efs/efs_sb.h>
58
59#include "dkcksum.h"
60#include "extern.h"
61
62__dead static void	usage(void);
63static void	getlabel(int);
64static void	setlabel(int, int);
65static int	getparts(int, int);
66static int	is_efs(int, uint32_t);
67static struct sgi_boot_block *convert_sgi_boot_block(unsigned char *);
68
69struct disklabel label;
70
71static void
72getlabel(int sd)
73{
74
75	if (ioctl(sd, DIOCGDINFO, &label) < 0) {
76		perror("get label");
77		exit(1);
78	}
79	/*
80	 * Some ports seem to not set the number of partitions
81	 * correctly, albeit they seem to set the raw partition ok!
82	 */
83	if (label.d_npartitions <= getrawpartition())
84		label.d_npartitions = getrawpartition() + 1;
85}
86
87static void
88setlabel(int sd, int doraw)
89{
90	int one = 1;
91
92	label.d_checksum = 0;
93	label.d_checksum = dkcksum(&label);
94	if (ioctl(sd, doraw ? DIOCWDINFO : DIOCSDINFO, &label) < 0) {
95		perror("set label");
96		exit(1);
97	}
98	if (!doraw)
99		/* If we haven't written to the disk, don't discard on close */
100		ioctl(sd, DIOCKLABEL, &one);
101
102}
103
104static int
105getparts(int sd, int verbose)
106{
107	unsigned char		buf[DEV_BSIZE];
108	struct sgi_boot_block  *vh;
109	struct partition	npe;
110	int			i, j, changed;
111
112	changed = 0;
113
114	if (lseek(sd, 0, SEEK_SET) == -1) {
115		perror("seek vh");
116		exit(1);
117	}
118	if ((i = read(sd, buf, sizeof(buf))) != DEV_BSIZE) {
119		perror("read vh");
120		exit(1);
121	}
122	vh = convert_sgi_boot_block(buf);
123
124	if (vh->magic != SGI_BOOT_BLOCK_MAGIC)
125		return (changed);
126
127	if (label.d_secsize != SGI_BOOT_BLOCK_BLOCKSIZE)
128		changed++;
129	label.d_secsize = SGI_BOOT_BLOCK_BLOCKSIZE;
130
131	for (i = j = 0; i < SGI_BOOT_BLOCK_MAXPARTITIONS; i++) {
132		if (vh->partitions[i].blocks == 0)
133			continue;
134
135		if (j == MAXPARTITIONS)
136			break;
137
138		switch (vh->partitions[i].type) {
139		case SGI_PTYPE_EFS:
140		/*
141		 * For some reason, my IRIX CDs list EFS partitions as SYSV!?
142		 */
143		case SGI_PTYPE_SYSV:
144			if (is_efs(sd, vh->partitions[i].first)) {
145				npe.p_fstype = FS_EFS;
146				npe.p_size = vh->partitions[i].blocks;
147				npe.p_offset = vh->partitions[i].first;
148				npe.p_fsize = 0;
149				npe.p_frag = 0;
150				npe.p_cpg = 0;
151			}
152			break;
153
154		case SGI_PTYPE_VOLUME:
155			if (label.d_secperunit != (uint32_t)vh->partitions[i].blocks)
156				changed++;
157			label.d_secperunit = vh->partitions[i].blocks;
158			continue;
159
160		default:
161			continue;
162		}
163
164		if (j >= label.d_npartitions)
165			break;
166
167		if (j == getrawpartition()) {
168			if (++j >= label.d_npartitions)
169				break;
170		}
171
172		if (memcmp(&label.d_partitions[j], &npe, sizeof(npe)) != 0) {
173			label.d_partitions[j] = npe;
174			changed++;
175		}
176
177		j++;
178	}
179
180	/* XXX - fudge */
181	if (label.d_nsectors != 1 || label.d_ntracks != 1 ||
182	    label.d_secpercyl != 1 || label.d_ncylinders != label.d_secperunit)
183		changed++;
184	label.d_nsectors = 1;
185	label.d_ntracks = 1;
186	label.d_secpercyl = 1;
187	label.d_ncylinders = label.d_secperunit;
188
189	i = getrawpartition();
190	if (label.d_partitions[i].p_fstype != FS_UNUSED ||
191	    label.d_partitions[i].p_offset != 0 ||
192	    label.d_partitions[i].p_size != label.d_secperunit) {
193		label.d_partitions[i].p_fstype = FS_UNUSED;
194		label.d_partitions[i].p_offset = 0;
195		label.d_partitions[i].p_size = label.d_secperunit;
196		changed++;
197	}
198
199	return (changed);
200}
201
202static int
203is_efs(int sd, uint32_t blkoff)
204{
205	struct efs_sb sb;
206	off_t oldoff;
207
208	if ((oldoff = lseek(sd, 0, SEEK_CUR)) == -1) {
209		perror("is_efs lseek 0");
210		exit(1);
211	}
212
213	blkoff *= SGI_BOOT_BLOCK_BLOCKSIZE;
214	if (lseek(sd, blkoff + (EFS_BB_SB * EFS_BB_SIZE), SEEK_SET) == -1) {
215		perror("is_efs lseek 1");
216		exit(1);
217	}
218
219	if (read(sd, &sb, sizeof(sb)) != sizeof(sb)) {
220		perror("is_efs read");
221		exit(1);
222	}
223
224	if (lseek(sd, oldoff, SEEK_SET) == -1) {
225		perror("is_efs lseek 2");
226		exit(1);
227	}
228
229	BE32TOH(sb.sb_magic);
230
231	return (sb.sb_magic == EFS_SB_MAGIC || sb.sb_magic == EFS_SB_NEWMAGIC);
232}
233
234static struct sgi_boot_block *
235convert_sgi_boot_block(unsigned char *buf)
236{
237	struct sgi_boot_block *vh;
238	int i;
239
240	vh = (struct sgi_boot_block *)buf;
241
242	BE32TOH(vh->magic);
243	BE16TOH(vh->root);
244	BE16TOH(vh->swap);
245
246	BE16TOH(vh->dp.dp_cyls);
247	BE16TOH(vh->dp.dp_shd0);
248	BE16TOH(vh->dp.dp_trks0);
249	BE16TOH(vh->dp.dp_secs);
250	BE16TOH(vh->dp.dp_secbytes);
251	BE16TOH(vh->dp.dp_interleave);
252	BE32TOH(vh->dp.dp_flags);
253	BE32TOH(vh->dp.dp_datarate);
254	BE32TOH(vh->dp.dp_nretries);
255	BE32TOH(vh->dp.dp_mspw);
256	BE16TOH(vh->dp.dp_xgap1);
257	BE16TOH(vh->dp.dp_xsync);
258	BE16TOH(vh->dp.dp_xrdly);
259	BE16TOH(vh->dp.dp_xgap2);
260	BE16TOH(vh->dp.dp_xrgate);
261	BE16TOH(vh->dp.dp_xwcont);
262
263	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; i++) {
264		BE32TOH(vh->voldir[i].block);
265		BE32TOH(vh->voldir[i].bytes);
266	}
267
268	for (i = 0; i < SGI_BOOT_BLOCK_MAXPARTITIONS; i++) {
269		BE32TOH(vh->partitions[i].blocks);
270		BE32TOH(vh->partitions[i].first);
271		BE32TOH(vh->partitions[i].type);
272	}
273
274	BE32TOH(vh->checksum);
275
276	return (vh);
277}
278
279static void
280usage(void)
281{
282	fprintf(stderr, "usage: %s [-fqrw] rawdisk\n",
283	    getprogname());
284	exit(1);
285}
286
287
288int
289main(int argc, char **argv)
290{
291	int	sd, ch, changed;
292	char	name[MAXPATHLEN];
293	int	force;			/* force label update */
294	int	raw;			/* update on-disk label as well */
295	int	verbose;		/* verbose output */
296	int	write_it;		/* update in-core label if changed */
297
298	force = 0;
299	raw = 0;
300	verbose = 1;
301	write_it = 0;
302	while ((ch = getopt(argc, argv, "fqrw")) != -1) {
303		switch (ch) {
304		case 'f':
305			force = 1;
306			break;
307		case 'q':
308			verbose = 0;
309			break;
310		case 'r':
311			raw = 1;
312			break;
313		case 'w':
314			write_it = 1;
315			break;
316		default:
317			usage();
318		}
319	}
320	argc -= optind;
321	argv += optind;
322	if (argc != 1)
323		usage();
324
325	if ((sd = opendisk(argv[0], write_it ? O_RDWR : O_RDONLY, name,
326	    (size_t)MAXPATHLEN, 1)) < 0) {
327		perror(argv[0]);
328		exit(1);
329	}
330	getlabel(sd);
331	changed = getparts(sd, verbose);
332
333	if (verbose) {
334		putchar('\n');
335		showpartitions(stdout, &label, 0);
336		putchar('\n');
337	}
338	if (write_it) {
339		if (! changed && ! force)
340			printf("No change; not updating disk label.\n");
341		else {
342			if (verbose)
343				printf("Updating in-core %sdisk label.\n",
344				    raw ? "and on-disk " : "");
345raw = 0; /* XXX */
346			setlabel(sd, raw);
347		}
348	} else {
349		printf("Not updating disk label.\n");
350	}
351	close(sd);
352	return (0);
353}
354