374 lines
9.2 KiB
C
374 lines
9.2 KiB
C
/*
|
|
* cdcat: get and replace files inside an ISO-9660 CDROM image
|
|
* Based on cdcat by Robert Nordier
|
|
*
|
|
* Copyright (c) 2011 Giuseppe Gatta
|
|
*
|
|
* Copyright (c) 2007 Robert Nordier. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted subject to the following conditions:
|
|
*
|
|
*1. Existing copyright notices in source and other files must be
|
|
* retained.
|
|
*
|
|
*2. Redistributions in whatever form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#define CDCAT_VERSION "0.5"
|
|
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
enum
|
|
{
|
|
cdrom_mode_1,
|
|
cdrom_mode_1_raw,
|
|
cdrom_mode_2,
|
|
};
|
|
|
|
enum
|
|
{
|
|
cdcat_oper_read,
|
|
cdcat_oper_write,
|
|
cdcat_oper_showoffset
|
|
};
|
|
|
|
int cdcat_cdrom_mode = cdrom_mode_1;
|
|
int cdcat_oper = cdcat_oper_read;
|
|
|
|
#define SECSIZ 2048
|
|
#define NAMLEN 255
|
|
|
|
#define sw(x,y) ((x)<<8|(y))
|
|
#define cv4(x) ((*(unsigned char*)(x)) | ((*(unsigned char*)(x+1))<<8) | \
|
|
((*(unsigned char*)(x+2))<<16) | ((*(unsigned char*)(x+3))<<24))
|
|
|
|
/* ISO 9660 Primary Volume Descriptor */
|
|
static char cdmagic[] = {1, 'C', 'D', '0', '0', '1', 1, 0};
|
|
|
|
struct cddir {
|
|
unsigned char len_dr; /* length of directory record */
|
|
unsigned char len_ear; /* extended attribute record length */
|
|
unsigned char ext[8]; /* location of extent */
|
|
unsigned char size[8]; /* data length */
|
|
unsigned char time[7]; /* recording date and time */
|
|
unsigned char flags; /* file flags */
|
|
unsigned char fus; /* file unit size */
|
|
unsigned char gap; /* interleave gap size */
|
|
unsigned char vsn[4]; /* volume sequence number */
|
|
unsigned char len_fi; /* length of file identifier */
|
|
unsigned char fi[1]; /* file identifier ... */
|
|
};
|
|
|
|
struct dir {
|
|
unsigned int ext; /* starting block number */
|
|
unsigned int size; /* file size */
|
|
int type; /* file type ('d' or '-') */
|
|
char name[NAMLEN + 1]; /* file name */
|
|
};
|
|
|
|
static char *fn; /* special file name */
|
|
static int fd; /* special file descriptor */
|
|
|
|
int cdcat(char *);
|
|
void loaddir(struct cddir *, struct dir *);
|
|
void susp(unsigned char *, int, struct dir *);
|
|
int readblk(void *, unsigned int);
|
|
void writeblk(void *, unsigned int);
|
|
void error(char *);
|
|
|
|
void cdcat_print_usage()
|
|
{
|
|
fprintf(stderr, "usage: cdcat <options> iso-image [path]\n");
|
|
fprintf(stderr, "\n"
|
|
"cdcat can be used to explore an ISO9660 filesystem image\n"
|
|
"If path is not specified the root directory is listed, if it's a directory "
|
|
"the directory is listed or if it's a file the file is printed to standard output\n"
|
|
"The Rock Ridge extensions to the ISO9660 filesystem standard are supported\n"
|
|
"Original program (c) 2007 Robert Nordier.\n"
|
|
"\n"
|
|
"Options:\n"
|
|
"-help - This screen\n"
|
|
"-mode1 - The image is raw and has Mode 1 sectors\n"
|
|
"-mode2 - The image is raw and has Mode 2 sectors (like PlayStation dumps)\n"
|
|
"-replace - If [path] is specified, the data of the file is\n"
|
|
" replaced with input from standard input\n"
|
|
"-showoffset - Show file offset in image\n"
|
|
"-version - Display version\n\n");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *path;
|
|
int e;
|
|
int nargc;
|
|
int x;
|
|
|
|
for(x = 1; x < argc; x++)
|
|
{
|
|
if(strcmp(argv[x], "--") == 0 || argv[x][0] != '-')
|
|
break;
|
|
|
|
if(strcmp(argv[x], "-mode2") == 0 || strcmp(argv[x], "--mode2") == 0)
|
|
cdcat_cdrom_mode = cdrom_mode_2;
|
|
else if(strcmp(argv[x], "-mode1") == 0 || strcmp(argv[x], "--mode1") == 0)
|
|
cdcat_cdrom_mode = cdrom_mode_1_raw;
|
|
else if(strcmp(argv[x], "--help") == 0 || strcmp(argv[x], "-help") == 0)
|
|
{
|
|
cdcat_print_usage();
|
|
return 0;
|
|
}
|
|
else if(strcmp(argv[x], "--version") == 0 || strcmp(argv[x], "-version") == 0)
|
|
{
|
|
printf("cdcat version "CDCAT_VERSION"\n");
|
|
return 0;
|
|
}
|
|
else if(strcmp(argv[x], "--replace") == 0 || strcmp(argv[x], "-replace") == 0)
|
|
cdcat_oper = cdcat_oper_write;
|
|
else if(strcmp(argv[x], "--showoffset") == 0 || strcmp(argv[x], "-showoffset") == 0)
|
|
cdcat_oper = cdcat_oper_showoffset;
|
|
else
|
|
{
|
|
printf("Invalid option %s! Aborting.\n", argv[x]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
nargc = argc-(x-1);
|
|
|
|
if (nargc != 2 && nargc != 3) {
|
|
cdcat_print_usage();
|
|
exit(2);
|
|
}
|
|
|
|
fn = argv[x];
|
|
|
|
if ((fd = open(argv[x], cdcat_oper==cdcat_oper_read?O_RDONLY:O_RDWR)) == -1)
|
|
error("cannot open");
|
|
path = argv[x+1] ? argv[x+1] : "";
|
|
|
|
if ((e = cdcat(path)) != 0)
|
|
fprintf(stderr, "cdcat: %s: Not found\n", path);
|
|
return e;
|
|
}
|
|
|
|
int cdcat(char *path)
|
|
{
|
|
unsigned char buf[SECSIZ];
|
|
char name[NAMLEN + 1];
|
|
struct cddir *dp, *tp;
|
|
struct dir xd;
|
|
char *p, *q;
|
|
unsigned ext, size, bx, bn, x, i;
|
|
int type, n;
|
|
|
|
/*
|
|
* find primary volume descriptor
|
|
* and thence root directory
|
|
*/
|
|
bx = 64;
|
|
for (bn = 16; bn < bx; bn++) {
|
|
readblk(buf, bn);
|
|
if (strcmp((char *)buf, cdmagic) == 0)
|
|
break;
|
|
}
|
|
if (bn == bx)
|
|
error("Invalid argument");
|
|
dp = (struct cddir *)&buf[156];
|
|
loaddir(dp, &xd);
|
|
|
|
/*
|
|
* lookup, list, print ...
|
|
*/
|
|
for (p = path; dp; p = q) {
|
|
while (*p == '/')
|
|
p++;
|
|
for (q = p; *q && *q != '/'; q++);
|
|
if ((n = q - p)) {
|
|
if (n > NAMLEN)
|
|
n = NAMLEN;
|
|
memcpy(name, p, n);
|
|
name[n] = 0;
|
|
}
|
|
ext = xd.ext;
|
|
size = xd.size;
|
|
type = xd.type;
|
|
dp = NULL;
|
|
bx = ext + (size + (SECSIZ - 1)) / SECSIZ;
|
|
for (bn = ext; !dp && bn < bx; bn++) {
|
|
if (type == 'd')
|
|
{
|
|
readblk(buf, bn);
|
|
|
|
for (i = 0; !dp && buf[i]; i += buf[i]) {
|
|
tp = (struct cddir *)(buf + i);
|
|
loaddir(tp, &xd);
|
|
if (n == 0)
|
|
printf("%10u %c %s\n",
|
|
xd.size, xd.type, xd.name);
|
|
else if (strcmp(name, xd.name) == 0)
|
|
dp = tp;
|
|
}
|
|
}
|
|
else {
|
|
if(cdcat_oper == cdcat_oper_read)
|
|
{
|
|
readblk(buf, bn);
|
|
x = size < SECSIZ ? size : SECSIZ;
|
|
for (i = 0; i < x; i++)
|
|
putchar(buf[i]);
|
|
size -= x;
|
|
}else if(cdcat_oper == cdcat_oper_write)
|
|
{
|
|
x = size < SECSIZ ? size : SECSIZ;
|
|
for (i = 0; i < x; i++)
|
|
buf[i] = getchar();
|
|
|
|
writeblk(buf, bn);
|
|
|
|
size -= x;
|
|
}else if(cdcat_oper == cdcat_oper_showoffset)
|
|
{
|
|
printf("%d\n", readblk(buf, bn));
|
|
goto cdcat_end;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cdcat_end:
|
|
return n != 0;
|
|
}
|
|
|
|
/*
|
|
* Gather together the directory information that interests us.
|
|
* Any and all of this may be altered by a suitable SUSP field.
|
|
*/
|
|
void loaddir(struct cddir *dp, struct dir *xp)
|
|
{
|
|
int c;
|
|
|
|
xp->ext = cv4(dp->ext);
|
|
xp->size = cv4(dp->size);
|
|
xp->type = dp->flags & 2 ? 'd' : '-';
|
|
xp->name[0] = 0;
|
|
if (dp->fi[0] != 0) {
|
|
c = dp->len_fi | 1;
|
|
susp(dp->fi + c, dp->len_dr - 33 - c, xp);
|
|
}
|
|
if (xp->name[0] == 0)
|
|
{
|
|
if (dp->fi[0] == 0 || dp->fi[0] == 1)
|
|
strcpy(xp->name, dp->fi[0] == 0 ? "." : "..");
|
|
else
|
|
{
|
|
memcpy(xp->name, dp->fi, dp->len_fi);
|
|
xp->name[dp->len_fi] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SUSP/RRIP support: allowing UNIX-style file names and directories
|
|
* nested more than eight deep (among other things).
|
|
*/
|
|
void susp(unsigned char *sp, int n, struct dir *xp)
|
|
{
|
|
unsigned char buf[SECSIZ];
|
|
unsigned char *p;
|
|
int i, j;
|
|
|
|
for (p = sp; p < sp + n && *p;) {
|
|
if (p[3] != 1)
|
|
return;
|
|
switch (sw(p[0], p[1])) {
|
|
/* continuation area */
|
|
case sw('C', 'E'):
|
|
readblk(buf, cv4(&p[4]));
|
|
sp = buf + cv4(&p[12]);
|
|
n = cv4(&p[20]);
|
|
p = sp;
|
|
continue;
|
|
/* child link */
|
|
case sw('C', 'L'):
|
|
xp->ext = cv4(&p[4]);
|
|
xp->size = SECSIZ;
|
|
xp->type = 'd';
|
|
break;
|
|
/* alternate name */
|
|
case sw('N', 'M'):
|
|
for (j = 0; xp->name[j]; j++);
|
|
for (i = 5; i < p[2]; i++)
|
|
xp->name[j++] = p[i];
|
|
xp->name[j] = 0;
|
|
break;
|
|
}
|
|
p += p[2];
|
|
}
|
|
}
|
|
|
|
int readblk(void *buf, unsigned int blkno)
|
|
{
|
|
int r;
|
|
|
|
switch(cdcat_cdrom_mode)
|
|
{
|
|
case cdrom_mode_1:
|
|
r = lseek(fd, blkno * SECSIZ, 0);
|
|
break;
|
|
case cdrom_mode_1_raw:
|
|
r = lseek(fd, (blkno * 2352) + 16, 0);
|
|
break;
|
|
case cdrom_mode_2:
|
|
r = lseek(fd, (blkno * 2352) + 24, 0);
|
|
break;
|
|
}
|
|
|
|
if (read(fd, buf, SECSIZ) != SECSIZ)
|
|
error("read error");
|
|
|
|
return r;
|
|
}
|
|
|
|
void writeblk(void *buf, unsigned int blkno)
|
|
{
|
|
switch(cdcat_cdrom_mode)
|
|
{
|
|
case cdrom_mode_1:
|
|
lseek(fd, blkno * SECSIZ, 0);
|
|
break;
|
|
case cdrom_mode_1_raw:
|
|
lseek(fd, (blkno * 2352) + 16, 0);
|
|
break;
|
|
case cdrom_mode_2:
|
|
lseek(fd, (blkno * 2352) + 24, 0);
|
|
break;
|
|
}
|
|
|
|
if (write(fd, buf, SECSIZ) != SECSIZ)
|
|
error("write error");
|
|
}
|
|
|
|
void error(char *msg)
|
|
{
|
|
fprintf(stderr, "cdcat: %s: %s\n", fn, msg);
|
|
exit(2);
|
|
}
|