mirror of https://github.com/artizirk/dvhtool.git
494 lines
11 KiB
C
494 lines
11 KiB
C
/*
|
|
* Disk Volume Header Library
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
|
|
#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);
|
|
}
|