/* Copyright (C) 2022-2023 Patrick H. E. Foubet - e2li.org Ecole du Logiciel Libre d'Ivry - FRANCE This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see *******************************************************************/ #include #include #include #include #include #include #include #include #define VERSION "0.4" typedef uint32_t loc_t; typedef uint8_t byte; #define CD_SECTOR_SIZE 0x800 /* 2048 */ #define ISO_PADSIZE 0x10 /* 16 */ #ifndef __cplusplus typedef uint8_t bool; #define true (uint8_t)1 #define false (uint8_t)0 #endif #define ALIGN(x,y) (((x) + ((y)-1)) & (~((y)-1))) static char *isofilename, *targetfilename, *targetdirname, *hostfilename; static bool ecrase = false; struct iso_rep_t { uint8_t len; uint8_t ex_len; uint32_t lba; uint32_t big_lba; uint32_t file_len; uint32_t big_file_len; byte time_date[7]; byte file_flags; byte file_unit_size; byte interleave_gap_size; uint16_t volseq_num; uint16_t big_volseq_num; uint8_t fileid_len; char fileid[0]; } __attribute__((packed)); typedef struct iso_rep_t iso_rep_t; int getfilesize(FILE *f) { int spos = ftell(f); fseek(f, 0, SEEK_END); int len = ftell(f); fseek(f, spos, SEEK_SET); return len; } static int Verbose; static int Mode=-2; /* 0=list, 1=put, -1=get */ void CdSetloc(FILE *f, loc_t newpos) { fseeko(f, newpos * CD_SECTOR_SIZE, SEEK_SET); } void CdRead(void *buf, loc_t nsectors, FILE *f) { fread(buf, nsectors, CD_SECTOR_SIZE, f); } void CdWrite(void *buf, loc_t nsectors, FILE *f) { fwrite(buf, nsectors, CD_SECTOR_SIZE, f); } bool ispvd(byte *sec) { return( (sec[0] == 1) && (memcmp(sec+1, "CD001", 5) == 0)); } bool streq(const char *s1, const char *s2) { return (strcasecmp(s1,s2) == 0); } bool scmplen(const char *s1, const char *s2, int len) { for(int i = 0; i < len; i += 1) { if(tolower(s1[i]) != tolower(s2[i])) return false; } return true; } loc_t CdTell(FILE *fdi) { loc_t s = ftell(fdi); return (s & (~(0x7FF))) / 0x800; } bool CdFindFileInDir(iso_rep_t *out_dir, FILE* fdi, const char *filename, int filename_len, loc_t *out_secnum, loc_t *out_offset) { byte sec[CD_SECTOR_SIZE]; iso_rep_t *dir = (iso_rep_t*)(sec); int slen = filename_len; int dirp = 0; CdRead(sec, 1, fdi); if (Verbose) printf("FFID %s %d\n", filename, slen); while(dir->len != 0) { int dlen = dir->fileid_len; if(!(dir->file_flags & 2)) dlen -= 2; if ((Mode == 0)&&((strcmp(filename,"*")==0)||(strcmp(filename,"/*")==0))){ if (dlen>0) { dir->fileid[dlen] = '\0'; if ((dlen==1) && (dir->fileid[0] <= ' ')) { if (Verbose) printf(" + 0x%x %d\n",(int)dir->fileid[0], dlen); } else printf(" - %s %d\n",dir->fileid, dlen); } } else { if(dlen == slen) { if (Verbose) printf(" + %s\n",dir->fileid); if(scmplen(dir->fileid, filename, slen)) { *out_dir = *dir; if(out_secnum != NULL) *out_secnum = CdTell(fdi) - 1; if(out_offset != NULL) *out_offset = dirp; return true; } } } dirp += dir->len; if(dirp >= 0x800) { CdRead(sec, 1, fdi); dirp = dirp % 0x800; } dir = (iso_rep_t*)(sec + dirp); } return false; } bool CdFindFile(iso_rep_t *outdir, FILE *fdi, const char *filename, loc_t *out_secnum, loc_t *out_offset) { const char *next; const char *cur = filename; int slen; if (Verbose) printf("> %s\n",filename); do { next = strchr(cur, '/'); if(next == NULL) slen = strlen(cur); else slen = (int)(next - cur); if (!CdFindFileInDir(outdir, fdi, cur, slen, out_secnum, out_offset)) return false; if (next != NULL) { CdSetloc(fdi, outdir->lba); next++; cur = next; } else break; } while(next != NULL); return true; } /* options definitions */ static int help_flag; static struct option long_options[] = { {"verbose", no_argument, &Verbose, 1}, {"help", no_argument, &help_flag, 1}, {"list", no_argument, 0, 'l'}, {"put", no_argument, 0, 'p'}, {"get", no_argument, 0, 'g'}, {"host", required_argument, 0, 'h'}, {"file", required_argument, 0, 'f'}, {"dir", required_argument, 0, 'd'}, {0, 0, 0, 0} }; void explain(void) { puts("Utilisation : isoupd [options] fichier.iso"); puts(" -l, --list liste d'un repertoire (defaut)"); puts(" -p, --put modifie un fichier de l'iso"); puts(" -g, --get extrait un fichier de l'iso"); puts(" -v, --verbose mode verbeux, avec trace de la recherche"); puts(" -f, --file chemin du fichier dans l'iso"); puts(" -h, --host nom du fichier host. Obligatoire pour -p et -g"); puts(" -d, --dir nom du repertoire a lister dans l'iso"); puts(" --help affiche cette aide !"); } void messerr(char * m, int err) { fprintf(stderr,"ERREUR : %s !!\n", m); if (err) { explain(); exit(err); } } static int mode_contrad=0; void errmode(char * o) { fprintf(stderr,"Option %s en contradiction avec une autre !\n",o); mode_contrad=1; } int main(int N, char *P[]) { int c, l, opt_ind = 0; FILE *fdh, *fdi; char etoile[2] = "*"; if(sizeof(iso_rep_t) != 33) { fprintf(stderr, "Mauvaise compilation : tid=%lu\n",sizeof(iso_rep_t)); return 9; } while (1) { if ((c=getopt_long(N,P,"vlpgd:h:f:",long_options,&opt_ind))==-1) break; switch (c) { case 0: if (long_options[opt_ind].flag != 0) break; printf ("option %s", long_options[opt_ind].name); if (optarg) printf (" avec arg %s", optarg); printf ("\n"); break; case 'l': if (Mode != -2) errmode(P[opt_ind]); Mode=0; break; case 'p': if (Mode != -2) errmode(P[opt_ind]); Mode=1; break; case 'g': if (Mode != -2) errmode(P[opt_ind]); Mode=-1; break; case 'v': Verbose=1; break; case 'h': hostfilename = optarg; break; case 'f': targetfilename = optarg; break; case 'd': l = strlen(optarg); if (optarg[l-1]=='/') { l--; optarg[l]='\0'; } targetdirname = (char*) malloc(l+3); if (targetdirname == NULL) { perror("malloc"); return 4; } sprintf(targetdirname, "%s/%s", optarg,etoile); break; case '?': /* getopt_long already printed an error message. */ explain(); return 1; default: abort (); } } if (help_flag) { printf("isoupd version %s (c) E2L 2023\n",VERSION); explain(); return 0; } if (optind < N) { isofilename = P[optind++]; if (optind < N) { fprintf (stderr, "Non pris en compte : "); while (optind < N) fprintf (stderr, "%s ", P[optind++]); fprintf(stderr, "\n"); messerr("parametre(s) inutile(s)",3); } } /* verifications */ if (! isofilename) messerr("Fichier ISO absent",2); if (Mode == -2) Mode = 0; /* defaut */ switch(Mode) { case 0 : /* list */ if (targetfilename) messerr("option -f inutile",2); if (hostfilename) messerr("option -h inutile",2); if (!targetdirname) targetdirname = etoile; if (Verbose) printf("Liste de %s dans %s\n",targetdirname,isofilename); targetfilename = targetdirname; break; case 1 : /* put */ if (targetdirname) messerr("option -d inutile",2); if (!targetfilename) messerr("Chemin du fichier dans l'ISO absent",2); if (!hostfilename) messerr("fichier host source absent",2); if (Verbose) printf("PUT de %s vers %s dans %s\n",hostfilename,targetfilename,isofilename); break; case -1 : /* get */ if (targetdirname) messerr("option -d inutile",2); if (!targetfilename) messerr("Chemin du fichier dans l'ISO absent",2); if (!hostfilename) messerr("fichier host destination absent",2); if (Verbose) printf("GET de %s dans %s vers %s\n",targetfilename,isofilename,hostfilename); break; } if (Verbose) printf("Verbose : mode = %d\n",Mode); if ((fdi = fopen(isofilename, "r+")) == NULL) { fprintf(stderr, "Erreur ouverture ISO %s\n", isofilename); return 1; } if(hostfilename != NULL) { if ((fdh = fopen(hostfilename, "r")) == NULL) { if (Mode == 1) { /* put */ fprintf(stderr, "Erreur ouverture fichier %s\n", hostfilename); return 1; } } else { if (Mode == -1) { /* get */ fprintf(stderr,"Erreur : le fichier %s existe deja !\n",hostfilename); return 1; } } if (Mode == -1) { /* get */ if ((fdh = fopen(hostfilename, "w")) == NULL) { fprintf(stderr, "Erreur creation fichier %s\n", hostfilename); return 1; } } } byte sec[CD_SECTOR_SIZE]; iso_rep_t *dir = (iso_rep_t*)(sec); CdSetloc(fdi, ISO_PADSIZE); CdRead(sec, 1, fdi); if(!ispvd(sec)) { fprintf(stderr, "%s n'est pas un fichier ISO\n", isofilename); return 1; } loc_t root_lba = *(loc_t*)(sec+158); if (Verbose) printf("root lba = %x\n", root_lba); CdSetloc(fdi, root_lba); loc_t dir_lba, dir_offset; bool s = CdFindFile(dir, fdi, targetfilename, &dir_lba, &dir_offset); if (!Mode) { /* list */ fclose(fdi); return 0; } if(!s) { fprintf(stderr, "Fichier %s absent de l'ISO %s\n", targetfilename, isofilename); return 1; } CdSetloc(fdi, dir_lba); CdRead(sec, 1, fdi); dir = (iso_rep_t*)(sec + dir_offset); int maxsize = ALIGN(dir->file_len, 0x800); printf("Taille du fichier sur l'ISO : %d\n", dir->file_len); if (Mode == -1) { /* get */ byte *filedata = (byte*)(malloc(maxsize)); CdSetloc(fdi, dir->lba); CdRead(filedata, (maxsize / CD_SECTOR_SIZE), fdi); fwrite(filedata, 1, dir->file_len, fdh); fclose(fdh); fclose(fdi); printf("Extraction faite dans %s.\n",hostfilename); return 0; } int len = getfilesize(fdh); int aligned_len = ALIGN(len, 0x800); printf("Taille du fichier local : %d\n", len); if(!ecrase) { if(len > maxsize) { fprintf(stderr, "Le fichier %s depasse de %d octets (%d > %d)\n", hostfilename, len - maxsize, len, maxsize); fclose(fdh); fclose(fdi); return 1; } if((uint32_t)len != dir->file_len) { if((uint32_t)aligned_len < dir->file_len) { printf("ATTENTION: La mise en place de ce fichier\n va retrecir l'espace d'allocation disponible !\n"); printf(" (il sera de %d octets)\n", aligned_len); } printf("Les tailles sont differentes mais compatibles.\n"); } } printf("AVERTISSEMENT : Version %s de test !!!\n",VERSION); printf("============= Il faut faire une sauvegarde de l'ISO avant.\n"); printf("Modification ? [o/N]\n"); int ch = getchar(); if((ch != 'o') && (ch != 'O')) { printf("Annulation !!\n"); return 1; } byte *filedata = (byte*)(malloc(aligned_len)); memset(filedata, 0, aligned_len); fread(filedata, 1, len, fdh); fclose(fdh); printf("Ecriture de %d octets sur le secteur %u\n", len, dir->lba); CdSetloc(fdi, dir->lba); CdWrite(filedata, (aligned_len / CD_SECTOR_SIZE), fdi); dir->file_len = len; printf("Ecriture d'un nouveau repertoire sur %u\n", dir_lba); CdSetloc(fdi, dir_lba); CdWrite(sec, 1, fdi); printf("OK !\n"); fclose(fdi); return 0; }