/* * Fetch disklabel for disk. * Use ioctl to get label unless -r flag is given. */ struct disklabel32 * readlabel(int f) { const char *msg; struct disklabel32 *lp; int r; if (rflag) { r = read(f, bootarea, BBSIZE); if (r < BBSIZE) err(4, "%s", specname); for (lp = (struct disklabel32 *)bootarea; lp <= (struct disklabel32 *)(bootarea + BBSIZE - sizeof(*lp)); lp = (struct disklabel32 *)((char *)lp + 16)) { if (lp->d_magic == DISKMAGIC32 && lp->d_magic2 == DISKMAGIC32) break; } if (lp > (struct disklabel32 *)(bootarea+BBSIZE-sizeof(*lp)) || lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32 || dkcksum32(lp) != 0) { errx(1, "bad pack magic number (label is damaged, " "or pack is unlabeled)"); } if ((msg = fixlabel(f, lp, 0)) != NULL) errx(1, msg); } else { lp = &lab; if (ioctl(f, DIOCGDINFO32, lp) < 0) { l_perror("ioctl DIOCGDINFO32"); exit(4); } } return (lp); }
/* * Create a disklabel based on a disk_info structure, initializing * the appropriate fields and creating a raw partition that covers the * whole disk. * * If a diskslice is passed, the label is truncated to the slice */ static disklabel_t l32_clone_label(struct disk_info *info, struct diskslice *sp) { struct disklabel32 *lp; disklabel_t res; lp = kmalloc(sizeof *lp, M_DEVBUF, M_WAITOK | M_ZERO); lp->d_nsectors = info->d_secpertrack; lp->d_ntracks = info->d_nheads; lp->d_secpercyl = info->d_secpercyl; lp->d_secsize = info->d_media_blksize; if (sp) lp->d_secperunit = (u_int)sp->ds_size; else lp->d_secperunit = (u_int)info->d_media_blocks; if (lp->d_typename[0] == '\0') strncpy(lp->d_typename, "amnesiac", sizeof(lp->d_typename)); if (lp->d_packname[0] == '\0') strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); if (lp->d_nsectors == 0) lp->d_nsectors = 32; if (lp->d_ntracks == 0) lp->d_ntracks = 64; lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; lp->d_ncylinders = lp->d_secperunit / lp->d_secpercyl; if (lp->d_rpm == 0) lp->d_rpm = 3600; if (lp->d_interleave == 0) lp->d_interleave = 1; if (lp->d_npartitions < RAW_PART + 1) lp->d_npartitions = MAXPARTITIONS32; if (lp->d_bbsize == 0) lp->d_bbsize = BBSIZE; if (lp->d_sbsize == 0) lp->d_sbsize = SBSIZE; /* * Used by various devices to create a compatibility slice which * allows us to mount root from devices which do not have a * disklabel. Particularly: CDs. */ lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; if (info->d_dsflags & DSO_COMPATPARTA) { lp->d_partitions[0].p_size = lp->d_secperunit; lp->d_partitions[0].p_fstype = FS_OTHER; } lp->d_magic = DISKMAGIC32; lp->d_magic2 = DISKMAGIC32; lp->d_checksum = dkcksum32(lp); res.lab32 = lp; return (res); }
/* * Attempt to read a disk label from a device. * * Returns NULL on sucess, and an error string on failure */ static const char * l32_readdisklabel(cdev_t dev, struct diskslice *sp, disklabel_t *lpp, struct disk_info *info) { disklabel_t lpx; struct buf *bp; struct disklabel32 *dlp; const char *msg = NULL; int secsize = info->d_media_blksize; bp = geteblk(secsize); bp->b_bio1.bio_offset = (off_t)LABELSECTOR32 * secsize; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = secsize; bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_READ; dev_dstrategy(dev, &bp->b_bio1); if (biowait(&bp->b_bio1, "labrd")) msg = "I/O error"; else for (dlp = (struct disklabel32 *)bp->b_data; dlp <= (struct disklabel32 *)((char *)bp->b_data + secsize - sizeof(*dlp)); dlp = (struct disklabel32 *)((char *)dlp + sizeof(long))) { if (dlp->d_magic != DISKMAGIC32 || dlp->d_magic2 != DISKMAGIC32) { /* * NOTE! dsreadandsetlabel() does a strcmp() on * this string. */ if (msg == NULL) msg = "no disk label"; } else if (dlp->d_npartitions > MAXPARTITIONS32 || dkcksum32(dlp) != 0) { msg = "disk label corrupted"; } else { lpx.lab32 = dlp; msg = l32_fixlabel(NULL, sp, lpx, FALSE); if (msg == NULL) { (*lpp).lab32 = kmalloc(sizeof(*dlp), M_DEVBUF, M_WAITOK|M_ZERO); *(*lpp).lab32 = *dlp; } break; } } bp->b_flags |= B_INVAL | B_AGE; brelse(bp); return (msg); }
int writelabel(int f, const char *boot, struct disklabel32 *lp) { const char *msg; int flag; int r; if (disable_write) { Warning("write to disk label suppressed - label was as follows:"); display(stdout, lp); return (0); } else { /* make sure we are not overwriting our boot code */ if (checkoldboot(f, boot)) errx(4, "Will not overwrite old bootblocks w/ label, install new boot blocks first!"); setbootflag(lp); lp->d_magic = DISKMAGIC32; lp->d_magic2 = DISKMAGIC32; lp->d_checksum = 0; lp->d_checksum = dkcksum32(lp); if (rflag) { /* * First set the kernel disk label, * then write a label to the raw disk. * If the SDINFO ioctl fails because it is unimplemented, * keep going; otherwise, the kernel consistency checks * may prevent us from changing the current (in-core) * label. */ if (ioctl(f, DIOCSDINFO32, lp) < 0 && errno != ENODEV && errno != ENOTTY) { l_perror("ioctl DIOCSDINFO32"); return (1); } lseek(f, (off_t)0, SEEK_SET); /* * write enable label sector before write * (if necessary), disable after writing. */ flag = 1; if (ioctl(f, DIOCWLABEL, &flag) < 0) warn("ioctl DIOCWLABEL"); msg = fixlabel(f, lp, 1); if (msg) { warn(msg); return (1); } r = write(f, boot, lp->d_bbsize); fixlabel(f, lp, 0); if (r != ((ssize_t)lp->d_bbsize)) { warn("write"); return (1); } #if NUMBOOT > 0 /* * Output the remainder of the disklabel */ if (bootbuf) { fixlabel(f, lp, 1); r = write(f, bootbuf, bootsize); fixlabel(f, lp, 0); if (r != bootsize) { warn("write"); return(1); } } #endif flag = 0; ioctl(f, DIOCWLABEL, &flag); } else if (ioctl(f, DIOCWDINFO32, lp) < 0) { l_perror("ioctl DIOCWDINFO32"); return (1); } } return (0); }
/* * Traditional 32 bit disklabels actually use absolute sector numbers on * disk, NOT slice relative sector numbres. The OS hides this from us * when we use DIOC ioctls to access the label, but newer versions of * Dragonfly no longer adjusts the disklabel when snooping reads or writes * so we have to figure it out ourselves. */ const char * fixlabel(int f, struct disklabel32 *lp, int writeadj) { const char *msg = NULL; struct partinfo info; struct partition32 *pp; u_int64_t start; u_int64_t end; u_int64_t offset; int part; int rev; size_t rev_len = sizeof(rev); if (sysctlbyname("kern.osrevision", &rev, &rev_len, NULL, 0) < 0) { errx(1, "Cannot use raw mode on non-DragonFly systems\n"); } if (rev < 200701) { warnx("Warning running new disklabel on old DragonFly systems,\n" "assuming the disk layer will fixup the label.\n"); sleep(3); return(NULL); } pp = &lp->d_partitions[RAW_PART]; if (forceflag) { info.media_offset = slice_start_lba * lp->d_secsize; info.media_blocks = pp->p_size; info.media_blksize = lp->d_secsize; } else if (ioctl(f, DIOCGPART, &info) < 0) { msg = "Unable to extract the slice starting LBA, " "you must use the -f <slice_start_lba> option\n" "to specify it manually, or perhaps try without " "using -r and let the kernel deal with it\n"; return(msg); } if (lp->d_magic != DISKMAGIC32 || lp->d_magic2 != DISKMAGIC32) return ("fixlabel: invalid magic"); if (dkcksum32(lp) != 0) return ("fixlabel: invalid checksum"); /* * What a mess. For ages old backwards compatibility the disklabel * on-disk stores absolute offsets instead of slice-relative offsets. * So fix it up when reading, writing, or snooping. * * The in-core label is always slice-relative. */ if (writeadj) { /* * incore -> disk */ start = 0; offset = info.media_offset / info.media_blksize; } else { /* * disk -> incore */ start = info.media_offset / info.media_blksize; offset = -info.media_offset / info.media_blksize; } if (pp->p_offset != start) return ("fixlabel: raw partition offset != slice offset"); if (pp->p_size != info.media_blocks) { if (pp->p_size > info.media_blocks) return ("fixlabel: raw partition size > slice size"); } end = start + info.media_blocks; if (start > end) return ("fixlabel: slice wraps"); if (lp->d_secpercyl <= 0) return ("fixlabel: d_secpercyl <= 0"); pp -= RAW_PART; for (part = 0; part < lp->d_npartitions; part++, pp++) { if (pp->p_offset != 0 || pp->p_size != 0) { if (pp->p_offset < start || pp->p_offset + pp->p_size > end || pp->p_offset + pp->p_size < pp->p_offset) { /* XXX else silently discard junk. */ bzero(pp, sizeof *pp); } else { pp->p_offset += offset; } } } lp->d_checksum = 0; lp->d_checksum = dkcksum32(lp); return (NULL); }
/* * Write disk label back to device after modification. */ static int l32_writedisklabel(cdev_t dev, struct diskslices *ssp, struct diskslice *sp, disklabel_t lpx) { struct disklabel32 *lp; struct disklabel32 *dlp; struct buf *bp; const char *msg; int error = 0; lp = lpx.lab32; if (lp->d_partitions[RAW_PART].p_offset != 0) return (EXDEV); /* not quite right */ bp = geteblk((int)lp->d_secsize); bp->b_bio1.bio_offset = (off_t)LABELSECTOR32 * lp->d_secsize; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; bp->b_bcount = lp->d_secsize; #if 1 /* * We read the label first to see if it's there, * in which case we will put ours at the same offset into the block.. * (I think this is stupid [Julian]) * Note that you can't write a label out over a corrupted label! * (also stupid.. how do you write the first one? by raw writes?) */ bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_READ; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labrd"); if (error) goto done; for (dlp = (struct disklabel32 *)bp->b_data; dlp <= (struct disklabel32 *) ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp)); dlp = (struct disklabel32 *)((char *)dlp + sizeof(long))) { if (dlp->d_magic == DISKMAGIC32 && dlp->d_magic2 == DISKMAGIC32 && dkcksum32(dlp) == 0) { *dlp = *lp; lpx.lab32 = dlp; msg = l32_fixlabel(NULL, sp, lpx, TRUE); if (msg) { error = EINVAL; } else { bp->b_cmd = BUF_CMD_WRITE; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; KKASSERT(dkpart(dev) == WHOLE_SLICE_PART); dev_dstrategy(dev, &bp->b_bio1); error = biowait(&bp->b_bio1, "labwr"); } goto done; } } error = ESRCH; done: #else bzero(bp->b_data, lp->d_secsize); dlp = (struct disklabel32 *)bp->b_data; *dlp = *lp; bp->b_flags &= ~B_INVAL; bp->b_cmd = BUF_CMD_WRITE; bp->b_bio1.bio_done = biodone_sync; bp->b_bio1.bio_flags |= BIO_SYNC; BUF_STRATEGY(bp, 1); error = biowait(&bp->b_bio1, "labwr"); #endif bp->b_flags |= B_INVAL | B_AGE; brelse(bp); return (error); }
/* * Check new disk label for sensibility before setting it. */ static int l32_setdisklabel(disklabel_t olpx, disklabel_t nlpx, struct diskslices *ssp, struct diskslice *sp, u_int32_t *openmask) { struct disklabel32 *olp, *nlp; struct partition32 *opp, *npp; int part; int i; olp = olpx.lab32; nlp = nlpx.lab32; /* * Check it is actually a disklabel we are looking at. */ if (nlp->d_magic != DISKMAGIC32 || nlp->d_magic2 != DISKMAGIC32 || dkcksum32(nlp) != 0) return (EINVAL); /* * For each partition that we think is open, check the new disklabel * for compatibility. Ignore special partitions (>= 128). */ i = 0; while (i < 128) { if (openmask[i >> 5] == 0) { i += 32; continue; } if ((openmask[i >> 5] & (1 << (i & 31))) == 0) { ++i; continue; } if (nlp->d_npartitions <= i) return (EBUSY); opp = &olp->d_partitions[i]; npp = &nlp->d_partitions[i]; if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) return (EBUSY); /* * Copy internally-set partition information * if new label doesn't include it. XXX * (If we are using it then we had better stay the same type) * This is possibly dubious, as someone else noted (XXX) */ if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { npp->p_fstype = opp->p_fstype; npp->p_fsize = opp->p_fsize; npp->p_frag = opp->p_frag; npp->p_cpg = opp->p_cpg; } ++i; } nlp->d_checksum = 0; nlp->d_checksum = dkcksum32(nlp); *olp = *nlp; if (olp->d_partitions[RAW_PART].p_offset) return (EXDEV); if (olp->d_secperunit > sp->ds_size) return (ENOSPC); for (part = 0; part < olp->d_npartitions; ++part) { if (olp->d_partitions[part].p_size > sp->ds_size) return(ENOSPC); } return (0); }