1 /* $NetBSD: mbrlabel.c,v 1.29 2018/03/30 13:14:25 mlelstv 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.29 2018/03/30 13:14:25 mlelstv 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 __dead static void usage(void); 59 static void getlabel(int); 60 static void setlabel(int, int); 61 static int getparts(int, u_int32_t, u_int32_t, int); 62 static u_int16_t getshort(void *); 63 static u_int32_t getlong(void *); 64 65 struct disklabel label; 66 67 static void 68 getlabel(int sd) 69 { 70 71 if (ioctl(sd, DIOCGDINFO, &label) < 0) { 72 perror("get label"); 73 exit(1); 74 } 75 /* 76 * Some ports seem to not set the number of partitions 77 * correctly, albeit they seem to set the raw partition ok! 78 */ 79 if (label.d_npartitions <= getrawpartition()) 80 label.d_npartitions = getrawpartition() + 1; 81 } 82 83 static void 84 setlabel(int sd, int doraw) 85 { 86 int one = 1; 87 88 label.d_checksum = 0; 89 label.d_checksum = dkcksum(&label); 90 if (ioctl(sd, doraw ? DIOCWDINFO : DIOCSDINFO, &label) < 0) { 91 perror("set label"); 92 exit(1); 93 } 94 if (!doraw) 95 /* If we haven't written to the disk, don't discard on close */ 96 ioctl(sd, DIOCKLABEL, &one); 97 98 } 99 100 static u_int16_t 101 getshort(void *p) 102 { 103 unsigned char *cp = p; 104 105 return (cp[0] | (cp[1] << 8)); 106 } 107 108 static u_int32_t 109 getlong(void *p) 110 { 111 unsigned char *cp = p; 112 113 return (cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24)); 114 } 115 116 static int 117 getparts(int sd, u_int32_t off, u_int32_t extoff, int verbose) 118 { 119 unsigned char *buf; 120 struct mbr_partition parts[MBR_PART_COUNT]; 121 struct partition npe; 122 off_t loff; 123 int i, j, unused, changed; 124 unsigned bsize = label.d_secsize; 125 126 if (bsize < DEV_BSIZE) { 127 fprintf(stderr,"Invalid sector size %u\n", bsize); 128 exit(1); 129 } 130 131 buf = malloc(bsize); 132 if (buf == NULL) { 133 perror("malloc I/O buffer"); 134 exit(1); 135 } 136 137 changed = 0; 138 loff = (off_t)off * bsize; 139 140 if (lseek(sd, loff, SEEK_SET) != loff) { 141 perror("seek label"); 142 exit(1); 143 } 144 if (read(sd, buf, bsize) != (ssize_t)bsize) { 145 if (off != MBR_BBSECTOR) 146 perror("read label (sector is possibly out of " 147 "range)"); 148 else 149 perror("read label"); 150 exit(1); 151 } 152 if (getshort(buf + MBR_MAGIC_OFFSET) != MBR_MAGIC) 153 return (changed); 154 memcpy(parts, buf + MBR_PART_OFFSET, sizeof parts); 155 156 /* scan partition table */ 157 for (i = 0; i < MBR_PART_COUNT; i++) { 158 if (parts[i].mbrp_type == 0 || 159 /* extended partitions are handled below */ 160 MBR_IS_EXTENDED(parts[i].mbrp_type)) 161 continue; 162 163 memset((void *)&npe, 0, sizeof(npe)); 164 npe.p_size = getlong(&parts[i].mbrp_size); 165 npe.p_offset = getlong(&parts[i].mbrp_start) + off; 166 npe.p_fstype = xlat_mbr_fstype(parts[i].mbrp_type); 167 168 /* find existing entry, or first free slot */ 169 unused = -1; /* flag as no free slot */ 170 if (verbose) 171 printf( 172 "Found %s partition; size %u (%u MB), offset %u\n", 173 fstypenames[npe.p_fstype], 174 npe.p_size, npe.p_size / 2048, npe.p_offset); 175 176 for (j = 0; j < label.d_npartitions; j++) { 177 struct partition *lpe; 178 179 if (j == RAW_PART) 180 continue; 181 lpe = &label.d_partitions[j]; 182 183 if (lpe->p_size == npe.p_size && 184 lpe->p_offset == npe.p_offset) { 185 if (verbose) 186 printf( 187 " skipping existing %s partition at slot %c.\n", 188 fstypenames[lpe->p_fstype], 189 j + 'a'); 190 unused = -2; /* flag as existing */ 191 break; 192 } 193 194 if (unused == -1 && lpe->p_size == 0 && 195 lpe->p_fstype == FS_UNUSED) 196 unused = j; 197 } 198 if (unused == -1) { 199 for (j = 0; j < label.d_npartitions; j++) { 200 struct partition *lpe; 201 202 if (j == RAW_PART) 203 continue; 204 lpe = &label.d_partitions[j]; 205 206 if ((npe.p_offset >= lpe->p_offset && 207 npe.p_offset < lpe->p_offset + lpe->p_size) || 208 (npe.p_offset + npe.p_size - 1 >= lpe->p_offset && 209 npe.p_offset + npe.p_size - 1 < lpe->p_offset + lpe->p_size)) { 210 printf( 211 " skipping overlapping %s partition at slot %c.\n", 212 fstypenames[lpe->p_fstype], 213 j + 'a'); 214 unused = -2; /* flag as existing */ 215 break; 216 } 217 } 218 } 219 if (unused == -2) 220 continue; /* entry exists, skip... */ 221 if (unused == -1) { 222 if (label.d_npartitions < MAXPARTITIONS) { 223 unused = label.d_npartitions; 224 label.d_npartitions++; 225 } else { 226 printf( 227 " WARNING: no slots free for %s partition.\n", 228 fstypenames[npe.p_fstype]); 229 continue; 230 } 231 } 232 233 if (verbose) 234 printf(" adding %s partition to slot %c.\n", 235 fstypenames[npe.p_fstype], unused + 'a'); 236 237 /* 238 * XXX guess some filesystem parameters, these should be 239 * scanned from the superblocks 240 */ 241 switch (npe.p_fstype) { 242 case FS_BSDFFS: 243 case FS_APPLEUFS: 244 npe.p_fsize = 1024; 245 npe.p_frag = 8; 246 npe.p_cpg = 16; 247 break; 248 case FS_BSDLFS: 249 npe.p_fsize = 1024; 250 npe.p_frag = 8; 251 npe.p_sgs = 7; 252 break; 253 } 254 255 changed++; 256 label.d_partitions[unused] = npe; 257 } 258 259 /* recursively scan extended partitions */ 260 for (i = 0; i < MBR_PART_COUNT; i++) { 261 u_int32_t poff; 262 263 if (MBR_IS_EXTENDED(parts[i].mbrp_type)) { 264 poff = getlong(&parts[i].mbrp_start) + extoff; 265 changed += getparts(sd, poff, 266 extoff ? extoff : poff, verbose); 267 } 268 } 269 270 free(buf); 271 return (changed); 272 } 273 274 static void 275 usage(void) 276 { 277 fprintf(stderr, "usage: %s [-fqrw] [-s sector] device\n", 278 getprogname()); 279 exit(1); 280 } 281 282 283 int 284 main(int argc, char **argv) 285 { 286 int sd, ch, changed; 287 char *ep, name[MAXPATHLEN]; 288 int force; /* force label update */ 289 int raw; /* update on-disk label as well */ 290 int verbose; /* verbose output */ 291 int write_it; /* update in-core label if changed */ 292 uint32_t sector; /* sector that contains the MBR */ 293 ulong ulsector; 294 295 force = 0; 296 raw = 0; 297 verbose = 1; 298 write_it = 0; 299 sector = MBR_BBSECTOR; 300 while ((ch = getopt(argc, argv, "fqrs:w")) != -1) { 301 switch (ch) { 302 case 'f': 303 force = 1; 304 break; 305 case 'q': 306 verbose = 0; 307 break; 308 case 'r': 309 raw = 1; 310 break; 311 case 's': 312 errno = 0; 313 ulsector = strtoul(optarg, &ep, 10); 314 if (optarg[0] == '\0' || *ep != '\0') 315 errx(EXIT_FAILURE, 316 "sector number (%s) incorrectly specified", 317 optarg); 318 if ((errno == ERANGE && ulsector == ULONG_MAX) || 319 ulsector > UINT32_MAX) 320 errx(EXIT_FAILURE, 321 "sector number (%s) out of range", 322 optarg); 323 sector = (uint32_t)ulsector; 324 break; 325 case 'w': 326 write_it = 1; 327 break; 328 default: 329 usage(); 330 } 331 } 332 argc -= optind; 333 argv += optind; 334 if (argc != 1) 335 usage(); 336 337 if ((sd = opendisk(argv[0], write_it ? O_RDWR : O_RDONLY, name, 338 (size_t)MAXPATHLEN, 0)) < 0) { 339 perror(argv[0]); 340 exit(1); 341 } 342 getlabel(sd); 343 changed = getparts(sd, sector, 0, verbose); 344 345 if (verbose) { 346 putchar('\n'); 347 showpartitions(stdout, &label, 0); 348 putchar('\n'); 349 } 350 if (write_it) { 351 if (! changed && ! force) 352 printf("No change; not updating disk label.\n"); 353 else { 354 if (verbose) 355 printf("Updating in-core %sdisk label.\n", 356 raw ? "and on-disk " : ""); 357 setlabel(sd, raw); 358 } 359 } else { 360 printf("Not updating disk label.\n"); 361 } 362 close(sd); 363 return (0); 364 } 365