The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_disk_mbr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: subr_disk_mbr.c,v 1.57 2021/05/17 08:50:36 mrg Exp $   */
    2 
    3 /*
    4  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. Neither the name of the University nor the names of its contributors
   16  *    may be used to endorse or promote products derived from this software
   17  *    without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  *
   31  *      @(#)ufs_disksubr.c      7.16 (Berkeley) 5/4/91
   32  */
   33 
   34 /*
   35  * Code to find a NetBSD label on a disk that contains an i386 style MBR.
   36  * The first NetBSD label found in the 2nd sector of a NetBSD partition
   37  * is used.
   38  * If we don't find a label searching the MBR, we look at the start of the
   39  * disk, if that fails then a label is faked up from the MBR.
   40  *
   41  * If there isn't a disklabel or anything in the MBR then the disc is searched
   42  * for ecma-167/iso9660/udf style partition indicators.
   43  * Useful for media or files that contain single filesystems (etc).
   44  *
   45  * This code will read host endian netbsd labels from little endian MBR.
   46  *
   47  * Based on the i386 disksubr.c
   48  *
   49  * Since the mbr only has 32bit fields for sector addresses, we do the same.
   50  *
   51  * XXX There are potential problems writing labels to disks where there
   52  * is only space for 8 netbsd partitions but this code has been compiled
   53  * with MAXPARTITIONS=16.
   54  */
   55 
   56 #include <sys/cdefs.h>
   57 __KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.57 2021/05/17 08:50:36 mrg Exp $");
   58 
   59 #ifdef _KERNEL_OPT
   60 #include "opt_mbr.h"
   61 #include "opt_disklabel.h"
   62 #endif /* _KERNEL_OPT */
   63 
   64 #include <sys/param.h>
   65 #include <sys/systm.h>
   66 #include <sys/buf.h>
   67 #include <sys/bootblock.h>
   68 #include <sys/disklabel.h>
   69 #include <sys/disk.h>
   70 #include <sys/syslog.h>
   71 #include <sys/vnode.h>
   72 #include <sys/fcntl.h>
   73 #include <sys/conf.h>
   74 #include <sys/cdio.h>
   75 #include <sys/dkbad.h>
   76 #include <fs/udf/ecma167-udf.h>
   77 
   78 #include <sys/kauth.h>
   79 
   80 typedef struct mbr_partition mbr_partition_t;
   81 
   82 /*
   83  * We allocate a buffer 3 sectors large, and look in all....
   84  * That means we find labels written by other ports with different offsets.
   85  * LABELSECTOR and LABELOFFSET are only used if the disk doesn't have a label.
   86  */
   87 #define SCANBLOCKS 3
   88 #define DISKLABEL_SIZE 404
   89 #if LABELSECTOR*DEV_BSIZE + LABELOFFSET > SCANBLOCKS*DEV_BSIZE - DISKLABEL_SIZE
   90 #if _MACHINE != ews4800mips /* XXX: fail silently, ews4800mips LABELSECTOR */
   91 #error Invalid LABELSECTOR or LABELOFFSET
   92 #endif
   93 #endif
   94 
   95 #define MBR_LABELSECTOR 1
   96 
   97 #define SCAN_CONTINUE   0
   98 #define SCAN_FOUND      1
   99 #define SCAN_ERROR      2
  100 
  101 typedef struct mbr_args {
  102         struct disklabel *lp;
  103         void            (*strat)(struct buf *);
  104         struct buf      *bp;
  105         const char      *msg;
  106         int             error;
  107         int             written;        /* number of times we wrote label */
  108         int             found_mbr;      /* set if disk has a valid mbr */
  109         uint            label_sector;   /* where we found the label */
  110         int             action;
  111         uint32_t        secperunit;
  112 #define READ_LABEL      1
  113 #define UPDATE_LABEL    2
  114 #define WRITE_LABEL     3
  115 } mbr_args_t;
  116 
  117 static int validate_label(mbr_args_t *, uint);
  118 static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint);
  119 static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint);
  120 
  121 static int
  122 read_sector(mbr_args_t *a, uint sector, int count)
  123 {
  124         int error;
  125 
  126         error = disk_read_sectors(a->strat, a->lp, a->bp, sector, count);
  127         if (error != 0)
  128                 a->error = error;
  129         return error;
  130 }
  131 
  132 /*
  133  * Scan MBR for partitions, call 'action' routine for each.
  134  */
  135 
  136 static int
  137 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint))
  138 {
  139         mbr_partition_t ptns[MBR_PART_COUNT];
  140         mbr_partition_t *dp;
  141         struct mbr_sector *mbr;
  142         uint ext_base, this_ext, next_ext;
  143         int rval;
  144         int i;
  145         int j;
  146 #ifdef COMPAT_386BSD_MBRPART
  147         int dp_386bsd = -1;
  148         int ap_386bsd = -1;
  149 #endif
  150 
  151         ext_base = 0;
  152         this_ext = 0;
  153         for (;;) {
  154                 if (read_sector(a, this_ext, 1)) {
  155                         a->msg = "dos partition I/O error";
  156                         return SCAN_ERROR;
  157                 }
  158 
  159                 /* Note: Magic number is little-endian. */
  160                 mbr = (void *)a->bp->b_data;
  161                 if (mbr->mbr_magic != htole16(MBR_MAGIC))
  162                         return SCAN_CONTINUE;
  163 
  164                 /*
  165                  * If this is a protective MBR, bail now.
  166                  */
  167                 if (mbr->mbr_parts[0].mbrp_type == MBR_PTYPE_PMBR
  168                     && mbr->mbr_parts[1].mbrp_type == MBR_PTYPE_UNUSED
  169                     && mbr->mbr_parts[2].mbrp_type == MBR_PTYPE_UNUSED
  170                     && mbr->mbr_parts[3].mbrp_type == MBR_PTYPE_UNUSED)
  171                         return SCAN_CONTINUE;
  172 
  173                 /* Copy data out of buffer so action can use bp */
  174                 memcpy(ptns, &mbr->mbr_parts, sizeof ptns);
  175 
  176                 /* Look for drivers and skip them */
  177                 if (ext_base == 0 && ptns[0].mbrp_type == MBR_PTYPE_DM6_DDO) {
  178                         /* We've found a DM6 DDO partition type (used by
  179                          * the Ontrack Disk Manager drivers).
  180                          *
  181                          * Ensure that there are no other partitions in the
  182                          * MBR and jump to the real partition table (stored
  183                          * in the first sector of the second track). */
  184                         bool ok = true;
  185 
  186                         for (i = 1; i < MBR_PART_COUNT; i++)
  187                                 if (ptns[i].mbrp_type != MBR_PTYPE_UNUSED)
  188                                         ok = false;
  189 
  190                         if (ok) {
  191                                 this_ext = le32toh(a->lp->d_secpercyl /
  192                                     a->lp->d_ntracks);
  193                                 continue;
  194                         }
  195                 }
  196 
  197                 /* look for NetBSD partition */
  198                 next_ext = 0;
  199                 dp = ptns;
  200                 j = 0;
  201                 for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
  202                         if (dp->mbrp_type == MBR_PTYPE_UNUSED)
  203                                 continue;
  204                         /* Check end of partition is inside disk limits */
  205                         if ((uint64_t)ext_base + le32toh(dp->mbrp_start) +
  206                             le32toh(dp->mbrp_size) > a->lp->d_secperunit) {
  207                                 /* This mbr doesn't look good.... */
  208                                 a->msg = "mbr partition exceeds disk size";
  209                                 /* ...but don't report this as an error (yet) */
  210                                 return SCAN_CONTINUE;
  211                         }
  212                         a->found_mbr = 1;
  213                         if (MBR_IS_EXTENDED(dp->mbrp_type)) {
  214                                 next_ext = le32toh(dp->mbrp_start);
  215                                 continue;
  216                         }
  217 #ifdef COMPAT_386BSD_MBRPART
  218                         if (dp->mbrp_type == MBR_PTYPE_386BSD) {
  219                                 /*
  220                                  * If more than one matches, take last,
  221                                  * as NetBSD install tool does.
  222                                  */
  223                                 if (this_ext == 0) {
  224                                         dp_386bsd = i;
  225                                         ap_386bsd = j;
  226                                 }
  227                                 continue;
  228                         }
  229 #endif
  230                         rval = (*actn)(a, dp, j, this_ext);
  231                         if (rval != SCAN_CONTINUE)
  232                                 return rval;
  233                         j++;
  234                 }
  235                 if (next_ext == 0)
  236                         break;
  237                 if (ext_base == 0) {
  238                         ext_base = next_ext;
  239                         next_ext = 0;
  240                 }
  241                 next_ext += ext_base;
  242                 if (next_ext <= this_ext)
  243                         break;
  244                 this_ext = next_ext;
  245         }
  246 #ifdef COMPAT_386BSD_MBRPART
  247         if (this_ext == 0 && dp_386bsd != -1)
  248                 return (*actn)(a, &ptns[dp_386bsd], ap_386bsd, 0);
  249 #endif
  250         return SCAN_CONTINUE;
  251 }
  252 
  253 
  254 static void
  255 scan_iso_vrs_session(mbr_args_t *a, uint32_t first_sector,
  256         int *is_iso9660, int *is_udf)
  257 {
  258         struct vrs_desc *vrsd;
  259         uint64_t vrs;
  260         int sector_size;
  261         int blks, inc;
  262 
  263         sector_size = a->lp->d_secsize;
  264         blks = sector_size / DEV_BSIZE;
  265         inc  = MAX(1, 2048 / sector_size);
  266 
  267         /* by definition */
  268         vrs = ((32*1024 + sector_size - 1) / sector_size)
  269                 + first_sector;
  270 
  271         /* read first vrs sector */
  272         if (read_sector(a, vrs * blks, 1))
  273                 return;
  274 
  275         /* skip all CD001 records */
  276         vrsd = a->bp->b_data;
  277         /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
  278         while (memcmp(vrsd->identifier, "CD001", 5) == 0) {
  279                 /* for sure */
  280                 *is_iso9660 = first_sector;
  281 
  282                 vrs += inc;
  283                 if (read_sector(a, vrs * blks, 1))
  284                         return;
  285         }
  286 
  287         /* search for BEA01 */
  288         vrsd = a->bp->b_data;
  289         /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
  290         if (memcmp(vrsd->identifier, "BEA01", 5))
  291                 return;
  292 
  293         /* read successor */
  294         vrs += inc;
  295         if (read_sector(a, vrs * blks, 1))
  296                 return;
  297 
  298         /* check for NSR[23] */
  299         vrsd = a->bp->b_data;
  300         /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
  301         if (memcmp(vrsd->identifier, "NSR0", 4))
  302                 return;
  303 
  304         *is_udf = first_sector;
  305 }
  306 
  307 
  308 /*
  309  * Scan for ISO Volume Recognition Sequences
  310  */
  311 
  312 static int
  313 scan_iso_vrs(mbr_args_t *a)
  314 {
  315         struct mmc_discinfo  di;
  316         struct mmc_trackinfo ti;
  317         dev_t dev;
  318         uint64_t sector;
  319         int is_iso9660, is_udf;
  320         int tracknr, sessionnr;
  321         int new_session, error;
  322 
  323         is_iso9660 = is_udf = -1;
  324 
  325         /* parse all sessions of disc if we're on a SCSI MMC device */
  326         if (a->lp->d_flags & D_SCSI_MMC) {
  327                 /* get disc info */
  328                 dev = a->bp->b_dev;
  329                 error = bdev_ioctl(dev, MMCGETDISCINFO, &di, FKIOCTL, curlwp);
  330                 if (error)
  331                         return SCAN_CONTINUE;
  332 
  333                 /* go trough all (data) tracks */
  334                 sessionnr = -1;
  335                 for (tracknr = di.first_track;
  336                     tracknr <= di.first_track_last_session; tracknr++)
  337                 {
  338                         ti.tracknr = tracknr;
  339                         error = bdev_ioctl(dev, MMCGETTRACKINFO, &ti,
  340                                         FKIOCTL, curlwp);
  341                         if (error)
  342                                 return SCAN_CONTINUE;
  343                         new_session = (ti.sessionnr != sessionnr);
  344                         sessionnr = ti.sessionnr;
  345                         if (new_session) {
  346                                 if (ti.flags & MMC_TRACKINFO_BLANK)
  347                                         continue;
  348                                 if (!(ti.flags & MMC_TRACKINFO_DATA))
  349                                         continue;
  350                                 sector = ti.track_start;
  351                                 scan_iso_vrs_session(a, sector,
  352                                         &is_iso9660, &is_udf);
  353                         }
  354                 }
  355         } else {
  356                 /* try start of disc */
  357                 sector = 0;
  358                 scan_iso_vrs_session(a, sector, &is_iso9660, &is_udf);
  359         }
  360 
  361         if ((is_iso9660 < 0) && (is_udf < 0))
  362                 return SCAN_CONTINUE;
  363 
  364         strncpy(a->lp->d_typename, "iso partition", 16);
  365 
  366         /* adjust session information for iso9660 partition */
  367         if (is_iso9660 >= 0) {
  368                 /* set 'a' partition to iso9660 */
  369                 a->lp->d_partitions[0].p_offset = 0;
  370                 a->lp->d_partitions[0].p_size   = a->lp->d_secperunit;
  371                 a->lp->d_partitions[0].p_cdsession = is_iso9660;
  372                 a->lp->d_partitions[0].p_fstype = FS_ISO9660;
  373         }
  374 
  375         /* UDF doesn't care about the cd session specified here */
  376 
  377         return SCAN_FOUND;
  378 }
  379 
  380 
  381 /*
  382  * Attempt to read a disk label from a device
  383  * using the indicated strategy routine.
  384  * The label must be partly set up before this:
  385  * secpercyl, secsize and anything required for a block i/o read
  386  * operation in the driver's strategy/start routines
  387  * must be filled in before calling us.
  388  *
  389  * If dos partition table requested, attempt to load it and
  390  * find disklabel inside a DOS partition. Also, if bad block
  391  * table needed, attempt to extract it as well. Return buffer
  392  * for use in signalling errors if requested.
  393  *
  394  * Returns null on success and an error string on failure.
  395  */
  396 const char *
  397 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
  398     struct cpu_disklabel *osdep)
  399 {
  400         int rval;
  401         int i;
  402         mbr_args_t a;
  403 
  404         memset(&a, 0, sizeof a);
  405         a.lp = lp;
  406         a.strat = strat;
  407         a.action = READ_LABEL;
  408 
  409         /* minimal requirements for architypal disk label */
  410         if (lp->d_secsize == 0)
  411                 lp->d_secsize = DEV_BSIZE;
  412         if (lp->d_secperunit == 0)
  413                 lp->d_secperunit = 0x1fffffff;
  414         a.secperunit = lp->d_secperunit;
  415         lp->d_npartitions = RAW_PART + 1;
  416         for (i = 0; i < RAW_PART; i++) {
  417                 lp->d_partitions[i].p_size = 0;
  418                 lp->d_partitions[i].p_offset = 0;
  419         }
  420         if (lp->d_partitions[RAW_PART].p_size == 0)
  421                 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
  422         lp->d_partitions[RAW_PART].p_offset = 0;
  423 
  424         /*
  425          * Set partition 'a' to be the whole disk.
  426          * Cleared if we find an mbr or a netbsd label.
  427          */
  428         lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
  429         lp->d_partitions[0].p_fstype = FS_BSDFFS;
  430 
  431         /*
  432          * Get a buffer big enough to read a disklabel in and initialize it
  433          * make it three sectors long for the validate_label(); see comment at
  434          * start of file.
  435          */
  436         a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
  437         a.bp->b_dev = dev;
  438 
  439         if (osdep)
  440                 /*
  441                  * Scan mbr searching for netbsd partition and saving
  442                  * bios partition information to use if the netbsd one
  443                  * is absent.
  444                  */
  445                 rval = scan_mbr(&a, look_netbsd_part);
  446         else
  447                 rval = SCAN_CONTINUE;
  448 
  449         if (rval == SCAN_CONTINUE) {
  450                 /* Look at start of disk */
  451                 rval = validate_label(&a, 0);
  452         }
  453 
  454         if (rval == SCAN_CONTINUE) {
  455                 rval = scan_iso_vrs(&a);
  456         }
  457 #if 0
  458         /*
  459          * Save sector where we found the label for the 'don't overwrite
  460          * the label' check in bounds_check_with_label.
  461          */
  462         if (rval == SCAN_FOUND)
  463                 xxx->label_sector = a.label_sector;
  464 #endif
  465 
  466         /* Obtain bad sector table if requested and present */
  467 #ifdef __HAVE_DISKLABEL_DKBAD
  468         if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) {
  469                 struct dkbad *bdp, *db;
  470                 int blkno;
  471 
  472                 bdp = &osdep->bad;
  473                 i = 0;
  474                 rval = SCAN_ERROR;
  475                 do {
  476                         /* read a bad sector table */
  477                         blkno = lp->d_secperunit - lp->d_nsectors + i;
  478                         if (lp->d_secsize > DEV_BSIZE)
  479                                 blkno *= lp->d_secsize / DEV_BSIZE;
  480                         else
  481                                 blkno /= DEV_BSIZE / lp->d_secsize;
  482                         /* if successful, validate, otherwise try another */
  483                         if (read_sector(&a, blkno, 1)) {
  484                                 a.msg = "bad sector table I/O error";
  485                                 continue;
  486                         }
  487                         db = (struct dkbad *)(a.bp->b_data);
  488 #define DKBAD_MAGIC 0x4321
  489                         if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) {
  490                                 a.msg = "bad sector table corrupted";
  491                                 continue;
  492                         }
  493                         rval = SCAN_FOUND;
  494                         *bdp = *db;
  495                         break;
  496                 } while (a.bp->b_error && (i += 2) < 10 &&
  497                         i < lp->d_nsectors);
  498         }
  499 #endif /* __HAVE_DISKLABEL_DKBAD */
  500 
  501         brelse(a.bp, 0);
  502         if (rval == SCAN_ERROR || rval == SCAN_CONTINUE)
  503                 return a.msg;
  504         return NULL;
  505 }
  506 
  507 static int
  508 look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
  509 {
  510         struct partition *pp;
  511         int ptn_base = ext_base + le32toh(dp->mbrp_start);
  512         int rval;
  513 
  514         if (
  515 #ifdef COMPAT_386BSD_MBRPART
  516             dp->mbrp_type == MBR_PTYPE_386BSD ||
  517 #endif
  518             dp->mbrp_type == MBR_PTYPE_NETBSD) {
  519                 rval = validate_label(a, ptn_base);
  520 
  521 #if RAW_PART == 3
  522                 /* Put actual location where we found the label into ptn 2 */
  523                 if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) {
  524                         a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size);
  525                         a->lp->d_partitions[2].p_offset = ptn_base;
  526                 }
  527 #endif
  528 
  529                 /* If we got a netbsd label look no further */
  530                 if (rval == SCAN_FOUND)
  531                         return rval;
  532         }
  533 
  534         /* Install main partitions into e..h and extended into i+ */
  535         if (ext_base == 0)
  536                 slot += 4;
  537         else {
  538                 slot = 4 + MBR_PART_COUNT;
  539                 pp = &a->lp->d_partitions[slot];
  540                 for (; slot < MAXPARTITIONS; pp++, slot++) {
  541                         /* This gets called twice - avoid duplicates */
  542                         if (pp->p_offset == ptn_base &&
  543                             pp->p_size == le32toh(dp->mbrp_size))
  544                                 break;
  545                         if (pp->p_size == 0)
  546                                 break;
  547                 }
  548         }
  549 
  550         if (slot < MAXPARTITIONS) {
  551                 /* Stop 'a' being the entire disk */
  552                 a->lp->d_partitions[0].p_size = 0;
  553                 a->lp->d_partitions[0].p_fstype = 0;
  554 
  555                 /* save partition info */
  556                 pp = &a->lp->d_partitions[slot];
  557                 pp->p_offset = ptn_base;
  558                 pp->p_size = le32toh(dp->mbrp_size);
  559                 pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type);
  560 
  561                 if (slot >= a->lp->d_npartitions)
  562                         a->lp->d_npartitions = slot + 1;
  563         }
  564 
  565         return SCAN_CONTINUE;
  566 }
  567 
  568 __noubsan
  569 static bool
  570 check_label_magic(const struct disklabel *dlp, uint32_t diskmagic)
  571 {
  572         return memcmp(&dlp->d_magic, &diskmagic, sizeof(diskmagic)) == 0 &&
  573             memcmp(&dlp->d_magic2, &diskmagic, sizeof(diskmagic)) == 0;
  574 }
  575 
  576 #ifdef DISKLABEL_EI
  577 /*
  578  * - For read, convert a label to the native byte order.
  579  * - For update or write, if a label already exists, keep its byte order.
  580  *   Otherwise, write a new label in the native byte order.
  581  */
  582 #endif
  583 static int
  584 validate_label(mbr_args_t *a, uint label_sector)
  585 {
  586         struct disklabel *dlp;
  587         char *dlp_lim, *dlp_byte;
  588         int error;
  589 #ifdef DISKLABEL_EI
  590         int swapped = 0;
  591         uint16_t npartitions;
  592 #endif
  593 
  594         /* Next, dig out disk label */
  595         if (read_sector(a, label_sector, SCANBLOCKS)) {
  596                 a->msg = "disk label read failed";
  597                 return SCAN_ERROR;
  598         }
  599 
  600         /* Locate disk label within block and validate */
  601         /*
  602          * XXX (dsl) This search may be a waste of time, a lot of other i386
  603          * code assumes the label is at offset LABELOFFSET (=0) in the sector.
  604          *
  605          * If we want to support disks from other netbsd ports, then the
  606          * code should also allow for a shorter label nearer the end of
  607          * the disk sector, and (IIRC) labels within 8k of the disk start.
  608          */
  609         dlp = (void *)a->bp->b_data;
  610         dlp_lim = (char *)a->bp->b_data + a->bp->b_bcount - sizeof *dlp;
  611         for (;; dlp = (void *)((char *)dlp + sizeof(uint32_t))) {
  612                 if ((char *)dlp > dlp_lim) {
  613                         if (a->action != WRITE_LABEL)
  614                                 return SCAN_CONTINUE;
  615                         /* Write at arch. dependent default location */
  616                         dlp_byte = (char *)a->bp->b_data + LABELOFFSET;
  617                         if (label_sector)
  618                                 dlp_byte += MBR_LABELSECTOR * a->lp->d_secsize;
  619                         else
  620                                 dlp_byte += LABELSECTOR * a->lp->d_secsize;
  621                         dlp = (void *)dlp_byte;
  622                         break;
  623                 }
  624                 if (!check_label_magic(dlp, DISKMAGIC))
  625 #ifdef DISKLABEL_EI
  626                 {
  627                         if (!check_label_magic(dlp, bswap32(DISKMAGIC)))
  628                                 continue;
  629 
  630                         /*
  631                          * The label is in the other byte order. We need to
  632                          * checksum before swapping the byte order.
  633                          */
  634                         npartitions = bswap16(dlp->d_npartitions);
  635                         if (npartitions > MAXPARTITIONS ||
  636                             dkcksum_sized(dlp, npartitions) != 0)
  637                                 goto corrupted;
  638 
  639                         swapped = 1;
  640                 }
  641 #else
  642                         continue;
  643 #endif
  644                 else if (dlp->d_npartitions > MAXPARTITIONS ||
  645                          dkcksum(dlp) != 0) {
  646 #ifdef DISKLABEL_EI
  647 corrupted:
  648 #endif
  649                         a->msg = "disk label corrupted";
  650                         continue;
  651                 }
  652                 break;
  653         }
  654 
  655         switch (a->action) {
  656         case READ_LABEL:
  657 #ifdef DISKLABEL_EI
  658                 if (swapped)
  659                         disklabel_swap(a->lp, dlp);
  660                 else
  661                         *a->lp = *dlp;
  662 #else
  663                 *a->lp = *dlp;
  664 #endif
  665                 if ((a->msg = convertdisklabel(a->lp, a->strat, a->bp,
  666                                               a->secperunit)) != NULL)
  667                         return SCAN_ERROR;
  668                 a->label_sector = label_sector;
  669                 return SCAN_FOUND;
  670         case UPDATE_LABEL:
  671         case WRITE_LABEL:
  672 #ifdef DISKLABEL_EI
  673                 /* DO NOT swap a->lp itself for later references. */
  674                 if (swapped)
  675                         disklabel_swap(dlp, a->lp);
  676                 else
  677                         *dlp = *a->lp;
  678 #else
  679                 *dlp = *a->lp;
  680 #endif
  681                 a->bp->b_oflags &= ~BO_DONE;
  682                 a->bp->b_flags &= ~B_READ;
  683                 a->bp->b_flags |= B_WRITE;
  684                 (*a->strat)(a->bp);
  685                 error = biowait(a->bp);
  686                 if (error != 0) {
  687                         a->error = error;
  688                         a->msg = "disk label write failed";
  689                         return SCAN_ERROR;
  690                 }
  691                 a->written++;
  692                 /* Write label to all mbr partitions */
  693                 return SCAN_CONTINUE;
  694         default:
  695                 return SCAN_ERROR;
  696         }
  697 }
  698 
  699 /*
  700  * Write disk label back to device after modification.
  701  */
  702 int
  703 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
  704     struct cpu_disklabel *osdep)
  705 {
  706         mbr_args_t a;
  707 
  708         memset(&a, 0, sizeof a);
  709         a.lp = lp;
  710         a.strat = strat;
  711 
  712         /* get a buffer and initialize it */
  713         a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
  714         a.bp->b_dev = dev;
  715 
  716         /* osdep => we expect an mbr with label in netbsd ptn */
  717         a.action = osdep != NULL ? WRITE_LABEL : UPDATE_LABEL;
  718 
  719         /* Write/update the label to every netbsd mbr partition */
  720         scan_mbr(&a, write_netbsd_label);
  721 
  722         /* Old write the label at the start of the volume on disks that
  723          * don't have a valid mbr (always update an existing one) */
  724         a.action = a.found_mbr ? UPDATE_LABEL : WRITE_LABEL;
  725         validate_label(&a, 0);
  726 
  727         if (a.written == 0 && a.error == 0)
  728                 a.error = ESRCH;
  729 
  730         brelse(a.bp, 0);
  731         return a.error;
  732 }
  733 
  734 static int
  735 write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
  736 {
  737         int ptn_base = ext_base + le32toh(dp->mbrp_start);
  738 
  739         if (dp->mbrp_type != MBR_PTYPE_NETBSD)
  740                 return SCAN_CONTINUE;
  741 
  742         return validate_label(a, ptn_base);
  743 }

Cache object: effb8677a4afd99ca1c9837a3b87dc3e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.