1 /* $NetBSD: main.c,v 1.21 2009/11/28 12:14:53 tsutsui Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio M. Merino Vidal.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1987, 1993
34 * The Regents of the University of California. All rights reserved.
36 * This code is derived from software contributed to Berkeley by
37 * Symmetric Computer Systems.
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 * 3. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 #if HAVE_NBTOOL_CONFIG_H
65 #include "nbtool_config.h"
68 #include <sys/cdefs.h>
70 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\
71 The Regents of the University of California. All rights reserved.");
76 static char sccsid
[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95";
77 /* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */
79 __RCSID("$NetBSD: main.c,v 1.21 2009/11/28 12:14:53 tsutsui Exp $");
83 #include <sys/param.h>
100 #include <ufs/ufs/dinode.h>
101 #include <ufs/ffs/fs.h>
103 #if HAVE_NBTOOL_CONFIG_H
104 #include <nbinclude/sys/disklabel.h>
105 #include <nbinclude/sys/disklabel_acorn.h>
106 #include <nbinclude/sys/bootblock.h>
107 #include "../../include/disktab.h"
109 #include <sys/ioctl.h>
110 #include <sys/disklabel.h>
111 #include <sys/disklabel_acorn.h>
112 #include <sys/bootblock.h>
115 #endif /* HAVE_NBTOOL_CONFIG_H */
117 #include "pathnames.h"
123 * Disklabel: read and write disklabels.
124 * The label is usually placed on one of the first sectors of the disk.
125 * Many machines also place a bootstrap in the same area,
126 * in which case the label is embedded in the bootstrap.
127 * The bootstrap source must leave space at the proper offset
128 * for the label on such machines.
132 #define BBSIZE 8192 /* size of boot area, with label */
135 #define DISKMAGIC_REV bswap32(DISKMAGIC)
136 /* To delete a label, we just invert the magic numbers */
137 #define DISKMAGIC_DELETED (~DISKMAGIC)
138 #define DISKMAGIC_DELETED_REV bswap32(~DISKMAGIC)
140 #define DEFEDITOR _PATH_VI
142 char specname
[MAXPATHLEN
];
144 /* Some global data, all too hard to pass about */
145 char bootarea
[BBSIZE
]; /* Buffer matching part of disk */
146 int bootarea_len
; /* Number of bytes we actually read */
147 static struct disklabel lab
; /* The label we have updated */
149 static int Aflag
; /* Action all labels */
150 static int Fflag
; /* Read/write from file */
151 static int rflag
; /* Read/write direct from disk */
152 static int tflag
; /* Format output as disktab */
153 int Cflag
; /* CHS format output */
154 static int Dflag
; /* Delete old labels (use with write) */
155 static int Iflag
; /* Read/write direct, but default if absent */
156 static int lflag
; /* List all known file system types and exit */
157 static int mflag
; /* Expect disk to contain an MBR */
159 static int read_all
; /* set if op = READ && Aflag */
161 static int write_label(int);
162 static int readlabel_direct(int);
163 static void writelabel_direct(int);
164 static int update_label(int, u_int
, u_int
);
165 static struct disklabel
*find_label(int, u_int
);
167 static void makedisktab(FILE *, struct disklabel
*);
168 static void makelabel(const char *, const char *);
169 static void l_perror(const char *);
170 static void readlabel(int);
171 static int edit(int);
172 static int editit(const char *);
173 static char *skip(char *);
174 static char *word(char *);
175 static int getasciilabel(FILE *, struct disklabel
*);
176 static void usage(void);
177 static int qsort_strcmp(const void *, const void *);
178 static int getulong(const char *, char, char **,
179 unsigned long *, unsigned long);
180 #define GETNUM32(a, v) getulong(a, '\0', NULL, v, UINT32_MAX)
181 #define GETNUM16(a, v) getulong(a, '\0', NULL, v, UINT16_MAX)
182 #define GETNUM8(a, v) getulong(a, '\0', NULL, v, UINT8_MAX)
184 static int set_writable_fd
= -1;
186 #if HAVE_NBTOOL_CONFIG_H
187 #define GETLABELOFFSET() LABELOFFSET
188 #define GETLABELSECTOR() LABELSECTOR
189 #else /* HAVE_NBTOOL_CONFIG_H */
190 #define GETLABELOFFSET() getlabeloffset()
191 #define GETLABELSECTOR() getlabelsector()
194 /* Default location for label - only used if we don't find one to update */
195 #define LABEL_OFFSET (dklabel_getlabelsector() * DEV_BSIZE + dklabel_getlabeloffset())
198 * For portability it doesn't make sense to use any other value....
199 * Except, maybe, the size of a physical sector.
200 * This value is used if we have to write a label to the start of an mbr ptn.
202 #ifndef LABELOFFSET_MBR
203 #define LABELOFFSET_MBR 512
206 #if HAVE_NBTOOL_CONFIG_H
208 opendisk(const char *path
, int flags
, char *buf
, int buflen
, int cooked
)
211 f
= open(path
, flags
, 0);
212 strlcpy(buf
, path
, buflen
);
217 dk_ioctl(int f
, void *arg
)
222 #define dk_ioctl(f, cmd, arg) dk_ioctl(f, arg)
224 #define dk_ioctl(f, cmd, arg) ioctl(f, cmd, arg)
225 #endif /* HAVE_NBTOOL_CONFIG_H */
228 dklabel_getlabelsector(void)
230 unsigned long int nval
;
234 if ((val
= getenv("DISKLABELSECTOR")) == NULL
)
235 return GETLABELSECTOR();
236 if ((nval
= strtoul(val
, &end
, 10)) == ULONG_MAX
&& errno
== ERANGE
)
237 err(EXIT_FAILURE
, "DISKLABELSECTOR in environment");
242 dklabel_getlabeloffset(void)
244 unsigned long int nval
;
248 if ((val
= getenv("DISKLABELOFFSET")) == NULL
)
249 return GETLABELOFFSET();
250 if ((nval
= strtoul(val
, &end
, 10)) == ULONG_MAX
&& errno
== ERANGE
)
251 err(EXIT_FAILURE
, "DISKLABELOFFSET in environment");
259 dk_ioctl(set_writable_fd
, DIOCWLABEL
, &zero
);
263 main(int argc
, char *argv
[])
271 UNSPEC
, EDIT
, READ
, RESTORE
, SETWRITABLE
, SETREADONLY
,
272 WRITE
, INTERACT
, DELETE
273 } op
= UNSPEC
, old_op
;
278 #if HAVE_NBTOOL_CONFIG_H
279 /* We must avoid doing any ioctl requests */
284 while ((ch
= getopt(argc
, argv
, "ACDFINRWb:ef:ilmrs:tvw")) != -1) {
287 case 'A': /* Action all labels */
291 case 'C': /* Display in CHS format */
294 case 'D': /* Delete all existing labels */
298 case 'F': /* Treat 'disk' as a regular file */
300 rflag
= 1; /* Force direct access */
302 case 'I': /* Use default label if none found */
304 rflag
= 1; /* Implies direct access */
306 case 'R': /* Restore label from text file */
309 case 'N': /* Disallow writes to label sector */
312 case 'W': /* Allow writes to label sector */
315 case 'e': /* Edit label with $EDITOR */
318 case 'f': /* Name of disktab file */
319 if (setdisktab(optarg
) == -1)
322 case 'i': /* Edit using built-in editor */
325 case 'l': /* List all known file system types and exit */
328 case 'm': /* Expect disk to have an MBR */
331 case 'r': /* Read/write label directly from disk */
334 case 't': /* Format output as a disktab entry */
337 case 'v': /* verbose/diag output */
340 case 'w': /* Write label based on disktab entry */
347 if (old_op
!= UNSPEC
&& old_op
!= op
)
354 exit(list_fs_types() ? EXIT_SUCCESS
: EXIT_FAILURE
);
357 op
= Dflag
? DELETE
: READ
;
362 if (Iflag
&& op
!= EDIT
&& op
!= INTERACT
)
366 f
= opendisk(dkname
, op
== READ
? O_RDONLY
: O_RDWR
,
367 specname
, sizeof specname
, 0);
369 err(4, "%s", specname
);
371 if (!Fflag
&& fstat(f
, &sb
) == 0 && S_ISREG(sb
.st_mode
))
376 case DELETE
: /* Remove all existing labels */
380 writelabel_direct(f
);
395 * XXX: Fill some default values so checklabel does not fail
397 if (lab
.d_bbsize
== 0)
398 lab
.d_bbsize
= BBSIZE
;
399 if (lab
.d_sbsize
== 0)
400 lab
.d_sbsize
= SBLOCKSIZE
;
410 /* Label got printed in the bowels of readlabel */
413 makedisktab(stdout
, &lab
);
415 showinfo(stdout
, &lab
, specname
);
416 showpartitions(stdout
, &lab
, Cflag
);
418 error
= checklabel(&lab
);
426 if (!(t
= fopen(argv
[1], "r")))
427 err(4, "%s", argv
[1]);
428 if (getasciilabel(t
, &lab
))
429 error
= write_label(f
);
442 if (dk_ioctl(f
, DIOCWLABEL
, &writable
) < 0)
443 err(4, "ioctl DIOCWLABEL");
446 case WRITE
: /* Create label from /etc/disktab entry & write */
447 if (argc
< 2 || argc
> 3)
449 makelabel(argv
[1], argv
[2]);
450 if (checklabel(&lab
) == 0)
451 error
= write_label(f
);
464 * Construct a prototype disklabel from /etc/disktab.
467 makelabel(const char *type
, const char *name
)
469 struct disklabel
*dp
;
471 dp
= getdiskbyname(type
);
473 errx(1, "unknown disk type: %s", type
);
476 /* d_packname is union d_boot[01], so zero */
477 (void)memset(lab
.d_packname
, 0, sizeof(lab
.d_packname
));
479 (void)strncpy(lab
.d_packname
, name
, sizeof(lab
.d_packname
));
487 lab
.d_magic
= DISKMAGIC
;
488 lab
.d_magic2
= DISKMAGIC
;
490 lab
.d_checksum
= dkcksum(&lab
);
493 /* Write the label directly to the disk */
496 * First set the kernel disk label,
497 * then write a label to the raw disk.
498 * If the SDINFO ioctl fails because it is unimplemented,
499 * keep going; otherwise, the kernel consistency checks
500 * may prevent us from changing the current (in-core)
503 if (!Fflag
&& dk_ioctl(f
, DIOCSDINFO
, &lab
) < 0 &&
504 errno
!= ENODEV
&& errno
!= ENOTTY
) {
505 l_perror("ioctl DIOCSDINFO");
509 * write enable label sector before write (if necessary),
510 * disable after writing.
514 if (dk_ioctl(f
, DIOCWLABEL
, &writable
) < 0)
515 perror("ioctl DIOCWLABEL");
517 atexit(clear_writable
);
520 writelabel_direct(f
);
523 * Now issue a DIOCWDINFO. This will let the kernel convert the
524 * disklabel to some machdep format if needed.
526 /* XXX: This is stupid! */
527 if (!Fflag
&& dk_ioctl(f
, DIOCWDINFO
, &lab
) < 0) {
528 l_perror("ioctl DIOCWDINFO");
532 /* Get the kernel to write the label */
533 if (dk_ioctl(f
, DIOCWDINFO
, &lab
) < 0) {
534 l_perror("ioctl DIOCWDINFO");
540 if (lab
.d_type
== DTYPE_SMD
&& lab
.d_flags
& D_BADSECT
&&
541 lab
.d_secsize
== 512) {
542 /* Write the label to the odd sectors of the last track! */
547 if (pread(f
, sec0
, 512, 0) < 512) {
548 warn("read master label to write alternates");
552 alt
= lab
.d_ncylinders
* lab
.d_secpercyl
- lab
.d_nsectors
;
553 for (i
= 1; i
< 11 && (uint32_t)i
< lab
.d_nsectors
; i
+= 2) {
554 if (pwrite(f
, sec0
, 512, (off_t
)(alt
+ i
) * 512) < 512)
555 warn("alternate label %d write", i
/2);
558 #endif /* VAX_ALTLABELS */
564 writelabel(int f
, struct disklabel
*lp
)
568 return write_label(f
);
572 l_perror(const char *s
)
578 warnx("%s: No disk label on disk;\n"
579 "use \"disklabel -I\" to install initial label", s
);
583 warnx("%s: Label magic number or checksum is wrong!\n"
584 "(disklabel or kernel is out of date?)", s
);
588 warnx("%s: Open partition would move or shrink", s
);
592 warnx("%s: Labeled partition or 'a' partition must start"
593 " at beginning of disk", s
);
602 #ifdef NO_MBR_SUPPORT
603 #define process_mbr(f, action) 1
606 * Scan DOS/MBR partition table and extended partition list for NetBSD ptns.
609 process_mbr(int f
, int (*action
)(int, u_int
))
611 struct mbr_partition
*dp
;
612 struct mbr_sector mbr
;
615 u_int ext_base
, next_ext
, this_ext
, start
;
623 warnx("reading mbr sector %u", this_ext
);
624 if (pread(f
, &mbr
, sizeof mbr
, this_ext
* (off_t
)DEV_BSIZE
)
627 warn("Can't read master boot record %d",
632 /* Check if table is valid. */
633 if (mbr
.mbr_magic
!= htole16(MBR_MAGIC
)) {
635 warnx("Invalid signature in mbr record %d",
640 dp
= &mbr
.mbr_parts
[0];
642 /* Find NetBSD partition(s). */
643 for (part
= 0; part
< MBR_PART_COUNT
; dp
++, part
++) {
644 start
= le32toh(dp
->mbrp_start
);
645 switch (dp
->mbrp_type
) {
646 #ifdef COMPAT_386BSD_MBRPART
647 case MBR_PTYPE_386BSD
:
652 case MBR_PTYPE_NETBSD
:
653 res
= action(f
, this_ext
+ start
);
655 /* Found or failure */
658 /* Keep largest value */
662 case MBR_PTYPE_EXT_LBA
:
663 case MBR_PTYPE_EXT_LNX
:
671 /* No more extended partitions */
673 next_ext
+= ext_base
;
677 if (next_ext
<= this_ext
) {
679 warnx("Invalid extended chain %x <= %x",
683 /* Maybe we should check against the disk size... */
690 readlabel_mbr(int f
, u_int sector
)
692 struct disklabel
*disk_lp
;
694 disk_lp
= find_label(f
, sector
);
697 targettohlabel(&lab
, disk_lp
);
702 writelabel_mbr(int f
, u_int sector
)
704 return update_label(f
, sector
, mflag
? LABELOFFSET_MBR
: ~0U) ? 2 : 0;
707 #endif /* !NO_MBR_SUPPORT */
710 #define get_filecore_partition(f) 0
713 * static int filecore_checksum(u_char *bootblock)
715 * Calculates the filecore boot block checksum. This is used to validate
716 * a filecore boot block on the disk. If a boot block is validated then
717 * it is used to locate the partition table. If the boot block is not
718 * validated, it is assumed that the whole disk is NetBSD.
720 * The basic algorithm is:
722 * for (each byte in block, excluding checksum) {
728 * That's equivalent to summing all of the bytes in the block
729 * (excluding the checksum byte, of course), then calculating the
730 * checksum as "cksum = sum - ((sum - 1) / 255) * 255)". That
731 * expression may or may not yield a faster checksum function,
732 * but it's easier to reason about.
734 * Note that if you have a block filled with bytes of a single
735 * value "X" (regardless of that value!) and calculate the cksum
736 * of the block (excluding the checksum byte), you will _always_
737 * end up with a checksum of X. (Do the math; that can be derived
738 * from the checksum calculation function!) That means that
739 * blocks which contain bytes which all have the same value will
740 * always checksum properly. That's a _very_ unlikely occurence
741 * (probably impossible, actually) for a valid filecore boot block,
742 * so we treat such blocks as invalid.
745 filecore_checksum(u_char
*bootblock
)
747 u_char byte0
, accum_diff
;
753 byte0
= bootblock
[0];
756 * Sum the contents of the block, keeping track of whether
757 * or not all bytes are the same. If 'accum_diff' ends up
758 * being zero, all of the bytes are, in fact, the same.
760 for (i
= 0; i
< 511; ++i
) {
762 accum_diff
|= bootblock
[i
] ^ byte0
;
766 * Check to see if the checksum byte is the same as the
767 * rest of the bytes, too. (Note that if all of the bytes
768 * are the same except the checksum, a checksum compare
769 * won't succeed, but that's not our problem.)
771 accum_diff
|= bootblock
[i
] ^ byte0
;
773 /* All bytes in block are the same; call it invalid. */
777 return (sum
- ((sum
- 1) / 255) * 255);
781 * Check for the presence of a RiscOS filecore boot block
782 * indicating an ADFS file system on the disc.
783 * Return the offset to the NetBSD part of the disc if
784 * this can be determined.
785 * This routine will terminate disklabel if the disc
786 * is found to be ADFS only.
789 get_filecore_partition(int f
)
791 struct filecore_bootblock
*fcbb
;
792 static u_char bb
[DEV_BSIZE
];
794 struct riscix_partition_table
*riscix_part
;
797 if (pread(f
, bb
, sizeof(bb
), (off_t
)FILECORE_BOOT_SECTOR
* DEV_BSIZE
) != sizeof(bb
))
798 err(4, "can't read filecore boot block");
799 fcbb
= (struct filecore_bootblock
*)bb
;
801 /* Check if table is valid. */
802 if (filecore_checksum(bb
) != fcbb
->checksum
)
806 * Check for NetBSD/arm32 (RiscBSD) partition marker.
807 * If found the NetBSD disklabel location is easy.
809 offset
= (fcbb
->partition_cyl_low
+ (fcbb
->partition_cyl_high
<< 8))
810 * fcbb
->heads
* fcbb
->secspertrack
;
812 switch (fcbb
->partition_type
) {
814 case PARTITION_FORMAT_RISCBSD
:
817 case PARTITION_FORMAT_RISCIX
:
819 * Read the RISCiX partition table and search for the
820 * first partition named "RiscBSD", "NetBSD", or "Empty:"
822 * XXX is use of 'Empty:' really desirable?! -- cgd
825 if (pread(f
, bb
, sizeof(bb
), (off_t
)offset
* DEV_BSIZE
) != sizeof(bb
))
826 err(4, "can't read riscix partition table");
827 riscix_part
= (struct riscix_partition_table
*)bb
;
829 for (loop
= 0; loop
< NRISCIX_PARTITIONS
; ++loop
) {
830 if (strcmp((char *)riscix_part
->partitions
[loop
].rp_name
,
832 strcmp((char *)riscix_part
->partitions
[loop
].rp_name
,
834 strcmp((char *)riscix_part
->partitions
[loop
].rp_name
,
836 return riscix_part
->partitions
[loop
].rp_start
;
841 * Valid filecore boot block, RISCiX partition table
842 * but no NetBSD partition. We should leave this
845 errx(4, "cannot label: no NetBSD partition found"
846 " in RISCiX partition table");
850 * Valid filecore boot block and no non-ADFS partition.
851 * This means that the whole disc is allocated for ADFS
852 * so do not trash ! If the user really wants to put a
853 * NetBSD disklabel on the disc then they should remove
854 * the filecore boot block first with dd.
856 errx(4, "cannot label: filecore-only disk"
857 " (no non-ADFS partition)");
861 #endif /* USE_ACORN */
864 * Fetch disklabel for disk to 'lab'.
865 * Use ioctl to get label unless -r flag is given.
871 /* Get label directly from disk */
872 if (readlabel_direct(f
) == 0)
875 * There was no label on the disk. Get the fictious one
876 * as a basis for initialisation.
878 if (!Fflag
&& Iflag
&& (dk_ioctl(f
, DIOCGDINFO
, &lab
) == 0 ||
879 dk_ioctl(f
, DIOCGDEFLABEL
, &lab
) == 0))
882 /* Get label from kernel. */
883 if (dk_ioctl(f
, DIOCGDINFO
, &lab
) < 0)
884 err(4, "ioctl DIOCGDINFO");
889 /* We actually found one, and printed it... */
891 errx(1, "could not read existing label");
895 * Reading the label from the disk is largely a case of 'hunt the label'.
896 * and since different architectures default to different places there
897 * could even be more than one label that contradict each other!
898 * For now we look in the expected place, then search through likely
901 static struct disklabel
*
902 find_label(int f
, u_int sector
)
904 struct disklabel
*disk_lp
, hlp
;
906 const char *is_deleted
;
908 bootarea_len
= pread(f
, bootarea
, sizeof bootarea
,
909 sector
* (off_t
)DEV_BSIZE
);
910 if (bootarea_len
<= 0) {
912 warn("failed to read bootarea from sector %u", sector
);
917 warnx("read sector %u len %u looking for label",
918 sector
, bootarea_len
);
920 /* Check expected offset first */
921 for (offset
= LABEL_OFFSET
, i
= -4;; offset
= i
+= 4) {
923 disk_lp
= (void *)(bootarea
+ offset
);
924 if (i
== LABEL_OFFSET
)
926 if ((char *)(disk_lp
+ 1) > bootarea
+ bootarea_len
)
928 if (disk_lp
->d_magic2
!= disk_lp
->d_magic
)
930 if (read_all
&& (disk_lp
->d_magic
== DISKMAGIC_DELETED
||
931 disk_lp
->d_magic
== DISKMAGIC_DELETED_REV
)) {
932 disk_lp
->d_magic
^= ~0u;
933 disk_lp
->d_magic2
^= ~0u;
934 is_deleted
= "deleted ";
936 if (target32toh(disk_lp
->d_magic
) != DISKMAGIC
) {
937 /* XXX: Do something about byte-swapped labels ? */
938 if (target32toh(disk_lp
->d_magic
) == DISKMAGIC_REV
&&
939 target32toh(disk_lp
->d_magic2
) == DISKMAGIC_REV
)
940 warnx("ignoring %sbyteswapped label"
941 " at offset %u from sector %u",
942 is_deleted
, offset
, sector
);
945 if (target16toh(disk_lp
->d_npartitions
) > MAXPARTITIONS
||
946 dkcksum_target(disk_lp
) != 0) {
948 warnx("corrupt label found at offset %u in "
949 "sector %u", offset
, sector
);
953 warnx("%slabel found at offset %u from sector %u",
954 is_deleted
, offset
, sector
);
958 /* To print all the labels we have to do it here */
959 /* XXX: maybe we should compare them? */
960 targettohlabel(&hlp
, disk_lp
);
961 printf("# %ssector %u offset %u bytes\n",
962 is_deleted
, sector
, offset
);
964 makedisktab(stdout
, &hlp
);
966 showinfo(stdout
, &hlp
, specname
);
967 showpartitions(stdout
, &hlp
, Cflag
);
970 htotargetlabel(disk_lp
, &hlp
);
971 /* Remember we've found a label */
978 write_bootarea(int f
, u_int sector
)
982 if (bootarea_len
<= 0)
983 errx(1, "attempting to write after failed read");
985 #ifdef ALPHA_BOOTBLOCK_CKSUM
987 * The Alpha requires that the boot block be checksummed.
988 * <sys/bootblock.h> provides a macro to do it.
991 struct alpha_boot_block
*bb
;
993 bb
= (struct alpha_boot_block
*)(void *)bootarea
;
995 ALPHA_BOOT_BLOCK_CKSUM(bb
, &bb
->bb_cksum
);
997 #endif /* ALPHA_BOOTBLOCK_CKSUM */
999 wlen
= pwrite(f
, bootarea
, bootarea_len
, sector
* (off_t
)DEV_BSIZE
);
1000 if (wlen
== bootarea_len
)
1003 err(1, "disklabel write (sector %u) size %u failed",
1004 sector
, bootarea_len
);
1005 errx(1, "disklabel write (sector %u) size %u truncated to %d",
1006 sector
, bootarea_len
, wlen
);
1010 update_label(int f
, u_int label_sector
, u_int label_offset
)
1012 struct disklabel
*disk_lp
;
1014 disk_lp
= find_label(f
, label_sector
);
1016 if (disk_lp
&& Dflag
) {
1017 /* Invalidate the existing label */
1018 disk_lp
->d_magic
^= ~0u;
1019 disk_lp
->d_magic2
^= ~0u;
1021 write_bootarea(f
, label_sector
);
1022 /* Force label to default location */
1027 /* We are just deleting the label */
1030 if (disk_lp
== NULL
) {
1031 if (label_offset
== ~0u)
1033 /* Nothing on the disk - we need to add it */
1034 disk_lp
= (void *)(bootarea
+ label_offset
);
1035 if ((char *)(disk_lp
+ 1) > bootarea
+ bootarea_len
)
1036 errx(1, "no space in bootarea (sector %u) "
1037 "to create label", label_sector
);
1040 htotargetlabel(disk_lp
, &lab
);
1041 write_bootarea(f
, label_sector
);
1046 writelabel_direct(int f
)
1052 label_sector
= get_filecore_partition(f
);
1053 if (label_sector
!= 0)
1054 /* The offset needs to be that from the acorn ports... */
1055 written
= update_label(f
, label_sector
, DEV_BSIZE
);
1057 rval
= process_mbr(f
, writelabel_mbr
);
1059 if (rval
== 2 || written
)
1060 /* Don't add a label to sector 0, but update one if there */
1061 update_label(f
, 0, ~0u);
1063 update_label(f
, 0, LABEL_OFFSET
);
1067 readlabel_direct(int f
)
1069 struct disklabel
*disk_lp
;
1070 u_int filecore_partition_offset
;
1072 filecore_partition_offset
= get_filecore_partition(f
);
1073 if (filecore_partition_offset
!= 0) {
1074 disk_lp
= find_label(f
, filecore_partition_offset
);
1075 if (disk_lp
!= NULL
) {
1076 targettohlabel(&lab
, disk_lp
);
1081 if (mflag
&& process_mbr(f
, readlabel_mbr
) == 0)
1084 disk_lp
= find_label(f
, 0);
1085 if (disk_lp
!= NULL
) {
1086 targettohlabel(&lab
, disk_lp
);
1090 if (!mflag
&& process_mbr(f
, readlabel_mbr
) == 0)
1097 makedisktab(FILE *f
, struct disklabel
*lp
)
1101 struct partition
*pp
;
1104 (void) fprintf(f
, "%.*s|Automatically generated label:\\\n\t:dt=",
1105 (int) sizeof(lp
->d_typename
), lp
->d_typename
);
1106 if ((unsigned) lp
->d_type
< DKMAXTYPES
)
1107 (void) fprintf(f
, "%s:", dktypenames
[lp
->d_type
]);
1109 (void) fprintf(f
, "unknown%d:", lp
->d_type
);
1111 (void) fprintf(f
, "se#%d:", lp
->d_secsize
);
1112 (void) fprintf(f
, "ns#%d:", lp
->d_nsectors
);
1113 (void) fprintf(f
, "nt#%d:", lp
->d_ntracks
);
1114 (void) fprintf(f
, "sc#%d:", lp
->d_secpercyl
);
1115 (void) fprintf(f
, "nc#%d:", lp
->d_ncylinders
);
1117 if ((lp
->d_secpercyl
* lp
->d_ncylinders
) != lp
->d_secperunit
) {
1118 (void) fprintf(f
, "%ssu#%d:", did
, lp
->d_secperunit
);
1121 if (lp
->d_rpm
!= 3600) {
1122 (void) fprintf(f
, "%srm#%d:", did
, lp
->d_rpm
);
1125 if (lp
->d_interleave
!= 1) {
1126 (void) fprintf(f
, "%sil#%d:", did
, lp
->d_interleave
);
1129 if (lp
->d_trackskew
!= 0) {
1130 (void) fprintf(f
, "%ssk#%d:", did
, lp
->d_trackskew
);
1133 if (lp
->d_cylskew
!= 0) {
1134 (void) fprintf(f
, "%scs#%d:", did
, lp
->d_cylskew
);
1137 if (lp
->d_headswitch
!= 0) {
1138 (void) fprintf(f
, "%shs#%d:", did
, lp
->d_headswitch
);
1141 if (lp
->d_trkseek
!= 0) {
1142 (void) fprintf(f
, "%sts#%d:", did
, lp
->d_trkseek
);
1146 (void) fprintf(f
, "drivedata: ");
1147 for (i
= NDDATA
- 1; i
>= 0; i
--)
1148 if (lp
->d_drivedata
[i
])
1152 for (j
= 0; j
<= i
; j
++)
1153 (void) fprintf(f
, "%d ", lp
->d_drivedata
[j
]);
1155 pp
= lp
->d_partitions
;
1156 for (i
= 0; i
< lp
->d_npartitions
; i
++, pp
++) {
1159 (void) fprintf(f
, "\\\n\t:");
1160 (void) fprintf(f
, "p%c#%d:", c
, pp
->p_size
);
1161 (void) fprintf(f
, "o%c#%d:", c
, pp
->p_offset
);
1162 if (pp
->p_fstype
!= FS_UNUSED
) {
1163 if ((unsigned) pp
->p_fstype
< FSMAXTYPES
)
1164 (void) fprintf(f
, "t%c=%s:", c
,
1165 fstypenames
[pp
->p_fstype
]);
1167 (void) fprintf(f
, "t%c=unknown%d:",
1170 switch (pp
->p_fstype
) {
1180 (void) fprintf(f
, "b%c#%d:", c
,
1181 pp
->p_fsize
* pp
->p_frag
);
1182 (void) fprintf(f
, "f%c#%d:", c
, pp
->p_fsize
);
1189 (void) fprintf(f
, "\n");
1197 char tmpfil
[MAXPATHLEN
];
1202 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
1204 (void)snprintf(tmpfil
, sizeof(tmpfil
), "%s/%s", tmpdir
, TMPFILE
);
1205 if ((fd
= mkstemp(tmpfil
)) == -1 || (fp
= fdopen(fd
, "w")) == NULL
) {
1209 (void)fchmod(fd
, 0600);
1210 showinfo(fp
, &lab
, specname
);
1211 showpartitions(fp
, &lab
, Cflag
);
1214 if (!editit(tmpfil
))
1216 fp
= fopen(tmpfil
, "r");
1221 (void) memset(&lab
, 0, sizeof(lab
));
1222 get_ok
= getasciilabel(fp
, &lab
);
1224 if (get_ok
&& write_label(f
) == 0) {
1225 (void) unlink(tmpfil
);
1228 (void) printf("re-edit the label? [y]: ");
1229 (void) fflush(stdout
);
1230 first
= ch
= getchar();
1231 while (ch
!= '\n' && ch
!= EOF
)
1233 if (first
== 'n' || first
== 'N')
1236 (void)unlink(tmpfil
);
1241 editit(const char *tmpfil
)
1245 sigset_t nsigset
, osigset
;
1247 sigemptyset(&nsigset
);
1248 sigaddset(&nsigset
, SIGINT
);
1249 sigaddset(&nsigset
, SIGQUIT
);
1250 sigaddset(&nsigset
, SIGHUP
);
1251 sigprocmask(SIG_BLOCK
, &nsigset
, &osigset
);
1252 while ((pid
= fork()) < 0) {
1253 if (errno
!= EAGAIN
) {
1254 sigprocmask(SIG_SETMASK
, &osigset
, (sigset_t
*)0);
1265 sigprocmask(SIG_SETMASK
, &osigset
, (sigset_t
*)0);
1268 if ((ed
= getenv("EDITOR")) == (char *)0)
1271 * Jump through a few extra hoops in case someone's editor
1272 * is "editor arg1 arg2".
1274 asprintf(&buf
, "%s %s", ed
, tmpfil
);
1277 retval
= execlp(_PATH_BSHELL
, _PATH_BSHELL
, "-c", buf
, NULL
);
1282 while ((xpid
= wait(&status
)) >= 0)
1285 sigprocmask(SIG_SETMASK
, &osigset
, (sigset_t
*)0);
1293 cp
+= strspn(cp
, " \t");
1303 if (cp
== NULL
|| *cp
== '\0')
1306 cp
+= strcspn(cp
, " \t");
1310 cp
+= strspn(cp
, " \t");
1316 #define _CHECKLINE \
1317 if (tp == NULL || *tp == '\0') { \
1318 warnx("line %d: too few fields", lineno); \
1323 #define __CHECKLINE \
1324 if (*tp == NULL || **tp == '\0') { \
1325 warnx("line %d: too few fields", lineno); \
1330 static char _error_
[] = "";
1331 #define NXTNUM(n) if ((n = nxtnum(&tp, lineno),0) + tp != _error_) \
1333 #define NXTXNUM(n) if ((n = nxtxnum(&tp, lp, lineno),0) + tp != _error_) \
1336 static unsigned long
1337 nxtnum(char **tp
, int lineno
)
1343 if (getulong(*tp
, '\0', &cp
, &v
, UINT32_MAX
) != 0) {
1344 warnx("line %d: syntax error", lineno
);
1352 static unsigned long
1353 nxtxnum(char **tp
, struct disklabel
*lp
, int lineno
)
1360 if (getulong(cp
, '/', &ncp
, &n
, UINT32_MAX
) != 0)
1364 n
*= lp
->d_secpercyl
;
1366 if (getulong(cp
, '/', &ncp
, &v
, UINT32_MAX
) != 0)
1368 n
+= v
* lp
->d_nsectors
;
1370 if (getulong(cp
, '\0', &ncp
, &v
, UINT32_MAX
) != 0)
1377 warnx("line %d: invalid format", lineno
);
1383 * Read an ascii label in from fd f,
1384 * in the same format as that put out by showinfo() and showpartitions(),
1388 getasciilabel(FILE *f
, struct disklabel
*lp
)
1390 const char *const *cpp
, *s
;
1391 struct partition
*pp
;
1392 char *cp
, *tp
, line
[BUFSIZ
], tbuf
[15];
1399 lp
->d_bbsize
= BBSIZE
; /* XXX */
1400 lp
->d_sbsize
= SBLOCKSIZE
; /* XXX */
1401 while (fgets(line
, sizeof(line
) - 1, f
)) {
1403 if ((cp
= strpbrk(line
, "#\r\n")) != NULL
)
1406 if (cp
== NULL
) /* blank line or comment line */
1408 tp
= strchr(cp
, ':'); /* everything has a colon in it */
1410 warnx("line %d: syntax error", lineno
);
1414 *tp
++ = '\0', tp
= skip(tp
);
1415 if (!strcmp(cp
, "type")) {
1417 strlcpy(tbuf
, "unknown", sizeof(tbuf
));
1421 for (; cpp
< &dktypenames
[DKMAXTYPES
]; cpp
++)
1422 if ((s
= *cpp
) && !strcasecmp(s
, tp
)) {
1423 lp
->d_type
= cpp
- dktypenames
;
1426 if (GETNUM16(tp
, &v
) != 0) {
1427 warnx("line %d: syntax error", lineno
);
1431 if (v
>= DKMAXTYPES
)
1432 warnx("line %d: warning, unknown disk type: %s",
1437 if (!strcmp(cp
, "flags")) {
1438 for (v
= 0; (cp
= tp
) && *cp
!= '\0';) {
1440 if (!strcasecmp(cp
, "removable"))
1442 else if (!strcasecmp(cp
, "ecc"))
1444 else if (!strcasecmp(cp
, "badsect"))
1447 warnx("line %d: bad flag: %s",
1455 if (!strcmp(cp
, "drivedata")) {
1458 for (i
= 0; (cp
= tp
) && *cp
!= '\0' && i
< NDDATA
;) {
1459 if (GETNUM32(cp
, &v
) != 0) {
1460 warnx("line %d: bad drive data",
1464 lp
->d_drivedata
[i
] = v
;
1470 if (sscanf(cp
, "%lu partitions", &v
) == 1) {
1471 if (v
== 0 || v
> MAXPARTITIONS
) {
1472 warnx("line %d: bad # of partitions", lineno
);
1473 lp
->d_npartitions
= MAXPARTITIONS
;
1476 lp
->d_npartitions
= v
;
1483 if (!strcmp(cp
, "disk")) {
1484 strncpy(lp
->d_typename
, tp
, sizeof(lp
->d_typename
));
1487 if (!strcmp(cp
, "label")) {
1488 strncpy(lp
->d_packname
, tp
, sizeof(lp
->d_packname
));
1491 if (!strcmp(cp
, "bytes/sector")) {
1492 if (GETNUM32(tp
, &v
) != 0 || v
<= 0 || (v
% 512) != 0) {
1493 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1499 if (!strcmp(cp
, "sectors/track")) {
1500 if (GETNUM32(tp
, &v
) != 0) {
1501 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1507 if (!strcmp(cp
, "sectors/cylinder")) {
1508 if (GETNUM32(tp
, &v
) != 0) {
1509 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1512 lp
->d_secpercyl
= v
;
1515 if (!strcmp(cp
, "tracks/cylinder")) {
1516 if (GETNUM32(tp
, &v
) != 0) {
1517 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1523 if (!strcmp(cp
, "cylinders")) {
1524 if (GETNUM32(tp
, &v
) != 0) {
1525 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1528 lp
->d_ncylinders
= v
;
1531 if (!strcmp(cp
, "total sectors") ||
1532 !strcmp(cp
, "sectors/unit")) {
1533 if (GETNUM32(tp
, &v
) != 0) {
1534 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1537 lp
->d_secperunit
= v
;
1540 if (!strcmp(cp
, "rpm")) {
1541 if (GETNUM16(tp
, &v
) != 0) {
1542 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1548 if (!strcmp(cp
, "interleave")) {
1549 if (GETNUM16(tp
, &v
) != 0) {
1550 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1553 lp
->d_interleave
= v
;
1556 if (!strcmp(cp
, "trackskew")) {
1557 if (GETNUM16(tp
, &v
) != 0) {
1558 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1561 lp
->d_trackskew
= v
;
1564 if (!strcmp(cp
, "cylinderskew")) {
1565 if (GETNUM16(tp
, &v
) != 0) {
1566 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1572 if (!strcmp(cp
, "headswitch")) {
1573 if (GETNUM32(tp
, &v
) != 0) {
1574 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1577 lp
->d_headswitch
= v
;
1580 if (!strcmp(cp
, "track-to-track seek")) {
1581 if (GETNUM32(tp
, &v
) != 0) {
1582 warnx("line %d: bad %s: %s", lineno
, cp
, tp
);
1588 if ('a' > *cp
|| *cp
> 'z' || cp
[1] != '\0') {
1589 warnx("line %d: unknown field: %s", lineno
, cp
);
1594 /* We have a partition entry */
1597 if (part
>= MAXPARTITIONS
) {
1598 warnx("line %d: bad partition name: %s", lineno
, cp
);
1602 pp
= &lp
->d_partitions
[part
];
1604 NXTXNUM(pp
->p_size
);
1605 NXTXNUM(pp
->p_offset
);
1606 /* can't use word() here because of blanks in fstypenames[] */
1607 tp
+= strspn(tp
, " \t");
1611 for (; cpp
< &fstypenames
[FSMAXTYPES
]; cpp
++) {
1614 (cp
[strlen(s
)] != ' ' &&
1615 cp
[strlen(s
)] != '\t' &&
1616 cp
[strlen(s
)] != '\0'))
1618 if (!memcmp(s
, cp
, strlen(s
))) {
1619 pp
->p_fstype
= cpp
- fstypenames
;
1624 tp
+= strspn(tp
, " \t");
1632 if (isdigit(*cp
& 0xff)) {
1633 if (GETNUM8(cp
, &v
) != 0) {
1634 warnx("line %d: syntax error", lineno
);
1639 if ((unsigned)v
>= FSMAXTYPES
) {
1640 warnx("line %d: warning, unknown file system type: %s",
1642 warnx("tip: use -l to see all valid file system "
1648 switch (pp
->p_fstype
) {
1650 case FS_UNUSED
: /* XXX */
1651 NXTNUM(pp
->p_fsize
);
1652 if (pp
->p_fsize
== 0)
1655 pp
->p_frag
= v
/ pp
->p_fsize
;
1661 NXTNUM(pp
->p_fsize
);
1662 if (pp
->p_fsize
== 0)
1665 pp
->p_frag
= v
/ pp
->p_fsize
;
1669 NXTNUM(pp
->p_fsize
);
1670 if (pp
->p_fsize
== 0)
1673 pp
->p_frag
= v
/ pp
->p_fsize
;
1677 NXTNUM(pp
->p_fsize
);
1678 if (pp
->p_fsize
== 0)
1681 pp
->p_frag
= v
/ pp
->p_fsize
;
1684 NXTNUM(pp
->p_cdsession
);
1695 errors
+= checklabel(lp
);
1696 return (errors
== 0);
1700 * Check disklabel for errors and fill in
1701 * derived fields according to supplied values.
1704 checklabel(struct disklabel
*lp
)
1706 struct partition
*pp
, *qp
;
1711 if (lp
->d_secsize
== 0) {
1712 warnx("sector size %d", lp
->d_secsize
);
1715 if (lp
->d_nsectors
== 0) {
1716 warnx("sectors/track %d", lp
->d_nsectors
);
1719 if (lp
->d_ntracks
== 0) {
1720 warnx("tracks/cylinder %d", lp
->d_ntracks
);
1723 if (lp
->d_ncylinders
== 0) {
1724 warnx("cylinders/unit %d", lp
->d_ncylinders
);
1728 warnx("warning, revolutions/minute %d", lp
->d_rpm
);
1729 if (lp
->d_secpercyl
== 0)
1730 lp
->d_secpercyl
= lp
->d_nsectors
* lp
->d_ntracks
;
1731 if (lp
->d_secperunit
== 0)
1732 lp
->d_secperunit
= lp
->d_secpercyl
* lp
->d_ncylinders
;
1733 if (lp
->d_bbsize
== 0) {
1734 warnx("boot block size %d", lp
->d_bbsize
);
1736 } else if (lp
->d_bbsize
% lp
->d_secsize
)
1737 warnx("warning, boot block size %% sector-size != 0");
1738 if (lp
->d_sbsize
== 0) {
1739 warnx("super block size %d", lp
->d_sbsize
);
1741 } else if (lp
->d_sbsize
% lp
->d_secsize
)
1742 warnx("warning, super block size %% sector-size != 0");
1743 if (lp
->d_npartitions
> MAXPARTITIONS
)
1744 warnx("warning, number of partitions (%d) > MAXPARTITIONS (%d)",
1745 lp
->d_npartitions
, MAXPARTITIONS
);
1747 for (i
= MAXPARTITIONS
- 1; i
>= lp
->d_npartitions
; i
--) {
1749 pp
= &lp
->d_partitions
[i
];
1750 if (pp
->p_size
|| pp
->p_offset
) {
1751 warnx("warning, partition %c increased "
1752 "number of partitions from %d to %d",
1753 part
, lp
->d_npartitions
, i
+ 1);
1754 lp
->d_npartitions
= i
+ 1;
1758 for (i
= 0; i
< lp
->d_npartitions
; i
++) {
1760 pp
= &lp
->d_partitions
[i
];
1761 if (pp
->p_size
== 0 && pp
->p_offset
!= 0)
1762 warnx("warning, partition %c: size 0, but offset %d",
1763 part
, pp
->p_offset
);
1764 #ifdef STRICT_CYLINDER_ALIGNMENT
1765 if (pp
->p_offset
% lp
->d_secpercyl
) {
1766 warnx("warning, partition %c:"
1767 " not starting on cylinder boundary",
1771 #endif /* STRICT_CYLINDER_ALIGNMENT */
1772 if (pp
->p_offset
> lp
->d_secperunit
) {
1773 warnx("partition %c: offset past end of unit", part
);
1776 if (pp
->p_offset
+ pp
->p_size
> lp
->d_secperunit
) {
1777 warnx("partition %c: partition extends"
1778 " past end of unit",
1782 if (pp
->p_fstype
!= FS_UNUSED
)
1783 for (j
= i
+ 1; j
< lp
->d_npartitions
; j
++) {
1784 qp
= &lp
->d_partitions
[j
];
1785 if (qp
->p_fstype
== FS_UNUSED
)
1787 if (pp
->p_offset
< qp
->p_offset
+ qp
->p_size
&&
1788 qp
->p_offset
< pp
->p_offset
+ pp
->p_size
)
1789 warnx("partitions %c and %c overlap",
1799 static const struct {
1803 { "[-ACFrtv] disk", "(to read label)" },
1804 { "-w [-DFrv] [-f disktab] disk disktype [packid]", "(to write label)" },
1805 { "-e [-CDFIrv] disk", "(to edit label)" },
1806 { "-i [-DFIrv] disk", "(to create a label interactively)" },
1807 { "-D [-v] disk", "(to delete existing label(s))" },
1808 { "-R [-DFrv] disk protofile", "(to restore label)" },
1809 { "[-NW] disk", "(to write disable/enable label)" },
1810 { "-l", "(to show all known file system types)" },
1814 const char *pn
= getprogname();
1815 const char *t
= "usage:";
1817 for (i
= 0; usages
[i
].name
!= NULL
; i
++) {
1818 (void)fprintf(stderr
, "%s %s %s\n\t%s\n",
1819 t
, pn
, usages
[i
].name
, usages
[i
].expn
);
1826 getulong(const char *str
, char sep
, char **epp
, unsigned long *ul
,
1834 *ul
= strtoul(str
, epp
, 10);
1836 if ((*ul
== ULONG_MAX
&& errno
== ERANGE
) || *ul
> max
)
1839 if (*str
== '\0' || (**epp
!= '\0' && **epp
!= sep
&&
1840 !isspace((unsigned char)**epp
)))
1847 * This is a wrapper over the standard strcmp function to be used with
1848 * qsort on an array of pointers to strings.
1851 qsort_strcmp(const void *v1
, const void *v2
)
1853 const char *const *sp1
= (const char *const *)v1
;
1854 const char *const *sp2
= (const char *const *)v2
;
1856 return strcmp(*sp1
, *sp2
);
1860 * Prints all know file system types for a partition.
1861 * Returns 1 on success, 0 on failure.
1871 const char *const *namep
;
1873 namep
= fstypenames
;
1874 while (*namep
++ != NULL
)
1883 list
= (const char **)malloc(sizeof(char *) * nelems
);
1885 warnx("sorry, could not allocate memory for list");
1888 for (i
= 0; i
< nelems
; i
++)
1889 list
[i
] = fstypenames
[i
];
1891 qsort(list
, nelems
, sizeof(char *), qsort_strcmp
);
1893 for (i
= 0; i
< nelems
; i
++)
1894 (void)printf("%s\n", list
[i
]);