xref: /netbsd-src/sbin/mbrlabel/mbrlabel.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: mbrlabel.c,v 1.26 2005/12/28 06:03:15 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 1998 Wolfgang Solfrank.
5  * Copyright (C) 1998 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 #ifndef lint
36 __RCSID("$NetBSD: mbrlabel.c,v 1.26 2005/12/28 06:03:15 christos Exp $");
37 #endif /* not lint */
38 
39 #include <stdio.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <limits.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <util.h>
48 
49 #include <sys/param.h>
50 #define FSTYPENAMES
51 #include <sys/disklabel.h>
52 #include <sys/bootblock.h>
53 #include <sys/ioctl.h>
54 
55 #include "dkcksum.h"
56 #include "extern.h"
57 
58 int	main(int, char **);
59 void	usage(void);
60 void	getlabel(int);
61 void	setlabel(int, int);
62 int	getparts(int, u_int32_t, u_int32_t, int);
63 u_int16_t	getshort(void *);
64 u_int32_t	getlong(void *);
65 
66 struct disklabel label;
67 
68 void
69 getlabel(int sd)
70 {
71 
72 	if (ioctl(sd, DIOCGDINFO, &label) < 0) {
73 		perror("get label");
74 		exit(1);
75 	}
76 	/*
77 	 * Some ports seem to not set the number of partitions
78 	 * correctly, albeit they seem to set the raw partition ok!
79 	 */
80 	if (label.d_npartitions <= getrawpartition())
81 		label.d_npartitions = getrawpartition() + 1;
82 }
83 
84 void
85 setlabel(int sd, int doraw)
86 {
87 	int one = 1;
88 
89 	label.d_checksum = 0;
90 	label.d_checksum = dkcksum(&label);
91 	if (ioctl(sd, doraw ? DIOCWDINFO : DIOCSDINFO, &label) < 0) {
92 		perror("set label");
93 		exit(1);
94 	}
95 	if (!doraw)
96 		/* If we haven't written to the disk, don't discard on close */
97 		ioctl(sd, DIOCKLABEL, &one);
98 
99 }
100 
101 u_int16_t
102 getshort(void *p)
103 {
104 	unsigned char *cp = p;
105 
106 	return (cp[0] | (cp[1] << 8));
107 }
108 
109 u_int32_t
110 getlong(void *p)
111 {
112 	unsigned char *cp = p;
113 
114 	return (cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24));
115 }
116 
117 int
118 getparts(int sd, u_int32_t off, u_int32_t extoff, int verbose)
119 {
120 	unsigned char		buf[DEV_BSIZE];
121 	struct mbr_partition	parts[MBR_PART_COUNT];
122 	struct partition	npe;
123 	off_t			loff;
124 	int			i, j, unused, changed;
125 
126 	changed = 0;
127 	loff = (off_t)off * DEV_BSIZE;
128 
129 	if (lseek(sd, loff, SEEK_SET) != loff) {
130 		perror("seek label");
131 		exit(1);
132 	}
133 	if (read(sd, buf, sizeof buf) != DEV_BSIZE) {
134 		if (off != MBR_BBSECTOR)
135 			perror("read label (sector is possibly out of "
136 			    "range)");
137 		else
138 			perror("read label");
139 		exit(1);
140 	}
141 	if (getshort(buf + MBR_MAGIC_OFFSET) != MBR_MAGIC)
142 		return (changed);
143 	memcpy(parts, buf + MBR_PART_OFFSET, sizeof parts);
144 
145 				/* scan partition table */
146 	for (i = 0; i < MBR_PART_COUNT; i++) {
147 		if (parts[i].mbrp_type == 0 ||
148 				/* extended partitions are handled below */
149 		    MBR_IS_EXTENDED(parts[i].mbrp_type))
150 			continue;
151 
152 		memset((void *)&npe, 0, sizeof(npe));
153 		npe.p_size = getlong(&parts[i].mbrp_size);
154 		npe.p_offset = getlong(&parts[i].mbrp_start) + off;
155 		npe.p_fstype = xlat_mbr_fstype(parts[i].mbrp_type);
156 
157 				/* find existing entry, or first free slot */
158 		unused = -1;	/* flag as no free slot */
159 		if (verbose)
160 			printf(
161 			    "Found %s partition; size %u (%u MB), offset %u\n",
162 			    fstypenames[npe.p_fstype],
163 			    npe.p_size, npe.p_size / 2048, npe.p_offset);
164 		for (j = 0; j < label.d_npartitions; j++) {
165 			struct partition *lpe;
166 
167 			if (j == RAW_PART)
168 				continue;
169 			lpe = &label.d_partitions[j];
170 			if (lpe->p_size == npe.p_size &&
171 			    lpe->p_offset == npe.p_offset
172 #ifdef notyet
173 			    && (lpe->p_fstype == npe.p_fstype ||
174 			     lpe->p_fstype == FS_UNUSED) */
175 #endif
176 			     ) {
177 				if (verbose)
178 					printf(
179 			    "  skipping existing %s partition at slot %c.\n",
180 					    fstypenames[lpe->p_fstype],
181 					    j + 'a');
182 				unused = -2;	/* flag as existing */
183 				break;
184 			}
185 			if (unused == -1 && lpe->p_size == 0 &&
186 			    lpe->p_fstype == FS_UNUSED)
187 				unused = j;
188 		}
189 		if (unused == -2)
190 			continue;	/* entry exists, skip... */
191 		if (unused == -1) {
192 			if (label.d_npartitions < MAXPARTITIONS) {
193 				unused = label.d_npartitions;
194 				label.d_npartitions++;
195 			} else {
196 				printf(
197 				"  WARNING: no slots free for %s partition.\n",
198 				    fstypenames[npe.p_fstype]);
199 				continue;
200 			}
201 		}
202 
203 		if (verbose)
204 			printf("  adding %s partition to slot %c.\n",
205 			    fstypenames[npe.p_fstype], unused + 'a');
206 		switch (npe.p_fstype) {
207 		case FS_BSDFFS:
208 		case FS_APPLEUFS:
209 			npe.p_size = 16384;	/* XXX */
210 			npe.p_fsize = 1024;
211 			npe.p_frag = 8;
212 			npe.p_cpg = 16;
213 			break;
214 #ifdef	__does_not_happen__
215 		case FS_BSDLFS:
216 			npe.p_size = 16384;	/* XXX */
217 			npe.p_fsize = 1024;
218 			npe.p_frag = 8;
219 			npe.p_sgs = XXX;
220 			break;
221 #endif
222 		}
223 		changed++;
224 		label.d_partitions[unused] = npe;
225 	}
226 
227 				/* recursively scan extended partitions */
228 	for (i = 0; i < MBR_PART_COUNT; i++) {
229 		u_int32_t poff;
230 
231 		if (MBR_IS_EXTENDED(parts[i].mbrp_type)) {
232 			poff = getlong(&parts[i].mbrp_start) + extoff;
233 			changed += getparts(sd, poff,
234 			    extoff ? extoff : poff, verbose);
235 		}
236 	}
237 	return (changed);
238 }
239 
240 void
241 usage(void)
242 {
243 	fprintf(stderr, "usage: %s [-fqrw] [-s sector] rawdisk\n",
244 	    getprogname());
245 	exit(1);
246 }
247 
248 
249 int
250 main(int argc, char **argv)
251 {
252 	int	sd, ch, changed;
253 	char	*ep, name[MAXPATHLEN];
254 	int	force;			/* force label update */
255 	int	raw;			/* update on-disk label as well */
256 	int	verbose;		/* verbose output */
257 	int	write_it;		/* update in-core label if changed */
258 	uint32_t sector;		/* sector that contains the MBR */
259 	ulong	ulsector;
260 
261 	force = 0;
262 	raw = 0;
263 	verbose = 1;
264 	write_it = 0;
265 	sector = MBR_BBSECTOR;
266 	while ((ch = getopt(argc, argv, "fqrs:w")) != -1) {
267 		switch (ch) {
268 		case 'f':
269 			force = 1;
270 			break;
271 		case 'q':
272 			verbose = 0;
273 			break;
274 		case 'r':
275 			raw = 1;
276 			break;
277 		case 's':
278 			errno = 0;
279 			ulsector = strtoul(optarg, &ep, 10);
280 			if (optarg[0] == '\0' || *ep != '\0')
281 				errx(EXIT_FAILURE,
282 				    "sector number (%s) incorrectly specified",
283 				    optarg);
284 			if ((errno == ERANGE && ulsector == ULONG_MAX) ||
285 			    ulsector > UINT32_MAX)
286 			    	errx(EXIT_FAILURE,
287 				    "sector number (%s) out of range",
288 				    optarg);
289 			sector = (uint32_t)ulsector;
290 			break;
291 		case 'w':
292 			write_it = 1;
293 			break;
294 		default:
295 			usage();
296 		}
297 	}
298 	argc -= optind;
299 	argv += optind;
300 	if (argc != 1)
301 		usage();
302 
303 	if ((sd = opendisk(argv[0], write_it ? O_RDWR : O_RDONLY, name,
304 	    (size_t)MAXPATHLEN, 0)) < 0) {
305 		perror(argv[0]);
306 		exit(1);
307 	}
308 	getlabel(sd);
309 	changed = getparts(sd, sector, 0, verbose);
310 
311 	if (verbose) {
312 		putchar('\n');
313 		showpartitions(stdout, &label, 0);
314 		putchar('\n');
315 	}
316 	if (write_it) {
317 		if (! changed && ! force)
318 			printf("No change; not updating disk label.\n");
319 		else {
320 			if (verbose)
321 				printf("Updating in-core %sdisk label.\n",
322 				    raw ? "and on-disk " : "");
323 			setlabel(sd, raw);
324 		}
325 	} else {
326 		printf("Not updating disk label.\n");
327 	}
328 	close(sd);
329 	return (0);
330 }
331