xref: /netbsd-src/sbin/mbrlabel/mbrlabel.c (revision 20bad51bebdabf097abdafeb2ad247ded5441109)
1*20bad51bSmlelstv /*	$NetBSD: mbrlabel.c,v 1.29 2018/03/30 13:14:25 mlelstv Exp $	*/
24fc21ef2Sfvdl 
34fc21ef2Sfvdl /*
44fc21ef2Sfvdl  * Copyright (C) 1998 Wolfgang Solfrank.
54fc21ef2Sfvdl  * Copyright (C) 1998 TooLs GmbH.
64fc21ef2Sfvdl  * All rights reserved.
74fc21ef2Sfvdl  *
84fc21ef2Sfvdl  * Redistribution and use in source and binary forms, with or without
94fc21ef2Sfvdl  * modification, are permitted provided that the following conditions
104fc21ef2Sfvdl  * are met:
114fc21ef2Sfvdl  * 1. Redistributions of source code must retain the above copyright
124fc21ef2Sfvdl  *    notice, this list of conditions and the following disclaimer.
134fc21ef2Sfvdl  * 2. Redistributions in binary form must reproduce the above copyright
144fc21ef2Sfvdl  *    notice, this list of conditions and the following disclaimer in the
154fc21ef2Sfvdl  *    documentation and/or other materials provided with the distribution.
164fc21ef2Sfvdl  * 3. All advertising materials mentioning features or use of this software
174fc21ef2Sfvdl  *    must display the following acknowledgement:
184fc21ef2Sfvdl  *	This product includes software developed by TooLs GmbH.
194fc21ef2Sfvdl  * 4. The name of TooLs GmbH may not be used to endorse or promote products
204fc21ef2Sfvdl  *    derived from this software without specific prior written permission.
214fc21ef2Sfvdl  *
224fc21ef2Sfvdl  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
234fc21ef2Sfvdl  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
244fc21ef2Sfvdl  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
254fc21ef2Sfvdl  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
264fc21ef2Sfvdl  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
274fc21ef2Sfvdl  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
284fc21ef2Sfvdl  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
294fc21ef2Sfvdl  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
304fc21ef2Sfvdl  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
314fc21ef2Sfvdl  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
324fc21ef2Sfvdl  */
334fc21ef2Sfvdl 
344fc21ef2Sfvdl #include <sys/cdefs.h>
354fc21ef2Sfvdl #ifndef lint
36*20bad51bSmlelstv __RCSID("$NetBSD: mbrlabel.c,v 1.29 2018/03/30 13:14:25 mlelstv Exp $");
374fc21ef2Sfvdl #endif /* not lint */
384fc21ef2Sfvdl 
394fc21ef2Sfvdl #include <stdio.h>
40fbb8b407Sjmmv #include <err.h>
41fbb8b407Sjmmv #include <errno.h>
424fc21ef2Sfvdl #include <fcntl.h>
43fbb8b407Sjmmv #include <limits.h>
440f6818e4Smatt #include <stdlib.h>
454fc21ef2Sfvdl #include <string.h>
464fc21ef2Sfvdl #include <unistd.h>
474fc21ef2Sfvdl #include <util.h>
484fc21ef2Sfvdl 
494fc21ef2Sfvdl #include <sys/param.h>
50747375caSlukem #define FSTYPENAMES
514fc21ef2Sfvdl #include <sys/disklabel.h>
521c33b4e6Slukem #include <sys/bootblock.h>
534fc21ef2Sfvdl #include <sys/ioctl.h>
544fc21ef2Sfvdl 
554fc21ef2Sfvdl #include "dkcksum.h"
56747375caSlukem #include "extern.h"
574fc21ef2Sfvdl 
58c0a388c8Sjoerg __dead static void	usage(void);
59c0a388c8Sjoerg static void	getlabel(int);
60c0a388c8Sjoerg static void	setlabel(int, int);
61c0a388c8Sjoerg static int	getparts(int, u_int32_t, u_int32_t, int);
62c0a388c8Sjoerg static u_int16_t	getshort(void *);
63c0a388c8Sjoerg static u_int32_t	getlong(void *);
644fc21ef2Sfvdl 
654fc21ef2Sfvdl struct disklabel label;
664fc21ef2Sfvdl 
67c0a388c8Sjoerg static void
getlabel(int sd)6871288952Swiz getlabel(int sd)
694fc21ef2Sfvdl {
704fc21ef2Sfvdl 
714fc21ef2Sfvdl 	if (ioctl(sd, DIOCGDINFO, &label) < 0) {
724fc21ef2Sfvdl 		perror("get label");
734fc21ef2Sfvdl 		exit(1);
744fc21ef2Sfvdl 	}
754fc21ef2Sfvdl 	/*
764fc21ef2Sfvdl 	 * Some ports seem to not set the number of partitions
7741df6b74Swiz 	 * correctly, albeit they seem to set the raw partition ok!
784fc21ef2Sfvdl 	 */
79d78020d1Sad 	if (label.d_npartitions <= getrawpartition())
80d78020d1Sad 		label.d_npartitions = getrawpartition() + 1;
814fc21ef2Sfvdl }
824fc21ef2Sfvdl 
83c0a388c8Sjoerg static void
setlabel(int sd,int doraw)84fbace08cSlukem setlabel(int sd, int doraw)
854fc21ef2Sfvdl {
86ee0a07c0Sdsl 	int one = 1;
87747375caSlukem 
884fc21ef2Sfvdl 	label.d_checksum = 0;
894fc21ef2Sfvdl 	label.d_checksum = dkcksum(&label);
90fbace08cSlukem 	if (ioctl(sd, doraw ? DIOCWDINFO : DIOCSDINFO, &label) < 0) {
914fc21ef2Sfvdl 		perror("set label");
924fc21ef2Sfvdl 		exit(1);
934fc21ef2Sfvdl 	}
94ee0a07c0Sdsl 	if (!doraw)
95ee0a07c0Sdsl 		/* If we haven't written to the disk, don't discard on close */
96ee0a07c0Sdsl 		ioctl(sd, DIOCKLABEL, &one);
97ee0a07c0Sdsl 
984fc21ef2Sfvdl }
994fc21ef2Sfvdl 
100c0a388c8Sjoerg static u_int16_t
getshort(void * p)101ed9bdbd8Slukem getshort(void *p)
102ed9bdbd8Slukem {
103ed9bdbd8Slukem 	unsigned char *cp = p;
104ed9bdbd8Slukem 
105ed9bdbd8Slukem 	return (cp[0] | (cp[1] << 8));
106ed9bdbd8Slukem }
107ed9bdbd8Slukem 
108c0a388c8Sjoerg static u_int32_t
getlong(void * p)10971288952Swiz getlong(void *p)
1104fc21ef2Sfvdl {
1114fc21ef2Sfvdl 	unsigned char *cp = p;
1124fc21ef2Sfvdl 
113747375caSlukem 	return (cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24));
1144fc21ef2Sfvdl }
1154fc21ef2Sfvdl 
116c0a388c8Sjoerg static int
getparts(int sd,u_int32_t off,u_int32_t extoff,int verbose)117ed9bdbd8Slukem getparts(int sd, u_int32_t off, u_int32_t extoff, int verbose)
1184fc21ef2Sfvdl {
119*20bad51bSmlelstv 	unsigned char		*buf;
1201c33b4e6Slukem 	struct mbr_partition	parts[MBR_PART_COUNT];
121747375caSlukem 	struct partition	npe;
122747375caSlukem 	off_t			loff;
123747375caSlukem 	int			i, j, unused, changed;
124*20bad51bSmlelstv 	unsigned		bsize = label.d_secsize;
125*20bad51bSmlelstv 
126*20bad51bSmlelstv 	if (bsize < DEV_BSIZE) {
127*20bad51bSmlelstv 		fprintf(stderr,"Invalid sector size %u\n", bsize);
128*20bad51bSmlelstv 		exit(1);
129*20bad51bSmlelstv 	}
130*20bad51bSmlelstv 
131*20bad51bSmlelstv 	buf = malloc(bsize);
132*20bad51bSmlelstv 	if (buf == NULL) {
133*20bad51bSmlelstv 		perror("malloc I/O buffer");
134*20bad51bSmlelstv 		exit(1);
135*20bad51bSmlelstv 	}
1364fc21ef2Sfvdl 
137747375caSlukem 	changed = 0;
138*20bad51bSmlelstv 	loff = (off_t)off * bsize;
1394fc21ef2Sfvdl 
1404fc21ef2Sfvdl 	if (lseek(sd, loff, SEEK_SET) != loff) {
1414fc21ef2Sfvdl 		perror("seek label");
1424fc21ef2Sfvdl 		exit(1);
1434fc21ef2Sfvdl 	}
144*20bad51bSmlelstv 	if (read(sd, buf, bsize) != (ssize_t)bsize) {
145fbb8b407Sjmmv 		if (off != MBR_BBSECTOR)
146fbb8b407Sjmmv 			perror("read label (sector is possibly out of "
147fbb8b407Sjmmv 			    "range)");
148fbb8b407Sjmmv 		else
1494fc21ef2Sfvdl 			perror("read label");
1504fc21ef2Sfvdl 		exit(1);
1514fc21ef2Sfvdl 	}
1521c33b4e6Slukem 	if (getshort(buf + MBR_MAGIC_OFFSET) != MBR_MAGIC)
153747375caSlukem 		return (changed);
1541c33b4e6Slukem 	memcpy(parts, buf + MBR_PART_OFFSET, sizeof parts);
155747375caSlukem 
156747375caSlukem 				/* scan partition table */
1571c33b4e6Slukem 	for (i = 0; i < MBR_PART_COUNT; i++) {
1581c33b4e6Slukem 		if (parts[i].mbrp_type == 0 ||
159747375caSlukem 				/* extended partitions are handled below */
1601c33b4e6Slukem 		    MBR_IS_EXTENDED(parts[i].mbrp_type))
161747375caSlukem 			continue;
162747375caSlukem 
163747375caSlukem 		memset((void *)&npe, 0, sizeof(npe));
164747375caSlukem 		npe.p_size = getlong(&parts[i].mbrp_size);
165747375caSlukem 		npe.p_offset = getlong(&parts[i].mbrp_start) + off;
1661c33b4e6Slukem 		npe.p_fstype = xlat_mbr_fstype(parts[i].mbrp_type);
167747375caSlukem 
168747375caSlukem 				/* find existing entry, or first free slot */
169747375caSlukem 		unused = -1;	/* flag as no free slot */
170747375caSlukem 		if (verbose)
17103c663afSlukem 			printf(
17203c663afSlukem 			    "Found %s partition; size %u (%u MB), offset %u\n",
17303c663afSlukem 			    fstypenames[npe.p_fstype],
17403c663afSlukem 			    npe.p_size, npe.p_size / 2048, npe.p_offset);
175*20bad51bSmlelstv 
176747375caSlukem 		for (j = 0; j < label.d_npartitions; j++) {
177747375caSlukem 			struct partition *lpe;
178747375caSlukem 
179747375caSlukem 			if (j == RAW_PART)
180747375caSlukem 				continue;
181747375caSlukem 			lpe = &label.d_partitions[j];
182*20bad51bSmlelstv 
183747375caSlukem 			if (lpe->p_size == npe.p_size &&
184*20bad51bSmlelstv 			    lpe->p_offset == npe.p_offset) {
185747375caSlukem 				if (verbose)
186747375caSlukem 					printf(
187747375caSlukem 					    "  skipping existing %s partition at slot %c.\n",
188747375caSlukem 					    fstypenames[lpe->p_fstype],
189747375caSlukem 					    j + 'a');
190747375caSlukem 				unused = -2;	/* flag as existing */
1914fc21ef2Sfvdl 				break;
192747375caSlukem 			}
193*20bad51bSmlelstv 
194747375caSlukem 			if (unused == -1 && lpe->p_size == 0 &&
195747375caSlukem 			    lpe->p_fstype == FS_UNUSED)
196747375caSlukem 				unused = j;
197747375caSlukem 		}
198*20bad51bSmlelstv 		if (unused == -1) {
199*20bad51bSmlelstv 			for (j = 0; j < label.d_npartitions; j++) {
200*20bad51bSmlelstv 				struct partition *lpe;
201*20bad51bSmlelstv 
202*20bad51bSmlelstv 				if (j == RAW_PART)
203*20bad51bSmlelstv 					continue;
204*20bad51bSmlelstv 				lpe = &label.d_partitions[j];
205*20bad51bSmlelstv 
206*20bad51bSmlelstv 				if ((npe.p_offset >= lpe->p_offset &&
207*20bad51bSmlelstv 				    npe.p_offset < lpe->p_offset + lpe->p_size) ||
208*20bad51bSmlelstv 				   (npe.p_offset + npe.p_size - 1 >= lpe->p_offset &&
209*20bad51bSmlelstv 				    npe.p_offset + npe.p_size - 1 < lpe->p_offset + lpe->p_size)) {
210*20bad51bSmlelstv 					printf(
211*20bad51bSmlelstv 					    "  skipping overlapping %s partition at slot %c.\n",
212*20bad51bSmlelstv 					    fstypenames[lpe->p_fstype],
213*20bad51bSmlelstv 					    j + 'a');
214*20bad51bSmlelstv 					unused = -2;	/* flag as existing */
215*20bad51bSmlelstv 					break;
216*20bad51bSmlelstv 				}
217*20bad51bSmlelstv 			}
218*20bad51bSmlelstv 		}
219747375caSlukem 		if (unused == -2)
220747375caSlukem 			continue;	/* entry exists, skip... */
221747375caSlukem 		if (unused == -1) {
222747375caSlukem 			if (label.d_npartitions < MAXPARTITIONS) {
223747375caSlukem 				unused = label.d_npartitions;
224747375caSlukem 				label.d_npartitions++;
225747375caSlukem 			} else {
226747375caSlukem 				printf(
227747375caSlukem 				"  WARNING: no slots free for %s partition.\n",
228747375caSlukem 				    fstypenames[npe.p_fstype]);
229747375caSlukem 				continue;
230747375caSlukem 			}
231747375caSlukem 		}
232747375caSlukem 
233747375caSlukem 		if (verbose)
234747375caSlukem 			printf("  adding %s partition to slot %c.\n",
235747375caSlukem 			    fstypenames[npe.p_fstype], unused + 'a');
236*20bad51bSmlelstv 
237*20bad51bSmlelstv 		/*
238*20bad51bSmlelstv 		 * XXX guess some filesystem parameters, these should be
239*20bad51bSmlelstv 		 * scanned from the superblocks
240*20bad51bSmlelstv 		 */
241747375caSlukem 		switch (npe.p_fstype) {
2424fc21ef2Sfvdl 		case FS_BSDFFS:
2438ccb247dSdbj 		case FS_APPLEUFS:
244747375caSlukem 			npe.p_fsize = 1024;
245747375caSlukem 			npe.p_frag = 8;
246747375caSlukem 			npe.p_cpg = 16;
2474fc21ef2Sfvdl 			break;
2484fc21ef2Sfvdl 		case FS_BSDLFS:
249747375caSlukem 			npe.p_fsize = 1024;
250747375caSlukem 			npe.p_frag = 8;
251*20bad51bSmlelstv 			npe.p_sgs = 7;
2524fc21ef2Sfvdl 			break;
2534fc21ef2Sfvdl 		}
254*20bad51bSmlelstv 
255747375caSlukem 		changed++;
256747375caSlukem 		label.d_partitions[unused] = npe;
2574fc21ef2Sfvdl 	}
258747375caSlukem 
259747375caSlukem 				/* recursively scan extended partitions */
2601c33b4e6Slukem 	for (i = 0; i < MBR_PART_COUNT; i++) {
2614fc21ef2Sfvdl 		u_int32_t poff;
2624fc21ef2Sfvdl 
2631c33b4e6Slukem 		if (MBR_IS_EXTENDED(parts[i].mbrp_type)) {
264747375caSlukem 			poff = getlong(&parts[i].mbrp_start) + extoff;
265ed9bdbd8Slukem 			changed += getparts(sd, poff,
266747375caSlukem 			    extoff ? extoff : poff, verbose);
2674fc21ef2Sfvdl 		}
2684fc21ef2Sfvdl 	}
269*20bad51bSmlelstv 
270*20bad51bSmlelstv 	free(buf);
271747375caSlukem 	return (changed);
2724fc21ef2Sfvdl }
2734fc21ef2Sfvdl 
274c0a388c8Sjoerg static void
usage(void)27571288952Swiz usage(void)
2764fc21ef2Sfvdl {
2772dae1f92Swiz 	fprintf(stderr, "usage: %s [-fqrw] [-s sector] device\n",
278fbb8b407Sjmmv 	    getprogname());
2794fc21ef2Sfvdl 	exit(1);
2804fc21ef2Sfvdl }
2814fc21ef2Sfvdl 
282747375caSlukem 
2834fc21ef2Sfvdl int
main(int argc,char ** argv)28471288952Swiz main(int argc, char **argv)
2854fc21ef2Sfvdl {
286747375caSlukem 	int	sd, ch, changed;
287fbb8b407Sjmmv 	char	*ep, name[MAXPATHLEN];
288747375caSlukem 	int	force;			/* force label update */
289fbace08cSlukem 	int	raw;			/* update on-disk label as well */
290747375caSlukem 	int	verbose;		/* verbose output */
291ed9bdbd8Slukem 	int	write_it;		/* update in-core label if changed */
292fbb8b407Sjmmv 	uint32_t sector;		/* sector that contains the MBR */
2935de69f00Schristos 	ulong	ulsector;
2944fc21ef2Sfvdl 
295747375caSlukem 	force = 0;
296fbace08cSlukem 	raw = 0;
297747375caSlukem 	verbose = 1;
298ed9bdbd8Slukem 	write_it = 0;
299fbb8b407Sjmmv 	sector = MBR_BBSECTOR;
300fbb8b407Sjmmv 	while ((ch = getopt(argc, argv, "fqrs:w")) != -1) {
301747375caSlukem 		switch (ch) {
302747375caSlukem 		case 'f':
303747375caSlukem 			force = 1;
304747375caSlukem 			break;
305747375caSlukem 		case 'q':
306747375caSlukem 			verbose = 0;
307747375caSlukem 			break;
308fbace08cSlukem 		case 'r':
309fbace08cSlukem 			raw = 1;
310fbace08cSlukem 			break;
311fbb8b407Sjmmv 		case 's':
312fbb8b407Sjmmv 			errno = 0;
3135de69f00Schristos 			ulsector = strtoul(optarg, &ep, 10);
314fbb8b407Sjmmv 			if (optarg[0] == '\0' || *ep != '\0')
315fbb8b407Sjmmv 				errx(EXIT_FAILURE,
316fbb8b407Sjmmv 				    "sector number (%s) incorrectly specified",
317fbb8b407Sjmmv 				    optarg);
3185de69f00Schristos 			if ((errno == ERANGE && ulsector == ULONG_MAX) ||
3195de69f00Schristos 			    ulsector > UINT32_MAX)
320fbb8b407Sjmmv 			    	errx(EXIT_FAILURE,
321fbb8b407Sjmmv 				    "sector number (%s) out of range",
322fbb8b407Sjmmv 				    optarg);
3235de69f00Schristos 			sector = (uint32_t)ulsector;
324fbb8b407Sjmmv 			break;
325fbace08cSlukem 		case 'w':
326ed9bdbd8Slukem 			write_it = 1;
327fbace08cSlukem 			break;
328747375caSlukem 		default:
329747375caSlukem 			usage();
330747375caSlukem 		}
331747375caSlukem 	}
332747375caSlukem 	argc -= optind;
333747375caSlukem 	argv += optind;
334747375caSlukem 	if (argc != 1)
3354fc21ef2Sfvdl 		usage();
3364fc21ef2Sfvdl 
337381fee52Syamt 	if ((sd = opendisk(argv[0], write_it ? O_RDWR : O_RDONLY, name,
338381fee52Syamt 	    (size_t)MAXPATHLEN, 0)) < 0) {
339747375caSlukem 		perror(argv[0]);
3404fc21ef2Sfvdl 		exit(1);
3414fc21ef2Sfvdl 	}
3424fc21ef2Sfvdl 	getlabel(sd);
343fbb8b407Sjmmv 	changed = getparts(sd, sector, 0, verbose);
344747375caSlukem 
345747375caSlukem 	if (verbose) {
346747375caSlukem 		putchar('\n');
347747375caSlukem 		showpartitions(stdout, &label, 0);
348747375caSlukem 		putchar('\n');
349747375caSlukem 	}
350ed9bdbd8Slukem 	if (write_it) {
351fbace08cSlukem 		if (! changed && ! force)
352747375caSlukem 			printf("No change; not updating disk label.\n");
353747375caSlukem 		else {
354747375caSlukem 			if (verbose)
355fbace08cSlukem 				printf("Updating in-core %sdisk label.\n",
356fbace08cSlukem 				    raw ? "and on-disk " : "");
357fbace08cSlukem 			setlabel(sd, raw);
358747375caSlukem 		}
359747375caSlukem 	} else {
360747375caSlukem 		printf("Not updating disk label.\n");
361747375caSlukem 	}
3624fc21ef2Sfvdl 	close(sd);
363747375caSlukem 	return (0);
3644fc21ef2Sfvdl }
365