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