/* * 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 #include #include #include #include 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 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); }