diff options
Diffstat (limited to 'genisoimage/joliet.c')
-rw-r--r-- | genisoimage/joliet.c | 1456 |
1 files changed, 1456 insertions, 0 deletions
diff --git a/genisoimage/joliet.c b/genisoimage/joliet.c new file mode 100644 index 0000000..b36f8f7 --- /dev/null +++ b/genisoimage/joliet.c @@ -0,0 +1,1456 @@ +/* + * This file has been modified for the cdrkit suite. + * + * The behaviour and appearence of the program code below can differ to a major + * extent from the version distributed by the original author(s). + * + * For details, see Changelog file distributed with the cdrkit package. If you + * received this file from another source then ask the distributing person for + * a log of modifications. + * + */ + +/* @(#)joliet.c 1.38 05/05/01 joerg */ +/* + * 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,2000,2001 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 <mconfig.h> +#include "genisoimage.h" +#include <timedefs.h> +#include <utypes.h> +#include <intcvt.h> +#include <unls.h> /* For UNICODE translation */ +#include <schily.h> +#include <string.h> + +#ifdef USE_ICONV +#include <iconv.h> +#include <errno.h> +#endif + +static Uint jpath_table_index; +static struct directory **jpathlist; +static int next_jpath_index = 1; +static int jsort_goof; + +static char ucs_codes[] = { + '\0', /* UCS-level 0 is illegal */ + '@', /* UCS-level 1 */ + 'C', /* UCS-level 2 */ + 'E', /* UCS-level 3 */ +}; + +#ifdef UDF +#ifdef USE_ICONV +size_t +#else +void +#endif +convert_to_unicode(unsigned char *buffer, int size, char *source, + struct unls_table *inls); +int joliet_strlen(const char *string, struct unls_table *inls); +#else +#ifdef USE_ICONV +static size_t +#else +static void +#endif +convert_to_unicode(unsigned char *buffer, int size, char *source, + struct unls_table *inls); +static int joliet_strlen(const char *string, struct nls_table *inls); +#endif +static void get_joliet_vol_desc(struct iso_primary_descriptor *jvol_desc); +static void assign_joliet_directory_addresses(struct directory *node); +static void build_jpathlist(struct directory *node); +static int joliet_compare_paths(void const *r, void const *l); +static int generate_joliet_path_tables(void); +static void generate_one_joliet_directory(struct directory *dpnt, + FILE *outfile); +static int joliet_sort_n_finish(struct directory *this_dir); +static int joliet_compare_dirs(const void *rr, const void *ll); +static int joliet_sort_directory(struct directory_entry **sort_dir); +int joliet_sort_tree(struct directory *node); +static void generate_joliet_directories(struct directory *node, FILE *outfile); +static int jpathtab_write(FILE *outfile); +static int jdirtree_size(int starting_extent); +static int jroot_gen(void); +static int jdirtree_write(FILE *outfile); +static int jvd_write(FILE *outfile); +static int jpathtab_size(int starting_extent); + +/* + * conv_charset: convert to/from charsets via Unicode. + * + * Any unknown character is set to '_' + * + */ +unsigned char +conv_charset(unsigned char c, + struct unls_table *inls, + struct unls_table *onls) +{ + unsigned char uh; + unsigned char ul; + unsigned char uc; + unsigned char *up; + + /* if we have a null mapping, just return the input character */ + if (inls == onls) + return (c); + +#ifdef USE_ICONV + if(inls->unls_cs2uni == NULL || onls->unls_uni2cs == NULL) { + /* + * This shouldn't be reached + */ + static BOOL iconv_warned = FALSE; + if(!iconv_warned) { + fprintf(stderr, "Warning: Iconv conversion not supported in conv_charset.\n"); + iconv_warned = TRUE; + } + return (c); + } +#endif + + /* get high and low UNICODE bytes */ + uh = inls->unls_cs2uni[c].unls_high; + ul = inls->unls_cs2uni[c].unls_low; + + /* get the backconverted page from the output charset */ + up = onls->unls_uni2cs[uh]; + + /* if the page exists, get the backconverted character */ + if (up == NULL) + uc = '\0'; + else + uc = up[ul]; + + /* return the backconverted, if it's not NULL */ + return (uc ? uc : '_'); +} + +/* + * Function: convert_to_unicode + * + * Purpose: Perform a unicode conversion on a text string + * using the supplied input character set. + * + * Notes: + */ +#ifdef USE_ICONV +# if UDF +size_t +# else +static size_t +# endif +#else +# if UDF +void +# else +static void +# endif +#endif +convert_to_unicode(unsigned char *buffer, int size, char *source, + struct unls_table *inls) +{ + unsigned char *tmpbuf; + int i; + int j; + unsigned char uh, + ul, + uc, + *up; + + /* + * 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+1); + memcpy(tmpbuf, buffer, size); + tmpbuf[size] = 0; + } else { + tmpbuf = (Uchar *) source; + } + +#ifdef USE_ICONV + if (inls->iconv_d && inls->unls_cs2uni==NULL && + inls->unls_uni2cs==NULL) { + char *inptr = (char *)tmpbuf; + char *outptr = (char *)buffer; + size_t inleft = strlen((char *)tmpbuf); + size_t inlen = inleft; + size_t outleft = size; + + iconv(inls->iconv_d, NULL, NULL, NULL, NULL); + if(iconv(inls->iconv_d, &inptr, &inleft, &outptr, &outleft) == + (size_t)-1 && errno == EILSEQ) { + fprintf(stderr, "Incorrectly encoded string (%s) " + "encountered.\nPossibly creating an invalid " + "Joliet extension. Aborting.\n", source); + exit(1); + } + + for (i = 0; (i + 1) < size - outleft; i += 2) { /* Size may be odd!!!*/ + if (buffer[i]=='\0') { + switch (buffer[i+1]) { /* Invalid characters for Joliet */ + case '*': + case '/': + case ':': + case ';': + case '?': + case '\\': + buffer[i+1]='_'; + default: + if (buffer[i+1] == 0x7f || + buffer[i+1] < 0x20) + buffer[i+1]='_'; + } + } + } + if (size & 1) { /* beautification */ + buffer[size - 1] = 0; + } + if (source == NULL) { + free(tmpbuf); + } + return (inlen - inleft); + } +#endif + + /* + * 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! */ + /* + * JS integrated from: Achim_Kaiser@t-online.de + * SGE modified according to Linux kernel source + * Let all valid unicode characters pass + * through (according to charset). Others are set to '_' . + */ + uc = tmpbuf[j]; /* temporary copy */ + if (uc != '\0') { /* must be converted */ + uh = inls->unls_cs2uni[uc].unls_high; /* convert forward: */ + /* hibyte... */ + ul = inls->unls_cs2uni[uc].unls_low; /* ...lobyte */ + up = inls->unls_uni2cs[uh]; /* convert backward: */ + /* page... */ + if (up == NULL) + uc = '\0'; /* wrong unicode page */ + else + uc = up[ul]; /* backconverted character */ + if (uc != tmpbuf[j]) + uc = '\0'; /* should be identical */ + if (uc <= 0x1f || uc == 0x7f) + uc = '\0'; /* control char */ + switch (uc) { /* 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. + */ + uc = '_'; + } + } + buffer[i] = inls->unls_cs2uni[uc].unls_high; /* final UNICODE */ + /* conversion */ + buffer[i + 1] = inls->unls_cs2uni[uc].unls_low; + } + + if (size & 1) { /* beautification */ + buffer[size - 1] = 0; + } + if (source == NULL) { + free(tmpbuf); + } +#ifdef USE_ICONV + return j; +#endif +} + +/* + * 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 +int +#else +static int +#endif +joliet_strlen(const char *string, struct unls_table *inls) +{ + int rtn; + +#ifdef USE_ICONV + if (inls->iconv_d && inls->unls_cs2uni==NULL && + inls->unls_uni2cs==NULL) { + /* + * we const-cast since we're sure iconv won't change + * the string itself + */ + char *string_ptr = (char *)string; + size_t string_len = strlen(string); + + /* + * iconv has no way of finding out the required size + * in the target + */ + + char *tmp, *tmp_ptr; + /* we assume that the maximum length is 2 * jlen */ + size_t tmp_len = (size_t)jlen * 2 + 1; + tmp = e_malloc(tmp_len); + tmp_ptr = tmp; + + iconv(inls->iconv_d, NULL, NULL, NULL, NULL); + iconv(inls->iconv_d, &string_ptr, &string_len, &tmp_ptr, + &tmp_len); + + /* + * iconv advanced the tmp pointer with as many chars + * as it has written to it, so we add up the delta + */ + rtn = (tmp_ptr - tmp); + + free(tmp); + } else { + rtn = strlen(string) << 1; + } +#else + rtn = strlen(string) << 1; +#endif + + /* + * We do clamp the maximum length of a Joliet string to be the + * maximum path size. This helps to ensure that we don't completely + * bolix things up with very long paths. The Joliet specs say that + * the maximum length is 128 bytes, or 64 unicode characters. + */ + if (rtn > 2*jlen) { + rtn = 2*jlen; + } + 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. + */ +static void +get_joliet_vol_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); +} + +static void +assign_joliet_directory_addresses(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) || dpnt == reloc_dir)) { + assign_joliet_directory_addresses(dpnt->subdir); + } + dpnt = dpnt->next; + } +} + +static void +build_jpathlist(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(... */ + +static int +joliet_compare_paths(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]; + struct unls_table *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) { +#ifdef USE_ICONV + size_t ri, li; + + ri = convert_to_unicode(rtmp, 2, rpnt, rinls); + li = convert_to_unicode(ltmp, 2, lpnt, linls); + rpnt += ri; + lpnt += li; + if(!ri && !li) + return (0); + else if(ri && !li) + return (1); + else if(!ri && li) + return (-1); +#else + convert_to_unicode(rtmp, 2, rpnt, rinls); + convert_to_unicode(ltmp, 2, lpnt, linls); +#endif + + 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); + +#ifndef USE_ICONV + rpnt++; + lpnt++; +#endif + } + + if (*rpnt) + return (1); + if (*lpnt) + return (-1); + + return (0); + +}/* compare_paths(... */ + +static int +generate_joliet_path_tables() +{ + struct directory_entry *de; + struct directory *dpnt; + int fix; + int j; + int namelen; + char *npnt; + char *npnt1; + int tablesize; + + /* 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) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Entry %d not in path tables\n", j); +#else + fprintf(stderr, "Entry %d not in path tables\n", j); + exit(1); +#endif + } + npnt = dpnt->de_name; + + npnt1 = strrchr(npnt, PATH_SEPARATOR); + if (npnt1) { + npnt = npnt1 + 1; + } + de = dpnt->self; + if (!de) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Fatal Joliet goof - directory has amnesia\n"); +#else + fprintf(stderr, + "Fatal Joliet goof - directory has amnesia\n"); + exit(1); +#endif + } +#ifdef APPLE_HYB + if (USE_MAC_NAME(de)) + namelen = joliet_strlen(de->hfs_ent->name, hfs_inls); + else +#endif /* APPLE_HYB */ + namelen = joliet_strlen(de->name, 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->jpath_index > 0xffff) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Unable to generate sane path tables - too many directories (%d)\n", + dpnt->parent->jpath_index); +#else + fprintf(stderr, + "Unable to generate sane path tables - too many directories (%d)\n", + dpnt->parent->jpath_index); + exit(1); +#endif + } + + 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); + } 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); + } + + 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) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "Joliet path table lengths do not match %d expected: %d\n", + jpath_table_index, + jpath_table_size); +#else + fprintf(stderr, + "Joliet path table lengths do not match %d expected: %d\n", + jpath_table_index, + jpath_table_size); +#endif + } + return (0); +}/* generate_path_tables(... */ + +static void +generate_one_joliet_directory(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. */ +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, + "Unable to locate relocated directory\n"); +#else + fprintf(stderr, + "Unable to locate relocated directory\n"); + exit(1); +#endif + } + } 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, hfs_inls); + else +#endif /* APPLE_HYB */ + cvt_len = joliet_strlen(s_entry1->name, 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 (1 == 1) { + if (finddir->self == s_entry1) + break; + finddir = finddir->next; + if (!finddir) { +#ifdef USE_LIBSCHILY + comerrno(EX_BAD, "Fatal goof - unable to find directory location\n"); +#else + fprintf(stderr, "Fatal goof - unable to find directory location\n"); + exit(1); +#endif + } + } + 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) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "Unexpected joliet directory length %d expected: %d '%s'\n", + dpnt->jsize, + dir_index, dpnt->de_name); +#else + fprintf(stderr, + "Unexpected joliet directory length %d expected: %d '%s'\n", + dpnt->jsize, + dir_index, dpnt->de_name); +#endif + } + jtwrite(directory_buffer, total_size, 1, 0, FALSE); + xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE); + last_extent_written += total_size >> 11; + free(directory_buffer); +}/* generate_one_joliet_directory(... */ + +static int +joliet_sort_n_finish(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, hfs_inls) + + offsetof(struct iso_path_table, name[0]); + else +#endif /* APPLE_HYB */ + jpath_table_size += + joliet_strlen(s_entry->name, 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, hfs_inls) + + 1; + else +#endif /* APPLE_HYB */ + s_entry->jreclen = + offsetof(struct iso_directory_record, name[0]) + + joliet_strlen(s_entry->name, 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. + */ +static int +joliet_compare_dirs(const void *rr, const void *ll) +{ + char *rpnt, + *lpnt; + struct directory_entry **r, + **l; + unsigned char rtmp[2], + ltmp[2]; + struct unls_table *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 (strncmp(rpnt, lpnt, jlen) == 0) { +#ifdef USE_LIBSCHILY + errmsgno(EX_BAD, + "Error: %s and %s have the same Joliet name\n", + (*r)->whole_name, (*l)->whole_name); +#else + fprintf(stderr, + "Error: %s and %s have the same Joliet name\n", + (*r)->whole_name, (*l)->whole_name); +#endif + jsort_goof++; + } + /* + * 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_VIDEO + /* + * 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. + * <appro@fy.chalmers.se> + */ + /* + * 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_video) { + if (strcmp(rpnt, "VIDEO_TS.IFO") == 0) + return (-1); + if (strcmp(lpnt, "VIDEO_TS.IFO") == 0) + return (1); + } +#endif + + while (*rpnt && *lpnt) { +#ifdef USE_ICONV + size_t ri, li; +#endif + 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 + +#ifdef USE_ICONV + + ri = convert_to_unicode(rtmp, 2, rpnt, rinls); + li = convert_to_unicode(ltmp, 2, lpnt, linls); + rpnt += ri; + lpnt += li; + if(!ri && !li) + return (0); + else if(ri && !li) + return (1); + else if(!ri && li) + return (-1); +#else + convert_to_unicode(rtmp, 2, rpnt, rinls); + convert_to_unicode(ltmp, 2, lpnt, linls); +#endif + + 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); + +#ifndef USE_ICONV + rpnt++; + lpnt++; +#endif + } + if (*rpnt) + return (1); + if (*lpnt) + return (-1); + 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. + */ +static int +joliet_sort_directory(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) { + /* skip hidden entries */ + if (!(s_entry->de_flags & INHIBIT_JOLIET_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) { + /* skip hidden entries */ + if (!(s_entry->de_flags & INHIBIT_JOLIET_ENTRY)) { + sortlist[dcount] = s_entry; + dcount++; + } + s_entry = s_entry->next; + } + + jsort_goof = 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 + + /* 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); +} + +int +joliet_sort_tree(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); +} + +static void +generate_joliet_directories(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. + */ +static int +jpathtab_write(FILE *outfile) +{ + /* Next we write the path tables */ + jtwrite(jpath_table_l, jpath_blocks << 11, 1, 0, FALSE); + xfwrite(jpath_table_l, jpath_blocks << 11, 1, outfile, 0, FALSE); + last_extent_written += jpath_blocks; + jtwrite(jpath_table_m, jpath_blocks << 11, 1, 0, FALSE); + xfwrite(jpath_table_m, jpath_blocks << 11, 1, outfile, 0, FALSE); + last_extent_written += jpath_blocks; + free(jpath_table_l); + free(jpath_table_m); + jpath_table_l = NULL; + jpath_table_m = NULL; + return (0); +} + +static int +jdirtree_size(int starting_extent) +{ + assign_joliet_directory_addresses(root); + return (0); +} + +static 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); +} + +static int +jdirtree_write(FILE *outfile) +{ + generate_joliet_directories(root, outfile); + return (0); +} + +/* + * Function to write the EVD for the disc. + */ +static int +jvd_write(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); + jtwrite(&jvol_desc, SECTOR_SIZE, 1, 0, FALSE); + 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. + */ +static int +jpathtab_size(int 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" }; |