"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libisofs/iso1999.c" (30 Jan 2021, 30379 Bytes) of package /linux/misc/xorriso-1.5.4.pl02.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "iso1999.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

    1 /*
    2  * Copyright (c) 2007 Vreixo Formoso
    3  * Copyright (c) 2011-2014 Thomas Schmitt
    4  *
    5  * This file is part of the libisofs project; you can redistribute it and/or
    6  * modify it under the terms of the GNU General Public License version 2 
    7  * or later as published by the Free Software Foundation. 
    8  * See COPYING file for details.
    9  */
   10 
   11 #ifdef HAVE_CONFIG_H
   12 #include "../config.h"
   13 #endif
   14 
   15 #include "iso1999.h"
   16 #include "messages.h"
   17 #include "writer.h"
   18 #include "image.h"
   19 #include "filesrc.h"
   20 #include "eltorito.h"
   21 #include "util.h"
   22 #include "ecma119.h"
   23 
   24 #include <stdlib.h>
   25 #include <stdio.h>
   26 #include <string.h>
   27 
   28 static
   29 int get_iso1999_name(Ecma119Image *t, const char *str, char **fname)
   30 {
   31     int ret;
   32     char *name;
   33 
   34     if (fname == NULL) {
   35         return ISO_ASSERT_FAILURE;
   36     }
   37 
   38     if (str == NULL) {
   39         /* not an error, can be root node */
   40         *fname = NULL;
   41         return ISO_SUCCESS;
   42     }
   43 
   44     if (!strcmp(t->input_charset, t->output_charset)) {
   45         /* no conversion needed */
   46         name = strdup(str);
   47     } else {
   48         ret = strconv(str, t->input_charset, t->output_charset, &name);
   49         if (ret < 0) {
   50             ret = iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret,
   51                 "Charset conversion error. Can't convert %s from %s to %s",
   52                 str, t->input_charset, t->output_charset);
   53             if (ret < 0) {
   54                 return ret; /* aborted */
   55             }
   56 
   57             /* use the original name, it's the best we can do */
   58             name = strdup(str);
   59         }
   60     }
   61 
   62     /* ISO 9660:1999 7.5.1 */
   63     if (strlen(name) > 207) {
   64         name[207] = '\0';
   65     }
   66 
   67     *fname = name;
   68 
   69     return ISO_SUCCESS;
   70 }
   71 
   72 static
   73 void iso1999_node_free(Iso1999Node *node)
   74 {
   75     if (node == NULL) {
   76         return;
   77     }
   78     if (node->type == ISO1999_DIR) {
   79         size_t i;
   80         for (i = 0; i < node->info.dir->nchildren; i++) {
   81             iso1999_node_free(node->info.dir->children[i]);
   82         }
   83         if (node->info.dir->children != NULL)
   84             free(node->info.dir->children);
   85         free(node->info.dir);
   86     }
   87     iso_node_unref(node->node);
   88     free(node->name);
   89     free(node);
   90 }
   91 
   92 /**
   93  * Create a low level ISO 9660:1999 node
   94  * @return
   95  *      1 success, 0 ignored, < 0 error
   96  */
   97 static
   98 int create_node(Ecma119Image *t, IsoNode *iso, Iso1999Node **node)
   99 {
  100     int ret;
  101     Iso1999Node *n;
  102 
  103     n = calloc(1, sizeof(Iso1999Node));
  104     if (n == NULL) {
  105         return ISO_OUT_OF_MEM;
  106     }
  107 
  108     if (iso->type == LIBISO_DIR) {
  109         IsoDir *dir = (IsoDir*) iso;
  110         n->info.dir = calloc(1, sizeof(struct iso1999_dir_info));
  111         if (n->info.dir == NULL) {
  112             free(n);
  113             return ISO_OUT_OF_MEM;
  114         }
  115         n->info.dir->children = NULL;
  116         if (dir->nchildren > 0) {
  117             n->info.dir->children = calloc(sizeof(void*), dir->nchildren);
  118             if (n->info.dir->children == NULL) {
  119                 free(n->info.dir);
  120                 free(n);
  121                 return ISO_OUT_OF_MEM;
  122             }
  123         }
  124         n->type = ISO1999_DIR;
  125     } else if (iso->type == LIBISO_FILE) {
  126         /* it's a file */
  127         off_t size;
  128         IsoFileSrc *src;
  129         IsoFile *file = (IsoFile*) iso;
  130 
  131         size = iso_stream_get_size(file->stream);
  132         if (size > (off_t)MAX_ISO_FILE_SECTION_SIZE && t->opts->iso_level != 3) {
  133             char *ipath = iso_tree_get_node_path(iso);
  134             ret = iso_msg_submit(t->image->id, ISO_FILE_TOO_BIG, 0,
  135                          "File \"%s\" can't be added to image because is "
  136                          "greater than 4GB", ipath);
  137             free(n);
  138             free(ipath);
  139             return ret;
  140         }
  141 
  142         ret = iso_file_src_create(t, file, &src);
  143         if (ret < 0) {
  144             free(n);
  145             return ret;
  146         }
  147         n->info.file = src;
  148         n->type = ISO1999_FILE;
  149     } else if (iso->type == LIBISO_BOOT) {
  150         /* it's a el-torito boot catalog, that we write as a file */
  151         IsoFileSrc *src;
  152 
  153         ret = el_torito_catalog_file_src_create(t, &src);
  154         if (ret < 0) {
  155             free(n);
  156             return ret;
  157         }
  158         n->info.file = src;
  159         n->type = ISO1999_FILE;
  160     } else {
  161         /* should never happen */
  162         free(n);
  163         return ISO_ASSERT_FAILURE;
  164     }
  165 
  166     /* take a ref to the IsoNode */
  167     n->node = iso;
  168     iso_node_ref(iso);
  169 
  170     *node = n;
  171     return ISO_SUCCESS;
  172 }
  173 
  174 /**
  175  * Create the low level ISO 9660:1999 tree from the high level ISO tree.
  176  *
  177  * @return
  178  *      1 success, 0 file ignored, < 0 error
  179  */
  180 static
  181 int create_tree(Ecma119Image *t, IsoNode *iso, Iso1999Node **tree, int pathlen)
  182 {
  183     int ret, max_path;
  184     Iso1999Node *node = NULL;
  185     char *iso_name = NULL;
  186 
  187     if (t == NULL || iso == NULL || tree == NULL) {
  188         return ISO_NULL_POINTER;
  189     }
  190 
  191     if (iso->hidden & LIBISO_HIDE_ON_1999) {
  192         /* file will be ignored */
  193         return 0;
  194     }
  195     ret = get_iso1999_name(t, iso->name, &iso_name);
  196     if (ret < 0) {
  197         return ret;
  198     }
  199 
  200     max_path = pathlen + 1 + (iso_name ? strlen(iso_name): 0);
  201     if (!t->opts->allow_longer_paths && max_path > 255) {
  202         char *ipath = iso_tree_get_node_path(iso);
  203         ret = iso_msg_submit(t->image->id, ISO_FILE_IMGPATH_WRONG, 0,
  204                      "File \"%s\" can't be added to ISO 9660:1999 tree, "
  205                      "because its path length is larger than 255", ipath);
  206         free(iso_name);
  207         free(ipath);
  208         return ret;
  209     }
  210 
  211     switch (iso->type) {
  212     case LIBISO_FILE:
  213         ret = create_node(t, iso, &node);
  214         break;
  215     case LIBISO_DIR:
  216         {
  217             IsoNode *pos;
  218             IsoDir *dir = (IsoDir*)iso;
  219             ret = create_node(t, iso, &node);
  220             if (ret < 0) {
  221                 free(iso_name);
  222                 return ret;
  223             }
  224             pos = dir->children;
  225             while (pos) {
  226                 int cret;
  227                 Iso1999Node *child;
  228                 cret = create_tree(t, pos, &child, max_path);
  229                 if (cret < 0) {
  230                     /* error */
  231                     iso1999_node_free(node);
  232                     ret = cret;
  233                     break;
  234                 } else if (cret == ISO_SUCCESS) {
  235                     /* add child to this node */
  236                     int nchildren = node->info.dir->nchildren++;
  237                     node->info.dir->children[nchildren] = child;
  238                     child->parent = node;
  239                 }
  240                 pos = pos->next;
  241             }
  242         }
  243         break;
  244     case LIBISO_BOOT:
  245         if (t->eltorito) {
  246             ret = create_node(t, iso, &node);
  247         } else {
  248             /* log and ignore */
  249             ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0,
  250                 "El-Torito catalog found on a image without El-Torito.");
  251         }
  252         break;
  253     case LIBISO_SYMLINK:
  254     case LIBISO_SPECIAL:
  255         {
  256             char *ipath = iso_tree_get_node_path(iso);
  257             ret = iso_msg_submit(t->image->id, ISO_FILE_IGNORED, 0,
  258                      "Can't add %s to ISO 9660:1999 tree. This kind of files "
  259                      "can only be added to a Rock Ridget tree. Skipping.",
  260                      ipath);
  261             free(ipath);
  262         }
  263         break;
  264     default:
  265         /* should never happen */
  266         return ISO_ASSERT_FAILURE;
  267     }
  268     if (ret <= 0) {
  269         free(iso_name);
  270         return ret;
  271     }
  272     node->name = iso_name;
  273     *tree = node;
  274     return ISO_SUCCESS;
  275 }
  276 
  277 static int
  278 cmp_node(const void *f1, const void *f2)
  279 {
  280     Iso1999Node *f = *((Iso1999Node**)f1);
  281     Iso1999Node *g = *((Iso1999Node**)f2);
  282 
  283     /**
  284      * TODO #00027 Follow ISO 9660:1999 specs when sorting files
  285      * strcmp do not does exactly what ISO 9660:1999, 9.3, as characters
  286      * < 0x20 " " are allowed, so name len must be taken into account
  287      */
  288     return strcmp(f->name, g->name);
  289 }
  290 
  291 /**
  292  * Sort the entries inside an ISO 9660:1999 directory, according to
  293  * ISO 9660:1999, 9.3
  294  */
  295 static
  296 void sort_tree(Iso1999Node *root)
  297 {
  298     size_t i;
  299 
  300     if (root->info.dir->children == NULL)
  301         return;
  302     qsort(root->info.dir->children, root->info.dir->nchildren,
  303           sizeof(void*), cmp_node);
  304     for (i = 0; i < root->info.dir->nchildren; i++) {
  305         Iso1999Node *child = root->info.dir->children[i];
  306         if (child->type == ISO1999_DIR)
  307             sort_tree(child);
  308     }
  309 }
  310 
  311 static
  312 int mangle_single_dir(Ecma119Image *img, Iso1999Node *dir)
  313 {
  314     int ret;
  315     int i, nchildren;
  316     Iso1999Node **children;
  317     IsoHTable *table = NULL;
  318     int need_sort = 0;
  319     char *full_name = NULL, *tmp = NULL;
  320 
  321     nchildren = dir->info.dir->nchildren;
  322     if (nchildren <= 0) {
  323         ret = ISO_SUCCESS;
  324         goto ex;
  325     }
  326     children = dir->info.dir->children;
  327     LIBISO_ALLOC_MEM(full_name, char, 208);
  328     LIBISO_ALLOC_MEM(tmp, char, 208);
  329 
  330     /* a hash table will temporary hold the names, for fast searching */
  331     ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash,
  332                             (compare_function_t)strcmp, &table);
  333     if (ret < 0) {
  334         goto ex;
  335     }
  336     for (i = 0; i < nchildren; ++i) {
  337         char *name = children[i]->name;
  338         ret = iso_htable_add(table, name, name);
  339         if (ret < 0) {
  340             goto ex;
  341         }
  342     }
  343 
  344     for (i = 0; i < nchildren; ++i) {
  345         char *name, *ext;
  346         int max; /* computed max len for name, without extension */
  347         int j = i;
  348         int digits = 1; /* characters to change per name */
  349 
  350         /* first, find all child with same name */
  351         while (j + 1 < nchildren &&
  352                !cmp_node(children + i, children + j + 1)) {
  353             ++j;
  354         }
  355         if (j == i) {
  356             /* name is unique */
  357             continue;
  358         }
  359 
  360         /*
  361          * A max of 7 characters is good enough, it allows handling up to
  362          * 9,999,999 files with same name.
  363          */
  364         while (digits < 8) {
  365             int ok, k;
  366             char *dot;
  367             int change = 0; /* number to be written */
  368 
  369             /* copy name to buffer */
  370             strcpy(full_name, children[i]->name);
  371 
  372             /* compute name and extension */
  373             dot = strrchr(full_name, '.');
  374             if (dot != NULL && children[i]->type != ISO1999_DIR) {
  375 
  376                 /*
  377                  * File (not dir) with extension.
  378                  */
  379                 int extlen;
  380                 full_name[dot - full_name] = '\0';
  381                 name = full_name;
  382                 ext = dot + 1;
  383 
  384                 extlen = strlen(ext);
  385                 max = 207 - extlen - 1 - digits;
  386                 if (max <= 0) {
  387                     /* this can happen if extension is too long */
  388                     if (extlen + max > 3) {
  389                         /*
  390                          * reduce extension len, to give name an extra char
  391                          * note that max is negative or 0
  392                          */
  393                         extlen = extlen + max - 1;
  394                         ext[extlen] = '\0';
  395                         max = 207 - extlen - 1 - digits;
  396                     } else {
  397                         /*
  398                          * error, we don't support extensions < 3
  399                          * This can't happen with current limit of digits.
  400                          */
  401                         ret = ISO_ERROR;
  402                         goto ex;
  403                     }
  404                 }
  405                 /* ok, reduce name by digits */
  406                 if (name + max < dot) {
  407                     name[max] = '\0';
  408                 }
  409             } else {
  410                 /* Directory, or file without extension */
  411                 if (children[i]->type == ISO1999_DIR) {
  412                     dot = NULL; /* dots have no meaning in dirs */
  413                 }
  414                 max = 207 - digits;
  415                 name = full_name;
  416                 if ((size_t) max < strlen(name)) {
  417                     name[max] = '\0';
  418                 }
  419                 /* let ext be an empty string */
  420                 ext = name + strlen(name);
  421             }
  422 
  423             ok = 1;
  424             /* change name of each file */
  425             for (k = i; k <= j; ++k) {
  426                 char fmt[16];
  427                 if (dot != NULL) {
  428                     sprintf(fmt, "%%s%%0%dd.%%s", digits);
  429                 } else {
  430                     sprintf(fmt, "%%s%%0%dd%%s", digits);
  431                 }
  432                 while (1) {
  433                     sprintf(tmp, fmt, name, change, ext);
  434                     ++change;
  435                     if (change > int_pow(10, digits)) {
  436                         ok = 0;
  437                         break;
  438                     }
  439                     if (!iso_htable_get(table, tmp, NULL)) {
  440                         /* the name is unique, so it can be used */
  441                         break;
  442                     }
  443                 }
  444                 if (ok) {
  445                     char *new = strdup(tmp);
  446                     if (new == NULL) {
  447                         ret = ISO_OUT_OF_MEM;
  448                         goto ex;
  449                     }
  450                     iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"",
  451                                   children[k]->name, new);
  452 
  453                     iso_htable_remove_ptr(table, children[k]->name, NULL);
  454                     free(children[k]->name);
  455                     children[k]->name = new;
  456                     iso_htable_add(table, new, new);
  457 
  458                     /*
  459                      * if we change a name we need to sort again children
  460                      * at the end
  461                      */
  462                     need_sort = 1;
  463                 } else {
  464                     /* we need to increment digits */
  465                     break;
  466                 }
  467             }
  468             if (ok) {
  469                 break;
  470             } else {
  471                 ++digits;
  472             }
  473         }
  474         if (digits == 8) {
  475             ret = ISO_MANGLE_TOO_MUCH_FILES;
  476             goto ex;
  477         }
  478         i = j;
  479     }
  480 
  481     /*
  482      * If needed, sort again the files inside dir
  483      */
  484     if (need_sort) {
  485         qsort(children, nchildren, sizeof(void*), cmp_node);
  486     }
  487 
  488     ret = ISO_SUCCESS;
  489 
  490 ex:;
  491     iso_htable_destroy(table, NULL);
  492     LIBISO_FREE_MEM(tmp);
  493     LIBISO_FREE_MEM(full_name);
  494     return ret;
  495 }
  496 
  497 static
  498 int mangle_tree(Ecma119Image *t, Iso1999Node *dir)
  499 {
  500     int ret;
  501     size_t i;
  502 
  503     ret = mangle_single_dir(t, dir);
  504     if (ret < 0) {
  505         return ret;
  506     }
  507 
  508     /* recurse */
  509     for (i = 0; i < dir->info.dir->nchildren; ++i) {
  510         if (dir->info.dir->children[i]->type == ISO1999_DIR) {
  511             ret = mangle_tree(t, dir->info.dir->children[i]);
  512             if (ret < 0) {
  513                 /* error */
  514                 return ret;
  515             }
  516         }
  517     }
  518     return ISO_SUCCESS;
  519 }
  520 
  521 static
  522 int iso1999_tree_create(Ecma119Image *t)
  523 {
  524     int ret;
  525     Iso1999Node *root;
  526 
  527     if (t == NULL) {
  528         return ISO_NULL_POINTER;
  529     }
  530 
  531     ret = create_tree(t, (IsoNode*)t->image->root, &root, 0);
  532     if (ret <= 0) {
  533         if (ret == 0) {
  534             /* unexpected error, root ignored!! This can't happen */
  535             ret = ISO_ASSERT_FAILURE;
  536         }
  537         return ret;
  538     }
  539 
  540     /* the ISO 9660:1999 tree is stored in Ecma119Image target */
  541     t->iso1999_root = root;
  542 
  543     iso_msg_debug(t->image->id, "Sorting the ISO 9660:1999 tree...");
  544     sort_tree(root);
  545 
  546     iso_msg_debug(t->image->id, "Mangling ISO 9660:1999 names...");
  547     ret = mangle_tree(t, t->iso1999_root);
  548     if (ret < 0) {
  549         return ret;
  550     }
  551 
  552     return ISO_SUCCESS;
  553 }
  554 
  555 /**
  556  * Compute the size of a directory entry for a single node
  557  */
  558 static
  559 size_t calc_dirent_len(Ecma119Image *t, Iso1999Node *n)
  560 {
  561     int ret = n->name ? strlen(n->name) + 33 : 34;
  562     if (ret % 2)
  563         ret++;
  564     return ret;
  565 }
  566 
  567 /**
  568  * Computes the total size of all directory entries of a single dir, as
  569  * stated in ISO 9660:1999, 6.8.1.3
  570  */
  571 static
  572 size_t calc_dir_size(Ecma119Image *t, Iso1999Node *dir)
  573 {
  574     size_t i, len;
  575 
  576     /* size of "." and ".." entries */
  577     len = 34 + 34;
  578 
  579     for (i = 0; i < dir->info.dir->nchildren; ++i) {
  580         size_t remaining;
  581         int section, nsections;
  582         Iso1999Node *child = dir->info.dir->children[i];
  583         size_t dirent_len = calc_dirent_len(t, child);
  584 
  585         nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1;
  586         for (section = 0; section < nsections; ++section) {
  587             remaining = BLOCK_SIZE - (len % BLOCK_SIZE);
  588             if (dirent_len > remaining) {
  589                 /* child directory entry doesn't fit on block */
  590                 len += remaining + dirent_len;
  591             } else {
  592                 len += dirent_len;
  593             }
  594         }
  595     }
  596 
  597     /*
  598      * The size of a dir is always a multiple of block size, as we must add
  599      * the size of the unused space after the last directory record
  600      * (ISO 9660:1999, 6.8.1.3)
  601      */
  602     len = ROUND_UP(len, BLOCK_SIZE);
  603 
  604     /* cache the len */
  605     dir->info.dir->len = len;
  606     return len;
  607 }
  608 
  609 static
  610 void calc_dir_pos(Ecma119Image *t, Iso1999Node *dir)
  611 {
  612     size_t i, len;
  613 
  614     t->iso1999_ndirs++;
  615     dir->info.dir->block = t->curblock;
  616     len = calc_dir_size(t, dir);
  617     t->curblock += DIV_UP(len, BLOCK_SIZE);
  618     for (i = 0; i < dir->info.dir->nchildren; i++) {
  619         Iso1999Node *child = dir->info.dir->children[i];
  620         if (child->type == ISO1999_DIR) {
  621             calc_dir_pos(t, child);
  622         }
  623     }
  624 }
  625 
  626 /**
  627  * Compute the length of the path table (ISO 9660:1999, 6.9), in bytes.
  628  */
  629 static
  630 uint32_t calc_path_table_size(Iso1999Node *dir)
  631 {
  632     uint32_t size;
  633     size_t i;
  634 
  635     /* size of path table for this entry */
  636     size = 8;
  637     size += dir->name ? strlen(dir->name) : 2;
  638     size += (size % 2);
  639 
  640     /* and recurse */
  641     for (i = 0; i < dir->info.dir->nchildren; i++) {
  642         Iso1999Node *child = dir->info.dir->children[i];
  643         if (child->type == ISO1999_DIR) {
  644             size += calc_path_table_size(child);
  645         }
  646     }
  647     return size;
  648 }
  649 
  650 static
  651 int iso1999_writer_compute_data_blocks(IsoImageWriter *writer)
  652 {
  653     Ecma119Image *t;
  654     uint32_t path_table_size;
  655 
  656     if (writer == NULL) {
  657         return ISO_OUT_OF_MEM;
  658     }
  659 
  660     t = writer->target;
  661 
  662     /* compute position of directories */
  663     iso_msg_debug(t->image->id,
  664                   "Computing position of ISO 9660:1999 dir structure");
  665     t->iso1999_ndirs = 0;
  666     calc_dir_pos(t, t->iso1999_root);
  667 
  668     /* compute length of pathlist */
  669     iso_msg_debug(t->image->id, "Computing length of ISO 9660:1999 pathlist");
  670     path_table_size = calc_path_table_size(t->iso1999_root);
  671 
  672     /* compute location for path tables */
  673     t->iso1999_l_path_table_pos = t->curblock;
  674     t->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
  675     t->iso1999_m_path_table_pos = t->curblock;
  676     t->curblock += DIV_UP(path_table_size, BLOCK_SIZE);
  677     t->iso1999_path_table_size = path_table_size;
  678 
  679     return ISO_SUCCESS;
  680 }
  681 
  682 /**
  683  * Write a single directory record (ISO 9660:1999, 9.1).
  684  *
  685  * @param file_id
  686  *     if >= 0, we use it instead of the filename (for "." and ".." entries).
  687  * @param len_fi
  688  *     Computed length of the file identifier.
  689  */
  690 static
  691 void write_one_dir_record(Ecma119Image *t, Iso1999Node *node, int file_id,
  692                           uint8_t *buf, size_t len_fi, int extent)
  693 {
  694     uint32_t len;
  695     uint32_t block;
  696     uint8_t len_dr; /*< size of dir entry */
  697     int multi_extend = 0;
  698     uint8_t *name = (file_id >= 0) ? (uint8_t*)&file_id
  699             : (uint8_t*)node->name;
  700 
  701     struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
  702     IsoNode *iso;
  703 
  704     len_dr = 33 + len_fi + ((len_fi % 2) ? 0 : 1);
  705 
  706     memcpy(rec->file_id, name, len_fi);
  707 
  708     if (node->type == ISO1999_DIR) {
  709         /* use the cached length */
  710         len = node->info.dir->len;
  711         block = node->info.dir->block;
  712     } else if (node->type == ISO1999_FILE) {
  713         block = node->info.file->sections[extent].block;
  714         len = node->info.file->sections[extent].size;
  715         multi_extend = (node->info.file->nsections - 1 == extent) ? 0 : 1;
  716     } else {
  717         /*
  718          * for nodes other than files and dirs, we set both
  719          * len and block to 0
  720          */
  721         len = 0;
  722         block = 0;
  723     }
  724 
  725     /*
  726      * For ".." entry we need to write the parent info!
  727      */
  728     if (file_id == 1 && node->parent)
  729         node = node->parent;
  730 
  731     rec->len_dr[0] = len_dr;
  732     iso_bb(rec->block, block, 4);
  733     iso_bb(rec->length, len, 4);
  734 
  735     /* was: iso_datetime_7(rec->recording_time, t->now, t->opts->always_gmt);
  736     */
  737     iso= node->node;
  738     iso_datetime_7(rec->recording_time, 
  739                    (t->opts->dir_rec_mtime & 4) ? ( t->replace_timestamps ?
  740                                                     t->timestamp : iso->mtime )
  741                                                 : t->now, t->opts->always_gmt);
  742 
  743     rec->flags[0] = ((node->type == ISO1999_DIR) ? 2 : 0) | (multi_extend ? 0x80 : 0);
  744     iso_bb(rec->vol_seq_number, (uint32_t) 1, 2);
  745     rec->len_fi[0] = len_fi;
  746 }
  747 
  748 /**
  749  * Write the enhanced volume descriptor (ISO/IEC 9660:1999, 8.5)
  750  */
  751 static
  752 int iso1999_writer_write_vol_desc(IsoImageWriter *writer)
  753 {
  754     IsoImage *image;
  755     Ecma119Image *t;
  756 
  757     /* The enhanced volume descriptor is like the sup vol desc */
  758     struct ecma119_sup_vol_desc vol;
  759 
  760     char *vol_id = NULL, *pub_id = NULL, *data_id = NULL;
  761     char *volset_id = NULL, *system_id = NULL, *application_id = NULL;
  762     char *copyright_file_id = NULL, *abstract_file_id = NULL;
  763     char *biblio_file_id = NULL;
  764 
  765     if (writer == NULL) {
  766         return ISO_OUT_OF_MEM;
  767     }
  768 
  769     t = writer->target;
  770     image = t->image;
  771 
  772     iso_msg_debug(image->id, "Write Enhanced Vol Desc (ISO 9660:1999)");
  773 
  774     memset(&vol, 0, sizeof(struct ecma119_sup_vol_desc));
  775 
  776     get_iso1999_name(t, image->volume_id, &vol_id);
  777     str2a_char(t->input_charset, image->publisher_id, &pub_id);
  778     str2a_char(t->input_charset, image->data_preparer_id, &data_id);
  779     get_iso1999_name(t, image->volset_id, &volset_id);
  780 
  781     str2a_char(t->input_charset, image->system_id, &system_id);
  782     str2a_char(t->input_charset, image->application_id, &application_id);
  783     get_iso1999_name(t, image->copyright_file_id, &copyright_file_id);
  784     get_iso1999_name(t, image->abstract_file_id, &abstract_file_id);
  785     get_iso1999_name(t, image->biblio_file_id, &biblio_file_id);
  786 
  787     vol.vol_desc_type[0] = 2;
  788     memcpy(vol.std_identifier, "CD001", 5);
  789 
  790     /* descriptor version is 2 (ISO/IEC 9660:1999, 8.5.2) */
  791     vol.vol_desc_version[0] = 2;
  792     strncpy_pad((char*)vol.volume_id, vol_id, 32);
  793 
  794     iso_bb(vol.vol_space_size, t->vol_space_size, 4);
  795     iso_bb(vol.vol_set_size, (uint32_t) 1, 2);
  796     iso_bb(vol.vol_seq_number, (uint32_t) 1, 2);
  797     iso_bb(vol.block_size, (uint32_t) BLOCK_SIZE, 2);
  798     iso_bb(vol.path_table_size, t->iso1999_path_table_size, 4);
  799     iso_lsb(vol.l_path_table_pos, t->iso1999_l_path_table_pos, 4);
  800     iso_msb(vol.m_path_table_pos, t->iso1999_m_path_table_pos, 4);
  801 
  802     write_one_dir_record(t, t->iso1999_root, 0, vol.root_dir_record, 1, 0);
  803 
  804     strncpy_pad((char*)vol.vol_set_id, volset_id, 128);
  805     strncpy_pad((char*)vol.publisher_id, pub_id, 128);
  806     strncpy_pad((char*)vol.data_prep_id, data_id, 128);
  807 
  808     strncpy_pad((char*)vol.system_id, system_id, 32);
  809 
  810     strncpy_pad((char*)vol.application_id, application_id, 128);
  811     strncpy_pad((char*)vol.copyright_file_id, copyright_file_id, 37);
  812     strncpy_pad((char*)vol.abstract_file_id, abstract_file_id, 37);
  813     strncpy_pad((char*)vol.bibliographic_file_id, biblio_file_id, 37);
  814 
  815     ecma119_set_voldescr_times(writer, (struct ecma119_pri_vol_desc *) &vol);
  816     vol.file_structure_version[0] = 2;
  817 
  818     free(vol_id);
  819     free(volset_id);
  820     free(pub_id);
  821     free(data_id);
  822     free(system_id);
  823     free(application_id);
  824     free(copyright_file_id);
  825     free(abstract_file_id);
  826     free(biblio_file_id);
  827 
  828     /* Finally write the Volume Descriptor */
  829     return iso_write(t, &vol, sizeof(struct ecma119_sup_vol_desc));
  830 }
  831 
  832 static
  833 int write_one_dir(Ecma119Image *t, Iso1999Node *dir)
  834 {
  835     int ret;
  836     uint8_t *buffer = NULL;
  837     size_t i;
  838     size_t fi_len, len;
  839 
  840     /* buf will point to current write position on buffer */
  841     uint8_t *buf;
  842 
  843     LIBISO_ALLOC_MEM(buffer, uint8_t, BLOCK_SIZE);
  844     buf = buffer;
  845 
  846     /* write the "." and ".." entries first */
  847     write_one_dir_record(t, dir, 0, buf, 1, 0);
  848     buf += 34;
  849     write_one_dir_record(t, dir, 1, buf, 1, 0);
  850     buf += 34;
  851 
  852     for (i = 0; i < dir->info.dir->nchildren; i++) {
  853         int section, nsections;
  854         Iso1999Node *child = dir->info.dir->children[i];
  855 
  856         /* compute len of directory entry */
  857         fi_len = strlen(child->name);
  858         len = fi_len + 33 + ((fi_len % 2) ? 0 : 1);
  859 
  860         nsections = (child->type == ISO1999_FILE) ? child->info.file->nsections : 1;
  861         for (section = 0; section < nsections; ++section) {
  862             if ( (buf + len - buffer) > BLOCK_SIZE) {
  863                 /* dir doesn't fit in current block */
  864                 ret = iso_write(t, buffer, BLOCK_SIZE);
  865                 if (ret < 0) {
  866                     goto ex;
  867                 }
  868                 memset(buffer, 0, BLOCK_SIZE);
  869                 buf = buffer;
  870             }
  871             /* write the directory entry in any case */
  872             write_one_dir_record(t, child, -1, buf, fi_len, section);
  873             buf += len;
  874         }
  875     }
  876 
  877     /* write the last block */
  878     ret = iso_write(t, buffer, BLOCK_SIZE);
  879 ex:;
  880     LIBISO_FREE_MEM(buffer);
  881     return ret;
  882 }
  883 
  884 static
  885 int write_dirs(Ecma119Image *t, Iso1999Node *root)
  886 {
  887     int ret;
  888     size_t i;
  889 
  890     /* write all directory entries for this dir */
  891     ret = write_one_dir(t, root);
  892     if (ret < 0) {
  893         return ret;
  894     }
  895 
  896     /* recurse */
  897     for (i = 0; i < root->info.dir->nchildren; i++) {
  898         Iso1999Node *child = root->info.dir->children[i];
  899         if (child->type == ISO1999_DIR) {
  900             ret = write_dirs(t, child);
  901             if (ret < 0) {
  902                 return ret;
  903             }
  904         }
  905     }
  906     return ISO_SUCCESS;
  907 }
  908 
  909 static
  910 int write_path_table(Ecma119Image *t, Iso1999Node **pathlist, int l_type)
  911 {
  912     size_t i, len;
  913     uint8_t *buf = NULL;
  914     struct ecma119_path_table_record *rec;
  915     void (*write_int)(uint8_t*, uint32_t, int);
  916     Iso1999Node *dir;
  917     uint32_t path_table_size;
  918     int parent = 0;
  919     int ret= ISO_SUCCESS;
  920     uint8_t *zeros = NULL;
  921 
  922     /* 256 is just a convenient size large enough */
  923     LIBISO_ALLOC_MEM(buf, uint8_t, 256);
  924 
  925     path_table_size = 0;
  926     write_int = l_type ? iso_lsb : iso_msb;
  927 
  928     for (i = 0; i < t->iso1999_ndirs; i++) {
  929         dir = pathlist[i];
  930 
  931         /* find the index of the parent in the table */
  932         while ((i) && pathlist[parent] != dir->parent) {
  933             parent++;
  934         }
  935 
  936         /* write the Path Table Record (ECMA-119, 9.4) */
  937         memset(buf, 0, 256);
  938         rec = (struct ecma119_path_table_record*) buf;
  939         rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1;
  940         rec->len_xa[0] = 0;
  941         write_int(rec->block, dir->info.dir->block, 4);
  942         write_int(rec->parent, parent + 1, 2);
  943         if (dir->parent) {
  944             memcpy(rec->dir_id, dir->name, rec->len_di[0]);
  945         }
  946         len = 8 + rec->len_di[0] + (rec->len_di[0] % 2);
  947         ret = iso_write(t, buf, len);
  948         if (ret < 0) {
  949             /* error */
  950             goto ex;
  951         }
  952         path_table_size += len;
  953     }
  954 
  955     /* we need to fill the last block with zeros */
  956     path_table_size %= BLOCK_SIZE;
  957     if (path_table_size) {
  958         LIBISO_ALLOC_MEM(zeros, uint8_t, BLOCK_SIZE);
  959         len = BLOCK_SIZE - path_table_size;
  960         memset(zeros, 0, len);
  961         ret = iso_write(t, zeros, len);
  962     }
  963 ex:;
  964     LIBISO_FREE_MEM(zeros);
  965     LIBISO_FREE_MEM(buf);
  966     return ret;
  967 }
  968 
  969 static
  970 int write_path_tables(Ecma119Image *t)
  971 {
  972     int ret;
  973     size_t i, j, cur;
  974     Iso1999Node **pathlist;
  975 
  976     iso_msg_debug(t->image->id, "Writing ISO 9660:1999 Path tables");
  977 
  978     /* allocate temporal pathlist */
  979     pathlist = malloc(sizeof(void*) * t->iso1999_ndirs);
  980     if (pathlist == NULL) {
  981         return ISO_OUT_OF_MEM;
  982     }
  983     pathlist[0] = t->iso1999_root;
  984     cur = 1;
  985 
  986     for (i = 0; i < t->iso1999_ndirs; i++) {
  987         Iso1999Node *dir = pathlist[i];
  988         for (j = 0; j < dir->info.dir->nchildren; j++) {
  989             Iso1999Node *child = dir->info.dir->children[j];
  990             if (child->type == ISO1999_DIR) {
  991                 pathlist[cur++] = child;
  992             }
  993         }
  994     }
  995 
  996     /* Write L Path Table */
  997     ret = write_path_table(t, pathlist, 1);
  998     if (ret < 0) {
  999         goto write_path_tables_exit;
 1000     }
 1001 
 1002     /* Write L Path Table */
 1003     ret = write_path_table(t, pathlist, 0);
 1004 
 1005     write_path_tables_exit: ;
 1006     free(pathlist);
 1007     return ret;
 1008 }
 1009 
 1010 static
 1011 int iso1999_writer_write_data(IsoImageWriter *writer)
 1012 {
 1013     int ret;
 1014     Ecma119Image *t;
 1015 
 1016     if (writer == NULL) {
 1017         return ISO_NULL_POINTER;
 1018     }
 1019     t = writer->target;
 1020 
 1021     /* first of all, we write the directory structure */
 1022     ret = write_dirs(t, t->iso1999_root);
 1023     if (ret < 0) {
 1024         return ret;
 1025     }
 1026 
 1027     /* and write the path tables */
 1028     ret = write_path_tables(t);
 1029 
 1030     return ret;
 1031 }
 1032 
 1033 static
 1034 int iso1999_writer_free_data(IsoImageWriter *writer)
 1035 {
 1036     /* free the ISO 9660:1999 tree */
 1037     Ecma119Image *t = writer->target;
 1038     iso1999_node_free(t->iso1999_root);
 1039     return ISO_SUCCESS;
 1040 }
 1041 
 1042 int iso1999_writer_create(Ecma119Image *target)
 1043 {
 1044     int ret;
 1045     IsoImageWriter *writer;
 1046 
 1047     writer = malloc(sizeof(IsoImageWriter));
 1048     if (writer == NULL) {
 1049         return ISO_OUT_OF_MEM;
 1050     }
 1051 
 1052     writer->compute_data_blocks = iso1999_writer_compute_data_blocks;
 1053     writer->write_vol_desc = iso1999_writer_write_vol_desc;
 1054     writer->write_data = iso1999_writer_write_data;
 1055     writer->free_data = iso1999_writer_free_data;
 1056     writer->data = NULL;
 1057     writer->target = target;
 1058 
 1059     iso_msg_debug(target->image->id,
 1060                   "Creating low level ISO 9660:1999 tree...");
 1061     ret = iso1999_tree_create(target);
 1062     if (ret < 0) {
 1063         free((char *) writer);
 1064         return ret;
 1065     }
 1066 
 1067     /* add this writer to image */
 1068     target->writers[target->nwriters++] = writer;
 1069 
 1070     /* we need the volume descriptor */
 1071     target->curblock++;
 1072     return ISO_SUCCESS;
 1073 }