/* 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.1"
typedef uint32_t loc_t;
typedef uint8_t byte;
#define CD_SECTOR_SIZE 0x800
#define ISO_PADSIZE 0x10
#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 length_override = false;
struct iso_directory_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_directory_t iso_directory_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_flag;
static int mode_flag=-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 *isofile) {
loc_t s = ftell(isofile);
return (s & (~(0x7FF))) / 0x800;
}
bool CdFindFileInDir(iso_directory_t *out_dir, FILE* isofile, const char *filename, int filename_len, loc_t *out_secnum, loc_t *out_offset) {
byte sec[CD_SECTOR_SIZE];
iso_directory_t *dir = (iso_directory_t*)(sec);
int slen = filename_len;
int dirp = 0;
CdRead(sec, 1, isofile);
if (verbose_flag) 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_flag == 0) && ((strcmp(filename,"*")==0)||(strcmp(filename,"/*")==0))) {
if (dlen>0) {
dir->fileid[dlen] = '\0';
if ((dlen==1) && (dir->fileid[0] <= ' ')) {
if (verbose_flag) printf(" + 0x%x %d\n",(int)dir->fileid[0], dlen);
} else printf(" - %s %d\n",dir->fileid, dlen);
}
} else {
if(dlen == slen) {
if (verbose_flag) printf(" + %s\n",dir->fileid);
if(scmplen(dir->fileid, filename, slen) == true) {
*out_dir = *dir;
if(NULL != out_secnum) {
*out_secnum = CdTell(isofile) - 1;
}
if(NULL != out_offset) {
*out_offset = dirp;
}
return true;
}
}
}
dirp += dir->len;
if(dirp >= 0x800) {
CdRead(sec, 1, isofile);
dirp = dirp % 0x800;
}
dir = (iso_directory_t*)(sec + dirp);
}
return false;
}
bool CdFindFile(iso_directory_t *outdir, FILE *isofile, const char *filename, loc_t *out_secnum, loc_t *out_offset)
{
const char *next;
const char *cur = filename;
int slen;
if (verbose_flag) printf("> %s\n",filename);
do {
next = strchr(cur, '/');
if(next == NULL) {
slen = strlen(cur);
} else {
slen = (int)(next - cur);
}
if(false == CdFindFileInDir(outdir, isofile, cur, slen, out_secnum, out_offset)) {
return false;
}
if(next != NULL) {
CdSetloc(isofile, 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_flag, 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] isofile");
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");
}
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 *hostfile, *isofile;
char etoile[2] = "*";
if(sizeof(iso_directory_t) != 33) {
fprintf(stderr, "Bad Install %lu\n", sizeof(iso_directory_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_flag != -2) errmode(P[opt_ind]);
mode_flag=0;
break;
case 'p':
if (mode_flag != -2) errmode(P[opt_ind]);
mode_flag=1;
break;
case 'g':
if (mode_flag != -2) errmode(P[opt_ind]);
mode_flag=-1;
break;
case 'v':
verbose_flag=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_flag == -2) mode_flag = 0; /* defaut */
switch(mode_flag) {
case 0 : /* list */
if (targetfilename) messerr("option -f inutile",2);
if (hostfilename) messerr("option -h inutile",2);
if (!targetdirname) targetdirname = etoile;
if (verbose_flag) 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_flag) 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_flag) printf("GET de %s dans %s vers %s\n",targetfilename,isofilename,hostfilename);
break;
}
if (verbose_flag) printf("Verbose : mode = %d\n",mode_flag);
if ((isofile = fopen(isofilename, "r+")) == NULL) {
fprintf(stderr, "Erreur ouverture ISO %s\n", isofilename);
return 1;
}
if(hostfilename != NULL) {
if ((hostfile = fopen(hostfilename, "r")) == NULL) {
fprintf(stderr, "Erreur ouverture fichier %s\n", hostfilename);
return 1;
}
}
byte sec[CD_SECTOR_SIZE];
iso_directory_t *dir = (iso_directory_t*)(sec);
CdSetloc(isofile, ISO_PADSIZE);
CdRead(sec, 1, isofile);
if(ispvd(sec) == false) {
fprintf(stderr, "%s n'est pas un fichier ISO\n", isofilename);
return 1;
}
loc_t root_lba = *(loc_t*)(sec+158);
if (verbose_flag) printf("root lba = %x\n", root_lba);
CdSetloc(isofile, root_lba);
loc_t dir_lba, dir_offset;
bool s = CdFindFile(dir, isofile, targetfilename, &dir_lba, &dir_offset);
if (!mode_flag) { /* list */
fclose(isofile);
return 0;
}
if(false == s) {
fprintf(stderr, "Fichier %s absent de l'ISO %s\n", targetfilename, isofilename);
return 1;
}
CdSetloc(isofile, dir_lba);
CdRead(sec, 1, isofile);
dir = (iso_directory_t*)(sec + dir_offset);
printf("File size on ISO is %d\n", dir->file_len);
int maxsize = ALIGN(dir->file_len, 0x800);
int len = getfilesize(hostfile);
int aligned_len = ALIGN(len, 0x800);
printf("File size on HOST is %d\n", len);
return 0; /* test */
if(false == length_override) {
if(len > maxsize) {
fprintf(stderr, "Le fichier %s depasse de %d octets (%d > %d)\n",
hostfilename,
len - maxsize,
len, maxsize
);
fclose(hostfile);
fclose(isofile);
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 va retrecir l'espace d'allocation disponible !\n");
printf(" (il sera de %d octets)\n", aligned_len);
}
printf("Les tailles sont differentes mais compatibles. 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, hostfile);
fclose(hostfile);
printf("Ecriture de %d octets sur le secteur %u\n", len, dir->lba);
CdSetloc(isofile, dir->lba);
CdWrite(filedata, (aligned_len / CD_SECTOR_SIZE), isofile);
dir->file_len = len;
printf("Ecriture d'un nouveau repertoire sur %u\n", dir_lba);
CdSetloc(isofile, dir_lba);
CdWrite(sec, 1, isofile);
printf("OK !\n");
fclose(isofile);
return 0;
}