tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: disklabel(8) allows addressing non-existent space



Hi,

the following patches are my supposal for:
 * Adding a batch mode (option -s)
 * Adding an option to set partition alignment rather than just cylinder or
   sector rounding
 * Add option to specify percentages. This is somewhat unclear, as it will
   currently take d_secperunit, which won't work with mbrs.
   We could add some magic to check whether we have an mbr, but this won't be
   very clean.
 * Also include the 'fixes' I posted to bin/45749. Though the segfault isn't
   fixed yet.

I still don't know if it is desirable to have mbr checks, doing some with a
constant partition c, etc.

Please have a look at the code and comment.

Regards, Julian
--- sbin/disklabel/Makefile
+++ sbin/disklabel/Makefile
@@ -1,10 +1,10 @@
 #      $NetBSD: Makefile,v 1.69 2011/08/30 12:39:52 bouyer Exp $
 #      @(#)Makefile    8.2 (Berkeley) 3/17/94
 
 PROG=  disklabel
-SRCS=  main.c dkcksum.c interact.c printlabel.c
+SRCS=  main.c dkcksum.c interact.c printlabel.c batch.c
 MAN=   disklabel.5 disklabel.8
 .if (${HOSTPROG:U} == "")
 DPADD+= ${LIBUTIL}
 LDADD+= -lutil
 .endif

--- sbin/disklabel/disklabel.8
+++ sbin/disklabel/disklabel.8
@@ -40,20 +40,33 @@
 .Nd read and write disk pack label
 .Sh SYNOPSIS
 .\" disklabel: read label
 .Nm
 .Op Fl ACDFrtv
+.Op Fl s Ar num/start/size/usage Ns Bo Ar /fsize/bsize Ns Bq Ar /cpg Bc
+.Op Fl p Ar param=value
 .Ar disk
 .\" disklabel -e: read/modify/write using $EDITOR
 .Nm
 .Fl e
 .Op Fl CDFIrv
+.Op Fl s Ar num/start/size/usage Ns Bo Ar /fsize/bsize Ns Bq Ar /cpg Bc
+.Op Fl p Ar param=value
 .Ar disk
 .\" disklabel -i: read/modify/write using builtin commands
 .Nm
 .Fl i
 .Op Fl DFIrv
+.Op Fl s Ar num/start/size/usage Ns Bo Ar /fsize/bsize Ns Bq Ar /cpg Bc
+.Op Fl p Ar param=value
+.Ar disk
+.\" disklabel -u: update without interaction
+.Nm
+.Fl u
+.Op Fl DFIrv
+.Op Fl s Ar num/start/size/usage Ns Bo Ar /fsize/bsize Ns Bq Ar /cpg Bc
+.Op Fl p Ar param=value
 .Ar disk
 .\" disklabel -R: write from edited output
 .Nm
 .Fl R
 .Op Fl DFrv
@@ -77,11 +90,11 @@
 can be used to install, examine, or modify the label on a disk drive or pack.
 When writing the label, it can be used to change the drive identification,
 the disk partitions on the drive, or to replace a damaged label.
 .Pp
 The
-.Fl e , i , l , R , w , N ,
+.Fl e , i , l , u , R , w , N ,
 and
 .Fl W
 options determine the basic operation.
 If none are specified the label
 is displayed.
@@ -98,10 +111,15 @@
 .It Fl i
 Interactively update the existing label and write it back to the disk.
 .It Fl l
 Show all known file system types (those that can be specified along a
 partition within the label) and exit.
+.It Fl u
+Update the label without further interaction (only useful in conjunction with
+.Fl s
+or
+.Fl p ) .
 .It Fl R
 Write (restore) a label by reading it from
 .Ar protofile .
 The file should be in the same format as the default output.
 .It Fl w
@@ -180,10 +198,49 @@
 .Fl F .
 .It Fl t
 Format the output as a
 .Xr disktab 5
 entry.
+.It Fl s Ar num/start/size/fstype Ns Bo Ar /fsize/bsize Ns Bq Ar /cpg Bc
+Set partition
+.Ar num ,
+given as a lower letter to a partition with start sector
+.Ar start ,
+size
+.Ar size
+and file system type
+.Ar fstype .
+Overlapping partitions will be deleted.
+You can set the
+.Ar start
+to
+.Dq x
+to make it start directly after partition
+.Dq x .
+If you specify this option when showing a label, you see the disklabel as if 
the 
+changes were applied.
+.Pp
+Optionally, you can also specify the fragment size with
+.Ar fsize
+and the block size with
+.Ar bsize ,
+as well as additionally the number of cylinders per group (or segment shift in 
+case of LFS) with
+.Ar cpg .
+.It Fl p Ar param=value Ns Op Ns , Ns Ar param=value , Ns Ar ...
+Set disklabel parameter
+.Ar param
+to value
+.Ar value .
+you can specify multiple parameters by separating them by commas.
+.Pp
+The names of the values are those as defined in
+.Xr disklabel 5
+without the leading
+.Ar d_ ,
+thus being
+.Ar type , typename , packname , npartitions , secsize , nsectors , ntracks , 
secpercyl , ncylinders , secperunit , rpm , interleave , trackskew , cylskew , 
headswitch , trkseek .
 .It Fl v
 Be verbose about the operations being done, in particular the disk sectors
 being read and written.
 Specifying
 .Fl v
@@ -236,10 +293,16 @@
 .Dl Ic disklabel sd0
 .Pp
 Display the in-core label for sd0 as obtained via
 .Pa /dev/rsd0c .
 .Pp
+.Dl Ic disklabel -s a/1024m/2048m/unused sd0
+.Pp
+Display the in-core label for sd0, but change partition
+.Dq a
+to start at 1024MB and be 2048MB large, deleting any overlapping partitions.
+.Pp
 .Dl Ic disklabel -i -r sd0
 .Pp
 Read the on-disk label for sd0, edit it using the built-in interactive editor 
and reinstall in-core as well
 as on-disk.
 .Pp
@@ -272,10 +335,23 @@
 .Pp
 .Dl Ic disklabel -R sd0 mylabel
 .Pp
 Restore the on-disk and in-core label for sd0 from information in
 .Pa mylabel .
+.Pp
+.Dl Ic disklabel -u -p typename=primary
+.Pp
+Change the disklabel's name to
+.Dq primary .
+.Pp
+.Dl Ic disklabel -u -s a/1g/50%/4.2BSD -p typename=primary sd0
+.Pp
+Change partition
+.Dq a
+of disk sd0 to start at 1GB from the beginning of the disklabel, take 50% of
+the whole space and change the name of the disklabel to
+.Dq primary .
 .Sh DIAGNOSTICS
 The kernel device drivers will not allow the size of a disk partition
 to be decreased or the offset of a partition to be changed while it is open.
 Some device drivers create a label containing only a single large partition
 if a disk is unlabeled; thus, the label must be written to the

--- sbin/disklabel/interact.c
+++ sbin/disklabel/interact.c
@@ -57,21 +57,19 @@
 static void    cmd_print(struct disklabel *, char *, int);
 static void    cmd_printall(struct disklabel *, char *, int);
 static void    cmd_info(struct disklabel *, char *, int);
 static void    cmd_part(struct disklabel *, char *, int);
 static void    cmd_label(struct disklabel *, char *, int);
-static void    cmd_round(struct disklabel *, char *, int);
+static void    cmd_align(struct disklabel *, char *, int);
 static void    cmd_name(struct disklabel *, char *, int);
 static void    cmd_listfstypes(struct disklabel *, char *, int);
 static int     runcmd(struct disklabel *, char *, int);
 static int     getinput(const char *, const char *, const char *, char *);
 static int     alphacmp(const void *, const void *);
 static void    defnum(struct disklabel *, char *, uint32_t);
 static void    dumpnames(const char *, const char * const *, size_t);
-static intmax_t        getnum(struct disklabel *, char *, intmax_t);
 
-static int rounding = 0;       /* sector rounding */
 static int chaining = 0;       /* make partitions contiguous */
 
 static struct cmds {
        const char *name;
        void (*func)(struct disklabel *, char *, int);
@@ -83,16 +81,15 @@
        { "I",  cmd_info,       "change label information" },
        { "L",  cmd_listfstypes,"list all known file system types" },
        { "N",  cmd_name,       "name the label" },
        { "P",  cmd_print,      "print current partition table" },
        { "Q",  NULL,           "quit" },
-       { "R",  cmd_round,      "rounding (c)ylinders (s)ectors" },
+       { "A",  cmd_align,      "set alignment" },
        { "W",  cmd_label,      "write the current partition table" },
        { NULL, NULL,           NULL }
 };
-       
-       
+
 
 static void
 cmd_help(struct disklabel *lp, char *s, int fd)
 {
        struct cmds *cmd;
@@ -150,11 +147,12 @@
 cmd_info(struct disklabel *lp, char *s, int fd)
 {
        char    line[BUFSIZ];
        char    def[BUFSIZ];
        int     v, i;
-       u_int32_t u;
+       u_int32_t u32;
+       u_int16_t u16;
 
        printf("# Current values:\n");
        showinfo(stdout, lp, specname);
 
        /* d_type */
@@ -207,15 +205,15 @@
                i = getinput(":", "Number of partitions", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint16(line, &u16)) {
                        printf("Invalid number of partitions `%s'\n", line);
                        continue;
                }
-               lp->d_npartitions = u;
+               lp->d_npartitions = u16;
                break;
        }
 
        /* d_secsize */
        for (;;) {
@@ -223,15 +221,15 @@
                i = getinput(":", "Sector size (bytes)", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid sector size `%s'\n", line);
                        continue;
                }
-               lp->d_secsize = u;
+               lp->d_secsize = u32;
                break;
        }
 
        /* d_nsectors */
        for (;;) {
@@ -239,15 +237,15 @@
                i = getinput(":", "Number of sectors per track", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid number of sectors `%s'\n", line);
                        continue;
                }
-               lp->d_nsectors = u;
+               lp->d_nsectors = u32;
                break;
        }
 
        /* d_ntracks */
        for (;;) {
@@ -255,15 +253,15 @@
                i = getinput(":", "Number of tracks per cylinder", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid number of tracks `%s'\n", line);
                        continue;
                }
-               lp->d_ntracks = u;
+               lp->d_ntracks = u32;
                break;
        }
 
        /* d_secpercyl */
        for (;;) {
@@ -271,16 +269,16 @@
                i = getinput(":", "Number of sectors/cylinder", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid number of sector/cylinder `%s'\n",
                            line);
                        continue;
                }
-               lp->d_secpercyl = u;
+               lp->d_secpercyl = u32;
                break;
        }
 
        /* d_ncylinders */
        for (;;) {
@@ -288,15 +286,15 @@
                i = getinput(":", "Total number of cylinders", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid sector size `%s'\n", line);
                        continue;
                }
-               lp->d_ncylinders = u;
+               lp->d_ncylinders = u32;
                break;
        }
 
        /* d_secperunit */
        for (;;) {
@@ -304,15 +302,15 @@
                i = getinput(":", "Total number of sectors", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid number of sectors `%s'\n", line);
                        continue;
                }
-               lp->d_secperunit = u;
+               lp->d_secperunit = u32;
                break;
        }
 
        /* d_rpm */
 
@@ -322,15 +320,15 @@
                i = getinput(":", "Hardware sectors interleave", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint16(line, &u16)) {
                        printf("Invalid sector interleave `%s'\n", line);
                        continue;
                }
-               lp->d_interleave = u;
+               lp->d_interleave = u16;
                break;
        }
 
        /* d_trackskew */
        for (;;) {
@@ -338,15 +336,15 @@
                i = getinput(":", "Sector 0 skew, per track", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint16(line, &u16)) {
                        printf("Invalid track sector skew `%s'\n", line);
                        continue;
                }
-               lp->d_trackskew = u;
+               lp->d_trackskew = u16;
                break;
        }
 
        /* d_cylskew */
        for (;;) {
@@ -354,15 +352,15 @@
                i = getinput(":", "Sector 0 skew, per cylinder", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint16(line, &u16)) {
                        printf("Invalid cylinder sector `%s'\n", line);
                        continue;
                }
-               lp->d_cylskew = u;
+               lp->d_cylskew = u16;
                break;
        }
 
        /* d_headswitch */
        for (;;) {
@@ -370,15 +368,15 @@
                i = getinput(":", "Head switch time (usec)", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid head switch time `%s'\n", line);
                        continue;
                }
-               lp->d_headswitch = u;
+               lp->d_headswitch = u32;
                break;
        }
 
        /* d_trkseek */
        for (;;) {
@@ -386,15 +384,15 @@
                i = getinput(":", "Track seek time (usec)", def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if (sscanf(line, "%" SCNu32, &u) != 1) {
+               if (strtouint32(line, &u32)) {
                        printf("Invalid track seek time `%s'\n", line);
                        continue;
                }
-               lp->d_trkseek = u;
+               lp->d_trkseek = u32;
                break;
        }
 }
 
 
@@ -413,32 +411,33 @@
        (void) strncpy(lp->d_packname, line, sizeof(lp->d_packname));
 }
 
 
 static void
-cmd_round(struct disklabel *lp, char *s, int fd)
+cmd_align(struct disklabel *lp, char *s, int fd)
 {
        int     i;
        char    line[BUFSIZ];
+       char *outputstr;
 
-       i = getinput(":", "Rounding", rounding ? "cylinders" : "sectors", line);
+       if (!asprintf(&outputstr, "%d", ptn_alignment))
+               return;
+       i = getinput(":", "Rounding", outputstr, line);
+       free(outputstr);
        if (i <= 0)
                return;
 
-       switch (line[0]) {
-       case 'c':
-       case 'C':
-               rounding = 1;
-               return;
-       case 's':
-       case 'S':
-               rounding = 0;
-               return;
-       default:
-               printf("Rounding can be (c)ylinders or (s)ectors\n");
-               return;
-       }
+       if (strlen(line) == 1)
+               if (line[0] == 'c' || line[0] == 'C') {
+                       set_ptn_alignment(lp, "1cyl");
+                       return;
+               } else if (line[0] == 's' || line[0] == 'S') {
+                       set_ptn_alignment(lp, "1sec");
+                       return;
+               }
+
+       set_ptn_alignment(lp, line);
 }
 
 
 static void
 cmd_part(struct disklabel *lp, char *s, int fd)
@@ -502,12 +501,16 @@
                        } else {
                                p->p_offset = cp[line[0] - 'a'].p_offset +
                                    cp[line[0] - 'a'].p_size;
                        }
                } else {
-                       if ((im = getnum(lp, line, 0)) == -1 || im < 0) {
+                       if (strlen(line) == 1 && line[0] == '$')
+                               im = 0;
+                       else if ((im = modstrtoint(lp, line, 1)) == -1) {
                                printf("Bad offset `%s'\n", line);
+                               printf("Valid units: (S)ectors, (C)ylinders, 
(K)ilo, (M)ega, "
+                                               "(G)iga, (T)era\n");
                                continue;
                        } else if (im > 0xffffffffLL ||
                                   (uint32_t)im > lp->d_secperunit) {
                                printf("Offset `%s' out of range\n", line);
                                continue;
@@ -522,13 +525,16 @@
                    def, line);
                if (i == -1)
                        return;
                else if (i == 0)
                        break;
-               if ((im = getnum(lp, line, lp->d_secperunit - p->p_offset))
-                   == -1) {
+               if (strlen(line) == 1 && line[0] == '$')
+                       im = lp->d_secperunit - p->p_offset;
+               else if ((im = modstrtoint(lp, line, 1)) == -1) {
                        printf("Bad size `%s'\n", line);
+                       printf("Valid units: (S)ectors, (C)ylinders, (K)ilo, 
(M)ega, "
+                                       "(G)iga, (T)era\n");
                        continue;
                } else if (im > 0xffffffffLL ||
                           (im + p->p_offset) > lp->d_secperunit) {
                        printf("Size `%s' out of range\n", line);
                        continue;
@@ -702,76 +708,13 @@
 
 static void
 defnum(struct disklabel *lp, char *buf, uint32_t size)
 {
 
-       (void) snprintf(buf, BUFSIZ, "%.40gc, %" PRIu32 "s, %.40gM",
+       (void) snprintf(buf, BUFSIZ, "%.4gc, %" PRIu32 "s, %.4gM",
            size / (float) lp->d_secpercyl,
            size, size  * (lp->d_secsize / (float) (1024 * 1024)));
-}
-
-
-static intmax_t
-getnum(struct disklabel *lp, char *buf, intmax_t defaultval)
-{
-       char    *ep;
-       double   d;
-       intmax_t rv;
-
-       if (defaultval && buf[0] == '$' && buf[1] == 0)
-               return defaultval;
-
-       d = strtod(buf, &ep);
-       if (buf == ep)
-               return -1;
-
-#define ROUND(a)       ((((a) / lp->d_secpercyl) + \
-                        (((a) % lp->d_secpercyl) ? 1 : 0)) * lp->d_secpercyl)
-
-       switch (*ep) {
-       case '\0':
-       case 's':
-       case 'S':
-               rv = (intmax_t) d;
-               break;
-
-       case 'c':
-       case 'C':
-               rv = (intmax_t) (d * lp->d_secpercyl);
-               break;
-
-       case 'k':
-       case 'K':
-               rv =  (intmax_t) (d * 1024 / lp->d_secsize);
-               break;
-
-       case 'm':
-       case 'M':
-               rv =  (intmax_t) (d * 1024 * 1024 / lp->d_secsize);
-               break;
-
-       case 'g':
-       case 'G':
-               rv =  (intmax_t) (d * 1024 * 1024 * 1024 / lp->d_secsize);
-               break;
-
-       case 't':
-       case 'T':
-               rv =  (intmax_t) (d * 1024 * 1024 * 1024 * 1024 / 
lp->d_secsize);
-               break;
-
-       default:
-               printf("Unit error %c\n", *ep);
-               printf("Valid units: (S)ectors, (C)ylinders, (K)ilo, (M)ega, "
-                   "(G)iga, (T)era");
-               return -1;
-       }
-
-       if (rounding)
-               return ROUND(rv);
-       else
-               return rv;
 }
 
 
 void
 interact(struct disklabel *lp, int fd)

--- sbin/disklabel/main.c
+++ sbin/disklabel/main.c
@@ -108,10 +108,12 @@
 #else
 #include <sys/ioctl.h>
 #include <sys/disklabel.h>
 #include <sys/disklabel_acorn.h>
 #include <sys/bootblock.h>
+#include <sys/sysctl.h>
+#include <sys/param.h>
 #include <util.h>
 #include <disktab.h>
 #endif /* HAVE_NBTOOL_CONFIG_H */
 
 #include "pathnames.h"
@@ -155,10 +157,11 @@
 static int     Iflag;          /* Read/write direct, but default if absent */
 static int     lflag;          /* List all known file system types and exit */
 static int     mflag;          /* Expect disk to contain an MBR */
 static int verbose;
 static int read_all;           /* set if op = READ && Aflag */
+int    ptn_alignment = 1;      /* Partition alignment. extern in extern.h */
 
 static int write_label(int);
 static int readlabel_direct(int);
 static void writelabel_direct(int);
 static int update_label(int, u_int, u_int);
@@ -265,15 +268,18 @@
 main(int argc, char *argv[])
 {
        FILE    *t;
        int      ch, f, error;
        char    *dkname;
+       char    *partstr = NULL;
+       char    *labelstr = NULL;
+       char    *alignstr = NULL;
        struct stat sb;
        int      writable;
        enum {
                UNSPEC, EDIT, READ, RESTORE, SETWRITABLE, SETREADONLY,
-               WRITE, INTERACT, DELETE
+               WRITE, INTERACT, DELETE, BATCH
        } op = UNSPEC, old_op;
 
        mflag = GETLABELUSESMBR();
        if (mflag < 0) {
                warn("getlabelusesmbr() failed");
@@ -283,11 +289,11 @@
        /* We must avoid doing any ioctl requests */
        Fflag = rflag = 1;
 #endif
 
        error = 0;
-       while ((ch = getopt(argc, argv, "ACDFINRWb:ef:ilmrs:tvw")) != -1) {
+       while ((ch = getopt(argc, argv, "ACDFINRWa:ef:s:p:uilmrtvw")) != -1) {
                old_op = op;
                switch (ch) {
                case 'A':       /* Action all labels */
                        Aflag = 1;
                        rflag = 1;
@@ -314,10 +320,15 @@
                        op = SETREADONLY;
                        break;
                case 'W':       /* Allow writes to label sector */
                        op = SETWRITABLE;
                        break;
+               case 'a':       /* Set partition alignment */
+                       alignstr = strdup(optarg);
+                       if (!alignstr)
+                               err(1, "Could not allocate string.");
+                       break;
                case 'e':       /* Edit label with $EDITOR */
                        op = EDIT;
                        break;
                case 'f':       /* Name of disktab file */
                        if (setdisktab(optarg) == -1)
@@ -330,10 +341,15 @@
                        lflag = 1;
                        break;
                case 'm':       /* Expect disk to have an MBR */
                        mflag ^= 1;
                        break;
+               case 'p':       /* Change disklabel values */
+                       labelstr = strdup(optarg);
+                       if (!labelstr)
+                               err(1, "Could not allocate string.");
+                       break;
                case 'r':       /* Read/write label directly from disk */
                        rflag = 1;
                        break;
                case 't':       /* Format output as a disktab entry */
                        tflag = 1;
@@ -341,10 +357,18 @@
                case 'v':       /* verbose/diag output */
                        verbose++;
                        break;
                case 'w':       /* Write label based on disktab entry */
                        op = WRITE;
+                       break;
+               case 's':       /* Preset partition values. */
+                       partstr = strdup(optarg);
+                       if (!partstr)
+                               err(1, "Could not allocate string.");
+                       break;
+               case 'u':       /* Update label without further interaction. */
+                       op = BATCH;
                        break;
                case '?':
                default:
                        usage();
                }
@@ -381,22 +405,41 @@
                if (argc != 1)
                        usage();
                Dflag = 2;
                writelabel_direct(f);
                break;
+       
+       case BATCH:
+               if (argc != 1)
+                       usage();
+               readlabel(f);
+               set_ptn_alignment(&lab, alignstr);
+               free(alignstr);
+               parse_partstr(&lab, &partstr);
+               parse_labelstr(&lab, &labelstr);
+               batchedit(&lab, f);
+               break;
 
        case EDIT:
                if (argc != 1)
                        usage();
                readlabel(f);
+               set_ptn_alignment(&lab, alignstr);
+               free(alignstr);
+               parse_partstr(&lab, &partstr);
+               parse_labelstr(&lab, &labelstr);
                error = edit(f);
                break;
 
        case INTERACT:
                if (argc != 1)
                        usage();
                readlabel(f);
+               set_ptn_alignment(&lab, alignstr);
+               free(alignstr);
+               parse_partstr(&lab, &partstr);
+               parse_labelstr(&lab, &labelstr);
                /*
                 * XXX: Fill some default values so checklabel does not fail
                 */
                if (lab.d_bbsize == 0)
                        lab.d_bbsize = BBSIZE;
@@ -414,10 +457,14 @@
                        /* Label got printed in the bowels of readlabel */
                        break;
                if (tflag)
                        makedisktab(stdout, &lab);
                else {
+                       set_ptn_alignment(&lab, alignstr);
+                       free(alignstr);
+                       parse_partstr(&lab, &partstr);
+                       parse_labelstr(&lab, &labelstr);
                        showinfo(stdout, &lab, specname);
                        showpartitions(stdout, &lab, Cflag);
                }
                error = checklabel(&lab);
                if (error)
@@ -1906,6 +1953,128 @@
                        free(list);
                }
        }
 
        return ret;
+}
+
+/* Convert a string to a uint16_t. Returns -1 on error, 0 on success. */
+int
+strtouint16(char *intstr, uint16_t *val16)
+{
+       long int lval;
+       char *endstr;
+
+       lval = strtoll(intstr, &endstr, 10);
+       if (*endstr != '\0')
+               return -1;
+       if (lval > UINT16_MAX)
+               return -1;
+
+       *val16 = (uint16_t) lval;
+
+       return 0;
+}
+
+/* Convert a string to a uint32_t. Returns -1 on error, 0 on success. */
+int
+strtouint32(char *intstr, uint32_t *val32)
+{
+       long int lval;
+       char *endstr;
+
+       lval = strtoll(intstr, &endstr, 10);
+       if (*endstr != '\0')
+               return -1;
+       if (lval > UINT32_MAX)
+               return -1;
+
+       *val32 = (uint32_t) lval;
+
+       return 0;
+}
+
+/* Convert a string with modifier to int, return negative value on error.
+ * If rounding is 0, do not round. If rounding is 1, round. If rounding is 2, 
+ * round down, if rounding is 3, round up. */
+intmax_t
+modstrtoint(struct disklabel *lp, char *modstr, int rounding)
+{
+       char *cp;
+       intmax_t ret;
+       int len, part;
+       size_t partlen;
+
+       ret = strtoll(modstr, &cp, 10);
+       len = strcspn(cp, " \t\n");
+       if (modstr == cp)
+               return -1;
+       if (!strncasecmp(cp, "sector", len))
+               {}
+       else if (!strncasecmp(cp, "tb", len)) /* Might be too long for int. */
+               ret *= (intmax_t)(1024 * 1024 * 1024 / lp->d_secsize * 1024);
+       else if (!strncasecmp(cp, "gb", len))
+               ret *= 1024 * 1024 * 1024 / lp->d_secsize;
+       else if (!strncasecmp(cp, "mb", len))
+               ret *= 1024 * 1024 / lp->d_secsize;
+       else if (!strncasecmp(cp, "kb", len))
+               ret *= 1024 / lp->d_secsize;
+       else if (!strncasecmp(cp, "cylinder", len))
+               ret *= lp->d_secpercyl;
+       else if (!strncasecmp(cp, "%", len)) {
+               if (ret < 0 || ret > 100)
+                       err(1, "Percentage must be in [0, 100], is %d.", ret);
+               /* lp->d_secperunit is the best approximation for the right 
value we can 
+                * get. We cannot determine the mbr partition size for sure if 
there is 
+                * one, so simply compute percentages of the whole disk. */
+               // XXX: We could also use some magic involving checks of 
partition c, 
+               // etc., to be sure to take percentages of the mbr.
+               ret *= lp->d_secperunit / 100;
+               ret += ptn_alignment;
+       } else
+               return -1;
+
+       switch (rounding) {
+               case 0: /* Nothing at all. */
+                       break;
+               case 1: /* Round. */
+                       ret = ((ret / ptn_alignment) + (ret % ptn_alignment ? 1 
: 0)) * ptn_alignment;
+                       break;
+               case 2: /* Round down. */
+                       ret = (ret / ptn_alignment) * ptn_alignment;
+                       break;
+       }
+
+       return ret;
+}
+
+/* The alignment stuff is mostly copied from fdisk(8). */
+void
+set_ptn_alignment(struct disklabel *lp, char *alignstr)
+{
+       intmax_t i;
+
+       /* Default to using 'traditional' cylinder alignment */
+       ptn_alignment = lp->d_secpercyl;
+
+       if (alignstr != NULL) {
+               i = modstrtoint(lp, alignstr, 0);
+               if (i != -1)
+                       ptn_alignment = (unsigned int)i;
+               else
+                       errx(1, "Invalid alignment specifier.");
+       } else {
+               i = lp->d_partitions[0].p_offset + lp->d_partitions[0].p_size;
+               if (i != 0) {
+                       if (!(i & 2047)) {
+                               /* Should nearly always extend to 1MB */
+                               ptn_alignment = 2048;
+                       }
+               } else {
+                       /* Use 1MB alignment for large disks */
+                       if (lp->d_secperunit > 2048 * 1024 * 128)
+                               ptn_alignment = 2048;
+               }
+       }
+
+       return;
 }

--- sbin/disklabel/extern.h
+++ sbin/disklabel/extern.h
@@ -28,9 +28,17 @@
 int    checklabel(struct disklabel *);
 void   showinfo(FILE *, struct disklabel *, const char *);
 void   showpartitions(FILE *, struct disklabel *, int);
 void   showpartition(FILE *, struct disklabel *, int, int);
 void   interact(struct disklabel *, int);
+void   parse_partstr(struct disklabel *, char **);
+void   parse_labelstr(struct disklabel *, char **);
+void   batchedit(struct disklabel *, int);
+void   set_ptn_alignment(struct disklabel *, char *);
+int strtouint16(char *, uint16_t *);
+int strtouint32(char *, uint32_t *);
+intmax_t modstrtoint(struct disklabel *, char *, int);
 int    list_fs_types(void);
 
 extern char    specname[];
 extern int      Cflag;
+extern int     ptn_alignment;

#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: interact.c,v 1.35 2011/01/06 21:39:01 apb Exp $");
#endif /* lint */

#include <sys/param.h>
#define FSTYPENAMES
#define DKTYPENAMES

#include <err.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#if HAVE_NBTOOL_CONFIG_H
#define getmaxpartitions()      MAXPARTITIONS
#include <nbinclude/sys/disklabel.h>
#else
#include <util.h>
#include <sys/disklabel.h>
#endif /* HAVE_NBTOOL_CONFIG_H */

#include "extern.h"


static void change_type(struct disklabel *, char *);
static void change_typename(struct disklabel *, char *);
static void change_packname(struct disklabel *, char *);
static void change_npartitions(struct disklabel *, char *);
static void change_secsize(struct disklabel *, char *);
static void change_nsectors(struct disklabel *, char *);
static void change_ntracks(struct disklabel *, char *);
static void change_secpercyl(struct disklabel *, char *);
static void change_ncylinders(struct disklabel *, char *);
static void change_secperunit(struct disklabel *, char *);
static void change_rpm(struct disklabel *, char *);
static void change_interleave(struct disklabel *, char *);
static void change_trackskew(struct disklabel *, char *);
static void change_cylskew(struct disklabel *, char *);
static void change_headswitch(struct disklabel *, char *);
static void change_trseek (struct disklabel *, char *);

static struct changefunc {
        const char *name;
        void (*func)(struct disklabel *, char *);
} changefuncs[] = {
        { "type",       change_type },
        { "typename",   change_typename },
        { "packname",   change_packname },
        { "npartitions",        change_npartitions },
        { "secsize",    change_secsize },
        { "nsectors",   change_nsectors },
        { "ntracks",    change_ntracks },
        { "secpercyl",  change_secpercyl },
        { "ncylinders", change_ncylinders },
        { "secperunit", change_secperunit },
        { "rpm",        change_rpm },
        { "interleave", change_interleave },
        { "trackskew",  change_trackskew },
        { "cylskew",    change_cylskew },
        { "headswitch", change_headswitch },
        { "trkseek",    change_trseek },
        { NULL, NULL }
};


void
parse_partstr(struct disklabel *labelp, char **partstr)
{
        int i, done;
        intmax_t im;
        int part;
        char *parttok;
        char *tokparam; /* Intermediate values for strtok_r */
        struct partition *p, ps;

        if (*partstr == NULL)
                return;

        /* Partition number */
        parttok = strtok_r(*partstr, "/", &tokparam);
        if (parttok == NULL)
                errx(1, "Invalid partition string: Number not given");
        if (strlen(parttok) != 1)
                errx(1, "Invalid partition identifier %s", parttok);
        part = parttok[0] - 'a';
        p = &labelp->d_partitions[part];
        if (part >= labelp->d_npartitions)
                labelp->d_npartitions = part + 1;

        (void)memcpy(&ps, p, sizeof(ps));

        /* Partition start/offset */
        // XXX: TODO: How to determine the offset, and e.g. alignment? Just 
setting 
        // it to 0 is not enough, that wil make partitions really start at 0, 
even 
        // if they are in an mbr.
        parttok = strtok_r(NULL, "/", &tokparam);
        if (parttok == NULL)
                errx(1, "Invalid partition string: Offset/start not given");
        if (strlen(parttok) == 1 &&
                parttok[0] >= 'a' && parttok[0] < 'a' + getmaxpartitions()) {
                struct partition *cp = labelp->d_partitions;

                if ((cp[parttok[0] - 'a'].p_offset +
                    cp[parttok[0] - 'a'].p_size) >= labelp->d_secperunit)
                        errx(1, "Bad offset `%s'", parttok);
                else
                        p->p_offset = cp[parttok[0] - 'a'].p_offset +
                            cp[parttok[0] - 'a'].p_size;
        } else {
                im = modstrtoint(labelp, parttok, 1);
                if (im == -1)
                        errx(1, "Bad offset `%s'", parttok);
                else if (im > 0xffffffffLL ||
                           (uint32_t)im > labelp->d_secperunit)
                        errx(1, "Offset `%s' out of range", parttok);
                p->p_offset = (uint32_t)im;
        }

        /* Partition size */
        parttok = strtok_r(NULL, "/", &tokparam);
        if (parttok == NULL)
                errx(1, "Invalid partition string: Size not given");
        if ((im = modstrtoint(labelp, parttok, 2)) == -1)
                errx(1, "Bad size `%s'", parttok);
        else if (im > 0xffffffffLL ||
                   (im + p->p_offset) > labelp->d_secperunit)
                errx(1, "Size `%s' out of range", parttok);
        p->p_size = im;

        /* Partition type */
        parttok = strtok_r(NULL, "/", &tokparam);
        if (parttok == NULL)
                errx(1, "Invalid partition string: Type not given");
        done = 0;
        for (i = 0; i < FSMAXTYPES; i++)
                if (!strcasecmp(parttok, fstypenames[i])) {
                        p->p_fstype = i;
                        done = 1;
                }
        if (!done)
                errx(1, "Invalid partition type: %s", parttok);

        /* Clear out partitions that are in our way */
        done = 0;
        for (i = 0; i < labelp->d_npartitions; i++)
                if (((labelp->d_partitions[i].p_offset > p->p_offset &&
                                labelp->d_partitions[i].p_offset
                                < p->p_offset + p->p_size)
                        || (labelp->d_partitions[i].p_offset
                                + labelp->d_partitions[i].p_size
                                > p->p_offset
                                && labelp->d_partitions[i].p_offset
                                + labelp->d_partitions[i].p_size
                                < p->p_offset + p->p_size)
                        || (labelp->d_partitions[i].p_offset < p->p_offset
                                && labelp->d_partitions[i].p_offset
                                + labelp->d_partitions[i].p_size
                                > p->p_offset + p->p_size))
                        && labelp->d_partitions[i].p_fstype != FS_UNUSED) {
                        labelp->d_partitions[i].p_offset = 0;
                        labelp->d_partitions[i].p_size = 0;
                        if (labelp->d_npartitions == i - 1)
                                done = 1;
                }

        /* Decrease npartitions appropriately */
        if (done) {
                for (i = 0; i < labelp->d_npartitions; i++) {
                        if (labelp->d_partitions[i].p_size == 0
                                        && labelp->d_partitions[i].p_offset == 
0)
                                im = i;
                }
                labelp->d_npartitions = (uint16_t)im + 1;
        }

        free(*partstr);
        return;
}

void
parse_labelstr(struct disklabel *labelp, char **labelstr)
{
        int funcdone;
        char *labeltok;
        char *paramtok, *valuetok;
        char *tokparam, *tokparam2; /* Intermediate values for strtok_r */
        struct changefunc *tmpcfunc;

        if (*labelstr == NULL)
                return;

        for (labeltok = strtok_r(*labelstr, ",", &tokparam); labeltok != NULL;
                        labeltok = strtok_r(NULL, ",", &tokparam)) {
                funcdone = 0;

                paramtok = strtok_r(labeltok, "=", &tokparam2);
                if (paramtok == NULL)
                        errx(1, "Invalid parameter to -p");

                valuetok = strtok_r(labeltok, "=", &tokparam2);
                if (valuetok == NULL)
                        errx(1, "Invalid parameter to -p: %s has no value", 
paramtok);

                if (strtok_r(labeltok, "=", &tokparam2) != NULL)
                        errx(1, "Too many equality signs for flag -p: %s", 
paramtok);

                for (tmpcfunc = changefuncs; tmpcfunc->name != NULL; 
*tmpcfunc++) {
                        if (!strcmp(tmpcfunc->name, paramtok)) {
                                funcdone = 1;
                                (*tmpcfunc->func)(labelp, valuetok);
                                break;
                        }
                }
                if (funcdone == 0)
                        errx(1, "Invalid parameter to -p: %s", paramtok);
        }
        return;
}

void
batchedit(struct disklabel *labelp, int fd)
{
        if (checklabel(labelp) != 0) {
                errx(1, "Internal failure: Invalid label");
        }

        if (writelabel(fd, labelp) != 0) {
                errx(1, "Label not written");
        }

        printf("Label written\n");

        return;
}

static void
change_type(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        i = labelp->d_type;
        if (i < 0 || i >= DKMAXTYPES)
                i = 0;
        for (i = 0; i < DKMAXTYPES; i++)
                if (!strcasecmp(dktypenames[i], labelstr)) {
                        labelp->d_type = i;
                        return;
                }

        if (strtouint16(labelstr, &i) || i >= DKMAXTYPES) {
                errx(1, "Unknown disk type: %s", labelstr);
        }
        labelp->d_type = i;
}

static void
change_typename(struct disklabel *labelp, char *labelstr)
{
        if (strlen(labelstr) > sizeof(labelp->d_typename))
                warn("Label typename %s too long, max. length: %d", labelstr,
                                sizeof(labelp->d_typename));
        (void) strncpy(labelp->d_typename, labelstr, 
sizeof(labelp->d_typename));
}

static void
change_packname(struct disklabel *labelp, char *labelstr)
{
        if (strlen(labelstr) > sizeof(labelp->d_packname))
                warn("Label packname %s too long, max. length: %d", labelstr,
                                sizeof(labelp->d_packname));
        (void) strncpy(labelp->d_packname, labelstr, 
sizeof(labelp->d_packname));
}

static void
change_npartitions(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        if (strtouint16(labelstr, &i))
                errx(1, "Invalid npartitions: %s", labelstr);
        labelp->d_npartitions = i;
}

static void
change_secsize(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid secsize: %s", labelstr);
        labelp->d_secsize = i;
}

static void
change_nsectors(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid nsectors: %s", labelstr);
        labelp->d_secsize = i;
}

static void
change_ntracks(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid ntracks: %s", labelstr);
        labelp->d_ntracks = i;
}

static void
change_secpercyl(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid secpercyl: %s", labelstr);
        labelp->d_secpercyl = i;
}

static void
change_ncylinders(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid ncylinders: %s", labelstr);
        labelp->d_ncylinders = i;
}

static void
change_secperunit(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid secperunit: %s", labelstr);
        labelp->d_secperunit = i;
}

static void
change_rpm(struct disklabel *labelp, char *labelstr)
{
        // XXX: What is up with rpm? Why wasn't it set from disklabel -i before?
}

static void
change_interleave(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        if (strtouint16(labelstr, &i))
                errx(1, "Invalid interleave: %s", labelstr);
        labelp->d_interleave = i;
}

static void
change_trackskew(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        if (strtouint16(labelstr, &i))
                errx(1, "Invalid trackskew: %s", labelstr);
        labelp->d_trackskew = i;
}

static void
change_cylskew(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        if (strtouint16(labelstr, &i))
                errx(1, "Invalid cylskew: %s", labelstr);
        labelp->d_cylskew = i;
}

static void
change_headswitch(struct disklabel *labelp, char *labelstr)
{
        uint16_t i;

        if (strtouint16(labelstr, &i))
                errx(1, "Invalid headswitch: %s", labelstr);
        labelp->d_headswitch = i;
}

static void
change_trseek(struct disklabel *labelp, char *labelstr)
{
        uint32_t i;

        if (strtouint32(labelstr, &i))
                errx(1, "Invalid trkseek: %s", labelstr);
        labelp->d_trkseek = i;
}

Attachment: signature.asc
Description: PGP signature



Home | Main Index | Thread Index | Old Index