Initial commit
This commit is contained in:
commit
a6d33cae7c
30
doc/cfg.txt
Normal file
30
doc/cfg.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# <- This is a comment. Anything on a line after this character
|
||||||
|
# will be ignored.
|
||||||
|
#
|
||||||
|
# Configuration format is like this:
|
||||||
|
#
|
||||||
|
# filename ext start end skip-min skip-max options
|
||||||
|
#
|
||||||
|
# Where the individual options are:
|
||||||
|
# filename: The filename, without the extension. For example,
|
||||||
|
# "COMMAND.COM" would be referred to as COMMAND here.
|
||||||
|
# Case-insensitive, set to * to match any.
|
||||||
|
# ext: The file extension. Using the above example, this
|
||||||
|
# would be COM. Also accepts the * wildcard.
|
||||||
|
# start: The byte offset to start corrupting at.
|
||||||
|
# end: The byte offset to stop corrupting at. If set to 0, the
|
||||||
|
# corruption will continue until the end of the file.
|
||||||
|
# skip-min: Minimum bytes to skip between each corruption.
|
||||||
|
# skip-max: Maximum bytes to skip between each corruption.
|
||||||
|
# options: Comma-delimited list of the following:
|
||||||
|
# shr=X: Shift bytes right X times
|
||||||
|
# shl=X: Shift bytes left X times
|
||||||
|
# add=X: Add X to bytes
|
||||||
|
# xor=X: Bitwise XOR to bytes
|
||||||
|
#
|
||||||
|
# skip-min and skip-max are used as bounds for the actual random amount
|
||||||
|
# of bytes skipped each iteration.
|
||||||
|
#
|
||||||
|
# Some examples of possible configuration values are below:
|
||||||
|
* exe 200 0 500 700 xor=50,add=10
|
||||||
|
* dat 100 0 200 250 shr=3
|
1
src/config.c
Normal file
1
src/config.c
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/* TODO */
|
22
src/config.h
Normal file
22
src/config.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef _CONFIG_H
|
||||||
|
#define _CONFIG_H
|
||||||
|
|
||||||
|
struct conf_opts_t {
|
||||||
|
uint8_t shr;
|
||||||
|
uint8_t shl;
|
||||||
|
uint8_t add;
|
||||||
|
uint8_t bit_xor;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct config_t {
|
||||||
|
char filename[9];
|
||||||
|
char ext[4];
|
||||||
|
uint16_t start;
|
||||||
|
uint16_t end;
|
||||||
|
uint16_t skip_a;
|
||||||
|
uint16_t skip_b;
|
||||||
|
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !_CONFIG_H */
|
10
src/debug.h
Normal file
10
src/debug.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef _DEBUG_H
|
||||||
|
#define _DEBUG_H
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define DPRINTF(x) printf(x)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !_DEBUG_H */
|
272
src/dosfs.c
Normal file
272
src/dosfs.c
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "dosfs.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens an image a la open(2). Returns 0 on success, or
|
||||||
|
* some error number on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
open_image(const char *path, int flags, dosfs_t *fsd)
|
||||||
|
{
|
||||||
|
int res, ifd;
|
||||||
|
|
||||||
|
if (fsd == NULL)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
ifd = open(path, flags);
|
||||||
|
if (ifd == -1)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
memset(&fsd->ib, 0, sizeof(struct bpb));
|
||||||
|
|
||||||
|
res = read(ifd, &fsd->ib, sizeof(struct bpb));
|
||||||
|
if (res == -1) {
|
||||||
|
close(ifd);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsd->ib.jmp[0] != 0xEB || fsd->ib.jmp[2] != 0x90) {
|
||||||
|
DPRINTF("hm... doesn't look like a FAT image?\n");
|
||||||
|
close(ifd);
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsd->ib.sect_ct == 0 || fsd->ib.fat_size == 0) {
|
||||||
|
DPRINTF("sect_ct or fat_size == 0, likely fat>32 (unsupported)\n");
|
||||||
|
close(ifd);
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsd->root_size = ((fsd->ib.dirent_ct * 32) + (fsd->ib.sect_size - 1)) /
|
||||||
|
fsd->ib.sect_size;
|
||||||
|
|
||||||
|
fsd->data_size = fsd->ib.sect_ct - (fsd->ib.resv_sect +
|
||||||
|
(fsd->ib.fat_ct * fsd->ib.fat_size) + fsd->root_size);
|
||||||
|
|
||||||
|
if (fsd->data_size < FAT12_MAX_CLUSTERS) {
|
||||||
|
fsd->fs_ver = 12;
|
||||||
|
} else if (fsd->data_size < FAT16_MAX_CLUSTERS) {
|
||||||
|
fsd->fs_ver = 16;
|
||||||
|
} else {
|
||||||
|
DPRINTF("data_size >= FAT12_MAX_CLUSTERS, likely fat>32 (unsupported)\n");
|
||||||
|
close(ifd);
|
||||||
|
return EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsd->ifd = ifd;
|
||||||
|
fsd->root_loc = (fsd->ib.resv_sect +
|
||||||
|
(fsd->ib.fat_ct * fsd->ib.fat_size)) * fsd->ib.sect_size;
|
||||||
|
fsd->data_loc = fsd->root_loc + (fsd->root_size * fsd->ib.sect_size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees the file descriptor for a given image.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
close_image(dosfs_t *fsd)
|
||||||
|
{
|
||||||
|
close(fsd->ifd);
|
||||||
|
fsd->ifd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads directory listing at given offset. Returns a pointer
|
||||||
|
* to a dosfile_t on success, or NULL on failure (errno set).
|
||||||
|
*/
|
||||||
|
dosfile_t *
|
||||||
|
dos_listdir(dosfs_t *fsd, unsigned int offset)
|
||||||
|
{
|
||||||
|
dosfile_t *start, *cur;
|
||||||
|
struct dos_dirent *dirs = NULL;
|
||||||
|
int i, j, res, root_ents;
|
||||||
|
|
||||||
|
start = malloc(sizeof(dosfile_t));
|
||||||
|
if (start == NULL) {
|
||||||
|
DPRINTF("malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not sure if we should be trusting the bpb for anything,
|
||||||
|
* but relying on it here will be better than reading every
|
||||||
|
* single entry one at a time.
|
||||||
|
*/
|
||||||
|
root_ents = (fsd->root_size * fsd->ib.sect_size) /
|
||||||
|
sizeof(struct dos_dirent);
|
||||||
|
|
||||||
|
res = reallocarr(&dirs, root_ents, sizeof(struct dos_dirent));
|
||||||
|
if (res == -1) {
|
||||||
|
DPRINTF("reallocarr failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = pread(fsd->ifd, dirs, (root_ents * sizeof(struct dos_dirent)),
|
||||||
|
offset);
|
||||||
|
if (res == -1) {
|
||||||
|
DPRINTF("pread failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterate through every dir entry */
|
||||||
|
cur = start;
|
||||||
|
for (i = 0; i < root_ents; i++) {
|
||||||
|
if (dirs[i].filename[0] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
cur->next = malloc(sizeof(dosfile_t));
|
||||||
|
if (cur->next == NULL) {
|
||||||
|
DPRINTF("inner malloc failed!?\n");
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (dirs[i].filename[j] == ' ') {
|
||||||
|
cur->fname[j] = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
cur->fname[j] = dirs[i].filename[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur->fname[8] = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < 3; j++) {
|
||||||
|
cur->fext[j] = dirs[i].filename[j+8];
|
||||||
|
}
|
||||||
|
cur->fext[4] = 0;
|
||||||
|
|
||||||
|
(void)memcpy(&cur->ent, &dirs[i], sizeof(struct dos_dirent));
|
||||||
|
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dirs);
|
||||||
|
|
||||||
|
/* cleanup if something went wrong */
|
||||||
|
if (res != 0) {
|
||||||
|
dos_freedir(start);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees a directory listing.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dos_freedir(dosfile_t *dirs)
|
||||||
|
{
|
||||||
|
dosfile_t *cur, *prev;
|
||||||
|
|
||||||
|
cur = dirs;
|
||||||
|
while (cur != NULL) {
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
free(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int *
|
||||||
|
read_fat_chain(dosfs_t *fsd, unsigned int first, int *length)
|
||||||
|
{
|
||||||
|
unsigned int cur, foff, byte_off, *chain = NULL;
|
||||||
|
uint16_t next;
|
||||||
|
int len = 0, res;
|
||||||
|
|
||||||
|
cur = first;
|
||||||
|
for (;;) {
|
||||||
|
len++;
|
||||||
|
res = reallocarr(&chain, len, sizeof(unsigned int));
|
||||||
|
if (res == -1) {
|
||||||
|
DPRINTF("reallocarr failed\n");
|
||||||
|
free(chain);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
foff = (fsd->fs_ver == 12) ? (cur + (cur / 2)) : /* fat12 */
|
||||||
|
(cur * 2); /* fat16 */
|
||||||
|
byte_off = (fsd->ib.resv_sect * fsd->ib.sect_size) + foff;
|
||||||
|
|
||||||
|
/* XXX: endianness */
|
||||||
|
res = pread(fsd->ifd, &next, 2, byte_off);
|
||||||
|
if (res == -1) {
|
||||||
|
DPRINTF("pread failed\n");
|
||||||
|
free(chain);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fsd->fs_ver == 12) {
|
||||||
|
if (cur & 0x0001) {
|
||||||
|
next = next >> 4;
|
||||||
|
} else {
|
||||||
|
next = next & 0x0FFF;
|
||||||
|
}
|
||||||
|
if (next == 0x0FFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (next == 0xFFFF) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain[len-1] = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*length = len;
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: reads a file to a given buffer
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dos_fread(dosfs_t *fsd, dosfile_t *file, void *buf, int max_size)
|
||||||
|
{
|
||||||
|
unsigned int *chain = NULL;
|
||||||
|
int clen = 0, i, left, chunk, cbytes, off, res;
|
||||||
|
|
||||||
|
chain = read_fat_chain(fsd, file->ent.low_loc, &clen);
|
||||||
|
if (chain == NULL) {
|
||||||
|
return -1; /* XXX */
|
||||||
|
}
|
||||||
|
|
||||||
|
cbytes = fsd->ib.sect_size * fsd->ib.sect_per_cluster;
|
||||||
|
left = max_size;
|
||||||
|
|
||||||
|
for (i = 0; i < clen; i++) {
|
||||||
|
/* only copy as much as we can hold */
|
||||||
|
chunk = (left < cbytes) ? left : cbytes;
|
||||||
|
|
||||||
|
res = pread(fsd->ifd, buf + (i * cbytes),
|
||||||
|
chunk, fsd->data_loc + (chain[i] * cbytes));
|
||||||
|
if (res == -1) {
|
||||||
|
DPRINTF("pread failed\n");
|
||||||
|
free(chain);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
left -= chunk;
|
||||||
|
|
||||||
|
if (left <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
84
src/dosfs.h
Normal file
84
src/dosfs.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/* fat12 */
|
||||||
|
|
||||||
|
#ifndef _FAT12_H
|
||||||
|
#define _FAT12_H
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) bpb {
|
||||||
|
uint8_t jmp[3]; /* presumably some machine code */
|
||||||
|
char oem_id[8];
|
||||||
|
uint16_t sect_size;
|
||||||
|
uint8_t sect_per_cluster;
|
||||||
|
uint16_t resv_sect;
|
||||||
|
uint8_t fat_ct;
|
||||||
|
uint16_t dirent_ct;
|
||||||
|
uint16_t sect_ct;
|
||||||
|
uint8_t media_type;
|
||||||
|
uint16_t fat_size;
|
||||||
|
uint16_t track_size;
|
||||||
|
uint16_t heads;
|
||||||
|
uint32_t hidden_sect;
|
||||||
|
uint32_t larg_sect_ct;
|
||||||
|
/*
|
||||||
|
* XXX: Most PC98 disks don't seem to have the extended
|
||||||
|
* BPB. Should we even care about these?
|
||||||
|
*/
|
||||||
|
uint8_t drive_num;
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint8_t signature;
|
||||||
|
uint32_t serial_num;
|
||||||
|
char label[11];
|
||||||
|
char sys_id[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) dos_dirent {
|
||||||
|
char filename[11];
|
||||||
|
uint8_t attributes;
|
||||||
|
#define ATTR_READONLY 0x01
|
||||||
|
#define ATTR_HIDDEN 0x02
|
||||||
|
#define ATTR_SYSTEM 0x04
|
||||||
|
#define ATTR_VOLID 0x08
|
||||||
|
#define ATTR_ISDIR 0x10
|
||||||
|
#define ATTR_ARCHIVE 0x20
|
||||||
|
#define ATTR_LFN (ATTR_READONLY|ATTR_HIDDEN|ATTR_SYSTEM|ATTR_VOLID)
|
||||||
|
uint8_t _reserved;
|
||||||
|
uint8_t creation_time_tenths;
|
||||||
|
uint16_t creation_time;
|
||||||
|
uint16_t creation_date;
|
||||||
|
uint16_t access_date;
|
||||||
|
uint16_t high_loc; /* only used with FAT32 */
|
||||||
|
uint16_t mod_time;
|
||||||
|
uint16_t mod_date;
|
||||||
|
uint16_t low_loc; /* entry cluster location */
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FAT12_MAX_CLUSTERS 4085
|
||||||
|
#define FAT16_MAX_CLUSTERS 65525
|
||||||
|
|
||||||
|
typedef struct dosfs_t {
|
||||||
|
struct bpb ib;
|
||||||
|
unsigned int fs_ver;
|
||||||
|
int ifd;
|
||||||
|
unsigned int root_loc;
|
||||||
|
unsigned int root_size;
|
||||||
|
unsigned int data_loc;
|
||||||
|
unsigned int data_size;
|
||||||
|
} dosfs_t;
|
||||||
|
|
||||||
|
typedef struct dosfile_t {
|
||||||
|
struct dos_dirent ent;
|
||||||
|
char fname[9];
|
||||||
|
char fext[4];
|
||||||
|
struct dosfile_t *next;
|
||||||
|
} dosfile_t;
|
||||||
|
|
||||||
|
int open_image(const char *, int, dosfs_t *);
|
||||||
|
|
||||||
|
void close_image(dosfs_t *);
|
||||||
|
|
||||||
|
dosfile_t *dos_listdir(dosfs_t *, unsigned int);
|
||||||
|
int dos_fread(dosfs_t *, dosfile_t *, void *, int);
|
||||||
|
void dos_freedir(dosfile_t *);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !_FAT12_H */
|
86
src/main.c
Normal file
86
src/main.c
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "dosfs.h"
|
||||||
|
|
||||||
|
static void usage(void) __dead;
|
||||||
|
static int list_img(char *);
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int ch, list_mode;
|
||||||
|
|
||||||
|
setprogname(argv[0]);
|
||||||
|
|
||||||
|
list_mode = 0;
|
||||||
|
while ((ch = getopt(argc, argv, "l")) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'l':
|
||||||
|
list_mode = 1;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
|
if (argc != 1) {
|
||||||
|
usage();
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_mode) {
|
||||||
|
return list_img(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: rest of the owl */
|
||||||
|
fprintf(stderr, "not yet implemented :(\n");
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
list_img(char *path)
|
||||||
|
{
|
||||||
|
dosfs_t img = { 0 };
|
||||||
|
dosfile_t *rootdir, *cur;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = open_image(path, O_RDONLY, &img);
|
||||||
|
if (res != 0) {
|
||||||
|
fprintf(stderr, "err: couldn't open image (errno %d)\n", res);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootdir = dos_listdir(&img, img.root_loc);
|
||||||
|
if (rootdir == NULL) {
|
||||||
|
perror("couldn't read root dir");
|
||||||
|
close_image(&img);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("OEM ID: %.8s\nDirectory listing:\n", img.ib.oem_id);
|
||||||
|
for (cur = rootdir; cur->next != NULL; cur = cur->next) {
|
||||||
|
printf(" %s.%s\n", cur->fname, cur->fext);
|
||||||
|
}
|
||||||
|
|
||||||
|
dos_freedir(rootdir);
|
||||||
|
rootdir = cur = NULL;
|
||||||
|
close_image(&img);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage: %s [-l] image\n", getprogname());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue