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