1 1.26 mrg /* $NetBSD: sunlabel.c,v 1.26 2023/08/11 07:05:39 mrg Exp $ */ 2 1.1 mrg 3 1.3 christos /*- 4 1.3 christos * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 1.3 christos * All rights reserved. 6 1.3 christos * 7 1.3 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.4 mrg * by der Mouse. 9 1.3 christos * 10 1.3 christos * Redistribution and use in source and binary forms, with or without 11 1.3 christos * modification, are permitted provided that the following conditions 12 1.3 christos * are met: 13 1.3 christos * 1. Redistributions of source code must retain the above copyright 14 1.3 christos * notice, this list of conditions and the following disclaimer. 15 1.3 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.3 christos * notice, this list of conditions and the following disclaimer in the 17 1.3 christos * documentation and/or other materials provided with the distribution. 18 1.3 christos * 19 1.3 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.3 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.3 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.3 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.3 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.3 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.3 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.3 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.3 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.3 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.3 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.3 christos */ 31 1.1 mrg 32 1.19 apb #if HAVE_NBTOOL_CONFIG_H 33 1.19 apb #include "nbtool_config.h" 34 1.19 apb #endif 35 1.19 apb 36 1.3 christos #include <sys/cdefs.h> 37 1.10 augustss #if defined(__RCSID) && !defined(lint) 38 1.26 mrg __RCSID("$NetBSD: sunlabel.c,v 1.26 2023/08/11 07:05:39 mrg Exp $"); 39 1.10 augustss #endif 40 1.1 mrg 41 1.1 mrg #include <stdio.h> 42 1.1 mrg #include <errno.h> 43 1.13 kleink #include <fcntl.h> 44 1.1 mrg #include <ctype.h> 45 1.1 mrg #include <stdlib.h> 46 1.1 mrg #include <unistd.h> 47 1.12 matt #ifndef NO_TERMCAP_WIDTH 48 1.1 mrg #include <termcap.h> 49 1.12 matt #endif 50 1.10 augustss #include <string.h> 51 1.1 mrg #include <strings.h> 52 1.3 christos #include <inttypes.h> 53 1.3 christos #include <err.h> 54 1.4 mrg 55 1.1 mrg #include <sys/ioctl.h> 56 1.4 mrg 57 1.4 mrg /* If neither S_COMMAND nor NO_S_COMMAND is defined, guess. */ 58 1.4 mrg #if !defined(S_COMMAND) && !defined(NO_S_COMMAND) 59 1.4 mrg #define S_COMMAND 60 1.4 mrg #include <util.h> 61 1.10 augustss #include <sys/disklabel.h> 62 1.4 mrg #endif 63 1.1 mrg 64 1.3 christos /* 65 1.3 christos * NPART is the total number of partitions. This must be <= 43, given the 66 1.3 christos * amount of space available to store extended partitions. It also must be 67 1.3 christos * <=26, given the use of single letters to name partitions. The 8 is the 68 1.3 christos * number of `standard' partitions; this arguably should be a #define, since 69 1.3 christos * it occurs not only here but scattered throughout the code. 70 1.3 christos */ 71 1.1 mrg #define NPART 16 72 1.3 christos #define NXPART (NPART - 8) 73 1.3 christos #define PARTLETTER(i) ((i) + 'a') 74 1.3 christos #define LETTERPART(i) ((i) - 'a') 75 1.1 mrg 76 1.1 mrg /* 77 1.3 christos * A partition. We keep redundant information around, making sure 78 1.3 christos * that whenever we change one, we keep another constant and update 79 1.3 christos * the third. Which one is which depends. Arguably a partition 80 1.3 christos * should also know its partition number; here, if we need that we 81 1.3 christos * cheat, using (effectively) ptr-&label.partitions[0]. 82 1.1 mrg */ 83 1.1 mrg struct part { 84 1.3 christos uint32_t startcyl; 85 1.3 christos uint32_t nblk; 86 1.3 christos uint32_t endcyl; 87 1.3 christos }; 88 1.1 mrg 89 1.1 mrg /* 90 1.1 mrg * A label. As the embedded comments indicate, much of this structure 91 1.3 christos * corresponds directly to Sun's struct dk_label. Some of the values 92 1.3 christos * here are historical holdovers. Apparently really old Suns did 93 1.3 christos * their own sparing in software, so a sector or two per cylinder, 94 1.3 christos * plus a whole cylinder or two at the end, got set aside as spares. 95 1.3 christos * acyl and apc count those spares, and this is also why ncyl and pcyl 96 1.3 christos * both exist. These days the spares generally are hidden from the 97 1.3 christos * host by the disk, and there's no reason not to set 98 1.3 christos * ncyl=pcyl=ceil(device size/spc) and acyl=apc=0. 99 1.1 mrg * 100 1.1 mrg * Note also that the geometry assumptions behind having nhead and 101 1.3 christos * nsect assume that the sect/trk and trk/cyl values are constant 102 1.3 christos * across the whole drive. The latter is still usually true; the 103 1.3 christos * former isn't. In my experience, you can just put fixed values 104 1.3 christos * here; the basis for software knowing the drive geometry is also 105 1.3 christos * mostly invalid these days anyway. (I just use nhead=32 nsect=64, 106 1.3 christos * which gives me 1M "cylinders", a convenient size.) 107 1.1 mrg */ 108 1.1 mrg struct label { 109 1.3 christos /* BEGIN fields taken directly from struct dk_label */ 110 1.3 christos char asciilabel[128]; 111 1.3 christos uint32_t rpm; /* Spindle rotation speed - useless now */ 112 1.3 christos uint32_t pcyl; /* Physical cylinders */ 113 1.3 christos uint32_t apc; /* Alternative sectors per cylinder */ 114 1.3 christos uint32_t obs1; /* Obsolete? */ 115 1.3 christos uint32_t obs2; /* Obsolete? */ 116 1.3 christos uint32_t intrlv; /* Interleave - never anything but 1 IME */ 117 1.3 christos uint32_t ncyl; /* Number of usable cylinders */ 118 1.3 christos uint32_t acyl; /* Alternative cylinders - pcyl minus ncyl */ 119 1.3 christos uint32_t nhead; /* Tracks-per-cylinder (usually # of heads) */ 120 1.3 christos uint32_t nsect; /* Sectors-per-track */ 121 1.3 christos uint32_t obs3; /* Obsolete? */ 122 1.3 christos uint32_t obs4; /* Obsolete? */ 123 1.3 christos /* END fields taken directly from struct dk_label */ 124 1.3 christos uint32_t spc; /* Sectors per cylinder - nhead*nsect */ 125 1.3 christos uint32_t dirty:1;/* Modified since last read */ 126 1.3 christos struct part partitions[NPART];/* The partitions themselves */ 127 1.3 christos }; 128 1.1 mrg 129 1.1 mrg /* 130 1.1 mrg * Describes a field in the label. 131 1.1 mrg * 132 1.1 mrg * tag is a short name for the field, like "apc" or "nsect". loc is a 133 1.3 christos * pointer to the place in the label where it's stored. print is a 134 1.3 christos * function to print the value; the second argument is the current 135 1.3 christos * column number, and the return value is the new current column 136 1.3 christos * number. (This allows print functions to do proper line wrapping.) 137 1.3 christos * chval is called to change a field; the first argument is the 138 1.3 christos * command line portion that contains the new value (in text form). 139 1.3 christos * The chval function is responsible for parsing and error-checking as 140 1.3 christos * well as doing the modification. changed is a function which does 141 1.3 christos * field-specific actions necessary when the field has been changed. 142 1.3 christos * This could be rolled into the chval function, but I believe this 143 1.3 christos * way provides better code sharing. 144 1.1 mrg * 145 1.1 mrg * Note that while the fields in the label vary in size (8, 16, or 32 146 1.3 christos * bits), we store everything as ints in the label struct, above, and 147 1.3 christos * convert when packing and unpacking. This allows us to have only 148 1.3 christos * one numeric chval function. 149 1.1 mrg */ 150 1.1 mrg struct field { 151 1.3 christos const char *tag; 152 1.3 christos void *loc; 153 1.3 christos int (*print)(struct field *, int); 154 1.3 christos void (*chval)(const char *, struct field *); 155 1.3 christos void (*changed)(void); 156 1.3 christos int taglen; 157 1.3 christos }; 158 1.1 mrg 159 1.1 mrg /* LABEL_MAGIC was chosen by Sun and cannot be trivially changed. */ 160 1.1 mrg #define LABEL_MAGIC 0xdabe 161 1.3 christos /* 162 1.3 christos * LABEL_XMAGIC needs to agree between here and any other code that uses 163 1.3 christos * extended partitions (mainly the kernel). 164 1.3 christos */ 165 1.1 mrg #define LABEL_XMAGIC (0x199d1fe2+8) 166 1.1 mrg 167 1.3 christos static int diskfd; /* fd on the disk */ 168 1.3 christos static const char *diskname; /* name of the disk, for messages */ 169 1.3 christos static int readonly; /* true iff it's open RO */ 170 1.3 christos static unsigned char labelbuf[512]; /* Buffer holding the label sector */ 171 1.3 christos static struct label label; /* The label itself. */ 172 1.3 christos static int fixmagic; /* -m, ignore bad magic #s */ 173 1.3 christos static int fixcksum; /* -s, ignore bad cksums */ 174 1.3 christos static int newlabel; /* -n, ignore all on-disk values */ 175 1.3 christos static int quiet; /* -q, don't print chatter */ 176 1.1 mrg 177 1.1 mrg /* 178 1.1 mrg * The various functions that go in the field function pointers. The 179 1.3 christos * _ascii functions are for 128-byte string fields (the ASCII label); 180 1.3 christos * the _int functions are for int-valued fields (everything else). 181 1.3 christos * update_spc is a `changed' function for updating the spc value when 182 1.3 christos * changing one of the two values that make it up. 183 1.3 christos */ 184 1.3 christos static int print_ascii(struct field *, int); 185 1.3 christos static void chval_ascii(const char *, struct field *); 186 1.3 christos static int print_int(struct field *, int); 187 1.3 christos static void chval_int(const char *, struct field *); 188 1.1 mrg static void update_spc(void); 189 1.1 mrg 190 1.1 mrg /* The fields themselves. */ 191 1.3 christos static struct field fields[] = 192 1.3 christos { 193 1.23 lukem {"ascii", &label.asciilabel[0], print_ascii, chval_ascii, 0, 0 }, 194 1.23 lukem {"rpm", &label.rpm, print_int, chval_int, 0, 0 }, 195 1.23 lukem {"pcyl", &label.pcyl, print_int, chval_int, 0, 0 }, 196 1.23 lukem {"apc", &label.apc, print_int, chval_int, 0, 0 }, 197 1.23 lukem {"obs1", &label.obs1, print_int, chval_int, 0, 0 }, 198 1.23 lukem {"obs2", &label.obs2, print_int, chval_int, 0, 0 }, 199 1.23 lukem {"intrlv", &label.intrlv, print_int, chval_int, 0, 0 }, 200 1.23 lukem {"ncyl", &label.ncyl, print_int, chval_int, 0, 0 }, 201 1.23 lukem {"acyl", &label.acyl, print_int, chval_int, 0, 0 }, 202 1.23 lukem {"nhead", &label.nhead, print_int, chval_int, update_spc, 0 }, 203 1.23 lukem {"nsect", &label.nsect, print_int, chval_int, update_spc, 0 }, 204 1.23 lukem {"obs3", &label.obs3, print_int, chval_int, 0, 0 }, 205 1.23 lukem {"obs4", &label.obs4, print_int, chval_int, 0, 0 }, 206 1.23 lukem {NULL, NULL, NULL, NULL, 0, 0 } 207 1.3 christos }; 208 1.7 lukem 209 1.1 mrg /* 210 1.1 mrg * We'd _like_ to use howmany() from the include files, but can't count 211 1.1 mrg * on its being present or working. 212 1.1 mrg */ 213 1.17 perry static inline uint32_t how_many(uint32_t amt, uint32_t unit) 214 1.17 perry __attribute__((const)); 215 1.17 perry static inline uint32_t 216 1.7 lukem how_many(uint32_t amt, uint32_t unit) 217 1.1 mrg { 218 1.3 christos return ((amt + unit - 1) / unit); 219 1.1 mrg } 220 1.1 mrg 221 1.1 mrg /* 222 1.1 mrg * Try opening the disk, given a name. If mustsucceed is true, we 223 1.1 mrg * "cannot fail"; failures produce gripe-and-exit, and if we return, 224 1.1 mrg * our return value is 1. Otherwise, we return 1 on success and 0 on 225 1.1 mrg * failure. 226 1.1 mrg */ 227 1.3 christos static int 228 1.3 christos trydisk(const char *s, int mustsucceed) 229 1.1 mrg { 230 1.3 christos int ro = 0; 231 1.1 mrg 232 1.3 christos diskname = s; 233 1.3 christos if ((diskfd = open(s, O_RDWR)) == -1 || 234 1.20 apb (diskfd = open(s, O_RDWR | O_NONBLOCK)) == -1) { 235 1.3 christos if ((diskfd = open(s, O_RDONLY)) == -1) { 236 1.3 christos if (mustsucceed) 237 1.3 christos err(1, "Cannot open `%s'", s); 238 1.3 christos else 239 1.3 christos return 0; 240 1.3 christos } 241 1.3 christos ro = 1; 242 1.3 christos } 243 1.3 christos if (ro && !quiet) 244 1.3 christos warnx("No write access, label is readonly"); 245 1.3 christos readonly = ro; 246 1.3 christos return 1; 247 1.1 mrg } 248 1.1 mrg 249 1.1 mrg /* 250 1.1 mrg * Set the disk device, given the user-supplied string. Note that even 251 1.3 christos * if we malloc, we never free, because either trydisk eventually 252 1.3 christos * succeeds, in which case the string is saved in diskname, or it 253 1.3 christos * fails, in which case we exit and freeing is irrelevant. 254 1.3 christos */ 255 1.3 christos static void 256 1.3 christos setdisk(const char *s) 257 1.3 christos { 258 1.3 christos char *tmp; 259 1.3 christos 260 1.3 christos if (strchr(s, '/')) { 261 1.3 christos trydisk(s, 1); 262 1.3 christos return; 263 1.3 christos } 264 1.3 christos if (trydisk(s, 0)) 265 1.3 christos return; 266 1.9 uwe #ifndef DISTRIB /* native tool: search in /dev */ 267 1.11 itojun asprintf(&tmp, "/dev/%s", s); 268 1.11 itojun if (!tmp) 269 1.11 itojun err(1, "malloc"); 270 1.11 itojun if (trydisk(tmp, 0)) { 271 1.11 itojun free(tmp); 272 1.3 christos return; 273 1.11 itojun } 274 1.11 itojun free(tmp); 275 1.11 itojun asprintf(&tmp, "/dev/%s%c", s, getrawpartition() + 'a'); 276 1.11 itojun if (!tmp) 277 1.11 itojun err(1, "malloc"); 278 1.11 itojun if (trydisk(tmp, 0)) { 279 1.11 itojun free(tmp); 280 1.3 christos return; 281 1.11 itojun } 282 1.9 uwe #endif 283 1.3 christos errx(1, "Can't find device for disk `%s'", s); 284 1.3 christos } 285 1.3 christos 286 1.18 perry static void usage(void) __dead; 287 1.3 christos static void 288 1.3 christos usage(void) 289 1.3 christos { 290 1.15 jmmv (void)fprintf(stderr, "usage: %s [-mnqs] disk\n", getprogname()); 291 1.3 christos exit(1); 292 1.2 mrg } 293 1.2 mrg 294 1.1 mrg /* 295 1.3 christos * Command-line arguments. We can have at most one non-flag 296 1.1 mrg * argument, which is the disk name; we can also have flags 297 1.1 mrg * 298 1.3 christos * -m 299 1.1 mrg * Turns on fixmagic, which causes bad magic numbers to be 300 1.1 mrg * ignored (though a complaint is still printed), rather 301 1.1 mrg * than being fatal errors. 302 1.1 mrg * 303 1.3 christos * -s 304 1.1 mrg * Turns on fixcksum, which causes bad checksums to be 305 1.1 mrg * ignored (though a complaint is still printed), rather 306 1.1 mrg * than being fatal errors. 307 1.1 mrg * 308 1.3 christos * -n 309 1.1 mrg * Turns on newlabel, which means we're creating a new 310 1.1 mrg * label and anything in the label sector should be 311 1.8 lukem * ignored. This is a bit like -m -s, except that it 312 1.8 lukem * doesn't print complaints and it ignores possible 313 1.8 lukem * garbage on-disk. 314 1.1 mrg * 315 1.1 mrg * -q 316 1.1 mrg * Turns on quiet, which suppresses printing of prompts 317 1.1 mrg * and other irrelevant chatter. If you're trying to use 318 1.1 mrg * sunlabel in an automated way, you probably want this. 319 1.1 mrg */ 320 1.7 lukem static void 321 1.7 lukem handleargs(int ac, char **av) 322 1.1 mrg { 323 1.3 christos int c; 324 1.3 christos 325 1.8 lukem while ((c = getopt(ac, av, "mnqs")) != -1) { 326 1.3 christos switch (c) { 327 1.3 christos case 'm': 328 1.3 christos fixmagic++; 329 1.3 christos break; 330 1.3 christos case 'n': 331 1.3 christos newlabel++; 332 1.3 christos break; 333 1.3 christos case 'q': 334 1.3 christos quiet++; 335 1.3 christos break; 336 1.3 christos case 's': 337 1.3 christos fixcksum++; 338 1.3 christos break; 339 1.3 christos case '?': 340 1.3 christos warnx("Illegal option `%c'", c); 341 1.3 christos usage(); 342 1.3 christos } 343 1.3 christos } 344 1.8 lukem ac -= optind; 345 1.8 lukem av += optind; 346 1.8 lukem if (ac != 1) 347 1.8 lukem usage(); 348 1.8 lukem setdisk(av[0]); 349 1.1 mrg } 350 1.8 lukem 351 1.1 mrg /* 352 1.1 mrg * Sets the ending cylinder for a partition. This exists mainly to 353 1.3 christos * centralize the check. (If spc is zero, cylinder numbers make 354 1.3 christos * little sense, and the code would otherwise die on divide-by-0 if we 355 1.3 christos * barged blindly ahead.) We need to call this on a partition 356 1.3 christos * whenever we change it; we need to call it on all partitions 357 1.3 christos * whenever we change spc. 358 1.3 christos */ 359 1.3 christos static void 360 1.3 christos set_endcyl(struct part *p) 361 1.3 christos { 362 1.3 christos if (label.spc == 0) { 363 1.3 christos p->endcyl = p->startcyl; 364 1.3 christos } else { 365 1.3 christos p->endcyl = p->startcyl + how_many(p->nblk, label.spc); 366 1.3 christos } 367 1.1 mrg } 368 1.1 mrg 369 1.1 mrg /* 370 1.1 mrg * Unpack a label from disk into the in-core label structure. If 371 1.3 christos * newlabel is set, we don't actually do so; we just synthesize a 372 1.3 christos * blank label instead. This is where knowledge of the Sun label 373 1.3 christos * format is kept for read; pack_label is the corresponding routine 374 1.3 christos * for write. We are careful to use labelbuf, l_s, or l_l as 375 1.3 christos * appropriate to avoid byte-sex issues, so we can work on 376 1.3 christos * little-endian machines. 377 1.1 mrg * 378 1.1 mrg * Note that a bad magic number for the extended partition information 379 1.3 christos * is not considered an error; it simply indicates there is no 380 1.3 christos * extended partition information. Arguably this is the Wrong Thing, 381 1.3 christos * and we should take zero as meaning no info, and anything other than 382 1.3 christos * zero or LABEL_XMAGIC as reason to gripe. 383 1.3 christos */ 384 1.3 christos static const char * 385 1.3 christos unpack_label(void) 386 1.3 christos { 387 1.3 christos unsigned short int l_s[256]; 388 1.3 christos unsigned long int l_l[128]; 389 1.3 christos int i; 390 1.3 christos unsigned long int sum; 391 1.3 christos int have_x; 392 1.3 christos 393 1.3 christos if (newlabel) { 394 1.3 christos bzero(&label.asciilabel[0], 128); 395 1.3 christos label.rpm = 0; 396 1.3 christos label.pcyl = 0; 397 1.3 christos label.apc = 0; 398 1.3 christos label.obs1 = 0; 399 1.3 christos label.obs2 = 0; 400 1.3 christos label.intrlv = 0; 401 1.3 christos label.ncyl = 0; 402 1.3 christos label.acyl = 0; 403 1.3 christos label.nhead = 0; 404 1.3 christos label.nsect = 0; 405 1.3 christos label.obs3 = 0; 406 1.3 christos label.obs4 = 0; 407 1.3 christos for (i = 0; i < NPART; i++) { 408 1.3 christos label.partitions[i].startcyl = 0; 409 1.3 christos label.partitions[i].nblk = 0; 410 1.3 christos set_endcyl(&label.partitions[i]); 411 1.3 christos } 412 1.3 christos label.spc = 0; 413 1.3 christos label.dirty = 1; 414 1.3 christos return (0); 415 1.3 christos } 416 1.3 christos for (i = 0; i < 256; i++) 417 1.3 christos l_s[i] = (labelbuf[i + i] << 8) | labelbuf[i + i + 1]; 418 1.3 christos for (i = 0; i < 128; i++) 419 1.3 christos l_l[i] = (l_s[i + i] << 16) | l_s[i + i + 1]; 420 1.3 christos if (l_s[254] != LABEL_MAGIC) { 421 1.3 christos if (fixmagic) { 422 1.3 christos label.dirty = 1; 423 1.3 christos warnx("ignoring incorrect magic number."); 424 1.3 christos } else { 425 1.3 christos return "bad magic number"; 426 1.3 christos } 427 1.3 christos } 428 1.3 christos sum = 0; 429 1.3 christos for (i = 0; i < 256; i++) 430 1.3 christos sum ^= l_s[i]; 431 1.3 christos label.dirty = 0; 432 1.3 christos if (sum != 0) { 433 1.3 christos if (fixcksum) { 434 1.3 christos label.dirty = 1; 435 1.3 christos warnx("ignoring incorrect checksum."); 436 1.3 christos } else { 437 1.3 christos return "checksum wrong"; 438 1.3 christos } 439 1.3 christos } 440 1.3 christos (void)memcpy(&label.asciilabel[0], &labelbuf[0], 128); 441 1.3 christos label.rpm = l_s[210]; 442 1.3 christos label.pcyl = l_s[211]; 443 1.3 christos label.apc = l_s[212]; 444 1.3 christos label.obs1 = l_s[213]; 445 1.3 christos label.obs2 = l_s[214]; 446 1.3 christos label.intrlv = l_s[215]; 447 1.3 christos label.ncyl = l_s[216]; 448 1.3 christos label.acyl = l_s[217]; 449 1.3 christos label.nhead = l_s[218]; 450 1.3 christos label.nsect = l_s[219]; 451 1.3 christos label.obs3 = l_s[220]; 452 1.3 christos label.obs4 = l_s[221]; 453 1.3 christos label.spc = label.nhead * label.nsect; 454 1.3 christos for (i = 0; i < 8; i++) { 455 1.3 christos label.partitions[i].startcyl = (uint32_t)l_l[i + i + 111]; 456 1.3 christos label.partitions[i].nblk = (uint32_t)l_l[i + i + 112]; 457 1.3 christos set_endcyl(&label.partitions[i]); 458 1.3 christos } 459 1.3 christos have_x = 0; 460 1.3 christos if (l_l[33] == LABEL_XMAGIC) { 461 1.3 christos sum = 0; 462 1.3 christos for (i = 0; i < ((NXPART * 2) + 1); i++) 463 1.3 christos sum += l_l[33 + i]; 464 1.3 christos if (sum != l_l[32]) { 465 1.3 christos if (fixcksum) { 466 1.3 christos label.dirty = 1; 467 1.3 christos warnx("Ignoring incorrect extended-partition checksum."); 468 1.3 christos have_x = 1; 469 1.3 christos } else { 470 1.3 christos warnx("Extended-partition magic right but checksum wrong."); 471 1.3 christos } 472 1.3 christos } else { 473 1.3 christos have_x = 1; 474 1.3 christos } 475 1.3 christos } 476 1.3 christos if (have_x) { 477 1.3 christos for (i = 0; i < NXPART; i++) { 478 1.3 christos int j = i + i + 34; 479 1.3 christos label.partitions[i + 8].startcyl = (uint32_t)l_l[j++]; 480 1.3 christos label.partitions[i + 8].nblk = (uint32_t)l_l[j++]; 481 1.3 christos set_endcyl(&label.partitions[i + 8]); 482 1.3 christos } 483 1.3 christos } else { 484 1.3 christos for (i = 0; i < NXPART; i++) { 485 1.3 christos label.partitions[i + 8].startcyl = 0; 486 1.3 christos label.partitions[i + 8].nblk = 0; 487 1.3 christos set_endcyl(&label.partitions[i + 8]); 488 1.3 christos } 489 1.3 christos } 490 1.3 christos return 0; 491 1.1 mrg } 492 1.1 mrg 493 1.1 mrg /* 494 1.1 mrg * Pack a label from the in-core label structure into on-disk format. 495 1.3 christos * This is where knowledge of the Sun label format is kept for write; 496 1.3 christos * unpack_label is the corresponding routine for read. If all 497 1.3 christos * partitions past the first 8 are size=0 cyl=0, we store all-0s in 498 1.3 christos * the extended partition space, to be fully compatible with Sun 499 1.3 christos * labels. Since AFIAK nothing works in that case that would break if 500 1.3 christos * we put extended partition info there in the same format we'd use if 501 1.3 christos * there were real info there, this is arguably unnecessary, but it's 502 1.3 christos * easy to do. 503 1.1 mrg * 504 1.1 mrg * We are careful to avoid endianness issues by constructing everything 505 1.3 christos * in an array of shorts. We do this rather than using chars or longs 506 1.3 christos * because the checksum is defined in terms of shorts; using chars or 507 1.3 christos * longs would simplify small amounts of code at the price of 508 1.3 christos * complicating more. 509 1.3 christos */ 510 1.3 christos static void 511 1.3 christos pack_label(void) 512 1.3 christos { 513 1.3 christos unsigned short int l_s[256]; 514 1.3 christos int i; 515 1.3 christos unsigned short int sum; 516 1.3 christos 517 1.3 christos memset(&l_s[0], 0, 512); 518 1.3 christos memcpy(&labelbuf[0], &label.asciilabel[0], 128); 519 1.3 christos for (i = 0; i < 64; i++) 520 1.3 christos l_s[i] = (labelbuf[i + i] << 8) | labelbuf[i + i + 1]; 521 1.3 christos l_s[210] = label.rpm; 522 1.3 christos l_s[211] = label.pcyl; 523 1.3 christos l_s[212] = label.apc; 524 1.3 christos l_s[213] = label.obs1; 525 1.3 christos l_s[214] = label.obs2; 526 1.3 christos l_s[215] = label.intrlv; 527 1.3 christos l_s[216] = label.ncyl; 528 1.3 christos l_s[217] = label.acyl; 529 1.3 christos l_s[218] = label.nhead; 530 1.3 christos l_s[219] = label.nsect; 531 1.3 christos l_s[220] = label.obs3; 532 1.3 christos l_s[221] = label.obs4; 533 1.3 christos for (i = 0; i < 8; i++) { 534 1.3 christos l_s[(i * 4) + 222] = label.partitions[i].startcyl >> 16; 535 1.3 christos l_s[(i * 4) + 223] = label.partitions[i].startcyl & 0xffff; 536 1.3 christos l_s[(i * 4) + 224] = label.partitions[i].nblk >> 16; 537 1.3 christos l_s[(i * 4) + 225] = label.partitions[i].nblk & 0xffff; 538 1.3 christos } 539 1.3 christos for (i = 0; i < NXPART; i++) { 540 1.3 christos if (label.partitions[i + 8].startcyl || 541 1.3 christos label.partitions[i + 8].nblk) 542 1.3 christos break; 543 1.3 christos } 544 1.3 christos if (i < NXPART) { 545 1.3 christos unsigned long int xsum; 546 1.3 christos l_s[66] = LABEL_XMAGIC >> 16; 547 1.3 christos l_s[67] = LABEL_XMAGIC & 0xffff; 548 1.3 christos for (i = 0; i < NXPART; i++) { 549 1.3 christos int j = (i * 4) + 68; 550 1.3 christos l_s[j++] = label.partitions[i + 8].startcyl >> 16; 551 1.3 christos l_s[j++] = label.partitions[i + 8].startcyl & 0xffff; 552 1.3 christos l_s[j++] = label.partitions[i + 8].nblk >> 16; 553 1.3 christos l_s[j++] = label.partitions[i + 8].nblk & 0xffff; 554 1.3 christos } 555 1.3 christos xsum = 0; 556 1.3 christos for (i = 0; i < ((NXPART * 2) + 1); i++) 557 1.3 christos xsum += (l_s[i + i + 66] << 16) | l_s[i + i + 67]; 558 1.3 christos l_s[64] = (int32_t)(xsum >> 16); 559 1.3 christos l_s[65] = (int32_t)(xsum & 0xffff); 560 1.3 christos } 561 1.3 christos l_s[254] = LABEL_MAGIC; 562 1.3 christos sum = 0; 563 1.3 christos for (i = 0; i < 255; i++) 564 1.3 christos sum ^= l_s[i]; 565 1.3 christos l_s[255] = sum; 566 1.3 christos for (i = 0; i < 256; i++) { 567 1.3 christos labelbuf[i + i] = ((uint32_t)l_s[i]) >> 8; 568 1.3 christos labelbuf[i + i + 1] = l_s[i] & 0xff; 569 1.3 christos } 570 1.1 mrg } 571 1.1 mrg 572 1.1 mrg /* 573 1.1 mrg * Get the label. Read it off the disk and unpack it. This function 574 1.1 mrg * is nothing but lseek, read, unpack_label, and error checking. 575 1.1 mrg */ 576 1.3 christos static void 577 1.3 christos getlabel(void) 578 1.1 mrg { 579 1.3 christos int rv; 580 1.3 christos const char *lerr; 581 1.3 christos 582 1.14 jmc if (lseek(diskfd, (off_t)0, SEEK_SET) == (off_t)-1) 583 1.3 christos err(1, "lseek to 0 on `%s' failed", diskname); 584 1.3 christos 585 1.3 christos if ((rv = read(diskfd, &labelbuf[0], 512)) == -1) 586 1.3 christos err(1, "read label from `%s' failed", diskname); 587 1.3 christos 588 1.3 christos if (rv != 512) 589 1.3 christos errx(1, "short read from `%s' wanted %d, got %d.", diskname, 590 1.3 christos 512, rv); 591 1.1 mrg 592 1.3 christos lerr = unpack_label(); 593 1.3 christos if (lerr) 594 1.6 grant errx(1, "bogus label on `%s' (%s)", diskname, lerr); 595 1.1 mrg } 596 1.1 mrg 597 1.1 mrg /* 598 1.1 mrg * Put the label. Pack it and write it to the disk. This function is 599 1.1 mrg * little more than pack_label, lseek, write, and error checking. 600 1.1 mrg */ 601 1.3 christos static void 602 1.3 christos putlabel(void) 603 1.1 mrg { 604 1.3 christos int rv; 605 1.1 mrg 606 1.3 christos if (readonly) { 607 1.3 christos warnx("No write access to `%s'", diskname); 608 1.3 christos return; 609 1.3 christos } 610 1.3 christos 611 1.14 jmc if (lseek(diskfd, (off_t)0, SEEK_SET) < (off_t)-1) 612 1.3 christos err(1, "lseek to 0 on `%s' failed", diskname); 613 1.3 christos 614 1.3 christos pack_label(); 615 1.3 christos 616 1.3 christos if ((rv = write(diskfd, &labelbuf[0], 512)) == -1) { 617 1.3 christos err(1, "write label to `%s' failed", diskname); 618 1.3 christos exit(1); 619 1.3 christos } 620 1.3 christos 621 1.3 christos if (rv != 512) 622 1.3 christos errx(1, "short write to `%s': wanted %d, got %d", 623 1.3 christos diskname, 512, rv); 624 1.3 christos 625 1.3 christos label.dirty = 0; 626 1.1 mrg } 627 1.1 mrg 628 1.1 mrg /* 629 1.1 mrg * Skip whitespace. Used several places in the command-line parsing 630 1.3 christos * code. 631 1.1 mrg */ 632 1.3 christos static void 633 1.3 christos skipspaces(const char **cpp) 634 1.1 mrg { 635 1.3 christos const char *cp = *cpp; 636 1.3 christos while (*cp && isspace((unsigned char)*cp)) 637 1.3 christos cp++; 638 1.3 christos *cpp = cp; 639 1.1 mrg } 640 1.1 mrg 641 1.1 mrg /* 642 1.1 mrg * Scan a number. The first arg points to the char * that's moving 643 1.1 mrg * along the string. The second arg points to where we should store 644 1.1 mrg * the result. The third arg says what we're scanning, for errors. 645 1.1 mrg * The return value is 0 on error, or nonzero if all goes well. 646 1.1 mrg */ 647 1.3 christos static int 648 1.3 christos scannum(const char **cpp, uint32_t *np, const char *tag) 649 1.1 mrg { 650 1.3 christos uint32_t v; 651 1.3 christos int nd; 652 1.3 christos const char *cp; 653 1.3 christos 654 1.3 christos skipspaces(cpp); 655 1.3 christos v = 0; 656 1.3 christos nd = 0; 657 1.3 christos 658 1.3 christos cp = *cpp; 659 1.16 dsl while (*cp && isdigit((unsigned char)*cp)) { 660 1.3 christos v = (10 * v) + (*cp++ - '0'); 661 1.3 christos nd++; 662 1.3 christos } 663 1.3 christos *cpp = cp; 664 1.1 mrg 665 1.3 christos if (nd == 0) { 666 1.3 christos printf("Missing/invalid %s: %s\n", tag, cp); 667 1.3 christos return (0); 668 1.3 christos } 669 1.3 christos *np = v; 670 1.3 christos return (1); 671 1.1 mrg } 672 1.1 mrg 673 1.1 mrg /* 674 1.1 mrg * Change a partition. pno is the number of the partition to change; 675 1.1 mrg * numbers is a pointer to the string containing the specification for 676 1.1 mrg * the new start and size. This always takes the form "start size", 677 1.1 mrg * where start can be 678 1.1 mrg * 679 1.1 mrg * a number 680 1.1 mrg * The partition starts at the beginning of that cylinder. 681 1.1 mrg * 682 1.1 mrg * start-X 683 1.1 mrg * The partition starts at the same place partition X does. 684 1.1 mrg * 685 1.1 mrg * end-X 686 1.1 mrg * The partition starts at the place partition X ends. If 687 1.1 mrg * partition X does not exactly on a cylinder boundary, it 688 1.1 mrg * is effectively rounded up. 689 1.1 mrg * 690 1.1 mrg * and size can be 691 1.1 mrg * 692 1.1 mrg * a number 693 1.1 mrg * The partition is that many sectors long. 694 1.1 mrg * 695 1.1 mrg * num/num/num 696 1.1 mrg * The three numbers are cyl/trk/sect counts. n1/n2/n3 is 697 1.1 mrg * equivalent to specifying a single number 698 1.1 mrg * ((n1*label.nhead)+n2)*label.nsect)+n3. In particular, 699 1.1 mrg * if label.nhead or label.nsect is zero, this has limited 700 1.1 mrg * usefulness. 701 1.1 mrg * 702 1.1 mrg * end-X 703 1.1 mrg * The partition ends where partition X ends. It is an 704 1.1 mrg * error for partition X to end before the specified start 705 1.1 mrg * point. This always goes to exactly where partition X 706 1.1 mrg * ends, even if that's partway through a cylinder. 707 1.1 mrg * 708 1.1 mrg * start-X 709 1.1 mrg * The partition extends to end exactly where partition X 710 1.1 mrg * begins. It is an error for partition X to begin before 711 1.1 mrg * the specified start point. 712 1.1 mrg * 713 1.1 mrg * size-X 714 1.1 mrg * The partition has the same size as partition X. 715 1.1 mrg * 716 1.1 mrg * If label.spc is nonzero but the partition size is not a multiple of 717 1.1 mrg * it, a warning is printed, since you usually don't want this. Most 718 1.1 mrg * often, in my experience, this comes from specifying a cylinder 719 1.1 mrg * count as a single number N instead of N/0/0. 720 1.1 mrg */ 721 1.3 christos static void 722 1.3 christos chpart(int pno, const char *numbers) 723 1.1 mrg { 724 1.3 christos uint32_t cyl0; 725 1.3 christos uint32_t size; 726 1.3 christos uint32_t sizec; 727 1.3 christos uint32_t sizet; 728 1.3 christos uint32_t sizes; 729 1.3 christos 730 1.3 christos skipspaces(&numbers); 731 1.3 christos if (!memcmp(numbers, "end-", 4) && numbers[4]) { 732 1.3 christos int epno = LETTERPART(numbers[4]); 733 1.3 christos if ((epno >= 0) && (epno < NPART)) { 734 1.3 christos cyl0 = label.partitions[epno].endcyl; 735 1.3 christos numbers += 5; 736 1.3 christos } else { 737 1.3 christos if (!scannum(&numbers, &cyl0, "starting cylinder")) 738 1.3 christos return; 739 1.3 christos } 740 1.3 christos } else if (!memcmp(numbers, "start-", 6) && numbers[6]) { 741 1.3 christos int spno = LETTERPART(numbers[6]); 742 1.3 christos if ((spno >= 0) && (spno < NPART)) { 743 1.3 christos cyl0 = label.partitions[spno].startcyl; 744 1.3 christos numbers += 7; 745 1.3 christos } else { 746 1.3 christos if (!scannum(&numbers, &cyl0, "starting cylinder")) 747 1.3 christos return; 748 1.3 christos } 749 1.3 christos } else { 750 1.3 christos if (!scannum(&numbers, &cyl0, "starting cylinder")) 751 1.3 christos return; 752 1.3 christos } 753 1.3 christos skipspaces(&numbers); 754 1.3 christos if (!memcmp(numbers, "end-", 4) && numbers[4]) { 755 1.3 christos int epno = LETTERPART(numbers[4]); 756 1.3 christos if ((epno >= 0) && (epno < NPART)) { 757 1.3 christos if (label.partitions[epno].endcyl <= cyl0) { 758 1.3 christos warnx("Partition %c ends before cylinder %u", 759 1.3 christos PARTLETTER(epno), cyl0); 760 1.3 christos return; 761 1.3 christos } 762 1.3 christos size = label.partitions[epno].nblk; 763 1.3 christos /* Be careful of unsigned arithmetic */ 764 1.3 christos if (cyl0 > label.partitions[epno].startcyl) { 765 1.3 christos size -= (cyl0 - label.partitions[epno].startcyl) 766 1.3 christos * label.spc; 767 1.3 christos } else if (cyl0 < label.partitions[epno].startcyl) { 768 1.3 christos size += (label.partitions[epno].startcyl - cyl0) 769 1.3 christos * label.spc; 770 1.3 christos } 771 1.3 christos numbers += 5; 772 1.3 christos } else { 773 1.3 christos if (!scannum(&numbers, &size, "partition size")) 774 1.3 christos return; 775 1.3 christos } 776 1.3 christos } else if (!memcmp(numbers, "start-", 6) && numbers[6]) { 777 1.3 christos int spno = LETTERPART(numbers[6]); 778 1.3 christos if ((spno >= 0) && (spno < NPART)) { 779 1.3 christos if (label.partitions[spno].startcyl <= cyl0) { 780 1.3 christos warnx("Partition %c starts before cylinder %u", 781 1.3 christos PARTLETTER(spno), cyl0); 782 1.3 christos return; 783 1.3 christos } 784 1.3 christos size = (label.partitions[spno].startcyl - cyl0) 785 1.3 christos * label.spc; 786 1.3 christos numbers += 7; 787 1.3 christos } else { 788 1.3 christos if (!scannum(&numbers, &size, "partition size")) 789 1.3 christos return; 790 1.3 christos } 791 1.3 christos } else if (!memcmp(numbers, "size-", 5) && numbers[5]) { 792 1.3 christos int spno = LETTERPART(numbers[5]); 793 1.3 christos if ((spno >= 0) && (spno < NPART)) { 794 1.3 christos size = label.partitions[spno].nblk; 795 1.3 christos numbers += 6; 796 1.3 christos } else { 797 1.3 christos if (!scannum(&numbers, &size, "partition size")) 798 1.3 christos return; 799 1.3 christos } 800 1.3 christos } else { 801 1.3 christos if (!scannum(&numbers, &size, "partition size")) 802 1.3 christos return; 803 1.3 christos skipspaces(&numbers); 804 1.3 christos if (*numbers == '/') { 805 1.3 christos sizec = size; 806 1.3 christos numbers++; 807 1.3 christos if (!scannum(&numbers, &sizet, 808 1.3 christos "partition size track value")) 809 1.3 christos return; 810 1.3 christos skipspaces(&numbers); 811 1.3 christos if (*numbers != '/') { 812 1.3 christos warnx("Invalid c/t/s syntax - no second slash"); 813 1.3 christos return; 814 1.3 christos } 815 1.3 christos numbers++; 816 1.3 christos if (!scannum(&numbers, &sizes, 817 1.3 christos "partition size sector value")) 818 1.3 christos return; 819 1.3 christos size = sizes + (label.nsect * (sizet 820 1.3 christos + (label.nhead * sizec))); 821 1.3 christos } 822 1.3 christos } 823 1.3 christos if (label.spc && (size % label.spc)) { 824 1.6 grant warnx("Size is not a multiple of cylinder size (is %u/%u/%u)", 825 1.3 christos size / label.spc, 826 1.3 christos (size % label.spc) / label.nsect, size % label.nsect); 827 1.3 christos } 828 1.3 christos label.partitions[pno].startcyl = cyl0; 829 1.3 christos label.partitions[pno].nblk = size; 830 1.3 christos set_endcyl(&label.partitions[pno]); 831 1.3 christos if ((label.partitions[pno].startcyl * label.spc) 832 1.3 christos + label.partitions[pno].nblk > label.spc * label.ncyl) { 833 1.3 christos warnx("Partition extends beyond end of disk"); 834 1.3 christos } 835 1.3 christos label.dirty = 1; 836 1.1 mrg } 837 1.1 mrg 838 1.1 mrg /* 839 1.1 mrg * Change a 128-byte-string field. There's currently only one such, 840 1.1 mrg * the ASCII label field. 841 1.1 mrg */ 842 1.3 christos static void 843 1.3 christos chval_ascii(const char *cp, struct field *f) 844 1.1 mrg { 845 1.3 christos const char *nl; 846 1.1 mrg 847 1.3 christos skipspaces(&cp); 848 1.3 christos if ((nl = strchr(cp, '\n')) == NULL) 849 1.3 christos nl = cp + strlen(cp); 850 1.3 christos if (nl - cp > 128) { 851 1.3 christos warnx("Ascii label string too long - max 128 characters"); 852 1.3 christos } else { 853 1.3 christos memset(f->loc, 0, 128); 854 1.3 christos memcpy(f->loc, cp, (size_t)(nl - cp)); 855 1.3 christos label.dirty = 1; 856 1.3 christos } 857 1.1 mrg } 858 1.1 mrg /* 859 1.1 mrg * Change an int-valued field. As noted above, there's only one 860 1.1 mrg * function, regardless of the field size in the on-disk label. 861 1.1 mrg */ 862 1.3 christos static void 863 1.3 christos chval_int(const char *cp, struct field *f) 864 1.1 mrg { 865 1.3 christos uint32_t v; 866 1.1 mrg 867 1.3 christos if (!scannum(&cp, &v, "value")) 868 1.3 christos return; 869 1.3 christos *(uint32_t *)f->loc = v; 870 1.3 christos label.dirty = 1; 871 1.1 mrg } 872 1.1 mrg /* 873 1.1 mrg * Change a field's value. The string argument contains the field name 874 1.1 mrg * and the new value in text form. Look up the field and call its 875 1.1 mrg * chval and changed functions. 876 1.1 mrg */ 877 1.3 christos static void 878 1.3 christos chvalue(const char *str) 879 1.1 mrg { 880 1.3 christos const char *cp; 881 1.3 christos int i; 882 1.3 christos size_t n; 883 1.3 christos 884 1.3 christos if (fields[0].taglen < 1) { 885 1.3 christos for (i = 0; fields[i].tag; i++) 886 1.3 christos fields[i].taglen = strlen(fields[i].tag); 887 1.3 christos } 888 1.3 christos skipspaces(&str); 889 1.3 christos cp = str; 890 1.16 dsl while (*cp && !isspace((unsigned char)*cp)) 891 1.3 christos cp++; 892 1.3 christos n = cp - str; 893 1.3 christos for (i = 0; fields[i].tag; i++) { 894 1.23 lukem if (((int)n == fields[i].taglen) && !memcmp(str, fields[i].tag, n)) { 895 1.3 christos (*fields[i].chval) (cp, &fields[i]); 896 1.3 christos if (fields[i].changed) 897 1.3 christos (*fields[i].changed)(); 898 1.3 christos break; 899 1.3 christos } 900 1.3 christos } 901 1.3 christos if (!fields[i].tag) 902 1.8 lukem warnx("Bad name %.*s - see L output for names", (int)n, str); 903 1.1 mrg } 904 1.1 mrg 905 1.1 mrg /* 906 1.1 mrg * `changed' function for the ntrack and nsect fields; update label.spc 907 1.1 mrg * and call set_endcyl on all partitions. 908 1.1 mrg */ 909 1.3 christos static void 910 1.3 christos update_spc(void) 911 1.1 mrg { 912 1.3 christos int i; 913 1.1 mrg 914 1.3 christos label.spc = label.nhead * label.nsect; 915 1.3 christos for (i = 0; i < NPART; i++) 916 1.3 christos set_endcyl(&label.partitions[i]); 917 1.1 mrg } 918 1.1 mrg 919 1.1 mrg /* 920 1.1 mrg * Print function for 128-byte-string fields. Currently only the ASCII 921 1.1 mrg * label, but we don't depend on that. 922 1.1 mrg */ 923 1.3 christos static int 924 1.21 dogcow print_ascii(struct field *f, int sofar) 925 1.1 mrg { 926 1.3 christos printf("%s: %.128s\n", f->tag, (char *)f->loc); 927 1.3 christos return 0; 928 1.1 mrg } 929 1.1 mrg 930 1.1 mrg /* 931 1.1 mrg * Print an int-valued field. We are careful to do proper line wrap, 932 1.1 mrg * making each value occupy 16 columns. 933 1.1 mrg */ 934 1.3 christos static int 935 1.3 christos print_int(struct field *f, int sofar) 936 1.1 mrg { 937 1.3 christos if (sofar >= 60) { 938 1.3 christos printf("\n"); 939 1.3 christos sofar = 0; 940 1.3 christos } 941 1.3 christos printf("%s: %-*u", f->tag, 14 - (int)strlen(f->tag), 942 1.3 christos *(uint32_t *)f->loc); 943 1.3 christos return sofar + 16; 944 1.1 mrg } 945 1.1 mrg 946 1.1 mrg /* 947 1.1 mrg * Print the whole label. Just call the print function for each field, 948 1.1 mrg * then append a newline if necessary. 949 1.1 mrg */ 950 1.3 christos static void 951 1.3 christos print_label(void) 952 1.1 mrg { 953 1.3 christos int i; 954 1.3 christos int c; 955 1.1 mrg 956 1.3 christos c = 0; 957 1.3 christos for (i = 0; fields[i].tag; i++) 958 1.3 christos c = (*fields[i].print) (&fields[i], c); 959 1.3 christos if (c > 0) 960 1.3 christos printf("\n"); 961 1.1 mrg } 962 1.1 mrg 963 1.1 mrg /* 964 1.1 mrg * Figure out how many columns wide the screen is. We impose a minimum 965 1.1 mrg * width of 20 columns; I suspect the output code has some issues if 966 1.1 mrg * we have fewer columns than partitions. 967 1.1 mrg */ 968 1.3 christos static int 969 1.3 christos screen_columns(void) 970 1.1 mrg { 971 1.3 christos int ncols; 972 1.1 mrg #ifndef NO_TERMCAP_WIDTH 973 1.3 christos char *term; 974 1.3 christos char tbuf[1024]; 975 1.1 mrg #endif 976 1.1 mrg #if defined(TIOCGWINSZ) 977 1.3 christos struct winsize wsz; 978 1.1 mrg #elif defined(TIOCGSIZE) 979 1.3 christos struct ttysize tsz; 980 1.1 mrg #endif 981 1.1 mrg 982 1.3 christos ncols = 80; 983 1.1 mrg #ifndef NO_TERMCAP_WIDTH 984 1.3 christos term = getenv("TERM"); 985 1.3 christos if (term && (tgetent(&tbuf[0], term) == 1)) { 986 1.3 christos int n = tgetnum("co"); 987 1.3 christos if (n > 1) 988 1.3 christos ncols = n; 989 1.3 christos } 990 1.1 mrg #endif 991 1.1 mrg #if defined(TIOCGWINSZ) 992 1.3 christos if ((ioctl(1, TIOCGWINSZ, &wsz) == 0) && (wsz.ws_col > 0)) { 993 1.3 christos ncols = wsz.ws_col; 994 1.3 christos } 995 1.1 mrg #elif defined(TIOCGSIZE) 996 1.3 christos if ((ioctl(1, TIOCGSIZE, &tsz) == 0) && (tsz.ts_cols > 0)) { 997 1.3 christos ncols = tsz.ts_cols; 998 1.3 christos } 999 1.1 mrg #endif 1000 1.3 christos if (ncols < 20) 1001 1.3 christos ncols = 20; 1002 1.3 christos return ncols; 1003 1.1 mrg } 1004 1.1 mrg 1005 1.1 mrg /* 1006 1.1 mrg * Print the partitions. The argument is true iff we should print all 1007 1.3 christos * partitions, even those set start=0 size=0. We generate one line 1008 1.3 christos * per partition (or, if all==0, per `interesting' partition), plus a 1009 1.3 christos * visually graphic map of partition letters. Most of the hair in the 1010 1.3 christos * visual display lies in ensuring that nothing takes up less than one 1011 1.3 christos * character column, that if two boundaries appear visually identical, 1012 1.3 christos * they _are_ identical. Within that constraint, we try to make the 1013 1.3 christos * number of character columns proportional to the size.... 1014 1.3 christos */ 1015 1.3 christos static void 1016 1.3 christos print_part(int all) 1017 1.3 christos { 1018 1.3 christos int i, j, k, n, r, c; 1019 1.3 christos size_t ncols; 1020 1.3 christos uint32_t edges[2 * NPART]; 1021 1.26 mrg int ce[2 * NPART] = {0}; /* XXXGCC12 */ 1022 1.3 christos int row[NPART]; 1023 1.3 christos unsigned char table[2 * NPART][NPART]; 1024 1.3 christos char *line; 1025 1.3 christos struct part *p = label.partitions; 1026 1.3 christos 1027 1.3 christos for (i = 0; i < NPART; i++) { 1028 1.3 christos if (all || p[i].startcyl || p[i].nblk) { 1029 1.3 christos printf("%c: start cyl = %6u, size = %8u (", 1030 1.3 christos PARTLETTER(i), p[i].startcyl, p[i].nblk); 1031 1.3 christos if (label.spc) { 1032 1.3 christos printf("%u/%u/%u - ", p[i].nblk / label.spc, 1033 1.3 christos (p[i].nblk % label.spc) / label.nsect, 1034 1.3 christos p[i].nblk % label.nsect); 1035 1.3 christos } 1036 1.3 christos printf("%gMb)\n", p[i].nblk / 2048.0); 1037 1.3 christos } 1038 1.3 christos } 1039 1.3 christos 1040 1.3 christos j = 0; 1041 1.3 christos for (i = 0; i < NPART; i++) { 1042 1.3 christos if (p[i].nblk > 0) { 1043 1.3 christos edges[j++] = p[i].startcyl; 1044 1.3 christos edges[j++] = p[i].endcyl; 1045 1.3 christos } 1046 1.3 christos } 1047 1.3 christos 1048 1.3 christos do { 1049 1.3 christos n = 0; 1050 1.3 christos for (i = 1; i < j; i++) { 1051 1.3 christos if (edges[i] < edges[i - 1]) { 1052 1.3 christos uint32_t t; 1053 1.3 christos t = edges[i]; 1054 1.3 christos edges[i] = edges[i - 1]; 1055 1.3 christos edges[i - 1] = t; 1056 1.3 christos n++; 1057 1.3 christos } 1058 1.3 christos } 1059 1.3 christos } while (n > 0); 1060 1.3 christos 1061 1.3 christos for (i = 1; i < j; i++) { 1062 1.3 christos if (edges[i] != edges[n]) { 1063 1.3 christos n++; 1064 1.3 christos if (n != i) 1065 1.3 christos edges[n] = edges[i]; 1066 1.3 christos } 1067 1.3 christos } 1068 1.3 christos 1069 1.3 christos n++; 1070 1.3 christos for (i = 0; i < NPART; i++) { 1071 1.3 christos if (p[i].nblk > 0) { 1072 1.3 christos for (j = 0; j < n; j++) { 1073 1.3 christos if ((p[i].startcyl <= edges[j]) && 1074 1.3 christos (p[i].endcyl > edges[j])) { 1075 1.3 christos table[j][i] = 1; 1076 1.3 christos } else { 1077 1.3 christos table[j][i] = 0; 1078 1.3 christos } 1079 1.3 christos } 1080 1.3 christos } 1081 1.3 christos } 1082 1.3 christos 1083 1.3 christos ncols = screen_columns() - 2; 1084 1.3 christos for (i = 0; i < n; i++) 1085 1.3 christos ce[i] = (edges[i] * ncols) / (double) edges[n - 1]; 1086 1.3 christos 1087 1.3 christos for (i = 1; i < n; i++) 1088 1.3 christos if (ce[i] <= ce[i - 1]) 1089 1.3 christos ce[i] = ce[i - 1] + 1; 1090 1.3 christos 1091 1.23 lukem if ((size_t)ce[n - 1] > ncols) { 1092 1.3 christos ce[n - 1] = ncols; 1093 1.3 christos for (i = n - 1; (i > 0) && (ce[i] <= ce[i - 1]); i--) 1094 1.3 christos ce[i - 1] = ce[i] - 1; 1095 1.3 christos if (ce[0] < 0) 1096 1.3 christos for (i = 0; i < n; i++) 1097 1.3 christos ce[i] = i; 1098 1.3 christos } 1099 1.3 christos 1100 1.3 christos printf("\n"); 1101 1.3 christos for (i = 0; i < NPART; i++) { 1102 1.3 christos if (p[i].nblk > 0) { 1103 1.3 christos r = -1; 1104 1.3 christos do { 1105 1.3 christos r++; 1106 1.3 christos for (j = i - 1; j >= 0; j--) { 1107 1.3 christos if (row[j] != r) 1108 1.3 christos continue; 1109 1.3 christos for (k = 0; k < n; k++) 1110 1.3 christos if (table[k][i] && table[k][j]) 1111 1.3 christos break; 1112 1.3 christos if (k < n) 1113 1.3 christos break; 1114 1.3 christos } 1115 1.3 christos } while (j >= 0); 1116 1.3 christos row[i] = r; 1117 1.3 christos } else { 1118 1.3 christos row[i] = -1; 1119 1.3 christos } 1120 1.3 christos } 1121 1.3 christos r = row[0]; 1122 1.3 christos for (i = 1; i < NPART; i++) 1123 1.3 christos if (row[i] > r) 1124 1.3 christos r = row[i]; 1125 1.3 christos 1126 1.3 christos if ((line = malloc(ncols + 1)) == NULL) 1127 1.3 christos err(1, "Can't allocate memory"); 1128 1.3 christos 1129 1.3 christos for (i = 0; i <= r; i++) { 1130 1.23 lukem for (j = 0; (size_t)j < ncols; j++) 1131 1.3 christos line[j] = ' '; 1132 1.3 christos for (j = 0; j < NPART; j++) { 1133 1.3 christos if (row[j] != i) 1134 1.3 christos continue; 1135 1.3 christos k = 0; 1136 1.3 christos for (k = 0; k < n; k++) { 1137 1.3 christos if (table[k][j]) { 1138 1.3 christos for (c = ce[k]; c < ce[k + 1]; c++) 1139 1.3 christos line[c] = 'a' + j; 1140 1.3 christos } 1141 1.3 christos } 1142 1.3 christos } 1143 1.3 christos for (j = ncols - 1; (j >= 0) && (line[j] == ' '); j--); 1144 1.3 christos printf("%.*s\n", j + 1, line); 1145 1.3 christos } 1146 1.3 christos free(line); 1147 1.1 mrg } 1148 1.1 mrg 1149 1.1 mrg #ifdef S_COMMAND 1150 1.1 mrg /* 1151 1.1 mrg * This computes an appropriate checksum for an in-core label. It's 1152 1.3 christos * not really related to the S command, except that it's needed only 1153 1.3 christos * by setlabel(), which is #ifdef S_COMMAND. 1154 1.1 mrg */ 1155 1.3 christos static unsigned short int 1156 1.3 christos dkcksum(const struct disklabel *lp) 1157 1.1 mrg { 1158 1.3 christos const unsigned short int *start; 1159 1.3 christos const unsigned short int *end; 1160 1.3 christos unsigned short int sum; 1161 1.3 christos const unsigned short int *p; 1162 1.3 christos 1163 1.3 christos start = (const void *)lp; 1164 1.3 christos end = (const void *)&lp->d_partitions[lp->d_npartitions]; 1165 1.3 christos sum = 0; 1166 1.3 christos for (p = start; p < end; p++) 1167 1.3 christos sum ^= *p; 1168 1.3 christos return (sum); 1169 1.1 mrg } 1170 1.1 mrg 1171 1.1 mrg /* 1172 1.1 mrg * Set the in-core label. This is basically putlabel, except it builds 1173 1.3 christos * a struct disklabel instead of a Sun label buffer, and uses 1174 1.3 christos * DIOCSDINFO instead of lseek-and-write. 1175 1.1 mrg */ 1176 1.3 christos static void 1177 1.3 christos setlabel(void) 1178 1.1 mrg { 1179 1.3 christos union { 1180 1.3 christos struct disklabel l; 1181 1.3 christos char pad[sizeof(struct disklabel) - 1182 1.3 christos (MAXPARTITIONS * sizeof(struct partition)) + 1183 1.3 christos (16 * sizeof(struct partition))]; 1184 1.3 christos } u; 1185 1.3 christos int i; 1186 1.3 christos struct part *p = label.partitions; 1187 1.3 christos 1188 1.3 christos if (ioctl(diskfd, DIOCGDINFO, &u.l) == -1) { 1189 1.3 christos warn("ioctl DIOCGDINFO failed"); 1190 1.3 christos return; 1191 1.3 christos } 1192 1.3 christos if (u.l.d_secsize != 512) { 1193 1.6 grant warnx("Disk claims %d-byte sectors", (int)u.l.d_secsize); 1194 1.3 christos } 1195 1.3 christos u.l.d_nsectors = label.nsect; 1196 1.3 christos u.l.d_ntracks = label.nhead; 1197 1.3 christos u.l.d_ncylinders = label.ncyl; 1198 1.3 christos u.l.d_secpercyl = label.nsect * label.nhead; 1199 1.3 christos u.l.d_rpm = label.rpm; 1200 1.3 christos u.l.d_interleave = label.intrlv; 1201 1.3 christos u.l.d_npartitions = getmaxpartitions(); 1202 1.3 christos memset(&u.l.d_partitions[0], 0, 1203 1.3 christos u.l.d_npartitions * sizeof(struct partition)); 1204 1.3 christos for (i = 0; i < u.l.d_npartitions; i++) { 1205 1.3 christos u.l.d_partitions[i].p_size = p[i].nblk; 1206 1.3 christos u.l.d_partitions[i].p_offset = p[i].startcyl 1207 1.3 christos * label.nsect * label.nhead; 1208 1.3 christos u.l.d_partitions[i].p_fsize = 0; 1209 1.3 christos u.l.d_partitions[i].p_fstype = (i == 1) ? FS_SWAP : 1210 1.3 christos (i == 2) ? FS_UNUSED : FS_BSDFFS; 1211 1.3 christos u.l.d_partitions[i].p_frag = 0; 1212 1.3 christos u.l.d_partitions[i].p_cpg = 0; 1213 1.3 christos } 1214 1.3 christos u.l.d_checksum = 0; 1215 1.3 christos u.l.d_checksum = dkcksum(&u.l); 1216 1.3 christos if (ioctl(diskfd, DIOCSDINFO, &u.l) == -1) { 1217 1.3 christos warn("ioctl DIOCSDINFO failed"); 1218 1.3 christos return; 1219 1.3 christos } 1220 1.1 mrg } 1221 1.1 mrg #endif 1222 1.1 mrg 1223 1.3 christos static const char *help[] = { 1224 1.7 lukem "?\t- print this help", 1225 1.7 lukem "L\t- print label, except for partition table", 1226 1.7 lukem "P\t- print partition table", 1227 1.7 lukem "PP\t- print partition table including size=0 offset=0 entries", 1228 1.3 christos "[abcdefghijklmnop] <cylno> <size> - change partition", 1229 1.3 christos "V <name> <value> - change a non-partition label value", 1230 1.7 lukem "W\t- write (possibly modified) label out", 1231 1.3 christos #ifdef S_COMMAND 1232 1.7 lukem "S\t- set label in the kernel (orthogonal to W)", 1233 1.3 christos #endif 1234 1.7 lukem "Q\t- quit program (error if no write since last change)", 1235 1.7 lukem "Q!\t- quit program (unconditionally) [EOF also quits]", 1236 1.3 christos NULL 1237 1.3 christos }; 1238 1.3 christos 1239 1.1 mrg /* 1240 1.1 mrg * Read and execute one command line from the user. 1241 1.1 mrg */ 1242 1.3 christos static void 1243 1.3 christos docmd(void) 1244 1.1 mrg { 1245 1.3 christos char cmdline[512]; 1246 1.3 christos int i; 1247 1.1 mrg 1248 1.3 christos if (!quiet) 1249 1.3 christos printf("sunlabel> "); 1250 1.3 christos if (fgets(&cmdline[0], sizeof(cmdline), stdin) != &cmdline[0]) 1251 1.3 christos exit(0); 1252 1.3 christos switch (cmdline[0]) { 1253 1.3 christos case '?': 1254 1.3 christos for (i = 0; help[i]; i++) 1255 1.3 christos printf("%s\n", help[i]); 1256 1.3 christos break; 1257 1.3 christos case 'L': 1258 1.3 christos print_label(); 1259 1.3 christos break; 1260 1.3 christos case 'P': 1261 1.3 christos print_part(cmdline[1] == 'P'); 1262 1.3 christos break; 1263 1.3 christos case 'W': 1264 1.3 christos putlabel(); 1265 1.3 christos break; 1266 1.3 christos case 'S': 1267 1.1 mrg #ifdef S_COMMAND 1268 1.3 christos setlabel(); 1269 1.1 mrg #else 1270 1.3 christos printf("This compilation doesn't support S.\n"); 1271 1.1 mrg #endif 1272 1.3 christos break; 1273 1.3 christos case 'Q': 1274 1.3 christos if ((cmdline[1] == '!') || !label.dirty) 1275 1.3 christos exit(0); 1276 1.3 christos printf("Label is dirty - use w to write it\n"); 1277 1.3 christos printf("Use Q! to quit anyway.\n"); 1278 1.3 christos break; 1279 1.3 christos case 'a': 1280 1.3 christos case 'b': 1281 1.3 christos case 'c': 1282 1.3 christos case 'd': 1283 1.3 christos case 'e': 1284 1.3 christos case 'f': 1285 1.3 christos case 'g': 1286 1.3 christos case 'h': 1287 1.3 christos case 'i': 1288 1.3 christos case 'j': 1289 1.3 christos case 'k': 1290 1.3 christos case 'l': 1291 1.3 christos case 'm': 1292 1.3 christos case 'n': 1293 1.3 christos case 'o': 1294 1.3 christos case 'p': 1295 1.3 christos chpart(LETTERPART(cmdline[0]), &cmdline[1]); 1296 1.3 christos break; 1297 1.3 christos case 'V': 1298 1.3 christos chvalue(&cmdline[1]); 1299 1.3 christos break; 1300 1.3 christos case '\n': 1301 1.3 christos break; 1302 1.3 christos default: 1303 1.3 christos printf("(Unrecognized command character %c ignored.)\n", 1304 1.3 christos cmdline[0]); 1305 1.3 christos break; 1306 1.3 christos } 1307 1.1 mrg } 1308 1.7 lukem 1309 1.1 mrg /* 1310 1.1 mrg * main() (duh!). Pretty boring. 1311 1.1 mrg */ 1312 1.3 christos int 1313 1.3 christos main(int ac, char **av) 1314 1.1 mrg { 1315 1.3 christos handleargs(ac, av); 1316 1.3 christos getlabel(); 1317 1.3 christos for (;;) 1318 1.3 christos docmd(); 1319 1.1 mrg } 1320