Skip to content

Instantly share code, notes, and snippets.

@tuxillo
Created December 15, 2014 23:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuxillo/6c704b1fc8d55df3b2d8 to your computer and use it in GitHub Desktop.
Save tuxillo/6c704b1fc8d55df3b2d8 to your computer and use it in GitHub Desktop.
diff --git a/sbin/disklabel64/disklabel64.c b/sbin/disklabel64/disklabel64.c
index afd77ec..05d4747 100644
--- a/sbin/disklabel64/disklabel64.c
+++ b/sbin/disklabel64/disklabel64.c
@@ -119,8 +119,11 @@ extern uint32_t crc32(const void *buf, size_t size);
#define BIG_NEWFS_FRAG 2048U
#define BIG_NEWFS_CPG 64U
+#define PARTOFF(p) (parts[p-1].dp_start * 512LU)
+
void makelabel(const char *, const char *, struct disklabel64 *);
int writelabel(int, struct disklabel64 *);
+static void readpartdata(int);
void l_perror(const char *);
struct disklabel64 *readlabel(int);
struct disklabel64 *makebootarea(int);
@@ -140,6 +143,7 @@ struct disklabel64 *getvirginlabel(void);
#define DEFEDITOR _PATH_VI
#define streq(a,b) (strcmp(a,b) == 0)
+int is_file;
char *dkname;
char *specname;
char tmpfil[] = PATH_TMPFILE;
@@ -151,7 +155,9 @@ struct disklabel64 lab;
char part_size_type[MAX_NUM_PARTS];
char part_offset_type[MAX_NUM_PARTS];
int part_set[MAX_NUM_PARTS];
+struct dos_partition parts[4];
+int partition;
int installboot; /* non-zero if we should install a boot program */
int boot1size;
int boot1lsize;
@@ -172,15 +178,16 @@ u_int32_t slice_start_lba;
#ifdef DEBUG
int debug;
-#define OPTIONS "BNRWb:denrs:Vw"
+#define OPTIONS "BNRWb:denrs:Vwp:"
#else
-#define OPTIONS "BNRWb:enrs:Vw"
+#define OPTIONS "BNRWb:enrs:Vwp:"
#endif
int
main(int argc, char *argv[])
{
struct disklabel64 *lp;
+ struct stat st;
FILE *t;
int ch, f = 0, flag, error = 0;
char *name = NULL;
@@ -231,6 +238,12 @@ main(int argc, char *argv[])
usage();
op = WRITE;
break;
+ case 'p':
+ partition = strtol(optarg, NULL, 10);
+ if (partition < 1 || partition > 4)
+ usage();
+ break;
+
#ifdef DEBUG
case 'd':
debug++;
@@ -255,12 +268,31 @@ main(int argc, char *argv[])
if (argc < 1)
usage();
- dkname = getdevpath(argv[0], 0);
- specname = dkname;
+ /*
+ * Check if we're passed a regular file. If so, set
+ * rflag as this is the only way to operate it, ioctl(2)
+ * won't work.
+ */
+ if ((stat(argv[0], &st) != -1)
+ && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
+ is_file = 1;
+ rflag++;
+ specname = argv[0];
+ } else {
+ dkname = getdevpath(argv[0], 0);
+ specname = dkname;
+ }
f = open(specname, op == READ ? O_RDONLY : O_RDWR);
if (f < 0)
err(4, "%s", specname);
+ /*
+ * Read in all DOS partitions, starting sector
+ * offsets will be needed later on.
+ */
+ if (is_file)
+ readpartdata(f);
+
switch(op) {
case UNSPEC:
@@ -275,7 +307,9 @@ main(int argc, char *argv[])
case NOWRITE:
flag = 0;
- if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
+ if (is_file)
+ errx(4, "can't disable label writing for files");
+ else if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
err(4, "ioctl DIOCWLABEL");
break;
@@ -329,7 +363,9 @@ main(int argc, char *argv[])
case WRITEABLE:
flag = 1;
- if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
+ if (is_file)
+ errx(4, "can't enable label writing for files");
+ else if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0)
err(4, "ioctl DIOCWLABEL");
break;
@@ -353,6 +389,26 @@ main(int argc, char *argv[])
}
/*
+ * Read dos partitions from file
+ */
+static
+void
+readpartdata(int f)
+{
+ char buf[512];
+ ssize_t bytes;
+
+ /*
+ * Read in DOS partitions to get the start offset
+ * for each one of them.
+ */
+ if ((bytes = read(f, buf, 512)) < 512)
+ errx(1, "Unexpected error when reading from file %s\n",
+ specname);
+ memcpy(parts, &buf[DOSPARTOFF], sizeof(parts));
+}
+
+/*
* Construct a prototype disklabel from /etc/disktab. As a side
* effect, set the names of the primary and secondary boot files
* if specified.
@@ -422,12 +478,19 @@ writelabel(int f, struct disklabel64 *lp)
* consistency checks may prevent us from changing
* the current (in-core) label.
*/
- if (ioctl(f, DIOCSDINFO64, lp) < 0 &&
- errno != ENODEV && errno != ENOTTY) {
- l_perror("ioctl DIOCSDINFO");
- return (1);
+ if (is_file) {
+ /* Seek file descriptor f to the desired offset */
+ if (lseek(f, PARTOFF(partition), SEEK_SET) < 0)
+ err(1, "lseek");
+ } else {
+ if (ioctl(f, DIOCSDINFO64, lp) < 0 &&
+ errno != ENODEV && errno != ENOTTY) {
+ l_perror("ioctl DIOCSDINFO");
+ return (1);
+ }
+ lseek(f, (off_t)0, SEEK_SET);
}
- lseek(f, (off_t)0, SEEK_SET);
+
/*
* The disklabel embeds areas which we may not
@@ -440,13 +503,13 @@ writelabel(int f, struct disklabel64 *lp)
sizeof(*lp) -
offsetof(struct disklabel64, d_magic));
}
-
+
/*
* write enable label sector before write
* (if necessary), disable after writing.
*/
flag = 1;
- if (ioctl(f, DIOCWLABEL, &flag) < 0)
+ if (!is_file && (ioctl(f, DIOCWLABEL, &flag) < 0))
warn("ioctl DIOCWLABEL");
r = write(f, boot1buf, boot1lsize);
@@ -465,8 +528,10 @@ writelabel(int f, struct disklabel64 *lp)
return(1);
}
}
- flag = 0;
- ioctl(f, DIOCWLABEL, &flag);
+ if (!is_file) {
+ flag = 0;
+ ioctl(f, DIOCWLABEL, &flag);
+ }
} else if (ioctl(f, DIOCWDINFO64, lp) < 0) {
l_perror("ioctl DIOCWDINFO64");
return (1);
@@ -579,6 +644,12 @@ makebootarea(int f)
else
secsize = 512;
+ if (is_file) {
+ /* Seek file descriptor f to the desired offset */
+ if (lseek(f, PARTOFF(partition), SEEK_SET) < 0)
+ err(1, "lseek");
+ }
+
if (boot1buf == NULL) {
size_t rsize;
@@ -1470,6 +1541,47 @@ checklabel(struct disklabel64 *lp)
}
/*
+ * Create a dummy disklabel for files
+ */
+static
+void
+getvirginlabel_file(struct disklabel64 *lp)
+{
+ struct stat st;
+ uint32_t blksize = 4096; /* XXX - hardcoding defaults uh? */
+ uint32_t ressize;
+ uint64_t blkmask = blksize - 1;
+ size_t lpcrcsize;
+
+ if (lp == NULL)
+ return;
+
+ bzero(lp, sizeof(*lp));
+ if (stat(specname, &st) < 0)
+ err(1, "Failed to stat file");
+ lp->d_total_size = st.st_size;
+ lp->d_magic = DISKMAGIC64;
+ lp->d_align = blksize;
+ lp->d_npartitions = MAXPARTITIONS64;
+ uuidgen(&lp->d_stor_uuid, 1);
+
+ ressize = offsetof(struct disklabel64, d_partitions[RESPARTITIONS64]);
+ ressize = (ressize + (uint32_t)blkmask) & ~blkmask;
+
+ lp->d_bbase = ressize;
+// lp->d_pbase = lp->d_bbase + ((32768 + blkmask) & ~blkmask);
+// lp->d_pbase = (lp->d_pbase + PALIGN_MASK) & ~(uint64_t)PALIGN_MASK;
+
+ lp->d_pbase += 32768 - (PARTOFF(partition)) % 32768;
+ lp->d_pstop = (lp->d_total_size - lp->d_bbase) & ~blkmask;
+ lp->d_abase = lp->d_pstop;
+ lpcrcsize = offsetof(struct disklabel64,
+ d_partitions[lp->d_npartitions]) -
+ offsetof(struct disklabel64, d_magic);
+ lp->d_crc = crc32(&lp->d_magic, lpcrcsize);
+}
+
+/*
* When operating on a "virgin" disk, try getting an initial label
* from the associated device driver. This might work for all device
* drivers that are able to fetch some initial device parameters
@@ -1486,6 +1598,11 @@ getvirginlabel(void)
struct disklabel64 *dl = &dlab;
int f;
+ if (is_file) {
+ getvirginlabel_file(dl);
+ return (dl);
+ }
+
if ((f = open(dkname, O_RDONLY)) == -1) {
warn("cannot open %s", dkname);
return (NULL);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment