dos33: fix some issues when copying files off disk image

ran across this when trying to debug a different issue

by default we can only get total sector size from the T/S lists
(which is only a multiple of 256 bytes) but in addition A (basic)
and B (binary) files also tell you the exact length.  So we used that
to truncate to the exact length

Only it turns out some programs (I'm looking at you, cracked version
of WAVY NAVY) intentionally set the file size to be wrong and so
we were truncating things improperly

I think this updated code is a bit more clear.  Hopefully it doesn't
break anything, I need a better test suite.
This commit is contained in:
Vince Weaver 2022-06-09 16:46:17 -04:00
parent db49ead0b6
commit 87f7fc15f5
3 changed files with 86 additions and 18 deletions

View File

@ -455,13 +455,14 @@ static int dos33_load_file(int fd,int fts,char *filename) {
unsigned char sector_buffer[BYTES_PER_SECTOR]; unsigned char sector_buffer[BYTES_PER_SECTOR];
int tsl_pointer=0,output_pointer=0; int tsl_pointer=0,output_pointer=0;
int result; int result;
int total_sectors=0,last_output_pointer=0;
/* FIXME! Warn if overwriting file! */ /* FIXME! Warn if overwriting file! */
output_fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666); output_fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
if (output_fd<0) { if (output_fd<0) {
fprintf(stderr,"Error! could not open %s for local save\n", fprintf(stderr,"Error! could not open %s for local save\n",
filename); filename);
return -1; return -ERROR_CANNOT_OPEN;
} }
catalog_file=fts>>16; catalog_file=fts>>16;
@ -480,7 +481,8 @@ static int dos33_load_file(int fd,int fts,char *filename) {
file_type=dos33_file_type(sector_buffer[CATALOG_FILE_LIST+ file_type=dos33_file_type(sector_buffer[CATALOG_FILE_LIST+
(catalog_file*CATALOG_ENTRY_SIZE)+FILE_TYPE]); (catalog_file*CATALOG_ENTRY_SIZE)+FILE_TYPE]);
// printf("file_type: %c\n",file_type); if (debug) printf("Using TSL at %02X:%02X\n",tsl_track,tsl_sector);
if (debug) printf("file_type: %c\n",file_type);
keep_saving: keep_saving:
/* Read in TSL Sector */ /* Read in TSL Sector */
@ -497,6 +499,8 @@ keep_saving:
if ((data_s==0) && (data_t==0)) { if ((data_s==0) && (data_t==0)) {
/* empty */ /* empty */
/* this is complicated, can be a "hole" if in middle */
/* or else ignored if at the end */
} }
else { else {
lseek(fd,DISK_OFFSET(data_t,data_s),SEEK_SET); lseek(fd,DISK_OFFSET(data_t,data_s),SEEK_SET);
@ -508,40 +512,66 @@ keep_saving:
switch(file_type) { switch(file_type) {
case 'A': case 'A':
case 'I': case 'I':
file_size=data_sector[0]+(data_sector[1]<<8)+2; file_size=data_sector[0]+
(data_sector[1]<<8)+2;
break; break;
case 'B': case 'B':
file_size=data_sector[2]+(data_sector[3]<<8)+4; file_size=data_sector[2]+
(data_sector[3]<<8)+4;
break; break;
default: default:
file_size=-1; file_size=-1;
} }
if (debug) printf("File size = %d\n",file_size);
} }
/* write the block read in out to the output file */ /* write the block read in out to the output file */
lseek(output_fd,output_pointer*BYTES_PER_SECTOR,SEEK_SET); lseek(output_fd,output_pointer*BYTES_PER_SECTOR,SEEK_SET);
result=write(output_fd,&data_sector,BYTES_PER_SECTOR); result=write(output_fd,&data_sector,BYTES_PER_SECTOR);
last_output_pointer=output_pointer+1;
} }
output_pointer++; output_pointer++;
tsl_pointer++; tsl_pointer++;
} }
total_sectors=last_output_pointer;
/* finished with TSL sector, see if we have another */ /* finished with TSL sector, see if we have another */
tsl_track=sector_buffer[TSL_NEXT_TRACK]; tsl_track=sector_buffer[TSL_NEXT_TRACK];
tsl_sector=sector_buffer[TSL_NEXT_SECTOR]; tsl_sector=sector_buffer[TSL_NEXT_SECTOR];
// printf("Next track/sector=%d/%d op=%d\n",tsl_track,tsl_sector,
// output_pointer*BYTES_PER_SECTOR);
if ((tsl_track==0) && (tsl_sector==0)) { if ((tsl_track==0) && (tsl_sector==0)) {
} }
else goto keep_saving; else {
if (debug) printf("Next track/sector=%02X:%02X op=%d\n",
tsl_track,tsl_sector,
output_pointer*BYTES_PER_SECTOR);
goto keep_saving;
}
/* Correct the file size */ /* Correct the file size */
/* Note: this can cause issues if the original disk image */
/* was doing something extra-clever and intentionally */
/* had a mis-matched file size */
if (file_size>=0) { if (file_size>=0) {
// printf("Truncating file size to %d\n",file_size);
if (debug) printf("Total size %d sectors (%d bytes)\n",
total_sectors,total_sectors*BYTES_PER_SECTOR);
/* Tricky, 0...255 will give sectors of 0 */
/* 256...511 will give sectors of 1 */
if (((file_size-1)/256)!=total_sectors-1) {
printf("Warning! Total size %d not within "
"256 bytes of sector total (%d %d)!\n",
file_size,file_size/256,total_sectors);
printf("This could mean your program is trying "
"to be clever. Not truncating.\n");
}
else {
printf("Auto-truncating file size to %d\n",file_size);
result=ftruncate(output_fd,file_size); result=ftruncate(output_fd,file_size);
} }
}
if (result<0) fprintf(stderr,"Error on I/O\n"); if (result<0) fprintf(stderr,"Error on I/O\n");
@ -828,8 +858,9 @@ static void display_help(char *name, int version_only) {
if (version_only) return; if (version_only) return;
printf("Usage: %s [-h] [-y] [-x] disk_image COMMAND [options]\n",name); printf("Usage: %s [-h] [-d] [-y] [-x] disk_image COMMAND [options]\n",name);
printf("\t-h : this help message\n"); printf("\t-h : this help message\n");
printf("\t-d : enable debugging\n");
printf("\t-y : always answer yes for anying warning questions\n"); printf("\t-y : always answer yes for anying warning questions\n");
printf("\t-x : ignore errors (useful for making invalid filenames)\n"); printf("\t-x : ignore errors (useful for making invalid filenames)\n");
printf("\n"); printf("\n");

View File

@ -63,6 +63,7 @@
#define ERROR_NO_SPACE 3 #define ERROR_NO_SPACE 3
#define ERROR_IMAGE_NOT_FOUND 4 #define ERROR_IMAGE_NOT_FOUND 4
#define ERROR_CATALOG_FULL 5 #define ERROR_CATALOG_FULL 5
#define ERROR_CANNOT_OPEN 6
/* dos33_vtoc_bitmap.c */ /* dos33_vtoc_bitmap.c */
int dos33_vtoc_free_space(unsigned char *vtoc); int dos33_vtoc_free_space(unsigned char *vtoc);

View File

@ -59,7 +59,7 @@ static void dump_vtoc(unsigned char *vtoc) {
int dos33_dump(unsigned char *vtoc, int fd) { int dos33_dump(unsigned char *vtoc, int fd) {
int num_tracks,catalog_t,catalog_s,file,ts_t,ts_s,ts_total; int num_tracks,catalog_t,catalog_s,file,ts_t,ts_s,ts_total;
int track,sector; int track,sector,type;
int i; int i;
int deleted=0; int deleted=0;
char temp_string[BUFSIZ]; char temp_string[BUFSIZ];
@ -67,6 +67,7 @@ int dos33_dump(unsigned char *vtoc, int fd) {
unsigned char catalog_buffer[BYTES_PER_SECTOR]; unsigned char catalog_buffer[BYTES_PER_SECTOR];
unsigned char data[BYTES_PER_SECTOR]; unsigned char data[BYTES_PER_SECTOR];
int result; int result;
int size_already=0;
/* Read Track 1 Sector 9 */ /* Read Track 1 Sector 9 */
lseek(fd,DISK_OFFSET(1,9),SEEK_SET); lseek(fd,DISK_OFFSET(1,9),SEEK_SET);
@ -104,6 +105,7 @@ repeat_catalog:
dump_sector(catalog_buffer); dump_sector(catalog_buffer);
for(file=0;file<7;file++) { for(file=0;file<7;file++) {
size_already=0;
printf("\n\n"); printf("\n\n");
ts_t=catalog_buffer[(CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_TS_LIST_T))]; ts_t=catalog_buffer[(CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_TS_LIST_T))];
@ -138,28 +140,62 @@ repeat_catalog:
printf("\tLocked = %s\n", printf("\tLocked = %s\n",
catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE)+FILE_TYPE]>0x7f? catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE)+FILE_TYPE]>0x7f?
"YES":"NO"); "YES":"NO");
printf("\tType = %c\n", type=dos33_file_type(catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE)+FILE_TYPE]);
dos33_file_type(catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE)+FILE_TYPE])); printf("\tType = %c\n",type);
printf("\tSize in sectors = %i\n", printf("\tSize in sectors = %i\n",
catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_SIZE_L)]+ catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_SIZE_L)]+
(catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_SIZE_H)]<<8)); (catalog_buffer[CATALOG_FILE_LIST+(file*CATALOG_ENTRY_SIZE+FILE_SIZE_H)]<<8));
repeat_tsl: /* read first sector to try to get size */
printf("\tT/S List $%02X/$%02X:\n",ts_t,ts_s); if (type=='B') {
}
if (deleted) goto continue_dump; if (deleted) goto continue_dump;
repeat_tsl:
/* read T/S list */
lseek(fd,DISK_OFFSET(ts_t,ts_s),SEEK_SET); lseek(fd,DISK_OFFSET(ts_t,ts_s),SEEK_SET);
result=read(fd,&tslist,BYTES_PER_SECTOR); result=read(fd,&tslist,BYTES_PER_SECTOR);
/* read data */
for(i=0;i<ts_total;i++) { for(i=0;i<ts_total;i++) {
track=tslist[TSL_LIST+(i*TSL_ENTRY_SIZE)]; track=tslist[TSL_LIST+(i*TSL_ENTRY_SIZE)];
sector=tslist[TSL_LIST+(i*TSL_ENTRY_SIZE)+1]; sector=tslist[TSL_LIST+(i*TSL_ENTRY_SIZE)+1];
if ((track==0) && (sector==0)) printf("."); if ((track==0) && (sector==0)) printf(".");
else printf("\n\t\t%02X/%02X",track,sector); else {
if (!size_already) {
/* Read Data */
lseek(fd,DISK_OFFSET(track,sector),SEEK_SET);
result=read(fd,data,BYTES_PER_SECTOR);
if (type=='B') {
printf("\tAddress=$%04X\n",
data[0]|(data[1]<<8));
printf("\tSize=$%04X (%d)\n",
data[2]|(data[3]<<8),
data[2]|(data[3]<<8));
}
else if ((type=='A') || (type=='I')) {
printf("\tSize=$%04X (%d)\n",
data[0]|(data[1]<<8),
data[0]|(data[1]<<8));
}
size_already=1;
printf("\n\tT/S List $%02X/$%02X:\n",ts_t,ts_s);
}
printf("\n\t\t%02X/%02X",track,sector);
}
} }
ts_t=tslist[TSL_NEXT_TRACK]; ts_t=tslist[TSL_NEXT_TRACK];
ts_s=tslist[TSL_NEXT_SECTOR]; ts_s=tslist[TSL_NEXT_SECTOR];
if (!((ts_s==0) && (ts_t==0))) goto repeat_tsl; if (!((ts_s==0) && (ts_t==0))) {
printf("\n\tNext T/S List $%02X/$%02X:\n",ts_t,ts_s);
goto repeat_tsl;
}
continue_dump:; continue_dump:;
} }