Virtual ISO: More filename calculation delegation to save more memory

This commit is contained in:
RichardG867
2022-03-31 17:08:20 -03:00
parent 5b555a2896
commit 9fcaa57264

View File

@@ -80,17 +80,16 @@ enum {
}; };
typedef struct _viso_entry_ { typedef struct _viso_entry_ {
char name_short[13];
union { /* save some memory */ union { /* save some memory */
uint64_t pt_offsets[4]; uint64_t pt_offsets[4];
FILE *file; FILE *file;
}; };
union { union {
char name_short[13];
uint64_t dr_offsets[2]; uint64_t dr_offsets[2];
uint64_t data_offset; uint64_t data_offset;
}; };
uint16_t name_joliet[111], pt_idx; /* name_joliet size limited by maximum directory record size */ uint16_t pt_idx;
uint8_t name_joliet_len;
struct stat stats; struct stat stats;
@@ -217,6 +216,7 @@ viso_convert_utf8(wchar_t *dest, const char *src, int buf_size)
case '"': \ case '"': \
case '%': \ case '%': \
case '&': \ case '&': \
case '\'': \
case '(': \ case '(': \
case ')': \ case ')': \
case '+': \ case '+': \
@@ -236,13 +236,13 @@ viso_convert_utf8(wchar_t *dest, const char *src, int buf_size)
case ':': \ case ':': \
case ';': \ case ';': \
case '?': \ case '?': \
case '\'': \
/* Valid for A but not for filenames or D. */ \ /* Valid for A but not for filenames or D. */ \
if ((charset < VISO_CHARSET_A) || (charset == VISO_CHARSET_FN)) \ if ((charset < VISO_CHARSET_A) || (charset == VISO_CHARSET_FN)) \
c = '_'; \ c = '_'; \
break; \ break; \
\ \
case 0x01 ... 0x1f: \ case 0x01 ... 0x1f: \
case '\\': \
/* Not valid for A, D or filenames. */ \ /* Not valid for A, D or filenames. */ \
if (charset <= VISO_CHARSET_FN) \ if (charset <= VISO_CHARSET_FN) \
c = '_'; \ c = '_'; \
@@ -261,23 +261,23 @@ VISO_WRITE_STR_FUNC(viso_write_string, uint8_t, char, )
VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16) VISO_WRITE_STR_FUNC(viso_write_wstring, uint16_t, wchar_t, cpu_to_be16)
static int static int
viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src) viso_fill_short_filename(char *data, const viso_entry_t *entry)
{ {
/* Get name and extension length. */ /* Get name and extension length. */
const char *ext_pos = strrchr(src, '.'); const char *ext_pos = strrchr(entry->basename, '.');
int name_len, ext_len; int name_len, ext_len;
if (ext_pos) { if (ext_pos) {
name_len = ext_pos - src; name_len = ext_pos - entry->basename;
ext_len = strlen(ext_pos); ext_len = strlen(ext_pos);
} else { } else {
name_len = strlen(src); name_len = strlen(entry->basename);
ext_len = 0; ext_len = 0;
} }
/* Copy name. */ /* Copy name. */
int name_copy_len = MIN(8, name_len); int name_copy_len = MIN(8, name_len);
viso_write_string((uint8_t *) dest, src, name_copy_len, VISO_CHARSET_D); viso_write_string((uint8_t *) data, entry->basename, name_copy_len, VISO_CHARSET_D);
dest[name_copy_len] = '\0'; data[name_copy_len] = '\0';
/* Copy extension to temporary buffer. */ /* Copy extension to temporary buffer. */
char ext[5] = { 0 }; char ext[5] = { 0 };
@@ -298,25 +298,25 @@ viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src)
int tail_len = -1; int tail_len = -1;
if (i) { if (i) {
tail_len = sprintf(tail, "~%d", i); tail_len = sprintf(tail, "~%d", i);
strcpy(&dest[MIN(name_copy_len, 8 - tail_len)], tail); strcpy(&data[MIN(name_copy_len, 8 - tail_len)], tail);
} }
/* Add extension to the filename if present. */ /* Add extension to the filename if present. */
if (ext[0]) if (ext[0])
strcat(dest, ext); strcat(data, ext);
/* Go through files in this directory to make sure this filename is unique. */ /* Go through files in this directory to make sure this filename is unique. */
viso_entry_t *entry = dir->first_child; viso_entry_t *other_entry = entry->parent->first_child;
while (entry) { while (other_entry) {
/* Flag and stop if this filename was seen. */ /* Flag and stop if this filename was seen. */
if ((entry->name_short != dest) && !strcmp(dest, entry->name_short)) { if ((other_entry->name_short != data) && !strcmp(data, other_entry->name_short)) {
tail_len = 0; tail_len = 0;
break; break;
} }
/* Move on to the next entry, and stop if the end of this directory was reached. */ /* Move on to the next entry, and stop if the end of this directory was reached. */
entry = entry->next; other_entry = other_entry->next;
if (entry && (entry->parent != dir)) if (other_entry && (other_entry->parent != entry->parent))
break; break;
} }
@@ -327,6 +327,63 @@ viso_get_short_filename(viso_entry_t *dir, char *dest, const char *src)
return 1; return 1;
} }
static size_t
viso_fill_fn_rr(uint8_t *data, const viso_entry_t *entry, size_t max_len)
{
/* Trim filename to max_len if needed. */
size_t len = strlen(entry->basename);
if (len > max_len) {
viso_write_string(data, entry->basename, max_len, VISO_CHARSET_FN);
/* Relocate extension if the original name exceeds the maximum length. */
if (!S_ISDIR(entry->stats.st_mode)) {
char *ext = strrchr(entry->basename, '.');
if (ext > entry->basename) {
len = strlen(ext);
if (len >= max_len)
len = max_len - 1; /* avoid creating a dotfile where there isn't one */
viso_write_string(data + (max_len - len), ext, len, VISO_CHARSET_FN);
}
}
return max_len;
} else {
viso_write_string(data, entry->basename, len, VISO_CHARSET_FN);
return len;
}
}
static size_t
viso_fill_fn_joliet(uint8_t *data, const viso_entry_t *entry, size_t max_len) /* note: receives and returns byte sizes */
{
/* Decode filename as UTF-8. */
size_t len = strlen(entry->basename);
wchar_t utf8dec[len + 1];
len = viso_convert_utf8(utf8dec, entry->basename, len + 1);
/* Trim decoded filename to max_len if needed. */
max_len /= 2;
if (len > max_len) {
viso_write_wstring((uint16_t *) data, utf8dec, max_len, VISO_CHARSET_FN);
/* Relocate extension if the original name exceeds the maximum length. */
if (!S_ISDIR(entry->stats.st_mode)) {
wchar_t *ext = wcsrchr(utf8dec, L'.');
if (ext > utf8dec) {
len = wcslen(ext);
if (len > max_len)
len = max_len;
viso_write_wstring(((uint16_t *) data) + (max_len - len), ext, len, VISO_CHARSET_FN);
}
}
return max_len * 2;
} else {
viso_write_wstring((uint16_t *) data, utf8dec, len, VISO_CHARSET_FN);
return len * 2;
}
}
static int static int
viso_fill_time(uint8_t *data, time_t time, int longform) viso_fill_time(uint8_t *data, time_t time, int longform)
{ {
@@ -477,31 +534,9 @@ viso_fill_dir_record(uint8_t *data, viso_entry_t *entry, int type)
*r = 5; /* length */ *r = 5; /* length */
*p++ = 1; /* version */ *p++ = 1; /* version */
*p++ = 0; /* flags */ *p++ = 0; /* flags */
*r += viso_fill_fn_rr(p, entry, 254 - (p - data)); /* name */
/* Trim Rock Ridge name to fit available space. */ p += (*r) - 5;
size_t len = strlen(entry->basename),
max_len = 254 - (p - data);
if (len > max_len) {
*r += max_len;
viso_write_string(p, entry->basename, max_len, VISO_CHARSET_FN);
p += max_len;
/* Relocate extension if this is a file whose name exceeds the maximum length. */
if (!S_ISDIR(entry->stats.st_mode)) {
char *ext = strrchr(entry->basename, '.');
if (ext > entry->basename) {
len = strlen(ext);
if (len >= max_len)
len = max_len - 1; /* avoid creating a dotfile where there isn't one */
viso_write_string(p - len, ext, len, VISO_CHARSET_FN);
}
}
} else {
*r += len;
viso_write_string(p, entry->basename, len, VISO_CHARSET_FN);
p += len;
}
pad_susp: pad_susp:
if ((p - data) & 1) /* padding for odd SUSP section lengths */ if ((p - data) & 1) /* padding for odd SUSP section lengths */
*p++ = 0; *p++ = 0;
@@ -510,8 +545,7 @@ pad_susp:
case VISO_DIR_JOLIET: case VISO_DIR_JOLIET:
q = p++; /* save location of the file ID length for later */ q = p++; /* save location of the file ID length for later */
*q = entry->name_joliet_len * sizeof(entry->name_joliet[0]); *q = viso_fill_fn_joliet(p, entry, 254 - (p - data));
memcpy(p, entry->name_joliet, *q); /* file ID */
p += *q; p += *q;
if (!((*q) & 1)) /* padding for even file ID lengths */ if (!((*q) & 1)) /* padding for even file ID lengths */
@@ -641,19 +675,16 @@ viso_init(const char *dirname, int *error)
cdrom_image_viso_log("VISO: init()\n"); cdrom_image_viso_log("VISO: init()\n");
/* Initialize our data structure. */ /* Initialize our data structure. */
viso_t *viso = (viso_t *) calloc(1, sizeof(viso_t)); viso_t *viso = (viso_t *) calloc(1, sizeof(viso_t));
uint8_t *data = NULL, *p; uint8_t *data = NULL, *p;
wchar_t *wtemp = NULL; *error = 1;
*error = 1;
if (viso == NULL) if (viso == NULL)
goto end; goto end;
viso->sector_size = VISO_SECTOR_SIZE; viso->sector_size = VISO_SECTOR_SIZE;
/* Prepare temporary data buffers. */ /* Prepare temporary data buffers. */
data = calloc(2, viso->sector_size); data = calloc(2, viso->sector_size);
int wtemp_len = MIN(64, sizeof(viso->root_dir->name_joliet) / sizeof(viso->root_dir->name_joliet[0])) + 1; if (!data)
wtemp = malloc(wtemp_len * sizeof(wchar_t));
if (!data || !wtemp)
goto end; goto end;
/* Open temporary file. */ /* Open temporary file. */
@@ -670,7 +701,7 @@ viso_init(const char *dirname, int *error)
cdrom_image_viso_log("VISO: Traversing directories:\n"); cdrom_image_viso_log("VISO: Traversing directories:\n");
viso_entry_t *dir, *last_dir, *last_entry; viso_entry_t *dir, *last_dir, *last_entry;
struct dirent *readdir_entry; struct dirent *readdir_entry;
int max_len, len, name_len; int len, name_len;
size_t dir_path_len; size_t dir_path_len;
/* Fill root directory entry. */ /* Fill root directory entry. */
@@ -706,11 +737,10 @@ viso_init(const char *dirname, int *error)
/* Stat the current directory or parent directory. */ /* Stat the current directory or parent directory. */
stat(i ? dir->parent->path : dir->path, &last_entry->stats); stat(i ? dir->parent->path : dir->path, &last_entry->stats);
/* Set short and long filenames. */ /* Set basename. */
strcpy(last_entry->name_short, i ? ".." : "."); strcpy(last_entry->name_short, i ? ".." : ".");
wcscpy(last_entry->name_joliet, i ? L".." : L".");
cdrom_image_viso_log("[%08X] %s => %s\n", last_entry, dir->path, last_entry->name_short); cdrom_image_viso_log("[%08X] %s => %s\n", last_entry, dir->path, last_entry->basename);
} }
/* Iterate through this directory's children. */ /* Iterate through this directory's children. */
@@ -754,31 +784,9 @@ viso_init(const char *dirname, int *error)
viso->eltorito_entry = last_entry; viso->eltorito_entry = last_entry;
/* Set short filename. */ /* Set short filename. */
if (viso_get_short_filename(dir, last_entry->name_short, readdir_entry->d_name)) if (viso_fill_short_filename(last_entry->name_short, last_entry))
goto end; goto end;
/* Set Joliet long filename. */
if (wtemp_len < (name_len + 1)) { /* grow wchar buffer if needed */
wtemp_len = name_len + 1;
wtemp = realloc(wtemp, wtemp_len * sizeof(wchar_t));
}
max_len = (sizeof(last_entry->name_joliet) / sizeof(last_entry->name_joliet[0])) - 1;
len = viso_convert_utf8(wtemp, readdir_entry->d_name, wtemp_len);
if (len > max_len) {
/* Relocate extension if this is a file whose name exceeds the maximum length. */
if (!S_ISDIR(last_entry->stats.st_mode)) {
wchar_t *wext = wcsrchr(wtemp, L'.');
if (wext) {
len = wcslen(wext);
memmove(wtemp + (max_len - len), wext, len * sizeof(wchar_t));
}
}
len = max_len;
}
viso_write_wstring(last_entry->name_joliet, wtemp, len, VISO_CHARSET_FN);
last_entry->name_joliet[len] = '\0';
last_entry->name_joliet_len = len;
cdrom_image_viso_log("[%08X] %s => [%-12s] %s\n", last_entry, dir->path, last_entry->name_short, last_entry->basename); cdrom_image_viso_log("[%08X] %s => [%-12s] %s\n", last_entry, dir->path, last_entry->name_short, last_entry->basename);
/* If this is a directory, add it to the traversal list. */ /* If this is a directory, add it to the traversal list. */
@@ -822,7 +830,8 @@ next_dir:
if (i) { if (i) {
viso_write_wstring((uint16_t *) p, EMU_NAME_W, 16, VISO_CHARSET_A); /* system ID */ viso_write_wstring((uint16_t *) p, EMU_NAME_W, 16, VISO_CHARSET_A); /* system ID */
p += 32; p += 32;
mbstowcs(wtemp, basename, 16); wchar_t wtemp[16];
viso_convert_utf8(wtemp, basename, 16);
viso_write_wstring((uint16_t *) p, wtemp, 16, VISO_CHARSET_D); /* volume ID */ viso_write_wstring((uint16_t *) p, wtemp, 16, VISO_CHARSET_D); /* volume ID */
p += 32; p += 32;
} else { } else {
@@ -1051,35 +1060,38 @@ next_dir:
continue; continue;
} }
cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, (i & 2) ? dir->basename : dir->name_short); cdrom_image_viso_log("[%08X] %s => %s\n", dir, dir->path, ((i & 2) || (dir == viso->root_dir)) ? dir->basename : dir->name_short);
/* Save this directory's path table index and offset. */ /* Save this directory's path table index and offset. */
dir->pt_idx = pt_idx; dir->pt_idx = pt_idx;
dir->pt_offsets[i] = ftello64(viso->tf.file); dir->pt_offsets[i] = ftello64(viso->tf.file);
/* Fill path table entry. */ /* Fill path table entry. */
if (dir == viso->root_dir) /* directory ID length */ p = data + 1; /* skip ID length for now */
*p++ = 0; /* extended attribute length */
*((uint32_t *) p) = 0; /* extent location (filled in later) */
p += 4;
*((uint16_t *) p) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */
p += 2;
if (dir == viso->root_dir) { /* directory ID and length */
data[0] = 1; data[0] = 1;
else if (i & 2) *p = 0x00;
data[0] = dir->name_joliet_len * sizeof(dir->name_joliet[0]); } else if (i & 2) {
else data[0] = viso_fill_fn_joliet(p, dir, 255);
} else {
data[0] = strlen(dir->name_short); data[0] = strlen(dir->name_short);
memcpy(p, dir->name_short, data[0]);
}
p += data[0];
data[1] = 0; /* extended attribute length */ if ((p - data) & 1) /* padding for odd directory ID lengths */
*((uint32_t *) &data[2]) = 0; /* extent location (filled in later) */ *p++ = 0x00;
*((uint16_t *) &data[6]) = (i & 1) ? cpu_to_be16(dir->parent->pt_idx) : cpu_to_le16(dir->parent->pt_idx); /* parent directory number */
if (dir == viso->root_dir) /* directory ID */
data[8] = 0;
else if (i & 2)
memcpy(&data[8], dir->name_joliet, data[0]);
else
memcpy(&data[8], dir->name_short, data[0]);
data[8 + data[0]] = 0; /* padding for odd directory ID lengths */
/* Write path table entry. */ /* Write path table entry. */
fwrite(data, 8 + data[0] + (data[0] & 1), 1, viso->tf.file); fwrite(data, p - data, 1, viso->tf.file);
/* Increment path table index and stop if it overflows. */ /* Increment path table index and stop if it overflows. */
if (++pt_idx == 0) if (++pt_idx == 0)
@@ -1133,7 +1145,7 @@ next_dir:
viso_pwrite(data, dir->pt_offsets[i << 1] + 2, 4, 1, viso->tf.file); /* little endian */ viso_pwrite(data, dir->pt_offsets[i << 1] + 2, 4, 1, viso->tf.file); /* little endian */
viso_pwrite(data + 4, dir->pt_offsets[(i << 1) | 1] + 2, 4, 1, viso->tf.file); /* big endian */ viso_pwrite(data + 4, dir->pt_offsets[(i << 1) | 1] + 2, 4, 1, viso->tf.file); /* big endian */
if (i) /* clear union if we no longer need path table offsets */ if (i) /* overwrite pt_offsets in the union if we no longer need them */
dir->file = NULL; dir->file = NULL;
/* Go through entries in this directory. */ /* Go through entries in this directory. */
@@ -1144,8 +1156,8 @@ next_dir:
goto next_entry; goto next_entry;
cdrom_image_viso_log("[%08X] %s => %s\n", entry, cdrom_image_viso_log("[%08X] %s => %s\n", entry,
entry->path ? entry->path : ((dir_type == VISO_DIR_PARENT) ? dir->parent->path : dir->path), ((dir_type == VISO_DIR_PARENT) ? dir->parent->path : ((dir_type < VISO_DIR_PARENT) ? dir->path : entry->path)),
i ? entry->basename : entry->name_short); ((dir_type == VISO_DIR_PARENT) ? ".." : ((dir_type < VISO_DIR_PARENT) ? "." : (i ? entry->basename : entry->name_short))));
/* Fill directory record. */ /* Fill directory record. */
viso_fill_dir_record(data, entry, dir_type); viso_fill_dir_record(data, entry, dir_type);
@@ -1158,7 +1170,7 @@ next_dir:
fwrite(p, write, 1, viso->tf.file); fwrite(p, write, 1, viso->tf.file);
} }
/* Write this entry's record's offset. */ /* Write this entry's record's offset. This overwrites name_short in the union. */
entry->dr_offsets[i] = ftello64(viso->tf.file); entry->dr_offsets[i] = ftello64(viso->tf.file);
/* Write data related to the . and .. pseudo-subdirectories, /* Write data related to the . and .. pseudo-subdirectories,
@@ -1313,8 +1325,6 @@ end:
cdrom_image_viso_log("VISO: Initialization failed\n"); cdrom_image_viso_log("VISO: Initialization failed\n");
if (data) if (data)
free(data); free(data);
if (wtemp)
free(wtemp);
viso_close(&viso->tf); viso_close(&viso->tf);
return NULL; return NULL;
} }