/* @(#)joliet.c 1.68 15/12/30 joerg */ #include #ifndef lint static UConst char sccsid[] = "@(#)joliet.c 1.68 15/12/30 joerg"; #endif /* * File joliet.c - handle Win95/WinNT long file/unicode extensions for iso9660. * * Copyright 1997 Eric Youngdale. * APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 22/2/2000 * Copyright (c) 1999-2015 J. Schilling * * 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 2, or (at your option) * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Joliet extensions for ISO9660. These are spottily documented by * Microsoft. In their infinite stupidity, they completely ignored * the possibility of using an SUSP record with the long filename * in it, and instead wrote out a duplicate directory tree with the * long filenames in it. * * I am not sure why they did this. One reason is that they get the path * tables with the long filenames in them. * * There are two basic principles to Joliet, and the non-Unicode variant * known as Romeo. Long filenames seem to be the main one, and the second * is that the character set and a few other things is substantially relaxed. * * The SVD is identical to the PVD, except: * * Id is 2, not 1 (indicates SVD). * escape_sequences contains UCS-2 indicator (levels 1, 2 or 3). * The root directory record points to a different extent (with different * size). * There are different path tables for the two sets of directory trees. * * The Unicode level is coded in the SVD as follows: * * Standard Level ASCII escape code * UCS-2 Level-1 %/@ * UCS-2 Level-2 %/C * UCS-2 Level-3 %/E * * The following fields are recorded in Unicode: * system_id * volume_id * volume_set_id * publisher_id * preparer_id * application_id * copyright_file_id * abstract_file_id * bibliographic_file_id * * Unicode strings are always encoded in big-endian format. * * In a directory record, everything is the same as with iso9660, except * that the name is recorded in unicode. The name length is specified in * total bytes, not in number of unicode characters. * * The character set used for the names is different with UCS - the * restrictions are that the following are not allowed: * * Characters (00)(00) through (00)(1f) (control chars) * (00)(2a) '*' * (00)(2f) '/' * (00)(3a) ':' * (00)(3b) ';' * (00)(3f) '?' * (00)(5c) '\' */ #include "mkisofs.h" #include #include #include #include #include LOCAL Uint jpath_table_index; LOCAL struct directory **jpathlist; LOCAL int next_jpath_index = 1; LOCAL int jsort_goof; LOCAL int jsort_glen; LOCAL char ucs_codes[] = { '\0', /* UCS-level 0 is illegal */ '@', /* UCS-level 1 */ 'C', /* UCS-level 2 */ 'E', /* UCS-level 3 */ }; #ifdef UDF EXPORT void convert_to_unicode __PR((unsigned char *buffer, int size, char *source, siconvt_t *inls)); EXPORT int joliet_strlen __PR((const char *string, size_t maxlen, siconvt_t *inls)); #else LOCAL void convert_to_unicode __PR((unsigned char *buffer, int size, char *source, siconvt_t *inls)); LOCAL int joliet_strlen __PR((const char *string, size_t maxlen, siconvt_t *inls)); #endif LOCAL void get_joliet_vol_desc __PR((struct iso_primary_descriptor *jvol_desc)); LOCAL void assign_joliet_directory_addresses __PR((struct directory *node)); LOCAL void build_jpathlist __PR((struct directory *node)); LOCAL int joliet_compare_paths __PR((void const *r, void const *l)); LOCAL int generate_joliet_path_tables __PR((void)); LOCAL void generate_one_joliet_directory __PR((struct directory *dpnt, FILE *outfile)); LOCAL int joliet_sort_n_finish __PR((struct directory *this_dir)); LOCAL int joliet_compare_dirs __PR((const void *rr, const void *ll)); LOCAL int joliet_sort_directory __PR((struct directory_entry **sort_dir)); EXPORT int joliet_sort_tree __PR((struct directory *node)); LOCAL void generate_joliet_directories __PR((struct directory *node, FILE *outfile)); LOCAL int jpathtab_write __PR((FILE *outfile)); LOCAL int jdirtree_size __PR((UInt32_t starting_extent)); LOCAL int jroot_gen __PR((void)); LOCAL int jdirtree_write __PR((FILE *outfile)); LOCAL int jvd_write __PR((FILE *outfile)); LOCAL int jpathtab_size __PR((UInt32_t starting_extent)); /* * conv_charset: convert to/from charsets via Unicode. * * Any unknown character is set to '_' * */ EXPORT void conv_charset(to, tosizep, from, fromsizep, inls, onls) unsigned char *to; size_t *tosizep; unsigned char *from; size_t *fromsizep; siconvt_t *inls; siconvt_t *onls; { UInt16_t unichar; size_t fromsize = *fromsizep; size_t tosize = *tosizep; Uchar ob[2]; /* 2 octets (16 Bit) UCS-2 */ if (fromsize == 0 || tosize == 0) return; /* * If we have a null mapping, just return the input character */ if (inls->sic_name == onls->sic_name) { *to = *from; (*fromsizep)--; (*tosizep)--; return; } #ifdef USE_ICONV #ifdef HAVE_ICONV_CONST #define __IC_CONST const #else #define __IC_CONST #endif if (use_iconv(inls)) { char *obuf = (char *)ob; size_t osize = 2; /* UCS-2 character size */ if (iconv(inls->sic_cd2uni, (__IC_CONST char **)&from, fromsizep, &obuf, &osize) == -1) { int err = geterrno(); if ((err == EINVAL || err == EILSEQ) && *fromsizep == fromsize) { ob[0] = 0; ob[1] = '_'; (*fromsizep)--; } } unichar = ob[0] * 256 + ob[1]; /* Compute 16 Bit UCS-2 char */ } else #endif { unsigned char c = *from; unichar = sic_c2uni(inls, c); /* Get the UNICODE char */ (*fromsizep)--; if (unichar == 0) unichar = '_'; ob[0] = unichar >> 8 & 0xFF; /* Compute 2 octet variant */ ob[1] = unichar & 0xFF; } #ifdef USE_ICONV if (use_iconv(onls)) { char *ibuf = (char *)ob; size_t isize = 2; /* UCS-2 character size */ if (iconv(onls->sic_uni2cd, (__IC_CONST char **)&ibuf, &isize, (char **)&to, tosizep) == -1) { int err = geterrno(); if ((err == EINVAL || err == EILSEQ) && *tosizep == tosize) { *to = '_'; (*tosizep)--; } } } else #endif { *to = sic_uni2c(onls, unichar); /* Get the backconverted char */ (*tosizep)--; } } /* * Function: convert_to_unicode * * Purpose: Perform a unicode conversion on a text string * using the supplied input character set. * * Notes: */ #ifdef UDF EXPORT void #else LOCAL void #endif convert_to_unicode(buffer, size, source, inls) unsigned char *buffer; int size; char *source; siconvt_t *inls; { unsigned char *tmpbuf; int i; int j; UInt16_t unichar; unsigned char uc; int jsize = size; /* * If we get a NULL pointer for the source, it means we have an * inplace copy, and we need to make a temporary working copy first. */ if (source == NULL) { tmpbuf = (Uchar *) e_malloc(size); memcpy(tmpbuf, buffer, size); } else { tmpbuf = (Uchar *) source; } /* * Now start copying characters. If the size was specified to be 0, * then assume the input was 0 terminated. */ j = 0; for (i = 0; (i + 1) < size; i += 2, j++) { /* Size may be odd! */ /* * Let all valid unicode characters pass * through (according to charset). Others are set to '_' . */ if (j < jsize) uc = tmpbuf[j]; /* temporary copy */ else uc = '\0'; if (uc == '\0') { jsize = j; unichar = 0; } else { /* must be converted */ #ifdef USE_ICONV if (use_iconv(inls)) { Uchar ob[2]; __IC_CONST char *inbuf = (char *)&tmpbuf[j]; size_t isize = 3; char *obuf = (char *)ob; size_t osize = 2; /* * iconv() from glibc ignores osize and thus * may try to access more than a single multi * byte character from the input and read from * non-existent memory. */ if (iconv(inls->sic_cd2uni, &inbuf, &isize, &obuf, &osize) == -1) { int err = geterrno(); if ((err == EINVAL || err == EILSEQ) && isize == 3) { ob[0] = ob[1] = 0; isize--; } } unichar = ob[0] * 256 + ob[1]; j += 2 - isize; } else #endif unichar = sic_c2uni(inls, uc); /* Get the UNICODE */ /* * This code is currently also used for UDF formatting. * Do not enforce silly Microsoft limitations in case * that we only create UDF extensions. */ if (!use_Joliet) goto all_chars; if (unichar <= 0x1f || unichar == 0x7f) unichar = '\0'; /* control char */ switch (unichar) { /* test special characters */ case '*': case '/': case ':': case ';': case '?': case '\\': case '\0': /* illegal char mark */ /* * Even Joliet has some standards as to what is * allowed in a pathname. Pretty tame in * comparison to what DOS restricts you to. */ unichar = '_'; } all_chars: ; } buffer[i] = unichar >> 8 & 0xFF; /* final UNICODE */ buffer[i + 1] = unichar & 0xFF; /* conversion */ } if (size & 1) { /* beautification */ buffer[size - 1] = 0; } if (source == NULL) { free(tmpbuf); } } /* * Function: joliet_strlen * * Purpose: Return length in bytes of string after conversion to unicode. * * Notes: This is provided mainly as a convenience so that when more * intelligent Unicode conversion for either Multibyte or 8-bit * codes is available that we can easily adapt. */ #ifdef UDF EXPORT int #else LOCAL int #endif joliet_strlen(string, maxlen, inls) const char *string; size_t maxlen; siconvt_t *inls; { int rtn = 0; #ifdef USE_ICONV if (use_iconv(inls)) { int j = 0; while (string[j] != '\0') { Uchar ob[2]; __IC_CONST char *inbuf = (char *)&string[j]; size_t isize = 3; char *obuf = (char *)ob; size_t osize = 2; /* * iconv() from glibc ignores osize and thus * may try to access more than a single multi * byte character from the input and read from * non-existent memory. */ if (iconv(inls->sic_cd2uni, &inbuf, &isize, &obuf, &osize) == -1) { int err = geterrno(); if ((err == EINVAL || err == EILSEQ) && isize == 3) { ob[0] = ob[1] = 0; isize--; } } j += 3 - isize; rtn += 2; } } else #endif rtn = strlen(string) << 1; /* * We do clamp the maximum length of a Joliet or UDF string to be the * maximum path size. */ if (rtn > 2*maxlen) { rtn = 2*maxlen; } return (rtn); } /* * Function: get_joliet_vol_desc * * Purpose: generate a Joliet compatible volume desc. * * Notes: Assume that we have the non-joliet vol desc * already present in the buffer. Just modifiy the * appropriate fields. */ LOCAL void get_joliet_vol_desc(jvol_desc) struct iso_primary_descriptor *jvol_desc; { jvol_desc->type[0] = ISO_VD_SUPPLEMENTARY; jvol_desc->version[0] = 1; jvol_desc->file_structure_version[0] = 1; /* * For now, always do Unicode level 3. * I don't really know what 1 and 2 are - perhaps a more limited * Unicode set. * FIXME(eric) - how does Romeo fit in here? */ sprintf(jvol_desc->escape_sequences, "%%/%c", ucs_codes[ucs_level]); /* Until we have Unicode path tables, leave these unset. */ set_733((char *)jvol_desc->path_table_size, jpath_table_size); set_731(jvol_desc->type_l_path_table, jpath_table[0]); set_731(jvol_desc->opt_type_l_path_table, jpath_table[1]); set_732(jvol_desc->type_m_path_table, jpath_table[2]); set_732(jvol_desc->opt_type_m_path_table, jpath_table[3]); /* Set this one up. */ memcpy(jvol_desc->root_directory_record, &jroot_record, offsetof(struct iso_directory_record, name[0]) + 1); /* * Finally, we have a bunch of strings to convert to Unicode. * FIXME(eric) - I don't know how to do this in general, * so we will just be really lazy and do a char -> short conversion. * We probably will want to filter any characters >= 0x80. */ convert_to_unicode((Uchar *)jvol_desc->system_id, sizeof (jvol_desc->system_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->volume_id, sizeof (jvol_desc->volume_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->volume_set_id, sizeof (jvol_desc->volume_set_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->publisher_id, sizeof (jvol_desc->publisher_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->preparer_id, sizeof (jvol_desc->preparer_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->application_id, sizeof (jvol_desc->application_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->copyright_file_id, sizeof (jvol_desc->copyright_file_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->abstract_file_id, sizeof (jvol_desc->abstract_file_id), NULL, in_nls); convert_to_unicode((Uchar *)jvol_desc->bibliographic_file_id, sizeof (jvol_desc->bibliographic_file_id), NULL, in_nls); } /* * Asssign Joliet & UDF addresses * We ignore all files that are neither in the Joliet nor in the UDF tree */ LOCAL void assign_joliet_directory_addresses(node) struct directory *node; { int dir_size; struct directory *dpnt; dpnt = node; while (dpnt) { if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) { /* * If we already have an extent for this * (i.e. it came from a multisession disc), then * don't reassign a new extent. */ dpnt->jpath_index = next_jpath_index++; if (dpnt->jextent == 0) { dpnt->jextent = last_extent; dir_size = ISO_BLOCKS(dpnt->jsize); last_extent += dir_size; } } /* skip if hidden - but not for the rr_moved dir */ if (dpnt->subdir && ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0 || dpnt == reloc_dir)) { assign_joliet_directory_addresses(dpnt->subdir); } dpnt = dpnt->next; } } LOCAL void build_jpathlist(node) struct directory *node; { struct directory *dpnt; dpnt = node; while (dpnt) { if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) { jpathlist[dpnt->jpath_index] = dpnt; } if (dpnt->subdir) build_jpathlist(dpnt->subdir); dpnt = dpnt->next; } } /* build_jpathlist(... */ LOCAL int joliet_compare_paths(r, l) void const *r; void const *l; { struct directory const *ll = *(struct directory * const *) l; struct directory const *rr = *(struct directory * const *) r; int rparent, lparent; char *rpnt, *lpnt; unsigned char rtmp[2], ltmp[2]; siconvt_t *rinls, *linls; /* make sure root directory is first */ if (rr == root) return (-1); if (ll == root) return (1); rparent = rr->parent->jpath_index; lparent = ll->parent->jpath_index; if (rr->parent == reloc_dir) { rparent = rr->self->parent_rec->filedir->jpath_index; } if (ll->parent == reloc_dir) { lparent = ll->self->parent_rec->filedir->jpath_index; } if (rparent < lparent) { return (-1); } if (rparent > lparent) { return (1); } #ifdef APPLE_HYB /* * we may be using the HFS name - so select the correct input * charset */ if (USE_MAC_NAME(rr->self)) { rpnt = rr->self->hfs_ent->name; rinls = hfs_inls; } else { rpnt = rr->self->name; rinls = in_nls; } if (USE_MAC_NAME(ll->self)) { lpnt = ll->self->hfs_ent->name; linls = hfs_inls; } else { lpnt = ll->self->name; linls = in_nls; } #else rpnt = rr->self->name; lpnt = ll->self->name; linls = rinls = in_nls; #endif /* APPLE_HYB */ /* compare the Unicode names */ while (*rpnt && *lpnt) { convert_to_unicode(rtmp, 2, rpnt, rinls); convert_to_unicode(ltmp, 2, lpnt, linls); if (a_to_u_2_byte(rtmp) < a_to_u_2_byte(ltmp)) return (-1); if (a_to_u_2_byte(rtmp) > a_to_u_2_byte(ltmp)) return (1); rpnt++; lpnt++; } if (*rpnt) return (1); if (*lpnt) return (-1); return (0); } /* compare_paths(... */ LOCAL int generate_joliet_path_tables() { struct directory_entry *de; struct directory *dpnt; int fix; int j; int namelen; char *npnt; char *npnt1; int tablesize; unsigned int jpindex; /* First allocate memory for the tables and initialize the memory */ tablesize = jpath_blocks << 11; jpath_table_m = (char *)e_malloc(tablesize); jpath_table_l = (char *)e_malloc(tablesize); memset(jpath_table_l, 0, tablesize); memset(jpath_table_m, 0, tablesize); /* Now start filling in the path tables. Start with root directory */ jpath_table_index = 0; jpathlist = (struct directory **)e_malloc(sizeof (struct directory *) * next_jpath_index); memset(jpathlist, 0, sizeof (struct directory *) * next_jpath_index); build_jpathlist(root); do { fix = 0; #ifdef PROTOTYPES qsort(&jpathlist[1], next_jpath_index - 1, sizeof (struct directory *), (int (*) (const void *, const void *)) joliet_compare_paths); #else qsort(&jpathlist[1], next_jpath_index - 1, sizeof (struct directory *), joliet_compare_paths); #endif for (j = 1; j < next_jpath_index; j++) { if (jpathlist[j]->jpath_index != j) { jpathlist[j]->jpath_index = j; fix++; } } } while (fix); for (j = 1; j < next_jpath_index; j++) { dpnt = jpathlist[j]; if (!dpnt) { comerrno(EX_BAD, _("Entry %d not in path tables\n"), j); } npnt = dpnt->de_name; npnt1 = strrchr(npnt, PATH_SEPARATOR); if (npnt1) { npnt = npnt1 + 1; } de = dpnt->self; if (!de) { comerrno(EX_BAD, _("Fatal Joliet goof - directory has amnesia\n")); } #ifdef APPLE_HYB if (USE_MAC_NAME(de)) namelen = joliet_strlen(de->hfs_ent->name, jlen, hfs_inls); else #endif /* APPLE_HYB */ namelen = joliet_strlen(de->name, jlen, in_nls); if (dpnt == root) { jpath_table_l[jpath_table_index] = 1; jpath_table_m[jpath_table_index] = 1; } else { jpath_table_l[jpath_table_index] = namelen; jpath_table_m[jpath_table_index] = namelen; } jpath_table_index += 2; set_731(jpath_table_l + jpath_table_index, dpnt->jextent); set_732(jpath_table_m + jpath_table_index, dpnt->jextent); jpath_table_index += 4; if (dpnt->parent != reloc_dir) { set_721(jpath_table_l + jpath_table_index, dpnt->parent->jpath_index); set_722(jpath_table_m + jpath_table_index, dpnt->parent->jpath_index); jpindex = dpnt->parent->jpath_index; } else { set_721(jpath_table_l + jpath_table_index, dpnt->self->parent_rec->filedir->jpath_index); set_722(jpath_table_m + jpath_table_index, dpnt->self->parent_rec->filedir->jpath_index); jpindex = dpnt->self->parent_rec->filedir->jpath_index; } if (jpindex > 0xffff) { static int warned = 0; if (!warned) { warned++; errmsgno(EX_BAD, _("Unable to generate sane Joliet path tables - too many directories (%u)\n"), jpindex); if (!nolimitpathtables) errmsgno(EX_BAD, _("Try to use the option -no-limit-pathtables\n")); } if (!nolimitpathtables) exit(EX_BAD); /* * Let it point to the root directory instead. */ set_721(jpath_table_l + jpath_table_index, 1); set_722(jpath_table_m + jpath_table_index, 1); } jpath_table_index += 2; /* * The root directory is still represented in non-unicode * fashion. */ if (dpnt == root) { jpath_table_l[jpath_table_index] = 0; jpath_table_m[jpath_table_index] = 0; jpath_table_index++; } else { #ifdef APPLE_HYB if (USE_MAC_NAME(de)) { convert_to_unicode((Uchar *) jpath_table_l + jpath_table_index, namelen, de->hfs_ent->name, hfs_inls); convert_to_unicode((Uchar *) jpath_table_m + jpath_table_index, namelen, de->hfs_ent->name, hfs_inls); } else { #endif /* APPLE_HYB */ convert_to_unicode((Uchar *) jpath_table_l + jpath_table_index, namelen, de->name, in_nls); convert_to_unicode((Uchar *) jpath_table_m + jpath_table_index, namelen, de->name, in_nls); #ifdef APPLE_HYB } #endif /* APPLE_HYB */ jpath_table_index += namelen; } if (jpath_table_index & 1) { jpath_table_index++; /* For odd lengths we pad */ } } free(jpathlist); if (jpath_table_index != jpath_table_size) { errmsgno(EX_BAD, _("Joliet path table lengths do not match %d expected: %d\n"), jpath_table_index, jpath_table_size); } return (0); } /* generate_path_tables(... */ LOCAL void generate_one_joliet_directory(dpnt, outfile) struct directory *dpnt; FILE *outfile; { unsigned int dir_index; char *directory_buffer; int new_reclen; struct directory_entry *s_entry; struct directory_entry *s_entry1; struct iso_directory_record jrec; unsigned int total_size; int cvt_len; struct directory *finddir; total_size = ISO_ROUND_UP(dpnt->jsize); directory_buffer = (char *)e_malloc(total_size); memset(directory_buffer, 0, total_size); dir_index = 0; s_entry = dpnt->jcontents; while (s_entry) { if (s_entry->de_flags & INHIBIT_JOLIET_ENTRY) { s_entry = s_entry->jnext; continue; } /* * If this entry was a directory that was relocated, * we have a bit of trouble here. We need to dig out the real * thing and put it back here. In the Joliet tree, there is * no relocated rock ridge, as there are no depth limits to a * directory tree. */ if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) { for (s_entry1 = reloc_dir->contents; s_entry1; s_entry1 = s_entry1->next) { if (s_entry1->parent_rec == s_entry) { break; } } if (s_entry1 == NULL) { /* We got trouble. */ comerrno(EX_BAD, _("Unable to locate relocated directory\n")); } } else { s_entry1 = s_entry; } /* * We do not allow directory entries to cross sector * boundaries. Simply pad, and then start the next entry at * the next sector */ new_reclen = s_entry1->jreclen; if ((dir_index & (SECTOR_SIZE - 1)) + new_reclen >= SECTOR_SIZE) { dir_index = ISO_ROUND_UP(dir_index); } memcpy(&jrec, &s_entry1->isorec, offsetof(struct iso_directory_record, name[0])); #ifdef APPLE_HYB /* Use the HFS name if it exists */ if (USE_MAC_NAME(s_entry1)) cvt_len = joliet_strlen(s_entry1->hfs_ent->name, jlen, hfs_inls); else #endif /* APPLE_HYB */ cvt_len = joliet_strlen(s_entry1->name, jlen, in_nls); /* * Fix the record length * - this was the non-Joliet version we were seeing. */ jrec.name_len[0] = cvt_len; jrec.length[0] = s_entry1->jreclen; /* * If this is a directory, * fix the correct size and extent number. */ if ((jrec.flags[0] & ISO_DIRECTORY) != 0) { if (strcmp(s_entry1->name, ".") == 0) { jrec.name_len[0] = 1; set_733((char *)jrec.extent, dpnt->jextent); set_733((char *)jrec.size, ISO_ROUND_UP(dpnt->jsize)); } else if (strcmp(s_entry1->name, "..") == 0) { jrec.name_len[0] = 1; if (dpnt->parent == reloc_dir) { set_733((char *)jrec.extent, dpnt->self->parent_rec->filedir->jextent); set_733((char *)jrec.size, ISO_ROUND_UP(dpnt->self->parent_rec->filedir->jsize)); } else { set_733((char *)jrec.extent, dpnt->parent->jextent); set_733((char *)jrec.size, ISO_ROUND_UP(dpnt->parent->jsize)); } } else { if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0) { finddir = reloc_dir->subdir; } else { finddir = dpnt->subdir; } while (finddir && finddir->self != s_entry1) { finddir = finddir->next; } if (!finddir) { comerrno(EX_BAD, _("Fatal goof - unable to find directory location\n")); } set_733((char *)jrec.extent, finddir->jextent); set_733((char *)jrec.size, ISO_ROUND_UP(finddir->jsize)); } } memcpy(directory_buffer + dir_index, &jrec, offsetof(struct iso_directory_record, name[0])); dir_index += offsetof(struct iso_directory_record, name[0]); /* * Finally dump the Unicode version of the filename. * Note - . and .. are the same as with non-Joliet discs. */ if ((jrec.flags[0] & ISO_DIRECTORY) != 0 && strcmp(s_entry1->name, ".") == 0) { directory_buffer[dir_index++] = 0; } else if ((jrec.flags[0] & ISO_DIRECTORY) != 0 && strcmp(s_entry1->name, "..") == 0) { directory_buffer[dir_index++] = 1; } else { #ifdef APPLE_HYB if (USE_MAC_NAME(s_entry1)) { /* Use the HFS name if it exists */ convert_to_unicode( (Uchar *) directory_buffer+dir_index, cvt_len, s_entry1->hfs_ent->name, hfs_inls); } else #endif /* APPLE_HYB */ { convert_to_unicode( (Uchar *) directory_buffer+dir_index, cvt_len, s_entry1->name, in_nls); } dir_index += cvt_len; } if (dir_index & 1) { directory_buffer[dir_index++] = 0; } s_entry = s_entry->jnext; } if (dpnt->jsize != dir_index) { errmsgno(EX_BAD, _("Unexpected joliet directory length %d expected: %d '%s'\n"), dpnt->jsize, dir_index, dpnt->de_name); } xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE); last_extent_written += total_size >> 11; free(directory_buffer); } /* generate_one_joliet_directory(... */ LOCAL int joliet_sort_n_finish(this_dir) struct directory *this_dir; { struct directory_entry *s_entry; int status = 0; /* * don't want to skip this directory if it's the reloc_dir * at the moment */ if (this_dir != reloc_dir && this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) { return (0); } for (s_entry = this_dir->contents; s_entry; s_entry = s_entry->next) { /* skip hidden entries */ if ((s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0) { continue; } /* * First update the path table sizes for directories. * * Finally, set the length of the directory entry if Joliet is * used. The name is longer, but no Rock Ridge is ever used * here, so depending upon the options the entry size might * turn out to be about the same. The Unicode name is always * a multiple of 2 bytes, so we always add 1 to make it an * even number. */ if (s_entry->isorec.flags[0] & ISO_DIRECTORY) { if (strcmp(s_entry->name, ".") != 0 && strcmp(s_entry->name, "..") != 0) { #ifdef APPLE_HYB if (USE_MAC_NAME(s_entry)) /* Use the HFS name if it exists */ jpath_table_size += joliet_strlen(s_entry->hfs_ent->name, jlen, hfs_inls) + offsetof(struct iso_path_table, name[0]); else #endif /* APPLE_HYB */ jpath_table_size += joliet_strlen(s_entry->name, jlen, in_nls) + offsetof(struct iso_path_table, name[0]); if (jpath_table_size & 1) { jpath_table_size++; } } else { if (this_dir == root && strlen(s_entry->name) == 1) { jpath_table_size += 1 + offsetof(struct iso_path_table, name[0]); if (jpath_table_size & 1) jpath_table_size++; } } } if (strcmp(s_entry->name, ".") != 0 && strcmp(s_entry->name, "..") != 0) { #ifdef APPLE_HYB if (USE_MAC_NAME(s_entry)) /* Use the HFS name if it exists */ s_entry->jreclen = offsetof(struct iso_directory_record, name[0]) + joliet_strlen(s_entry->hfs_ent->name, jlen, hfs_inls) + 1; else #endif /* APPLE_HYB */ s_entry->jreclen = offsetof(struct iso_directory_record, name[0]) + joliet_strlen(s_entry->name, jlen, in_nls) + 1; } else { /* * Special - for '.' and '..' we generate the same * records we did for non-Joliet discs. */ s_entry->jreclen = offsetof(struct iso_directory_record, name[0]) + 1; } } if ((this_dir->dir_flags & INHIBIT_JOLIET_ENTRY) != 0) { return (0); } this_dir->jcontents = this_dir->contents; status = joliet_sort_directory(&this_dir->jcontents); /* * Now go through the directory and figure out how large this one will * be. Do not split a directory entry across a sector boundary */ s_entry = this_dir->jcontents; /* * XXX Is it ok to comment this out? */ /*XXX JS this_dir->ce_bytes = 0;*/ for (s_entry = this_dir->jcontents; s_entry; s_entry = s_entry->jnext) { int jreclen; if ((s_entry->de_flags & INHIBIT_JOLIET_ENTRY) != 0) { continue; } jreclen = s_entry->jreclen; if ((this_dir->jsize & (SECTOR_SIZE - 1)) + jreclen >= SECTOR_SIZE) { this_dir->jsize = ISO_ROUND_UP(this_dir->jsize); } this_dir->jsize += jreclen; } return (status); } /* * Similar to the iso9660 case, * except here we perform a full sort based upon the * regular name of the file, not the 8.3 version. */ LOCAL int joliet_compare_dirs(rr, ll) const void *rr; const void *ll; { char *rpnt, *lpnt; struct directory_entry **r, **l; unsigned char rtmp[2], ltmp[2]; siconvt_t *linls, *rinls; r = (struct directory_entry **)rr; l = (struct directory_entry **)ll; #ifdef APPLE_HYB /* * we may be using the HFS name - so select the correct input * charset */ if (USE_MAC_NAME(*r)) { rpnt = (*r)->hfs_ent->name; rinls = hfs_inls; } else { rpnt = (*r)->name; rinls = in_nls; } if (USE_MAC_NAME(*l)) { lpnt = (*l)->hfs_ent->name; linls = hfs_inls; } else { lpnt = (*l)->name; linls = in_nls; } #else rpnt = (*r)->name; lpnt = (*l)->name; rinls = linls = in_nls; #endif /* APPLE_HYB */ /* * If the entries are the same, this is an error. * Joliet specs allow for a maximum of 64 characters. * If we see different multi extent parts, it is OK to * have the same name more than once. */ if (strncmp(rpnt, lpnt, jlen) == 0) { #ifdef USE_LARGEFILES if ((*r)->mxpart == (*l)->mxpart) #endif { errmsgno(EX_BAD, _("Error: %s and %s have the same Joliet name\n"), (*r)->whole_name, (*l)->whole_name); jsort_goof++; { char *p1 = rpnt; char *p2 = lpnt; int len = 0; for (; *p1 == *p2; p1++, p2++, len++) { if (*p1 == '\0') break; } if (len > jsort_glen) jsort_glen = len; } } } /* * Put the '.' and '..' entries on the head of the sorted list. * For normal ASCII, this always happens to be the case, but out of * band characters cause this not to be the case sometimes. */ if (strcmp(rpnt, ".") == 0) return (-1); if (strcmp(lpnt, ".") == 0) return (1); if (strcmp(rpnt, "..") == 0) return (-1); if (strcmp(lpnt, "..") == 0) return (1); #ifdef DVD_AUD_VID /* * There're rumors claiming that some players assume VIDEO_TS.IFO * to be the first file in VIDEO_TS/ catalog. Well, it's basically * the only file a player has to actually look for, as the whole * video content can be "rolled down" from this file alone. * */ /* * XXX This code has to be moved from the Joliet implementation * XXX to the UDF implementation if we implement decent UDF support * XXX with a separate name space for the UDF file tree. */ if (dvd_aud_vid_flag & DVD_SPEC_VIDEO) { if (strcmp(rpnt, "VIDEO_TS.IFO") == 0) return (-1); if (strcmp(lpnt, "VIDEO_TS.IFO") == 0) return (1); } #endif while (*rpnt && *lpnt) { if (*rpnt == ';' && *lpnt != ';') return (-1); if (*rpnt != ';' && *lpnt == ';') return (1); if (*rpnt == ';' && *lpnt == ';') return (0); /* * Extensions are not special here. * Don't treat the dot as something that must be bumped to * the start of the list. */ #if 0 if (*rpnt == '.' && *lpnt != '.') return (-1); if (*rpnt != '.' && *lpnt == '.') return (1); #endif convert_to_unicode(rtmp, 2, rpnt, rinls); convert_to_unicode(ltmp, 2, lpnt, linls); if (a_to_u_2_byte(rtmp) < a_to_u_2_byte(ltmp)) return (-1); if (a_to_u_2_byte(rtmp) > a_to_u_2_byte(ltmp)) return (1); rpnt++; lpnt++; } if (*rpnt) return (1); if (*lpnt) return (-1); #ifdef USE_LARGEFILES /* * (*r)->mxpart == (*l)->mxpart cannot happen here */ if ((*r)->mxpart < (*l)->mxpart) return (-1); else if ((*r)->mxpart > (*l)->mxpart) return (1); #endif return (0); } /* * Function: sort_directory * * Purpose: Sort the directory in the appropriate ISO9660 * order. * * Notes: Returns 0 if OK, returns > 0 if an error occurred. */ LOCAL int joliet_sort_directory(sort_dir) struct directory_entry **sort_dir; { int dcount = 0; int i; struct directory_entry *s_entry; struct directory_entry **sortlist; s_entry = *sort_dir; while (s_entry) { /* * only colletc non-hidden entries */ if ((s_entry->de_flags & (INHIBIT_JOLIET_ENTRY|INHIBIT_UDF_ENTRY)) != (INHIBIT_JOLIET_ENTRY|INHIBIT_UDF_ENTRY)) dcount++; s_entry = s_entry->next; } /* OK, now we know how many there are. Build a vector for sorting. */ sortlist = (struct directory_entry **) e_malloc(sizeof (struct directory_entry *) * dcount); dcount = 0; s_entry = *sort_dir; while (s_entry) { /* * only collect non-hidden entries */ if ((s_entry->de_flags & (INHIBIT_JOLIET_ENTRY|INHIBIT_UDF_ENTRY)) != (INHIBIT_JOLIET_ENTRY|INHIBIT_UDF_ENTRY)) { sortlist[dcount] = s_entry; dcount++; } s_entry = s_entry->next; } jsort_goof = 0; jsort_glen = 0; #ifdef PROTOTYPES qsort(sortlist, dcount, sizeof (struct directory_entry *), (int (*) (const void *, const void *)) joliet_compare_dirs); #else qsort(sortlist, dcount, sizeof (struct directory_entry *), joliet_compare_dirs); #endif if (jsort_goof) { errmsgno(EX_BAD, _("Joliet file names differ after %d chars\n"), jsort_glen); if (jsort_glen > JLONGMAX) { errmsgno(EX_BAD, _("Cannot use Joliet, please remove -J from the option list.\n")); } else if (jsort_glen > JMAX) { errmsgno(EX_BAD, _("Try to use the option -joliet-long\n")); } } /* Now reassemble the linked list in the proper sorted order */ for (i = 0; i < dcount - 1; i++) { sortlist[i]->jnext = sortlist[i + 1]; } sortlist[dcount - 1]->jnext = NULL; *sort_dir = sortlist[0]; free(sortlist); return (jsort_goof); } EXPORT int joliet_sort_tree(node) struct directory *node; { struct directory *dpnt; int ret = 0; dpnt = node; while (dpnt) { ret = joliet_sort_n_finish(dpnt); if (ret) { break; } if (dpnt->subdir) ret = joliet_sort_tree(dpnt->subdir); if (ret) { break; } dpnt = dpnt->next; } return (ret); } LOCAL void generate_joliet_directories(node, outfile) struct directory *node; FILE *outfile; { struct directory *dpnt; dpnt = node; while (dpnt) { if ((dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) == 0) { /* * In theory we should never reuse a directory, so this * doesn't make much sense. */ if (dpnt->jextent > session_start) { generate_one_joliet_directory(dpnt, outfile); } } /* skip if hidden - but not for the rr_moved dir */ if (dpnt->subdir && (!(dpnt->dir_flags & INHIBIT_JOLIET_ENTRY) || dpnt == reloc_dir)) { generate_joliet_directories(dpnt->subdir, outfile); } dpnt = dpnt->next; } } /* * Function to write the EVD for the disc. */ LOCAL int jpathtab_write(outfile) FILE *outfile; { /* Next we write the path tables */ xfwrite(jpath_table_l, jpath_blocks << 11, 1, outfile, 0, FALSE); xfwrite(jpath_table_m, jpath_blocks << 11, 1, outfile, 0, FALSE); last_extent_written += 2 * jpath_blocks; free(jpath_table_l); free(jpath_table_m); jpath_table_l = NULL; jpath_table_m = NULL; return (0); } LOCAL int jdirtree_size(starting_extent) UInt32_t starting_extent; { assign_joliet_directory_addresses(root); return (0); } LOCAL int jroot_gen() { jroot_record.length[0] = 1 + offsetof(struct iso_directory_record, name[0]); jroot_record.ext_attr_length[0] = 0; set_733((char *)jroot_record.extent, root->jextent); set_733((char *)jroot_record.size, ISO_ROUND_UP(root->jsize)); iso9660_date(jroot_record.date, root_statbuf.st_mtime); jroot_record.flags[0] = ISO_DIRECTORY; jroot_record.file_unit_size[0] = 0; jroot_record.interleave[0] = 0; set_723(jroot_record.volume_sequence_number, volume_sequence_number); jroot_record.name_len[0] = 1; return (0); } LOCAL int jdirtree_write(outfile) FILE *outfile; { generate_joliet_directories(root, outfile); return (0); } /* * Function to write the EVD for the disc. */ LOCAL int jvd_write(outfile) FILE *outfile; { struct iso_primary_descriptor jvol_desc; /* Next we write out the boot volume descriptor for the disc */ jvol_desc = vol_desc; get_joliet_vol_desc(&jvol_desc); xfwrite(&jvol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE); last_extent_written++; return (0); } /* * Functions to describe padding block at the start of the disc. */ LOCAL int jpathtab_size(starting_extent) UInt32_t starting_extent; { jpath_table[0] = starting_extent; jpath_table[1] = 0; jpath_table[2] = jpath_table[0] + jpath_blocks; jpath_table[3] = 0; last_extent += 2 * jpath_blocks; return (0); } struct output_fragment joliet_desc = {NULL, oneblock_size, jroot_gen, jvd_write, "Joliet Volume Descriptor" }; struct output_fragment jpathtable_desc = {NULL, jpathtab_size, generate_joliet_path_tables, jpathtab_write, "Joliet path table" }; struct output_fragment jdirtree_desc = {NULL, jdirtree_size, NULL, jdirtree_write, "Joliet directory tree" };