/* * Disk Volume Header Library */ #include #include #include #include #include #include #include #include #include #include "dvhlib.h" #include "dvh.h" #define blksize 512 #ifdef DEBUG # define dprintf(x...) fprintf(stderr, x) #else # define dprintf(x...) do { } while(0) #endif void __attribute__((noreturn)) die(const char *message) { if (errno) fprintf(stderr, "%s: %s\n", message, strerror(errno)); else fprintf(stderr, "%s\n", message); exit(EXIT_FAILURE); } static const char * ptype2str(int ptype) { switch (ptype) { case PTYPE_VOLHDR: return "Volume Header"; case PTYPE_TRKREPL: return "Bad Track Replacement"; case PTYPE_SECREPL: return "Bad Sector Replacement"; case PTYPE_RAW: return "Data"; case PTYPE_BSD: return "BSD filesystem"; case PTYPE_SYSV: return "SysV filesystem"; case PTYPE_VOLUME: return "Volume"; case PTYPE_EFS: return "EFS"; case PTYPE_LVOL: return "Logical Volume"; case PTYPE_RLVOL: return "Raw Logical Volume"; case PTYPE_XFS: return "XFS"; case PTYPE_XFSLOG: return "XFS Log"; case PTYPE_XLV: return "XLV Volume"; case PTYPE_XVM: return "XVM Volume"; case PTYPE_LSWAP: return "Linux Swap"; case PTYPE_LINUX: return "Linux Native"; } return "Unknown Partition Type"; } void dvh_print_vh(const struct dvh_handle *dvh) { const struct volume_header *vh = &dvh->dvh_vh; printf("----- bootinfo -----\n"); printf("Root partition: %d\n", vh->vh_rootpt); printf("Swap partition: %d\n", vh->vh_swappt); printf("Bootfile: \"%s\"\n", vh->vh_bootfile); } void dvh_print_vd(const struct dvh_handle *dvh) { const struct volume_header *vh = &dvh->dvh_vh; char name[VDNAMESIZE+1]; int i; memset(name, 0, VDNAMESIZE+1); printf("----- directory entries -----\n"); for (i = 0; i < NVDIR; i++) { const struct volume_directory *vd; vd = &vh->vh_vd[i]; if (vd->vd_name[0] == '\0') continue; strncpy(name, vd->vd_name, VDNAMESIZE); printf("Entry #%d, name \"%s\", start %d, bytes %d\n", i, name, vd->vd_lbn, vd->vd_nbytes); } } void dvh_print_pt(const struct dvh_handle *dvh) { const struct volume_header *vh = &dvh->dvh_vh; int i; printf("----- partitions -----\n"); for (i = 0; i < NPARTAB; i++) { int32_t start, size, type; start = vh->vh_pt[i].pt_firstlbn; size = vh->vh_pt[i].pt_nblks; type = vh->vh_pt[i].pt_type; if (size == 0) continue; printf("Part# %2d, start %d, blks %d, type %s\n", i, start, size, ptype2str(type)); } } #define swap_short(s) swap_short_f(&(s)); static void swap_short_f(short *sp) { *sp = ntohs(*sp); } #define swap_int(i) swap_int_f(&(i)); static void swap_int_f(int *ip) { *ip = ntohl(*ip); } static uint32_t twos_complement_32bit_sum(uint32_t *base, int size) { int i; uint32_t sum = 0; size = size / sizeof(uint32_t); for (i = 0; i < size; i++) sum = sum - ntohl(base[i]); return sum; } static int verify_vh(const struct dvh_handle *dvh) { uint32_t csum; if (ntohl(dvh->dvh_vh.vh_magic) != VHMAGIC) { fprintf(stderr, "Bad magic\n"); return -1; } csum = twos_complement_32bit_sum((uint32_t *)&dvh->dvh_vh, sizeof(struct volume_header)); return csum != 0; } static void recalc_vh_csum(struct dvh_handle *dvh) { dvh->dvh_vh.vh_csum = 0; dvh->dvh_vh.vh_csum = htonl(twos_complement_32bit_sum( (uint32_t *)&dvh->dvh_vh, sizeof(struct volume_header))); } static void dvh_swap_device_parameters(struct device_parameters *dp) { swap_short(dp->dp_cyls); swap_short(dp->dp_shd0); swap_short(dp->dp_trks0); swap_short(dp->dp_secs); swap_short(dp->dp_secbytes); swap_short(dp->dp_interleave); swap_int(dp->dp_flags); swap_int(dp->dp_datarate); swap_int(dp->dp_nretries); swap_int(dp->dp_mspw); swap_short(dp->dp_xgap1); swap_short(dp->dp_xsync); swap_short(dp->dp_xrdly); swap_short(dp->dp_xgap2); swap_short(dp->dp_xrgate); swap_short(dp->dp_xwcont); } static void dvh_swap_volume_directory(struct volume_directory *vd) { swap_int(vd->vd_lbn); swap_int(vd->vd_nbytes); } static void dvh_swap_partition_table(struct partition_table *pt) { swap_int(pt->pt_nblks); swap_int(pt->pt_firstlbn); swap_int(pt->pt_type); } static void dvh_swap_volume_header(struct volume_header *vh) { int i; swap_int(vh->vh_magic); swap_short(vh->vh_rootpt); swap_short(vh->vh_swappt); dvh_swap_device_parameters(&vh->vh_dp); for (i = 0; i < NVDIR; i++) dvh_swap_volume_directory(vh->vh_vd + i); for (i = 0; i < NPARTAB; i++) dvh_swap_partition_table(vh->vh_pt + i); swap_int(vh->vh_csum); } struct dvh_handle * dvh_open(const char *vh, int mode) { struct dvh_handle *dvh = NULL; int flags, res, fd = 0; if (mode == DVH_READONLY) flags = O_RDONLY; else if (mode == DVH_READWRITE) flags = O_RDWR; else return NULL; dvh = malloc(sizeof(struct dvh_handle)); if (dvh == 0) return NULL; fd = open(vh, flags); if (fd == -1) goto out; res = pread(fd, &dvh->dvh_vh, sizeof(struct volume_header), 0); if (res != sizeof(struct volume_header)) { fprintf(stderr, "dvh_open: short read.\n"); goto out; } if (verify_vh(dvh)) { fprintf(stderr, "dvh_open: Bad volume header\n"); goto out; } dvh_swap_volume_header(&dvh->dvh_vh); dvh->dvh_fd = fd; return dvh; out: if (fd) close(fd); if (dvh) free(dvh); return NULL; } void dvh_close(struct dvh_handle *dvh) { close(dvh->dvh_fd); free(dvh); } void dvh_vh_to_file(const struct dvh_handle *dvh, const char *vh_name, const char *u_name) { const struct volume_header *vh = &dvh->dvh_vh; const struct volume_directory *vd = vh->vh_vd; char *buf; int i, res, ofd; for (i = 0; i < NVDIR; i++) { if (strncmp(vh_name, vd->vd_name, VDNAMESIZE) == 0) break; vd++; } if (i == NVDIR) die("Not found"); ofd = open(u_name, O_RDWR | O_CREAT | O_TRUNC, 0666); if (ofd == -1) die("Couldn't open destination fd"); buf = malloc(vd->vd_nbytes); if (buf == NULL) die("No memory"); res = pread(dvh->dvh_fd, buf, vd->vd_nbytes, vd->vd_lbn * blksize); if (res != vd->vd_nbytes) die("Short read"); res = pwrite(ofd, buf, vd->vd_nbytes, 0); if (res != vd->vd_nbytes) { unlink(u_name); die("Short write"); } free(buf); } void dvh_file_to_vh(struct dvh_handle *dvh, const char *u_name, const char *vh_name) { struct volume_header *vh = &dvh->dvh_vh; struct volume_directory *vd = vh->vh_vd; struct stat istat; long size; int i, res, ifd, dest, num=0, newAdded=0; char *buf[NVDIR]; ifd = open(u_name, O_RDONLY); if (ifd == -1) die("Couldn't open input fd"); res = fstat(ifd, &istat); if (res == -1) die("Couldn't stat source file"); /* calculate free blocks in vh */ size = vh->vh_pt[8].pt_nblks /* total vh size */ - ( vh->vh_pt[8].pt_firstlbn + 4 ) /* reserved area */ - (( istat.st_size + blksize - 1 ) / blksize ); /* pad to blocksize */ /* * Are we replacing an existing file, check for enough space and free * entry in volume header */ for (i = 0; i < NVDIR; i++) { if (strncmp(vh_name, vd->vd_name, VDNAMESIZE) == 0) { /* It's an existing file, delete it. */ memset(vd->vd_name, 0, VDNAMESIZE); vd->vd_nbytes = 0; } if ( vd->vd_nbytes ) { size -= (vd->vd_nbytes + blksize - 1 ) / blksize; /* pad to blocksize */ num++; } vd++; } if ( num == NVDIR ) die("No more free entries in volume header"); if ( size <= 0 ) die("Not enough space left in volume header"); /* copy all the other entries into a buffer */ vd = vh->vh_vd; for (i = 0; i < NVDIR; i++) { if ( vd->vd_nbytes ) { buf[i] = malloc(vd->vd_nbytes); if (buf[i] == NULL) die("No memory"); res = pread(dvh->dvh_fd, buf[i], vd->vd_nbytes, vd->vd_lbn * blksize); dprintf("Copying entry %d to tmp buffer\n", i); if (res != vd->vd_nbytes) die("Short read"); } else buf[i] = NULL; vd++; } /* look for a free entry and add the new one */ vd = vh->vh_vd; /* XXX The number 4 is observed from the IRIX dvh. */ dest = vh->vh_pt[8].pt_firstlbn + 4; for (i = 0; i < NVDIR; i++) { /* * add new entry if we find a free entry and we've not already * done it */ if ( ! vd->vd_nbytes && ! newAdded ) { dprintf("Adding new entry at position %d\n", i); vd->vd_nbytes = istat.st_size; strncpy(vd->vd_name, vh_name, VDNAMESIZE); newAdded = 1; buf[i] = malloc(vd->vd_nbytes); if (buf[i] == NULL) die("No memory"); res = pread(ifd, buf[i], vd->vd_nbytes, 0); if (res != vd->vd_nbytes) { die("Short read"); } close(ifd); } if ( vd->vd_nbytes ) { /* write buf to Volume Directory */ dprintf("Writing buf[%d]\n", i); vd->vd_lbn = dest; res = pwrite(dvh->dvh_fd, buf[i], vd->vd_nbytes, vd->vd_lbn * blksize); if (res != vd->vd_nbytes) { fprintf(stderr, "Wrote %d not %d\n", res, vd->vd_nbytes); die("Short write"); } } dest += (vd->vd_nbytes + blksize - 1) / blksize; vd++; } for ( i = 0; i < NVDIR; i++) { if( buf[i] ) free( buf[i] ); } /* write the new volume header too! */ dvh_swap_volume_header(vh); recalc_vh_csum(dvh); dprintf("About to rewrite the volume header: "); res = pwrite(dvh->dvh_fd, vh, sizeof(struct volume_header), 0); if ( res != sizeof(struct volume_header )) { die("Short write of volume header - bye bye\n"); } dprintf("wrote %d bytes\n", res); } void dvh_vh_remove(struct dvh_handle *dvh, const char *vh_name) { struct volume_header *vh = &dvh->dvh_vh; struct volume_directory *vd = vh->vh_vd; int i, res, dest, found = 0; char *buf[NVDIR]; /* copy all the other entries into a buffer */ vd = vh->vh_vd; for (i = 0; i < NVDIR; i++) { if (strncmp(vh_name, vd->vd_name, VDNAMESIZE) == 0) { memset(vd->vd_name, 0, VDNAMESIZE); vd->vd_nbytes = 0; found = 1; } if ( vd->vd_nbytes ) { buf[i] = malloc(vd->vd_nbytes); if (buf[i] == NULL) die("No memory"); res = pread(dvh->dvh_fd, buf[i], vd->vd_nbytes, vd->vd_lbn * blksize); dprintf("Copying entry %d to tmp buffer\n", i); if (res != vd->vd_nbytes) die("Short read"); } else buf[i] = NULL; vd++; } if ( ! found ) die("Not found"); vd = vh->vh_vd; /* XXX The number 4 is observed from the IRIX dvh. */ dest = vh->vh_pt[8].pt_firstlbn + 4; for (i = 0; i < NVDIR; i++) { if ( vd->vd_nbytes ) { /* write buf to Volume Directory */ dprintf("Writing buf[%d]\n", i); vd->vd_lbn = dest; res = pwrite(dvh->dvh_fd, buf[i], vd->vd_nbytes, vd->vd_lbn * blksize); if (res != vd->vd_nbytes) { fprintf(stderr, "Wrote %d not %d\n", res, vd->vd_nbytes); die("Short write"); } free( buf[i] ); } dest += (vd->vd_nbytes + 511) / 512; /* XXX Blocksize */ vd++; } /* write the new volume header too! */ dvh_swap_volume_header(vh); recalc_vh_csum(dvh); dprintf("About to rewrite the volume header: "); res = pwrite(dvh->dvh_fd, vh, sizeof(struct volume_header), 0); if ( res != sizeof(struct volume_header )) { die("Short write of volume header - bye bye\n"); } dprintf("wrote %d bytes\n", res); }