| /*- |
| * Copyright (c) 2009-2012 Michihiro NAKAJIMA |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "archive_platform.h" |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_SYS_UTSNAME_H |
| #include <sys/utsname.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #include <stdio.h> |
| #include <stdarg.h> |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #include <time.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_ZLIB_H |
| #include <cm_zlib.h> |
| #endif |
| |
| #include "archive.h" |
| #include "archive_endian.h" |
| #include "archive_entry.h" |
| #include "archive_entry_locale.h" |
| #include "archive_private.h" |
| #include "archive_rb.h" |
| #include "archive_write_private.h" |
| |
| #if defined(_WIN32) && !defined(__CYGWIN__) |
| #define getuid() 0 |
| #define getgid() 0 |
| #endif |
| |
| /*#define DEBUG 1*/ |
| #ifdef DEBUG |
| /* To compare to the ISO image file made by mkisofs. */ |
| #define COMPAT_MKISOFS 1 |
| #endif |
| |
| #define LOGICAL_BLOCK_BITS 11 |
| #define LOGICAL_BLOCK_SIZE 2048 |
| #define PATH_TABLE_BLOCK_SIZE 4096 |
| |
| #define SYSTEM_AREA_BLOCK 16 |
| #define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1 |
| #define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1 |
| #define BOOT_RECORD_DESCRIPTOR_BLOCK 1 |
| #define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1 |
| #define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1 |
| #define RRIP_ER_BLOCK 1 |
| #define PADDING_BLOCK 150 |
| |
| #define FD_1_2M_SIZE (1024 * 1200) |
| #define FD_1_44M_SIZE (1024 * 1440) |
| #define FD_2_88M_SIZE (1024 * 2880) |
| #define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */ |
| #define MAX_DEPTH 8 |
| #define RR_CE_SIZE 28 /* SUSP "CE" extension size */ |
| |
| #define FILE_FLAG_EXISTENCE 0x01 |
| #define FILE_FLAG_DIRECTORY 0x02 |
| #define FILE_FLAG_ASSOCIATED 0x04 |
| #define FILE_FLAG_RECORD 0x08 |
| #define FILE_FLAG_PROTECTION 0x10 |
| #define FILE_FLAG_MULTI_EXTENT 0x80 |
| |
| static const char rrip_identifier[] = |
| "RRIP_1991A"; |
| static const char rrip_descriptor[] = |
| "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR " |
| "POSIX FILE SYSTEM SEMANTICS"; |
| static const char rrip_source[] = |
| "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. " |
| "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR " |
| "CONTACT INFORMATION."; |
| #define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1) |
| #define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1) |
| #define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1) |
| #define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \ |
| RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE) |
| |
| static const unsigned char zisofs_magic[8] = { |
| 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 |
| }; |
| |
| #define ZF_HEADER_SIZE 16 /* zisofs header size. */ |
| #define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */ |
| #define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS) |
| |
| /* |
| * Manage extra records. |
| */ |
| struct extr_rec { |
| int location; |
| int offset; |
| unsigned char buf[LOGICAL_BLOCK_SIZE]; |
| struct extr_rec *next; |
| }; |
| |
| struct ctl_extr_rec { |
| int use_extr; |
| unsigned char *bp; |
| struct isoent *isoent; |
| unsigned char *ce_ptr; |
| int cur_len; |
| int dr_len; |
| int limit; |
| int extr_off; |
| int extr_loc; |
| }; |
| #define DR_SAFETY RR_CE_SIZE |
| #define DR_LIMIT (254 - DR_SAFETY) |
| |
| /* |
| * The relation of struct isofile and isoent and archive_entry. |
| * |
| * Primary volume tree --> struct isoent |
| * | |
| * v |
| * struct isofile --> archive_entry |
| * ^ |
| * | |
| * Joliet volume tree --> struct isoent |
| * |
| * struct isoent has specific information for volume. |
| */ |
| |
| struct isofile { |
| /* Used for managing struct isofile list. */ |
| struct isofile *allnext; |
| struct isofile *datanext; |
| /* Used for managing a hardlinked struct isofile list. */ |
| struct isofile *hlnext; |
| struct isofile *hardlink_target; |
| |
| struct archive_entry *entry; |
| |
| /* |
| * Used for making a directory tree. |
| */ |
| struct archive_string parentdir; |
| struct archive_string basename; |
| struct archive_string basename_utf16; |
| struct archive_string symlink; |
| int dircnt; /* The number of elements of |
| * its parent directory */ |
| |
| /* |
| * Used for a Directory Record. |
| */ |
| struct content { |
| int64_t offset_of_temp; |
| int64_t size; |
| int blocks; |
| uint32_t location; |
| /* |
| * One extent equals one content. |
| * If this entry has multi extent, `next' variable points |
| * next content data. |
| */ |
| struct content *next; /* next content */ |
| } content, *cur_content; |
| int write_content; |
| |
| enum { |
| NO = 0, |
| BOOT_CATALOG, |
| BOOT_IMAGE |
| } boot; |
| |
| /* |
| * Used for a zisofs. |
| */ |
| struct { |
| unsigned char header_size; |
| unsigned char log2_bs; |
| uint32_t uncompressed_size; |
| } zisofs; |
| }; |
| |
| struct isoent { |
| /* Keep `rbnode' at the first member of struct isoent. */ |
| struct archive_rb_node rbnode; |
| |
| struct isofile *file; |
| |
| struct isoent *parent; |
| /* A list of children.(use chnext) */ |
| struct { |
| struct isoent *first; |
| struct isoent **last; |
| int cnt; |
| } children; |
| struct archive_rb_tree rbtree; |
| |
| /* A list of sub directories.(use drnext) */ |
| struct { |
| struct isoent *first; |
| struct isoent **last; |
| int cnt; |
| } subdirs; |
| /* A sorted list of sub directories. */ |
| struct isoent **children_sorted; |
| /* Used for managing struct isoent list. */ |
| struct isoent *chnext; |
| struct isoent *drnext; |
| struct isoent *ptnext; |
| |
| /* |
| * Used for making a Directory Record. |
| */ |
| int dir_number; |
| struct { |
| int vd; |
| int self; |
| int parent; |
| int normal; |
| } dr_len; |
| uint32_t dir_location; |
| int dir_block; |
| |
| /* |
| * Identifier: |
| * on primary, ISO9660 file/directory name. |
| * on joliet, UCS2 file/directory name. |
| * ext_off : offset of identifier extension. |
| * ext_len : length of identifier extension. |
| * id_len : byte size of identifier. |
| * on primary, this is ext_off + ext_len + version length. |
| * on joliet, this is ext_off + ext_len. |
| * mb_len : length of multibyte-character of identifier. |
| * on primary, mb_len and id_len are always the same. |
| * on joliet, mb_len and id_len are different. |
| */ |
| char *identifier; |
| int ext_off; |
| int ext_len; |
| int id_len; |
| int mb_len; |
| |
| /* |
| * Used for making a Rockridge extension. |
| * This is a part of Directory Records. |
| */ |
| struct isoent *rr_parent; |
| struct isoent *rr_child; |
| |
| /* Extra Record.(which we call in this source file) |
| * A maximum size of the Directory Record is 254. |
| * so, if generated RRIP data of a file cannot into a Directory |
| * Record because of its size, that surplus data relocate this |
| * Extra Record. |
| */ |
| struct { |
| struct extr_rec *first; |
| struct extr_rec **last; |
| struct extr_rec *current; |
| } extr_rec_list; |
| |
| int virtual:1; |
| /* If set to one, this file type is a directory. |
| * A convenience flag to be used as |
| * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR". |
| */ |
| int dir:1; |
| }; |
| |
| struct hardlink { |
| struct archive_rb_node rbnode; |
| int nlink; |
| struct { |
| struct isofile *first; |
| struct isofile **last; |
| } file_list; |
| }; |
| |
| /* |
| * ISO writer options |
| */ |
| struct iso_option { |
| /* |
| * Usage : abstract-file=<value> |
| * Type : string, max 37 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -abstract <value> |
| * |
| * Specifies Abstract Filename. |
| * This file shall be described in the Root Directory |
| * and containing a abstract statement. |
| */ |
| unsigned int abstract_file:1; |
| #define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */ |
| #define ABSTRACT_FILE_SIZE 37 |
| |
| /* |
| * Usage : application-id=<value> |
| * Type : string, max 128 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -A/-appid <value>. |
| * |
| * Specifies Application Identifier. |
| * If the first byte is set to '_'(5F), the remaining |
| * bytes of this option shall specify an identifier |
| * for a file containing the identification of the |
| * application. |
| * This file shall be described in the Root Directory. |
| */ |
| unsigned int application_id:1; |
| #define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */ |
| #define APPLICATION_IDENTIFIER_SIZE 128 |
| |
| /* |
| * Usage : !allow-vernum |
| * Type : boolean |
| * Default: Enabled |
| * : Violates the ISO9660 standard if disable. |
| * COMPAT: mkisofs -N |
| * |
| * Allow filenames to use version numbers. |
| */ |
| unsigned int allow_vernum:1; |
| #define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */ |
| |
| /* |
| * Usage : biblio-file=<value> |
| * Type : string, max 37 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -biblio <value> |
| * |
| * Specifies Bibliographic Filename. |
| * This file shall be described in the Root Directory |
| * and containing bibliographic records. |
| */ |
| unsigned int biblio_file:1; |
| #define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */ |
| #define BIBLIO_FILE_SIZE 37 |
| |
| /* |
| * Usage : boot=<value> |
| * Type : string |
| * Default: Not specified |
| * COMPAT : mkisofs -b/-eltorito-boot <value> |
| * |
| * Specifies "El Torito" boot image file to make |
| * a bootable CD. |
| */ |
| unsigned int boot:1; |
| #define OPT_BOOT_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : boot-catalog=<value> |
| * Type : string |
| * Default: "boot.catalog" |
| * COMPAT : mkisofs -c/-eltorito-catalog <value> |
| * |
| * Specifies a fullpath of El Torito boot catalog. |
| */ |
| unsigned int boot_catalog:1; |
| #define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : boot-info-table |
| * Type : boolean |
| * Default: Disabled |
| * COMPAT : mkisofs -boot-info-table |
| * |
| * Modify the boot image file specified by `boot' |
| * option; ISO writer stores boot file information |
| * into the boot file in ISO image at offset 8 |
| * through offset 64. |
| */ |
| unsigned int boot_info_table:1; |
| #define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */ |
| |
| /* |
| * Usage : boot-load-seg=<value> |
| * Type : hexadecimal |
| * Default: Not specified |
| * COMPAT : mkisofs -boot-load-seg <value> |
| * |
| * Specifies a load segment for boot image. |
| * This is used with no-emulation mode. |
| */ |
| unsigned int boot_load_seg:1; |
| #define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : boot-load-size=<value> |
| * Type : decimal |
| * Default: Not specified |
| * COMPAT : mkisofs -boot-load-size <value> |
| * |
| * Specifies a sector count for boot image. |
| * This is used with no-emulation mode. |
| */ |
| unsigned int boot_load_size:1; |
| #define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : boot-type=<boot-media-type> |
| * : 'no-emulation' : 'no emulation' image |
| * : 'fd' : floppy disk image |
| * : 'hard-disk' : hard disk image |
| * Type : string |
| * Default: Auto detect |
| * : We check a size of boot image; |
| * : If the size is just 1.22M/1.44M/2.88M, |
| * : we assume boot_type is 'fd'; |
| * : otherwise boot_type is 'no-emulation'. |
| * COMPAT : |
| * boot=no-emulation |
| * mkisofs -no-emul-boot |
| * boot=fd |
| * This is a default on the mkisofs. |
| * boot=hard-disk |
| * mkisofs -hard-disk-boot |
| * |
| * Specifies a type of "El Torito" boot image. |
| */ |
| unsigned int boot_type:2; |
| #define OPT_BOOT_TYPE_AUTO 0 /* auto detect */ |
| #define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */ |
| #define OPT_BOOT_TYPE_FD 2 /* floppy disk image */ |
| #define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */ |
| #define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO |
| |
| /* |
| * Usage : compression-level=<value> |
| * Type : decimal |
| * Default: Not specified |
| * COMPAT : NONE |
| * |
| * Specifies compression level for option zisofs=direct. |
| */ |
| unsigned int compression_level:1; |
| #define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : copyright-file=<value> |
| * Type : string, max 37 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -copyright <value> |
| * |
| * Specifies Copyright Filename. |
| * This file shall be described in the Root Directory |
| * and containing a copyright statement. |
| */ |
| unsigned int copyright_file:1; |
| #define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */ |
| #define COPYRIGHT_FILE_SIZE 37 |
| |
| /* |
| * Usage : gid=<value> |
| * Type : decimal |
| * Default: Not specified |
| * COMPAT : mkisofs -gid <value> |
| * |
| * Specifies a group id to rewrite the group id of all files. |
| */ |
| unsigned int gid:1; |
| #define OPT_GID_DEFAULT 0 /* Not specified */ |
| |
| /* |
| * Usage : iso-level=[1234] |
| * Type : decimal |
| * Default: 1 |
| * COMPAT : mkisofs -iso-level <value> |
| * |
| * Specifies ISO9600 Level. |
| * Level 1: [DEFAULT] |
| * - limits each file size less than 4Gi bytes; |
| * - a File Name shall not contain more than eight |
| * d-characters or eight d1-characters; |
| * - a File Name Extension shall not contain more than |
| * three d-characters or three d1-characters; |
| * - a Directory Identifier shall not contain more |
| * than eight d-characters or eight d1-characters. |
| * Level 2: |
| * - limits each file size less than 4Giga bytes; |
| * - a File Name shall not contain more than thirty |
| * d-characters or thirty d1-characters; |
| * - a File Name Extension shall not contain more than |
| * thirty d-characters or thirty d1-characters; |
| * - a Directory Identifier shall not contain more |
| * than thirty-one d-characters or thirty-one |
| * d1-characters. |
| * Level 3: |
| * - no limit of file size; use multi extent. |
| * Level 4: |
| * - this level 4 simulates mkisofs option |
| * '-iso-level 4'; |
| * - crate a enhanced volume as mkisofs doing; |
| * - allow a File Name to have leading dot; |
| * - allow a File Name to have all ASCII letters; |
| * - allow a File Name to have multiple dots; |
| * - allow more then 8 depths of directory trees; |
| * - disable a version number to a File Name; |
| * - disable a forced period to the tail of a File Name; |
| * - the maximum length of files and directories is raised to 193. |
| * if rockridge option is disabled, raised to 207. |
| */ |
| unsigned int iso_level:3; |
| #define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */ |
| |
| /* |
| * Usage : joliet[=long] |
| * : !joliet |
| * : Do not generate Joliet Volume and Records. |
| * : joliet [DEFAULT] |
| * : Generates Joliet Volume and Directory Records. |
| * : [COMPAT: mkisofs -J/-joliet] |
| * : joliet=long |
| * : The joliet filenames are up to 103 Unicode |
| * : characters. |
| * : This option breaks the Joliet specification. |
| * : [COMPAT: mkisofs -J -joliet-long] |
| * Type : boolean/string |
| * Default: Enabled |
| * COMPAT : mkisofs -J / -joliet-long |
| * |
| * Generates Joliet Volume and Directory Records. |
| */ |
| unsigned int joliet:2; |
| #define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */ |
| #define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */ |
| #define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/ |
| #define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE |
| |
| /* |
| * Usage : !limit-depth |
| * Type : boolean |
| * Default: Enabled |
| * : Violates the ISO9660 standard if disable. |
| * COMPAT : mkisofs -D/-disable-deep-relocation |
| * |
| * The number of levels in hierarchy cannot exceed eight. |
| */ |
| unsigned int limit_depth:1; |
| #define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */ |
| |
| /* |
| * Usage : !limit-dirs |
| * Type : boolean |
| * Default: Enabled |
| * : Violates the ISO9660 standard if disable. |
| * COMPAT : mkisofs -no-limit-pathtables |
| * |
| * Limits the number of directories less than 65536 due |
| * to the size of the Parent Directory Number of Path |
| * Table. |
| */ |
| unsigned int limit_dirs:1; |
| #define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */ |
| |
| /* |
| * Usage : !pad |
| * Type : boolean |
| * Default: Enabled |
| * COMPAT : -pad/-no-pad |
| * |
| * Pads the end of the ISO image by null of 300Ki bytes. |
| */ |
| unsigned int pad:1; |
| #define OPT_PAD_DEFAULT 1 /* Enabled */ |
| |
| /* |
| * Usage : publisher=<value> |
| * Type : string, max 128 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -publisher <value> |
| * |
| * Specifies Publisher Identifier. |
| * If the first byte is set to '_'(5F), the remaining |
| * bytes of this option shall specify an identifier |
| * for a file containing the identification of the user. |
| * This file shall be described in the Root Directory. |
| */ |
| unsigned int publisher:1; |
| #define OPT_PUBLISHER_DEFAULT 0 /* Not specified */ |
| #define PUBLISHER_IDENTIFIER_SIZE 128 |
| |
| /* |
| * Usage : rockridge |
| * : !rockridge |
| * : disable to generate SUSP and RR records. |
| * : rockridge |
| * : the same as 'rockridge=useful'. |
| * : rockridge=strict |
| * : generate SUSP and RR records. |
| * : [COMPAT: mkisofs -R] |
| * : rockridge=useful [DEFAULT] |
| * : generate SUSP and RR records. |
| * : [COMPAT: mkisofs -r] |
| * : NOTE Our rockridge=useful option does not set a zero |
| * : to uid and gid, you should use application |
| * : option such as --gid,--gname,--uid and --uname |
| * : bsdtar options instead. |
| * Type : boolean/string |
| * Default: Enabled as rockridge=useful |
| * COMPAT : mkisofs -r / -R |
| * |
| * Generates SUSP and RR records. |
| */ |
| unsigned int rr:2; |
| #define OPT_RR_DISABLED 0 |
| #define OPT_RR_STRICT 1 |
| #define OPT_RR_USEFUL 2 |
| #define OPT_RR_DEFAULT OPT_RR_USEFUL |
| |
| /* |
| * Usage : volume-id=<value> |
| * Type : string, max 32 bytes |
| * Default: Not specified |
| * COMPAT : mkisofs -V <value> |
| * |
| * Specifies Volume Identifier. |
| */ |
| unsigned int volume_id:1; |
| #define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */ |
| #define VOLUME_IDENTIFIER_SIZE 32 |
| |
| /* |
| * Usage : !zisofs [DEFAULT] |
| * : Disable to generate RRIP 'ZF' extension. |
| * : zisofs |
| * : Make files zisofs file and generate RRIP 'ZF' |
| * : extension. So you do not need mkzftree utility |
| * : for making zisofs. |
| * : When the file size is less than one Logical Block |
| * : size, that file will not zisofs'ed since it does |
| * : reduce an ISO-image size. |
| * : |
| * : When you specify option 'boot=<boot-image>', that |
| * : 'boot-image' file won't be converted to zisofs file. |
| * Type : boolean |
| * Default: Disabled |
| * |
| * Generates RRIP 'ZF' System Use Entry. |
| */ |
| unsigned int zisofs:1; |
| #define OPT_ZISOFS_DISABLED 0 |
| #define OPT_ZISOFS_DIRECT 1 |
| #define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED |
| |
| }; |
| |
| struct iso9660 { |
| /* The creation time of ISO image. */ |
| time_t birth_time; |
| /* A file stream of a temporary file, which file contents |
| * save to until ISO image can be created. */ |
| int temp_fd; |
| |
| struct isofile *cur_file; |
| struct isoent *cur_dirent; |
| struct archive_string cur_dirstr; |
| uint64_t bytes_remaining; |
| int need_multi_extent; |
| |
| /* Temporary string buffer for Joliet extension. */ |
| struct archive_string utf16be; |
| struct archive_string mbs; |
| |
| struct archive_string_conv *sconv_to_utf16be; |
| struct archive_string_conv *sconv_from_utf16be; |
| |
| /* A list of all of struct isofile entries. */ |
| struct { |
| struct isofile *first; |
| struct isofile **last; |
| } all_file_list; |
| |
| /* A list of struct isofile entries which have its |
| * contents and are not a directory, a hardlinked file |
| * and a symlink file. */ |
| struct { |
| struct isofile *first; |
| struct isofile **last; |
| } data_file_list; |
| |
| /* Used for managing to find hardlinking files. */ |
| struct archive_rb_tree hardlink_rbtree; |
| |
| /* Used for making the Path Table Record. */ |
| struct vdd { |
| /* the root of entry tree. */ |
| struct isoent *rootent; |
| enum vdd_type { |
| VDD_PRIMARY, |
| VDD_JOLIET, |
| VDD_ENHANCED |
| } vdd_type; |
| |
| struct path_table { |
| struct isoent *first; |
| struct isoent **last; |
| struct isoent **sorted; |
| int cnt; |
| } *pathtbl; |
| int max_depth; |
| |
| int path_table_block; |
| int path_table_size; |
| int location_type_L_path_table; |
| int location_type_M_path_table; |
| int total_dir_block; |
| } primary, joliet; |
| |
| /* Used for making a Volume Descriptor. */ |
| int volume_space_size; |
| int volume_sequence_number; |
| int total_file_block; |
| struct archive_string volume_identifier; |
| struct archive_string publisher_identifier; |
| struct archive_string data_preparer_identifier; |
| struct archive_string application_identifier; |
| struct archive_string copyright_file_identifier; |
| struct archive_string abstract_file_identifier; |
| struct archive_string bibliographic_file_identifier; |
| |
| /* Used for making rockridge extensions. */ |
| int location_rrip_er; |
| |
| /* Used for making zisofs. */ |
| struct { |
| int detect_magic:1; |
| int making:1; |
| int allzero:1; |
| unsigned char magic_buffer[64]; |
| int magic_cnt; |
| |
| #ifdef HAVE_ZLIB_H |
| /* |
| * Copy a compressed file to iso9660.zisofs.temp_fd |
| * and also copy a uncompressed file(original file) to |
| * iso9660.temp_fd . If the number of logical block |
| * of the compressed file is less than the number of |
| * logical block of the uncompressed file, use it and |
| * remove the copy of the uncompressed file. |
| * but if not, we use uncompressed file and remove |
| * the copy of the compressed file. |
| */ |
| uint32_t *block_pointers; |
| size_t block_pointers_allocated; |
| int block_pointers_cnt; |
| int block_pointers_idx; |
| int64_t total_size; |
| int64_t block_offset; |
| |
| z_stream stream; |
| int stream_valid; |
| int64_t remaining; |
| int compression_level; |
| #endif |
| } zisofs; |
| |
| struct isoent *directories_too_deep; |
| int dircnt_max; |
| |
| /* Write buffer. */ |
| #define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32) |
| #define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining) |
| #define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \ |
| + wb_buffmax() - wb_remaining(a)) |
| unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32]; |
| size_t wbuff_remaining; |
| enum { |
| WB_TO_STREAM, |
| WB_TO_TEMP |
| } wbuff_type; |
| int64_t wbuff_offset; |
| int64_t wbuff_written; |
| int64_t wbuff_tail; |
| |
| /* 'El Torito' boot data. */ |
| struct { |
| /* boot catalog file */ |
| struct archive_string catalog_filename; |
| struct isoent *catalog; |
| /* boot image file */ |
| struct archive_string boot_filename; |
| struct isoent *boot; |
| |
| unsigned char platform_id; |
| #define BOOT_PLATFORM_X86 0 |
| #define BOOT_PLATFORM_PPC 1 |
| #define BOOT_PLATFORM_MAC 2 |
| struct archive_string id; |
| unsigned char media_type; |
| #define BOOT_MEDIA_NO_EMULATION 0 |
| #define BOOT_MEDIA_1_2M_DISKETTE 1 |
| #define BOOT_MEDIA_1_44M_DISKETTE 2 |
| #define BOOT_MEDIA_2_88M_DISKETTE 3 |
| #define BOOT_MEDIA_HARD_DISK 4 |
| unsigned char system_type; |
| uint16_t boot_load_seg; |
| uint16_t boot_load_size; |
| #define BOOT_LOAD_SIZE 4 |
| } el_torito; |
| |
| struct iso_option opt; |
| }; |
| |
| /* |
| * Types of Volume Descriptor |
| */ |
| enum VD_type { |
| VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */ |
| VDT_PRIMARY=1, /* Primary Volume Descriptor */ |
| VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */ |
| VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */ |
| }; |
| |
| /* |
| * Types of Directory Record |
| */ |
| enum dir_rec_type { |
| DIR_REC_VD, /* Stored in Volume Descriptor. */ |
| DIR_REC_SELF, /* Stored as Current Directory. */ |
| DIR_REC_PARENT, /* Stored as Parent Directory. */ |
| DIR_REC_NORMAL /* Stored as Child. */ |
| }; |
| |
| /* |
| * Kinds of Volume Descriptor Character |
| */ |
| enum vdc { |
| VDC_STD, |
| VDC_LOWERCASE, |
| VDC_UCS2, |
| VDC_UCS2_DIRECT |
| }; |
| |
| /* |
| * IDentifier Resolver. |
| * Used for resolving duplicated filenames. |
| */ |
| struct idr { |
| struct idrent { |
| struct archive_rb_node rbnode; |
| /* Used in wait_list. */ |
| struct idrent *wnext; |
| struct idrent *avail; |
| |
| struct isoent *isoent; |
| int weight; |
| int noff; |
| int rename_num; |
| } *idrent_pool; |
| |
| struct archive_rb_tree rbtree; |
| |
| struct { |
| struct idrent *first; |
| struct idrent **last; |
| } wait_list; |
| |
| int pool_size; |
| int pool_idx; |
| int num_size; |
| int null_size; |
| |
| char char_map[0x80]; |
| }; |
| |
| enum char_type { |
| A_CHAR, |
| D_CHAR |
| }; |
| |
| |
| static int iso9660_options(struct archive_write *, |
| const char *, const char *); |
| static int iso9660_write_header(struct archive_write *, |
| struct archive_entry *); |
| static ssize_t iso9660_write_data(struct archive_write *, |
| const void *, size_t); |
| static int iso9660_finish_entry(struct archive_write *); |
| static int iso9660_close(struct archive_write *); |
| static int iso9660_free(struct archive_write *); |
| |
| static void get_system_identitier(char *, size_t); |
| static void set_str(unsigned char *, const char *, size_t, char, |
| const char *); |
| static inline int joliet_allowed_char(unsigned char, unsigned char); |
| static int set_str_utf16be(struct archive_write *, unsigned char *, |
| const char *, size_t, uint16_t, enum vdc); |
| static int set_str_a_characters_bp(struct archive_write *, |
| unsigned char *, int, int, const char *, enum vdc); |
| static int set_str_d_characters_bp(struct archive_write *, |
| unsigned char *, int, int, const char *, enum vdc); |
| static void set_VD_bp(unsigned char *, enum VD_type, unsigned char); |
| static inline void set_unused_field_bp(unsigned char *, int, int); |
| |
| static unsigned char *extra_open_record(unsigned char *, int, |
| struct isoent *, struct ctl_extr_rec *); |
| static void extra_close_record(struct ctl_extr_rec *, int); |
| static unsigned char * extra_next_record(struct ctl_extr_rec *, int); |
| static unsigned char *extra_get_record(struct isoent *, int *, int *, int *); |
| static void extra_tell_used_size(struct ctl_extr_rec *, int); |
| static int extra_setup_location(struct isoent *, int); |
| static int set_directory_record_rr(unsigned char *, int, |
| struct isoent *, struct iso9660 *, enum dir_rec_type); |
| static int set_directory_record(unsigned char *, size_t, |
| struct isoent *, struct iso9660 *, enum dir_rec_type, |
| enum vdd_type); |
| static inline int get_dir_rec_size(struct iso9660 *, struct isoent *, |
| enum dir_rec_type, enum vdd_type); |
| static inline unsigned char *wb_buffptr(struct archive_write *); |
| static int wb_write_out(struct archive_write *); |
| static int wb_consume(struct archive_write *, size_t); |
| #ifdef HAVE_ZLIB_H |
| static int wb_set_offset(struct archive_write *, int64_t); |
| #endif |
| static int write_null(struct archive_write *, size_t); |
| static int write_VD_terminator(struct archive_write *); |
| static int set_file_identifier(unsigned char *, int, int, enum vdc, |
| struct archive_write *, struct vdd *, |
| struct archive_string *, const char *, int, |
| enum char_type); |
| static int write_VD(struct archive_write *, struct vdd *); |
| static int write_VD_boot_record(struct archive_write *); |
| static int write_information_block(struct archive_write *); |
| static int write_path_table(struct archive_write *, int, |
| struct vdd *); |
| static int write_directory_descriptors(struct archive_write *, |
| struct vdd *); |
| static int write_file_descriptors(struct archive_write *); |
| static int write_rr_ER(struct archive_write *); |
| static void calculate_path_table_size(struct vdd *); |
| |
| static void isofile_init_entry_list(struct iso9660 *); |
| static void isofile_add_entry(struct iso9660 *, struct isofile *); |
| static void isofile_free_all_entries(struct iso9660 *); |
| static void isofile_init_entry_data_file_list(struct iso9660 *); |
| static void isofile_add_data_file(struct iso9660 *, struct isofile *); |
| static struct isofile * isofile_new(struct archive_write *, |
| struct archive_entry *); |
| static void isofile_free(struct isofile *); |
| static int isofile_gen_utility_names(struct archive_write *, |
| struct isofile *); |
| static int isofile_register_hardlink(struct archive_write *, |
| struct isofile *); |
| static void isofile_connect_hardlink_files(struct iso9660 *); |
| static void isofile_init_hardlinks(struct iso9660 *); |
| static void isofile_free_hardlinks(struct iso9660 *); |
| |
| static struct isoent *isoent_new(struct isofile *); |
| static int isoent_clone_tree(struct archive_write *, |
| struct isoent **, struct isoent *); |
| static void _isoent_free(struct isoent *isoent); |
| static void isoent_free_all(struct isoent *); |
| static struct isoent * isoent_create_virtual_dir(struct archive_write *, |
| struct iso9660 *, const char *); |
| static int isoent_cmp_node(const struct archive_rb_node *, |
| const struct archive_rb_node *); |
| static int isoent_cmp_key(const struct archive_rb_node *, |
| const void *); |
| static int isoent_add_child_head(struct isoent *, struct isoent *); |
| static int isoent_add_child_tail(struct isoent *, struct isoent *); |
| static void isoent_remove_child(struct isoent *, struct isoent *); |
| static void isoent_setup_directory_location(struct iso9660 *, |
| int, struct vdd *); |
| static void isoent_setup_file_location(struct iso9660 *, int); |
| static int get_path_component(char *, size_t, const char *); |
| static int isoent_tree(struct archive_write *, struct isoent **); |
| static struct isoent *isoent_find_child(struct isoent *, const char *); |
| static struct isoent *isoent_find_entry(struct isoent *, const char *); |
| static void idr_relaxed_filenames(char *); |
| static void idr_init(struct iso9660 *, struct vdd *, struct idr *); |
| static void idr_cleanup(struct idr *); |
| static int idr_ensure_poolsize(struct archive_write *, struct idr *, |
| int); |
| static int idr_start(struct archive_write *, struct idr *, |
| int, int, int, int, const struct archive_rb_tree_ops *); |
| static void idr_register(struct idr *, struct isoent *, int, |
| int); |
| static void idr_extend_identifier(struct idrent *, int, int); |
| static void idr_resolve(struct idr *, void (*)(unsigned char *, int)); |
| static void idr_set_num(unsigned char *, int); |
| static void idr_set_num_beutf16(unsigned char *, int); |
| static int isoent_gen_iso9660_identifier(struct archive_write *, |
| struct isoent *, struct idr *); |
| static int isoent_gen_joliet_identifier(struct archive_write *, |
| struct isoent *, struct idr *); |
| static int isoent_cmp_iso9660_identifier(const struct isoent *, |
| const struct isoent *); |
| static int isoent_cmp_node_iso9660(const struct archive_rb_node *, |
| const struct archive_rb_node *); |
| static int isoent_cmp_key_iso9660(const struct archive_rb_node *, |
| const void *); |
| static int isoent_cmp_joliet_identifier(const struct isoent *, |
| const struct isoent *); |
| static int isoent_cmp_node_joliet(const struct archive_rb_node *, |
| const struct archive_rb_node *); |
| static int isoent_cmp_key_joliet(const struct archive_rb_node *, |
| const void *); |
| static inline void path_table_add_entry(struct path_table *, struct isoent *); |
| static inline struct isoent * path_table_last_entry(struct path_table *); |
| static int isoent_make_path_table(struct archive_write *); |
| static int isoent_find_out_boot_file(struct archive_write *, |
| struct isoent *); |
| static int isoent_create_boot_catalog(struct archive_write *, |
| struct isoent *); |
| static size_t fd_boot_image_size(int); |
| static int make_boot_catalog(struct archive_write *); |
| static int setup_boot_information(struct archive_write *); |
| |
| static int zisofs_init(struct archive_write *, struct isofile *); |
| static void zisofs_detect_magic(struct archive_write *, |
| const void *, size_t); |
| static int zisofs_write_to_temp(struct archive_write *, |
| const void *, size_t); |
| static int zisofs_finish_entry(struct archive_write *); |
| static int zisofs_rewind_boot_file(struct archive_write *); |
| static int zisofs_free(struct archive_write *); |
| |
| int |
| archive_write_set_format_iso9660(struct archive *_a) |
| { |
| struct archive_write *a = (struct archive_write *)_a; |
| struct iso9660 *iso9660; |
| |
| archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, |
| ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660"); |
| |
| /* If another format was already registered, unregister it. */ |
| if (a->format_free != NULL) |
| (a->format_free)(a); |
| |
| iso9660 = calloc(1, sizeof(*iso9660)); |
| if (iso9660 == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate iso9660 data"); |
| return (ARCHIVE_FATAL); |
| } |
| iso9660->birth_time = 0; |
| iso9660->temp_fd = -1; |
| iso9660->cur_file = NULL; |
| iso9660->primary.max_depth = 0; |
| iso9660->primary.vdd_type = VDD_PRIMARY; |
| iso9660->primary.pathtbl = NULL; |
| iso9660->joliet.rootent = NULL; |
| iso9660->joliet.max_depth = 0; |
| iso9660->joliet.vdd_type = VDD_JOLIET; |
| iso9660->joliet.pathtbl = NULL; |
| isofile_init_entry_list(iso9660); |
| isofile_init_entry_data_file_list(iso9660); |
| isofile_init_hardlinks(iso9660); |
| iso9660->directories_too_deep = NULL; |
| iso9660->dircnt_max = 1; |
| iso9660->wbuff_remaining = wb_buffmax(); |
| iso9660->wbuff_type = WB_TO_TEMP; |
| iso9660->wbuff_offset = 0; |
| iso9660->wbuff_written = 0; |
| iso9660->wbuff_tail = 0; |
| archive_string_init(&(iso9660->utf16be)); |
| archive_string_init(&(iso9660->mbs)); |
| |
| /* |
| * Init Identifiers used for PVD and SVD. |
| */ |
| archive_string_init(&(iso9660->volume_identifier)); |
| archive_strcpy(&(iso9660->volume_identifier), "CDROM"); |
| archive_string_init(&(iso9660->publisher_identifier)); |
| archive_string_init(&(iso9660->data_preparer_identifier)); |
| archive_string_init(&(iso9660->application_identifier)); |
| archive_strcpy(&(iso9660->application_identifier), |
| archive_version_string()); |
| archive_string_init(&(iso9660->copyright_file_identifier)); |
| archive_string_init(&(iso9660->abstract_file_identifier)); |
| archive_string_init(&(iso9660->bibliographic_file_identifier)); |
| |
| /* |
| * Init El Torito bootable CD variables. |
| */ |
| archive_string_init(&(iso9660->el_torito.catalog_filename)); |
| iso9660->el_torito.catalog = NULL; |
| /* Set default file name of boot catalog */ |
| archive_strcpy(&(iso9660->el_torito.catalog_filename), |
| "boot.catalog"); |
| archive_string_init(&(iso9660->el_torito.boot_filename)); |
| iso9660->el_torito.boot = NULL; |
| iso9660->el_torito.platform_id = BOOT_PLATFORM_X86; |
| archive_string_init(&(iso9660->el_torito.id)); |
| iso9660->el_torito.boot_load_seg = 0; |
| iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE; |
| |
| /* |
| * Init zisofs variables. |
| */ |
| #ifdef HAVE_ZLIB_H |
| iso9660->zisofs.block_pointers = NULL; |
| iso9660->zisofs.block_pointers_allocated = 0; |
| iso9660->zisofs.stream_valid = 0; |
| iso9660->zisofs.compression_level = 9; |
| memset(&(iso9660->zisofs.stream), 0, |
| sizeof(iso9660->zisofs.stream)); |
| #endif |
| |
| /* |
| * Set default value of iso9660 options. |
| */ |
| iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT; |
| iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT; |
| iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT; |
| iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT; |
| iso9660->opt.boot = OPT_BOOT_DEFAULT; |
| iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT; |
| iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT; |
| iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT; |
| iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT; |
| iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT; |
| iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT; |
| iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT; |
| iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT; |
| iso9660->opt.joliet = OPT_JOLIET_DEFAULT; |
| iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT; |
| iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT; |
| iso9660->opt.pad = OPT_PAD_DEFAULT; |
| iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT; |
| iso9660->opt.rr = OPT_RR_DEFAULT; |
| iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT; |
| iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT; |
| |
| /* Create the root directory. */ |
| iso9660->primary.rootent = |
| isoent_create_virtual_dir(a, iso9660, ""); |
| if (iso9660->primary.rootent == NULL) { |
| free(iso9660); |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| iso9660->primary.rootent->parent = iso9660->primary.rootent; |
| iso9660->cur_dirent = iso9660->primary.rootent; |
| archive_string_init(&(iso9660->cur_dirstr)); |
| archive_string_ensure(&(iso9660->cur_dirstr), 1); |
| iso9660->cur_dirstr.s[0] = 0; |
| iso9660->sconv_to_utf16be = NULL; |
| iso9660->sconv_from_utf16be = NULL; |
| |
| a->format_data = iso9660; |
| a->format_name = "iso9660"; |
| a->format_options = iso9660_options; |
| a->format_write_header = iso9660_write_header; |
| a->format_write_data = iso9660_write_data; |
| a->format_finish_entry = iso9660_finish_entry; |
| a->format_close = iso9660_close; |
| a->format_free = iso9660_free; |
| a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; |
| a->archive.archive_format_name = "ISO9660"; |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| get_str_opt(struct archive_write *a, struct archive_string *s, |
| size_t maxsize, const char *key, const char *value) |
| { |
| |
| if (strlen(value) > maxsize) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Value is longer than %zu characters " |
| "for option ``%s''", maxsize, key); |
| return (ARCHIVE_FATAL); |
| } |
| archive_strcpy(s, value); |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| get_num_opt(struct archive_write *a, int *num, int high, int low, |
| const char *key, const char *value) |
| { |
| const char *p = value; |
| int data = 0; |
| int neg = 0; |
| |
| if (p == NULL) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Invalid value(empty) for option ``%s''", key); |
| return (ARCHIVE_FATAL); |
| } |
| if (*p == '-') { |
| neg = 1; |
| p++; |
| } |
| while (*p) { |
| if (*p >= '0' && *p <= '9') |
| data = data * 10 + *p - '0'; |
| else { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Invalid value for option ``%s''", key); |
| return (ARCHIVE_FATAL); |
| } |
| if (data > high) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Invalid value(over %d) for " |
| "option ``%s''", high, key); |
| return (ARCHIVE_FATAL); |
| } |
| if (data < low) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Invalid value(under %d) for " |
| "option ``%s''", low, key); |
| return (ARCHIVE_FATAL); |
| } |
| p++; |
| } |
| if (neg) |
| data *= -1; |
| *num = data; |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| iso9660_options(struct archive_write *a, const char *key, const char *value) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| const char *p; |
| int r; |
| |
| switch (key[0]) { |
| case 'a': |
| if (strcmp(key, "abstract-file") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->abstract_file_identifier), |
| ABSTRACT_FILE_SIZE, key, value); |
| iso9660->opt.abstract_file = r == ARCHIVE_OK; |
| return (r); |
| } |
| if (strcmp(key, "application-id") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->application_identifier), |
| APPLICATION_IDENTIFIER_SIZE, key, value); |
| iso9660->opt.application_id = r == ARCHIVE_OK; |
| return (r); |
| } |
| if (strcmp(key, "allow-vernum") == 0) { |
| iso9660->opt.allow_vernum = value != NULL; |
| return (ARCHIVE_OK); |
| } |
| break; |
| case 'b': |
| if (strcmp(key, "biblio-file") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->bibliographic_file_identifier), |
| BIBLIO_FILE_SIZE, key, value); |
| iso9660->opt.biblio_file = r == ARCHIVE_OK; |
| return (r); |
| } |
| if (strcmp(key, "boot") == 0) { |
| if (value == NULL) |
| iso9660->opt.boot = 0; |
| else { |
| iso9660->opt.boot = 1; |
| archive_strcpy( |
| &(iso9660->el_torito.boot_filename), |
| value); |
| } |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "boot-catalog") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->el_torito.catalog_filename), |
| 1024, key, value); |
| iso9660->opt.boot_catalog = r == ARCHIVE_OK; |
| return (r); |
| } |
| if (strcmp(key, "boot-info-table") == 0) { |
| iso9660->opt.boot_info_table = value != NULL; |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "boot-load-seg") == 0) { |
| uint32_t seg; |
| |
| iso9660->opt.boot_load_seg = 0; |
| if (value == NULL) |
| goto invalid_value; |
| seg = 0; |
| p = value; |
| if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) |
| p += 2; |
| while (*p) { |
| if (seg) |
| seg <<= 4; |
| if (*p >= 'A' && *p <= 'F') |
| seg += *p - 'A' + 0x0a; |
| else if (*p >= 'a' && *p <= 'f') |
| seg += *p - 'a' + 0x0a; |
| else if (*p >= '0' && *p <= '9') |
| seg += *p - '0'; |
| else |
| goto invalid_value; |
| if (seg > 0xffff) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "Invalid value(over 0xffff) for " |
| "option ``%s''", key); |
| return (ARCHIVE_FATAL); |
| } |
| p++; |
| } |
| iso9660->el_torito.boot_load_seg = (uint16_t)seg; |
| iso9660->opt.boot_load_seg = 1; |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "boot-load-size") == 0) { |
| int num = 0; |
| r = get_num_opt(a, &num, 0xffff, 1, key, value); |
| iso9660->opt.boot_load_size = r == ARCHIVE_OK; |
| if (r != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->el_torito.boot_load_size = (uint16_t)num; |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "boot-type") == 0) { |
| if (value == NULL) |
| goto invalid_value; |
| if (strcmp(value, "no-emulation") == 0) |
| iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU; |
| else if (strcmp(value, "fd") == 0) |
| iso9660->opt.boot_type = OPT_BOOT_TYPE_FD; |
| else if (strcmp(value, "hard-disk") == 0) |
| iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK; |
| else |
| goto invalid_value; |
| return (ARCHIVE_OK); |
| } |
| break; |
| case 'c': |
| if (strcmp(key, "compression-level") == 0) { |
| #ifdef HAVE_ZLIB_H |
| if (value == NULL || |
| !(value[0] >= '0' && value[0] <= '9') || |
| value[1] != '\0') |
| goto invalid_value; |
| iso9660->zisofs.compression_level = value[0] - '0'; |
| iso9660->opt.compression_level = 1; |
| return (ARCHIVE_OK); |
| #else |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "Option ``%s'' " |
| "is not supported on this platform.", key); |
| return (ARCHIVE_FATAL); |
| #endif |
| } |
| if (strcmp(key, "copyright-file") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->copyright_file_identifier), |
| COPYRIGHT_FILE_SIZE, key, value); |
| iso9660->opt.copyright_file = r == ARCHIVE_OK; |
| return (r); |
| } |
| #ifdef DEBUG |
| /* Specifies Volume creation date and time; |
| * year(4),month(2),day(2),hour(2),minute(2),second(2). |
| * e.g. "20090929033757" |
| */ |
| if (strcmp(key, "creation") == 0) { |
| struct tm tm; |
| char buf[5]; |
| |
| p = value; |
| if (p == NULL || strlen(p) < 14) |
| goto invalid_value; |
| memset(&tm, 0, sizeof(tm)); |
| memcpy(buf, p, 4); buf[4] = '\0'; p += 4; |
| tm.tm_year = strtol(buf, NULL, 10) - 1900; |
| memcpy(buf, p, 2); buf[2] = '\0'; p += 2; |
| tm.tm_mon = strtol(buf, NULL, 10) - 1; |
| memcpy(buf, p, 2); buf[2] = '\0'; p += 2; |
| tm.tm_mday = strtol(buf, NULL, 10); |
| memcpy(buf, p, 2); buf[2] = '\0'; p += 2; |
| tm.tm_hour = strtol(buf, NULL, 10); |
| memcpy(buf, p, 2); buf[2] = '\0'; p += 2; |
| tm.tm_min = strtol(buf, NULL, 10); |
| memcpy(buf, p, 2); buf[2] = '\0'; |
| tm.tm_sec = strtol(buf, NULL, 10); |
| iso9660->birth_time = mktime(&tm); |
| return (ARCHIVE_OK); |
| } |
| #endif |
| break; |
| case 'i': |
| if (strcmp(key, "iso-level") == 0) { |
| if (value != NULL && value[1] == '\0' && |
| (value[0] >= '1' && value[0] <= '4')) { |
| iso9660->opt.iso_level = value[0]-'0'; |
| return (ARCHIVE_OK); |
| } |
| goto invalid_value; |
| } |
| break; |
| case 'j': |
| if (strcmp(key, "joliet") == 0) { |
| if (value == NULL) |
| iso9660->opt.joliet = OPT_JOLIET_DISABLE; |
| else if (strcmp(value, "1") == 0) |
| iso9660->opt.joliet = OPT_JOLIET_ENABLE; |
| else if (strcmp(value, "long") == 0) |
| iso9660->opt.joliet = OPT_JOLIET_LONGNAME; |
| else |
| goto invalid_value; |
| return (ARCHIVE_OK); |
| } |
| break; |
| case 'l': |
| if (strcmp(key, "limit-depth") == 0) { |
| iso9660->opt.limit_depth = value != NULL; |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "limit-dirs") == 0) { |
| iso9660->opt.limit_dirs = value != NULL; |
| return (ARCHIVE_OK); |
| } |
| break; |
| case 'p': |
| if (strcmp(key, "pad") == 0) { |
| iso9660->opt.pad = value != NULL; |
| return (ARCHIVE_OK); |
| } |
| if (strcmp(key, "publisher") == 0) { |
| r = get_str_opt(a, |
| &(iso9660->publisher_identifier), |
| PUBLISHER_IDENTIFIER_SIZE, key, value); |
| iso9660->opt.publisher = r == ARCHIVE_OK; |
| return (r); |
| } |
| break; |
| case 'r': |
| if (strcmp(key, "rockridge") == 0 || |
| strcmp(key, "Rockridge") == 0) { |
| if (value == NULL) |
| iso9660->opt.rr = OPT_RR_DISABLED; |
| else if (strcmp(value, "1") == 0) |
| iso9660->opt.rr = OPT_RR_USEFUL; |
| else if (strcmp(value, "strict") == 0) |
| iso9660->opt.rr = OPT_RR_STRICT; |
| else if (strcmp(value, "useful") == 0) |
| iso9660->opt.rr = OPT_RR_USEFUL; |
| else |
| goto invalid_value; |
| return (ARCHIVE_OK); |
| } |
| break; |
| case 'v': |
| if (strcmp(key, "volume-id") == 0) { |
| r = get_str_opt(a, &(iso9660->volume_identifier), |
| VOLUME_IDENTIFIER_SIZE, key, value); |
| iso9660->opt.volume_id = r == ARCHIVE_OK; |
| return (r); |
| } |
| break; |
| case 'z': |
| if (strcmp(key, "zisofs") == 0) { |
| if (value == NULL) |
| iso9660->opt.zisofs = OPT_ZISOFS_DISABLED; |
| else { |
| #ifdef HAVE_ZLIB_H |
| iso9660->opt.zisofs = OPT_ZISOFS_DIRECT; |
| #else |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "``zisofs'' " |
| "is not supported on this platform."); |
| return (ARCHIVE_FATAL); |
| #endif |
| } |
| return (ARCHIVE_OK); |
| } |
| break; |
| } |
| |
| /* Note: The "warn" return is just to inform the options |
| * supervisor that we didn't handle it. It will generate |
| * a suitable error if no one used this option. */ |
| return (ARCHIVE_WARN); |
| |
| invalid_value: |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Invalid value for option ``%s''", key); |
| return (ARCHIVE_FAILED); |
| } |
| |
| static int |
| iso9660_write_header(struct archive_write *a, struct archive_entry *entry) |
| { |
| struct iso9660 *iso9660; |
| struct isofile *file; |
| struct isoent *isoent; |
| int r, ret = ARCHIVE_OK; |
| |
| iso9660 = a->format_data; |
| |
| iso9660->cur_file = NULL; |
| iso9660->bytes_remaining = 0; |
| iso9660->need_multi_extent = 0; |
| if (archive_entry_filetype(entry) == AE_IFLNK |
| && iso9660->opt.rr == OPT_RR_DISABLED) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Ignore symlink file."); |
| iso9660->cur_file = NULL; |
| return (ARCHIVE_WARN); |
| } |
| if (archive_entry_filetype(entry) == AE_IFREG && |
| archive_entry_size(entry) >= MULTI_EXTENT_SIZE) { |
| if (iso9660->opt.iso_level < 3) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "Ignore over %lld bytes file. " |
| "This file too large.", |
| MULTI_EXTENT_SIZE); |
| iso9660->cur_file = NULL; |
| return (ARCHIVE_WARN); |
| } |
| iso9660->need_multi_extent = 1; |
| } |
| |
| file = isofile_new(a, entry); |
| if (file == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate data"); |
| return (ARCHIVE_FATAL); |
| } |
| r = isofile_gen_utility_names(a, file); |
| if (r < ARCHIVE_WARN) { |
| isofile_free(file); |
| return (r); |
| } |
| else if (r < ret) |
| ret = r; |
| |
| /* |
| * Ignore a path which looks like the top of directory name |
| * since we have already made the root directory of an ISO image. |
| */ |
| if (archive_strlen(&(file->parentdir)) == 0 && |
| archive_strlen(&(file->basename)) == 0) { |
| isofile_free(file); |
| return (r); |
| } |
| |
| isofile_add_entry(iso9660, file); |
| isoent = isoent_new(file); |
| if (isoent == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate data"); |
| return (ARCHIVE_FATAL); |
| } |
| if (isoent->file->dircnt > iso9660->dircnt_max) |
| iso9660->dircnt_max = isoent->file->dircnt; |
| |
| /* Add the current file into tree */ |
| r = isoent_tree(a, &isoent); |
| if (r != ARCHIVE_OK) |
| return (r); |
| |
| /* If there is the same file in tree and |
| * the current file is older than the file in tree. |
| * So we don't need the current file data anymore. */ |
| if (isoent->file != file) |
| return (ARCHIVE_OK); |
| |
| /* Non regular files contents are unneeded to be saved to |
| * temporary files. */ |
| if (archive_entry_filetype(file->entry) != AE_IFREG) |
| return (ret); |
| |
| /* |
| * Set the current file to cur_file to read its contents. |
| */ |
| iso9660->cur_file = file; |
| |
| if (archive_entry_nlink(file->entry) > 1) { |
| r = isofile_register_hardlink(a, file); |
| if (r != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* |
| * Prepare to save the contents of the file. |
| */ |
| if (iso9660->temp_fd < 0) { |
| iso9660->temp_fd = __archive_mktemp(NULL); |
| if (iso9660->temp_fd < 0) { |
| archive_set_error(&a->archive, errno, |
| "Couldn't create temporary file"); |
| return (ARCHIVE_FATAL); |
| } |
| } |
| |
| /* Save an offset of current file in temporary file. */ |
| file->content.offset_of_temp = wb_offset(a); |
| file->cur_content = &(file->content); |
| r = zisofs_init(a, file); |
| if (r < ret) |
| ret = r; |
| iso9660->bytes_remaining = archive_entry_size(file->entry); |
| |
| return (ret); |
| } |
| |
| static int |
| write_to_temp(struct archive_write *a, const void *buff, size_t s) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| ssize_t written; |
| const unsigned char *b; |
| |
| b = (const unsigned char *)buff; |
| while (s) { |
| written = write(iso9660->temp_fd, b, s); |
| if (written < 0) { |
| archive_set_error(&a->archive, errno, |
| "Can't write to temporary file"); |
| return (ARCHIVE_FATAL); |
| } |
| s -= written; |
| b += written; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| wb_write_to_temp(struct archive_write *a, const void *buff, size_t s) |
| { |
| const char *xp = buff; |
| size_t xs = s; |
| |
| /* |
| * If a written data size is big enough to use system-call |
| * and there is no waiting data, this calls write_to_temp() in |
| * order to reduce a extra memory copy. |
| */ |
| if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) { |
| struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; |
| xs = s % LOGICAL_BLOCK_SIZE; |
| iso9660->wbuff_offset += s - xs; |
| if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| if (xs == 0) |
| return (ARCHIVE_OK); |
| xp += s - xs; |
| } |
| |
| while (xs) { |
| size_t size = xs; |
| if (size > wb_remaining(a)) |
| size = wb_remaining(a); |
| memcpy(wb_buffptr(a), xp, size); |
| if (wb_consume(a, size) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| xs -= size; |
| xp += size; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| wb_write_padding_to_temp(struct archive_write *a, int64_t csize) |
| { |
| size_t ns; |
| int ret; |
| |
| ns = (size_t)(csize % LOGICAL_BLOCK_SIZE); |
| if (ns != 0) |
| ret = write_null(a, LOGICAL_BLOCK_SIZE - ns); |
| else |
| ret = ARCHIVE_OK; |
| return (ret); |
| } |
| |
| static ssize_t |
| write_iso9660_data(struct archive_write *a, const void *buff, size_t s) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| size_t ws; |
| |
| if (iso9660->temp_fd < 0) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Couldn't create temporary file"); |
| return (ARCHIVE_FATAL); |
| } |
| |
| ws = s; |
| if (iso9660->need_multi_extent && |
| (iso9660->cur_file->cur_content->size + ws) >= |
| (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) { |
| struct content *con; |
| size_t ts; |
| |
| ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE - |
| iso9660->cur_file->cur_content->size); |
| |
| if (iso9660->zisofs.detect_magic) |
| zisofs_detect_magic(a, buff, ts); |
| |
| if (iso9660->zisofs.making) { |
| if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } else { |
| if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->cur_file->cur_content->size += ts; |
| } |
| |
| /* Write padding. */ |
| if (wb_write_padding_to_temp(a, |
| iso9660->cur_file->cur_content->size) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Compute the logical block number. */ |
| iso9660->cur_file->cur_content->blocks = (int) |
| ((iso9660->cur_file->cur_content->size |
| + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); |
| |
| /* |
| * Make next extent. |
| */ |
| ws -= ts; |
| buff = (const void *)(((const unsigned char *)buff) + ts); |
| /* Make a content for next extent. */ |
| con = calloc(1, sizeof(*con)); |
| if (con == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate content data"); |
| return (ARCHIVE_FATAL); |
| } |
| con->offset_of_temp = wb_offset(a); |
| iso9660->cur_file->cur_content->next = con; |
| iso9660->cur_file->cur_content = con; |
| #ifdef HAVE_ZLIB_H |
| iso9660->zisofs.block_offset = 0; |
| #endif |
| } |
| |
| if (iso9660->zisofs.detect_magic) |
| zisofs_detect_magic(a, buff, ws); |
| |
| if (iso9660->zisofs.making) { |
| if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } else { |
| if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->cur_file->cur_content->size += ws; |
| } |
| |
| return (s); |
| } |
| |
| static ssize_t |
| iso9660_write_data(struct archive_write *a, const void *buff, size_t s) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| ssize_t r; |
| |
| if (iso9660->cur_file == NULL) |
| return (0); |
| if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) |
| return (0); |
| if (s > iso9660->bytes_remaining) |
| s = (size_t)iso9660->bytes_remaining; |
| if (s == 0) |
| return (0); |
| |
| r = write_iso9660_data(a, buff, s); |
| if (r > 0) |
| iso9660->bytes_remaining -= r; |
| return (r); |
| } |
| |
| static int |
| iso9660_finish_entry(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| |
| if (iso9660->cur_file == NULL) |
| return (ARCHIVE_OK); |
| if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG) |
| return (ARCHIVE_OK); |
| if (iso9660->cur_file->content.size == 0) |
| return (ARCHIVE_OK); |
| |
| /* If there are unwritten data, write null data instead. */ |
| while (iso9660->bytes_remaining > 0) { |
| size_t s; |
| |
| s = (iso9660->bytes_remaining > a->null_length)? |
| a->null_length: (size_t)iso9660->bytes_remaining; |
| if (write_iso9660_data(a, a->nulls, s) < 0) |
| return (ARCHIVE_FATAL); |
| iso9660->bytes_remaining -= s; |
| } |
| |
| if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write padding. */ |
| if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size) |
| != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Compute the logical block number. */ |
| iso9660->cur_file->cur_content->blocks = (int) |
| ((iso9660->cur_file->cur_content->size |
| + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); |
| |
| /* Add the current file to data file list. */ |
| isofile_add_data_file(iso9660, iso9660->cur_file); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| iso9660_close(struct archive_write *a) |
| { |
| struct iso9660 *iso9660; |
| int ret, blocks; |
| |
| iso9660 = a->format_data; |
| |
| /* |
| * Write remaining data out to the temporary file. |
| */ |
| if (wb_remaining(a) > 0) { |
| ret = wb_write_out(a); |
| if (ret < 0) |
| return (ret); |
| } |
| |
| /* |
| * Preparations... |
| */ |
| #ifdef DEBUG |
| if (iso9660->birth_time == 0) |
| #endif |
| time(&(iso9660->birth_time)); |
| |
| /* |
| * Prepare a bootable ISO image. |
| */ |
| if (iso9660->opt.boot) { |
| /* Find out the boot file entry. */ |
| ret = isoent_find_out_boot_file(a, iso9660->primary.rootent); |
| if (ret < 0) |
| return (ret); |
| /* Reconvert the boot file from zisofs'ed form to |
| * plain form. */ |
| ret = zisofs_rewind_boot_file(a); |
| if (ret < 0) |
| return (ret); |
| /* Write remaining data out to the temporary file. */ |
| if (wb_remaining(a) > 0) { |
| ret = wb_write_out(a); |
| if (ret < 0) |
| return (ret); |
| } |
| /* Create the boot catalog. */ |
| ret = isoent_create_boot_catalog(a, iso9660->primary.rootent); |
| if (ret < 0) |
| return (ret); |
| } |
| |
| /* |
| * Prepare joliet extensions. |
| */ |
| if (iso9660->opt.joliet) { |
| /* Make a new tree for joliet. */ |
| ret = isoent_clone_tree(a, &(iso9660->joliet.rootent), |
| iso9660->primary.rootent); |
| if (ret < 0) |
| return (ret); |
| /* Make sure we have UTF-16BE converters. |
| * if there is no file entry, converters are still |
| * uninitialized. */ |
| if (iso9660->sconv_to_utf16be == NULL) { |
| iso9660->sconv_to_utf16be = |
| archive_string_conversion_to_charset( |
| &(a->archive), "UTF-16BE", 1); |
| if (iso9660->sconv_to_utf16be == NULL) |
| /* Couldn't allocate memory */ |
| return (ARCHIVE_FATAL); |
| iso9660->sconv_from_utf16be = |
| archive_string_conversion_from_charset( |
| &(a->archive), "UTF-16BE", 1); |
| if (iso9660->sconv_from_utf16be == NULL) |
| /* Couldn't allocate memory */ |
| return (ARCHIVE_FATAL); |
| } |
| } |
| |
| /* |
| * Make Path Tables. |
| */ |
| ret = isoent_make_path_table(a); |
| if (ret < 0) |
| return (ret); |
| |
| /* |
| * Calculate a total volume size and setup all locations of |
| * contents of an iso9660 image. |
| */ |
| blocks = SYSTEM_AREA_BLOCK |
| + PRIMARY_VOLUME_DESCRIPTOR_BLOCK |
| + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK |
| + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; |
| if (iso9660->opt.boot) |
| blocks += BOOT_RECORD_DESCRIPTOR_BLOCK; |
| if (iso9660->opt.joliet) |
| blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; |
| if (iso9660->opt.iso_level == 4) |
| blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK; |
| |
| /* Setup the locations of Path Table. */ |
| iso9660->primary.location_type_L_path_table = blocks; |
| blocks += iso9660->primary.path_table_block; |
| iso9660->primary.location_type_M_path_table = blocks; |
| blocks += iso9660->primary.path_table_block; |
| if (iso9660->opt.joliet) { |
| iso9660->joliet.location_type_L_path_table = blocks; |
| blocks += iso9660->joliet.path_table_block; |
| iso9660->joliet.location_type_M_path_table = blocks; |
| blocks += iso9660->joliet.path_table_block; |
| } |
| |
| /* Setup the locations of directories. */ |
| isoent_setup_directory_location(iso9660, blocks, |
| &(iso9660->primary)); |
| blocks += iso9660->primary.total_dir_block; |
| if (iso9660->opt.joliet) { |
| isoent_setup_directory_location(iso9660, blocks, |
| &(iso9660->joliet)); |
| blocks += iso9660->joliet.total_dir_block; |
| } |
| |
| if (iso9660->opt.rr) { |
| iso9660->location_rrip_er = blocks; |
| blocks += RRIP_ER_BLOCK; |
| } |
| |
| /* Setup the locations of all file contents. */ |
| isoent_setup_file_location(iso9660, blocks); |
| blocks += iso9660->total_file_block; |
| if (iso9660->opt.boot && iso9660->opt.boot_info_table) { |
| ret = setup_boot_information(a); |
| if (ret < 0) |
| return (ret); |
| } |
| |
| /* Now we have a total volume size. */ |
| iso9660->volume_space_size = blocks; |
| if (iso9660->opt.pad) |
| iso9660->volume_space_size += PADDING_BLOCK; |
| iso9660->volume_sequence_number = 1; |
| |
| |
| /* |
| * Write an ISO 9660 image. |
| */ |
| |
| /* Switch to start using wbuff as file buffer. */ |
| iso9660->wbuff_remaining = wb_buffmax(); |
| iso9660->wbuff_type = WB_TO_STREAM; |
| iso9660->wbuff_offset = 0; |
| iso9660->wbuff_written = 0; |
| iso9660->wbuff_tail = 0; |
| |
| /* Write The System Area */ |
| ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Primary Volume Descriptor */ |
| ret = write_VD(a, &(iso9660->primary)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| if (iso9660->opt.boot) { |
| /* Write Boot Record Volume Descriptor */ |
| ret = write_VD_boot_record(a); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| if (iso9660->opt.iso_level == 4) { |
| /* Write Enhanced Volume Descriptor */ |
| iso9660->primary.vdd_type = VDD_ENHANCED; |
| ret = write_VD(a, &(iso9660->primary)); |
| iso9660->primary.vdd_type = VDD_PRIMARY; |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| if (iso9660->opt.joliet) { |
| ret = write_VD(a, &(iso9660->joliet)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* Write Volume Descriptor Set Terminator */ |
| ret = write_VD_terminator(a); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Non-ISO File System Information */ |
| ret = write_information_block(a); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Type L Path Table */ |
| ret = write_path_table(a, 0, &(iso9660->primary)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Type M Path Table */ |
| ret = write_path_table(a, 1, &(iso9660->primary)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| if (iso9660->opt.joliet) { |
| /* Write Type L Path Table */ |
| ret = write_path_table(a, 0, &(iso9660->joliet)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Type M Path Table */ |
| ret = write_path_table(a, 1, &(iso9660->joliet)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* Write Directory Descriptors */ |
| ret = write_directory_descriptors(a, &(iso9660->primary)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| if (iso9660->opt.joliet) { |
| ret = write_directory_descriptors(a, &(iso9660->joliet)); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| if (iso9660->opt.rr) { |
| /* Write Rockridge ER(Extensions Reference) */ |
| ret = write_rr_ER(a); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* Write File Descriptors */ |
| ret = write_file_descriptors(a); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Write Padding */ |
| if (iso9660->opt.pad) { |
| ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE); |
| if (ret != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| |
| if (iso9660->directories_too_deep != NULL) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "%s: Directories too deep.", |
| archive_entry_pathname( |
| iso9660->directories_too_deep->file->entry)); |
| return (ARCHIVE_WARN); |
| } |
| |
| /* Write remaining data out. */ |
| ret = wb_write_out(a); |
| |
| return (ret); |
| } |
| |
| static int |
| iso9660_free(struct archive_write *a) |
| { |
| struct iso9660 *iso9660; |
| int i, ret; |
| |
| iso9660 = a->format_data; |
| |
| /* Close the temporary file. */ |
| if (iso9660->temp_fd >= 0) |
| close(iso9660->temp_fd); |
| |
| /* Free some stuff for zisofs operations. */ |
| ret = zisofs_free(a); |
| |
| /* Remove directory entries in tree which includes file entries. */ |
| isoent_free_all(iso9660->primary.rootent); |
| for (i = 0; i < iso9660->primary.max_depth; i++) |
| free(iso9660->primary.pathtbl[i].sorted); |
| free(iso9660->primary.pathtbl); |
| |
| if (iso9660->opt.joliet) { |
| isoent_free_all(iso9660->joliet.rootent); |
| for (i = 0; i < iso9660->joliet.max_depth; i++) |
| free(iso9660->joliet.pathtbl[i].sorted); |
| free(iso9660->joliet.pathtbl); |
| } |
| |
| /* Remove isofile entries. */ |
| isofile_free_all_entries(iso9660); |
| isofile_free_hardlinks(iso9660); |
| |
| archive_string_free(&(iso9660->cur_dirstr)); |
| archive_string_free(&(iso9660->volume_identifier)); |
| archive_string_free(&(iso9660->publisher_identifier)); |
| archive_string_free(&(iso9660->data_preparer_identifier)); |
| archive_string_free(&(iso9660->application_identifier)); |
| archive_string_free(&(iso9660->copyright_file_identifier)); |
| archive_string_free(&(iso9660->abstract_file_identifier)); |
| archive_string_free(&(iso9660->bibliographic_file_identifier)); |
| archive_string_free(&(iso9660->el_torito.catalog_filename)); |
| archive_string_free(&(iso9660->el_torito.boot_filename)); |
| archive_string_free(&(iso9660->el_torito.id)); |
| archive_string_free(&(iso9660->utf16be)); |
| archive_string_free(&(iso9660->mbs)); |
| |
| free(iso9660); |
| a->format_data = NULL; |
| |
| return (ret); |
| } |
| |
| /* |
| * Get the System Identifier |
| */ |
| static void |
| get_system_identitier(char *system_id, size_t size) |
| { |
| #if defined(HAVE_SYS_UTSNAME_H) |
| struct utsname u; |
| |
| uname(&u); |
| strncpy(system_id, u.sysname, size-1); |
| system_id[size-1] = '\0'; |
| #elif defined(_WIN32) && !defined(__CYGWIN__) |
| strncpy(system_id, "Windows", size-1); |
| system_id[size-1] = '\0'; |
| #else |
| #error no way to get the system identifier on your platform. |
| #endif |
| } |
| |
| static void |
| set_str(unsigned char *p, const char *s, size_t l, char f, const char *map) |
| { |
| unsigned char c; |
| |
| if (s == NULL) |
| s = ""; |
| while ((c = *s++) != 0 && l > 0) { |
| if (c >= 0x80 || map[c] == 0) |
| { |
| /* illegal character */ |
| if (c >= 'a' && c <= 'z') { |
| /* convert c from a-z to A-Z */ |
| c -= 0x20; |
| } else |
| c = 0x5f; |
| } |
| *p++ = c; |
| l--; |
| } |
| /* If l isn't zero, fill p buffer by the character |
| * which indicated by f. */ |
| if (l > 0) |
| memset(p , f, l); |
| } |
| |
| static inline int |
| joliet_allowed_char(unsigned char high, unsigned char low) |
| { |
| int utf16 = (high << 8) | low; |
| |
| if (utf16 <= 0x001F) |
| return (0); |
| |
| switch (utf16) { |
| case 0x002A: /* '*' */ |
| case 0x002F: /* '/' */ |
| case 0x003A: /* ':' */ |
| case 0x003B: /* ';' */ |
| case 0x003F: /* '?' */ |
| case 0x005C: /* '\' */ |
| return (0);/* Not allowed. */ |
| } |
| return (1); |
| } |
| |
| static int |
| set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s, |
| size_t l, uint16_t uf, enum vdc vdc) |
| { |
| size_t size, i; |
| int onepad; |
| |
| if (s == NULL) |
| s = ""; |
| if (l & 0x01) { |
| onepad = 1; |
| l &= ~1; |
| } else |
| onepad = 0; |
| if (vdc == VDC_UCS2) { |
| struct iso9660 *iso9660 = a->format_data; |
| if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s), |
| iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory for UTF-16BE"); |
| return (ARCHIVE_FATAL); |
| } |
| size = iso9660->utf16be.length; |
| if (size > l) |
| size = l; |
| memcpy(p, iso9660->utf16be.s, size); |
| } else { |
| const uint16_t *u16 = (const uint16_t *)s; |
| |
| size = 0; |
| while (*u16++) |
| size += 2; |
| if (size > l) |
| size = l; |
| memcpy(p, s, size); |
| } |
| for (i = 0; i < size; i += 2, p += 2) { |
| if (!joliet_allowed_char(p[0], p[1])) |
| archive_be16enc(p, 0x005F);/* '_' */ |
| } |
| l -= size; |
| while (l > 0) { |
| archive_be16enc(p, uf); |
| p += 2; |
| l -= 2; |
| } |
| if (onepad) |
| *p = 0; |
| return (ARCHIVE_OK); |
| } |
| |
| static const char a_characters_map[0x80] = { |
| /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ |
| 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ |
| }; |
| |
| static const char a1_characters_map[0x80] = { |
| /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ |
| 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ |
| }; |
| |
| static const char d_characters_map[0x80] = { |
| /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */ |
| }; |
| |
| static const char d1_characters_map[0x80] = { |
| /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */ |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */ |
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */ |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */ |
| }; |
| |
| static int |
| set_str_a_characters_bp(struct archive_write *a, unsigned char *bp, |
| int from, int to, const char *s, enum vdc vdc) |
| { |
| int r; |
| |
| switch (vdc) { |
| case VDC_STD: |
| set_str(bp+from, s, to - from + 1, 0x20, |
| a_characters_map); |
| r = ARCHIVE_OK; |
| break; |
| case VDC_LOWERCASE: |
| set_str(bp+from, s, to - from + 1, 0x20, |
| a1_characters_map); |
| r = ARCHIVE_OK; |
| break; |
| case VDC_UCS2: |
| case VDC_UCS2_DIRECT: |
| r = set_str_utf16be(a, bp+from, s, to - from + 1, |
| 0x0020, vdc); |
| break; |
| default: |
| r = ARCHIVE_FATAL; |
| } |
| return (r); |
| } |
| |
| static int |
| set_str_d_characters_bp(struct archive_write *a, unsigned char *bp, |
| int from, int to, const char *s, enum vdc vdc) |
| { |
| int r; |
| |
| switch (vdc) { |
| case VDC_STD: |
| set_str(bp+from, s, to - from + 1, 0x20, |
| d_characters_map); |
| r = ARCHIVE_OK; |
| break; |
| case VDC_LOWERCASE: |
| set_str(bp+from, s, to - from + 1, 0x20, |
| d1_characters_map); |
| r = ARCHIVE_OK; |
| break; |
| case VDC_UCS2: |
| case VDC_UCS2_DIRECT: |
| r = set_str_utf16be(a, bp+from, s, to - from + 1, |
| 0x0020, vdc); |
| break; |
| default: |
| r = ARCHIVE_FATAL; |
| } |
| return (r); |
| } |
| |
| static void |
| set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver) |
| { |
| |
| /* Volume Descriptor Type */ |
| bp[1] = (unsigned char)type; |
| /* Standard Identifier */ |
| memcpy(bp + 2, "CD001", 5); |
| /* Volume Descriptor Version */ |
| bp[7] = ver; |
| } |
| |
| static inline void |
| set_unused_field_bp(unsigned char *bp, int from, int to) |
| { |
| memset(bp + from, 0, to - from + 1); |
| } |
| |
| /* |
| * 8-bit unsigned numerical values. |
| * ISO9660 Standard 7.1.1 |
| */ |
| static inline void |
| set_num_711(unsigned char *p, unsigned char value) |
| { |
| *p = value; |
| } |
| |
| /* |
| * 8-bit signed numerical values. |
| * ISO9660 Standard 7.1.2 |
| */ |
| static inline void |
| set_num_712(unsigned char *p, char value) |
| { |
| *((char *)p) = value; |
| } |
| |
| /* |
| * Least significant byte first. |
| * ISO9660 Standard 7.2.1 |
| */ |
| static inline void |
| set_num_721(unsigned char *p, uint16_t value) |
| { |
| archive_le16enc(p, value); |
| } |
| |
| /* |
| * Most significant byte first. |
| * ISO9660 Standard 7.2.2 |
| */ |
| static inline void |
| set_num_722(unsigned char *p, uint16_t value) |
| { |
| archive_be16enc(p, value); |
| } |
| |
| /* |
| * Both-byte orders. |
| * ISO9660 Standard 7.2.3 |
| */ |
| static void |
| set_num_723(unsigned char *p, uint16_t value) |
| { |
| archive_le16enc(p, value); |
| archive_be16enc(p+2, value); |
| } |
| |
| /* |
| * Least significant byte first. |
| * ISO9660 Standard 7.3.1 |
| */ |
| static inline void |
| set_num_731(unsigned char *p, uint32_t value) |
| { |
| archive_le32enc(p, value); |
| } |
| |
| /* |
| * Most significant byte first. |
| * ISO9660 Standard 7.3.2 |
| */ |
| static inline void |
| set_num_732(unsigned char *p, uint32_t value) |
| { |
| archive_be32enc(p, value); |
| } |
| |
| /* |
| * Both-byte orders. |
| * ISO9660 Standard 7.3.3 |
| */ |
| static inline void |
| set_num_733(unsigned char *p, uint32_t value) |
| { |
| archive_le32enc(p, value); |
| archive_be32enc(p+4, value); |
| } |
| |
| static void |
| set_digit(unsigned char *p, size_t s, int value) |
| { |
| |
| while (s--) { |
| p[s] = '0' + (value % 10); |
| value /= 10; |
| } |
| } |
| |
| #if defined(HAVE_STRUCT_TM_TM_GMTOFF) |
| #define get_gmoffset(tm) ((tm)->tm_gmtoff) |
| #elif defined(HAVE_STRUCT_TM___TM_GMTOFF) |
| #define get_gmoffset(tm) ((tm)->__tm_gmtoff) |
| #else |
| static long |
| get_gmoffset(struct tm *tm) |
| { |
| long offset; |
| |
| #if defined(HAVE__GET_TIMEZONE) |
| _get_timezone(&offset); |
| #elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) |
| offset = _timezone; |
| #else |
| offset = timezone; |
| #endif |
| offset *= -1; |
| if (tm->tm_isdst) |
| offset += 3600; |
| return (offset); |
| } |
| #endif |
| |
| static void |
| get_tmfromtime(struct tm *tm, time_t *t) |
| { |
| #if HAVE_LOCALTIME_R |
| tzset(); |
| localtime_r(t, tm); |
| #elif HAVE__LOCALTIME64_S |
| __time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits |
| _localtime64_s(tm, &tmp_t); |
| #else |
| memcpy(tm, localtime(t), sizeof(*tm)); |
| #endif |
| } |
| |
| /* |
| * Date and Time Format. |
| * ISO9660 Standard 8.4.26.1 |
| */ |
| static void |
| set_date_time(unsigned char *p, time_t t) |
| { |
| struct tm tm; |
| |
| get_tmfromtime(&tm, &t); |
| set_digit(p, 4, tm.tm_year + 1900); |
| set_digit(p+4, 2, tm.tm_mon + 1); |
| set_digit(p+6, 2, tm.tm_mday); |
| set_digit(p+8, 2, tm.tm_hour); |
| set_digit(p+10, 2, tm.tm_min); |
| set_digit(p+12, 2, tm.tm_sec); |
| set_digit(p+14, 2, 0); |
| set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15))); |
| } |
| |
| static void |
| set_date_time_null(unsigned char *p) |
| { |
| memset(p, (int)'0', 16); |
| p[16] = 0; |
| } |
| |
| static void |
| set_time_915(unsigned char *p, time_t t) |
| { |
| struct tm tm; |
| |
| get_tmfromtime(&tm, &t); |
| set_num_711(p+0, tm.tm_year); |
| set_num_711(p+1, tm.tm_mon+1); |
| set_num_711(p+2, tm.tm_mday); |
| set_num_711(p+3, tm.tm_hour); |
| set_num_711(p+4, tm.tm_min); |
| set_num_711(p+5, tm.tm_sec); |
| set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15))); |
| } |
| |
| |
| /* |
| * Write SUSP "CE" System Use Entry. |
| */ |
| static int |
| set_SUSP_CE(unsigned char *p, int location, int offset, int size) |
| { |
| unsigned char *bp = p -1; |
| /* Extend the System Use Area |
| * "CE" Format: |
| * len ver |
| * +----+----+----+----+-----------+-----------+ |
| * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 | |
| * +----+----+----+----+-----------+-----------+ |
| * 0 1 2 3 4 12 20 |
| * +-----------+ |
| * | LOCATION3 | |
| * +-----------+ |
| * 20 28 |
| * LOCATION1 : Location of Continuation of System Use Area. |
| * LOCATION2 : Offset to Start of Continuation. |
| * LOCATION3 : Length of the Continuation. |
| */ |
| |
| bp[1] = 'C'; |
| bp[2] = 'E'; |
| bp[3] = RR_CE_SIZE; /* length */ |
| bp[4] = 1; /* version */ |
| set_num_733(bp+5, location); |
| set_num_733(bp+13, offset); |
| set_num_733(bp+21, size); |
| return (RR_CE_SIZE); |
| } |
| |
| /* |
| * The functions, which names are beginning with extra_, are used to |
| * control extra records. |
| * The maximum size of a Directory Record is 254. When a filename is |
| * very long, all of RRIP data of a file won't stored to the Directory |
| * Record and so remaining RRIP data store to an extra record instead. |
| */ |
| static unsigned char * |
| extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent, |
| struct ctl_extr_rec *ctl) |
| { |
| ctl->bp = bp; |
| if (bp != NULL) |
| bp += dr_len; |
| ctl->use_extr = 0; |
| ctl->isoent = isoent; |
| ctl->ce_ptr = NULL; |
| ctl->cur_len = ctl->dr_len = dr_len; |
| ctl->limit = DR_LIMIT; |
| |
| return (bp); |
| } |
| |
| static void |
| extra_close_record(struct ctl_extr_rec *ctl, int ce_size) |
| { |
| int padding = 0; |
| |
| if (ce_size > 0) |
| extra_tell_used_size(ctl, ce_size); |
| /* Padding. */ |
| if (ctl->cur_len & 0x01) { |
| ctl->cur_len++; |
| if (ctl->bp != NULL) |
| ctl->bp[ctl->cur_len] = 0; |
| padding = 1; |
| } |
| if (ctl->use_extr) { |
| if (ctl->ce_ptr != NULL) |
| set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc, |
| ctl->extr_off, ctl->cur_len - padding); |
| } else |
| ctl->dr_len = ctl->cur_len; |
| } |
| |
| #define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len) |
| |
| static unsigned char * |
| extra_next_record(struct ctl_extr_rec *ctl, int length) |
| { |
| int cur_len = ctl->cur_len;/* save cur_len */ |
| |
| /* Close the current extra record or Directory Record. */ |
| extra_close_record(ctl, RR_CE_SIZE); |
| |
| /* Get a next extra record. */ |
| ctl->use_extr = 1; |
| if (ctl->bp != NULL) { |
| /* Storing data into an extra record. */ |
| unsigned char *p; |
| |
| /* Save the pointer where a CE extension will be |
| * stored to. */ |
| ctl->ce_ptr = &ctl->bp[cur_len+1]; |
| p = extra_get_record(ctl->isoent, |
| &ctl->limit, &ctl->extr_off, &ctl->extr_loc); |
| ctl->bp = p - 1;/* the base of bp offset is 1. */ |
| } else |
| /* Calculating the size of an extra record. */ |
| (void)extra_get_record(ctl->isoent, |
| &ctl->limit, NULL, NULL); |
| ctl->cur_len = 0; |
| /* Check if an extra record is almost full. |
| * If so, get a next one. */ |
| if (extra_space(ctl) < length) |
| (void)extra_next_record(ctl, length); |
| |
| return (ctl->bp); |
| } |
| |
| static inline struct extr_rec * |
| extra_last_record(struct isoent *isoent) |
| { |
| if (isoent->extr_rec_list.first == NULL) |
| return (NULL); |
| return ((struct extr_rec *)(void *) |
| ((char *)(isoent->extr_rec_list.last) |
| - offsetof(struct extr_rec, next))); |
| } |
| |
| static unsigned char * |
| extra_get_record(struct isoent *isoent, int *space, int *off, int *loc) |
| { |
| struct extr_rec *rec; |
| |
| isoent = isoent->parent; |
| if (off != NULL) { |
| /* Storing data into an extra record. */ |
| rec = isoent->extr_rec_list.current; |
| if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) |
| rec = rec->next; |
| } else { |
| /* Calculating the size of an extra record. */ |
| rec = extra_last_record(isoent); |
| if (rec == NULL || |
| DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) { |
| rec = malloc(sizeof(*rec)); |
| if (rec == NULL) |
| return (NULL); |
| rec->location = 0; |
| rec->offset = 0; |
| /* Insert `rec` into the tail of isoent->extr_rec_list */ |
| rec->next = NULL; |
| /* |
| * Note: testing isoent->extr_rec_list.last == NULL |
| * here is really unneeded since it has been already |
| * initialized at isoent_new function but Clang Static |
| * Analyzer claims that it is dereference of null |
| * pointer. |
| */ |
| if (isoent->extr_rec_list.last == NULL) |
| isoent->extr_rec_list.last = |
| &(isoent->extr_rec_list.first); |
| *isoent->extr_rec_list.last = rec; |
| isoent->extr_rec_list.last = &(rec->next); |
| } |
| } |
| *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY; |
| if (*space & 0x01) |
| *space -= 1;/* Keep padding space. */ |
| if (off != NULL) |
| *off = rec->offset; |
| if (loc != NULL) |
| *loc = rec->location; |
| isoent->extr_rec_list.current = rec; |
| |
| return (&rec->buf[rec->offset]); |
| } |
| |
| static void |
| extra_tell_used_size(struct ctl_extr_rec *ctl, int size) |
| { |
| struct isoent *isoent; |
| struct extr_rec *rec; |
| |
| if (ctl->use_extr) { |
| isoent = ctl->isoent->parent; |
| rec = isoent->extr_rec_list.current; |
| if (rec != NULL) |
| rec->offset += size; |
| } |
| ctl->cur_len += size; |
| } |
| |
| static int |
| extra_setup_location(struct isoent *isoent, int location) |
| { |
| struct extr_rec *rec; |
| int cnt; |
| |
| cnt = 0; |
| rec = isoent->extr_rec_list.first; |
| isoent->extr_rec_list.current = rec; |
| while (rec) { |
| cnt++; |
| rec->location = location++; |
| rec->offset = 0; |
| rec = rec->next; |
| } |
| return (cnt); |
| } |
| |
| /* |
| * Create the RRIP entries. |
| */ |
| static int |
| set_directory_record_rr(unsigned char *bp, int dr_len, |
| struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t) |
| { |
| /* Flags(BP 5) of the Rockridge "RR" System Use Field */ |
| unsigned char rr_flag; |
| #define RR_USE_PX 0x01 |
| #define RR_USE_PN 0x02 |
| #define RR_USE_SL 0x04 |
| #define RR_USE_NM 0x08 |
| #define RR_USE_CL 0x10 |
| #define RR_USE_PL 0x20 |
| #define RR_USE_RE 0x40 |
| #define RR_USE_TF 0x80 |
| int length; |
| struct ctl_extr_rec ctl; |
| struct isoent *rr_parent, *pxent; |
| struct isofile *file; |
| |
| bp = extra_open_record(bp, dr_len, isoent, &ctl); |
| |
| if (t == DIR_REC_PARENT) { |
| rr_parent = isoent->rr_parent; |
| pxent = isoent->parent; |
| if (rr_parent != NULL) |
| isoent = rr_parent; |
| else |
| isoent = isoent->parent; |
| } else { |
| rr_parent = NULL; |
| pxent = isoent; |
| } |
| file = isoent->file; |
| |
| if (t != DIR_REC_NORMAL) { |
| rr_flag = RR_USE_PX | RR_USE_TF; |
| if (rr_parent != NULL) |
| rr_flag |= RR_USE_PL; |
| } else { |
| rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF; |
| if (archive_entry_filetype(file->entry) == AE_IFLNK) |
| rr_flag |= RR_USE_SL; |
| if (isoent->rr_parent != NULL) |
| rr_flag |= RR_USE_RE; |
| if (isoent->rr_child != NULL) |
| rr_flag |= RR_USE_CL; |
| if (archive_entry_filetype(file->entry) == AE_IFCHR || |
| archive_entry_filetype(file->entry) == AE_IFBLK) |
| rr_flag |= RR_USE_PN; |
| #ifdef COMPAT_MKISOFS |
| /* |
| * mkisofs 2.01.01a63 records "RE" extension to |
| * the entry of "rr_moved" directory. |
| * I don't understand this behavior. |
| */ |
| if (isoent->virtual && |
| isoent->parent == iso9660->primary.rootent && |
| strcmp(isoent->file->basename.s, "rr_moved") == 0) |
| rr_flag |= RR_USE_RE; |
| #endif |
| } |
| |
| /* Write "SP" System Use Entry. */ |
| if (t == DIR_REC_SELF && isoent == isoent->parent) { |
| length = 7; |
| if (bp != NULL) { |
| bp[1] = 'S'; |
| bp[2] = 'P'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| bp[5] = 0xBE; /* Check Byte */ |
| bp[6] = 0xEF; /* Check Byte */ |
| bp[7] = 0; |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "RR" System Use Entry. */ |
| length = 5; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'R'; |
| bp[2] = 'R'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| bp[5] = rr_flag; |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| |
| /* Write "NM" System Use Entry. */ |
| if (rr_flag & RR_USE_NM) { |
| /* |
| * "NM" Format: |
| * e.g. a basename is 'foo' |
| * len ver flg |
| * +----+----+----+----+----+----+----+----+ |
| * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'| |
| * +----+----+----+----+----+----+----+----+ |
| * <----------------- len -----------------> |
| */ |
| size_t nmlen = file->basename.length; |
| const char *nm = file->basename.s; |
| size_t nmmax; |
| |
| if (extra_space(&ctl) < 6) |
| bp = extra_next_record(&ctl, 6); |
| if (bp != NULL) { |
| bp[1] = 'N'; |
| bp[2] = 'M'; |
| bp[4] = 1; /* version */ |
| } |
| nmmax = extra_space(&ctl); |
| if (nmmax > 0xff) |
| nmmax = 0xff; |
| while (nmlen + 5 > nmmax) { |
| length = (int)nmmax; |
| if (bp != NULL) { |
| bp[3] = length; |
| bp[5] = 0x01;/* Alternate Name continues |
| * in next "NM" field */ |
| memcpy(bp+6, nm, length - 5); |
| bp += length; |
| } |
| nmlen -= length - 5; |
| nm += length - 5; |
| extra_tell_used_size(&ctl, length); |
| if (extra_space(&ctl) < 6) { |
| bp = extra_next_record(&ctl, 6); |
| nmmax = extra_space(&ctl); |
| if (nmmax > 0xff) |
| nmmax = 0xff; |
| } |
| if (bp != NULL) { |
| bp[1] = 'N'; |
| bp[2] = 'M'; |
| bp[4] = 1; /* version */ |
| } |
| } |
| length = 5 + (int)nmlen; |
| if (bp != NULL) { |
| bp[3] = length; |
| bp[5] = 0; |
| memcpy(bp+6, nm, nmlen); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "PX" System Use Entry. */ |
| if (rr_flag & RR_USE_PX) { |
| /* |
| * "PX" Format: |
| * len ver |
| * +----+----+----+----+-----------+-----------+ |
| * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS | |
| * +----+----+----+----+-----------+-----------+ |
| * 0 1 2 3 4 12 20 |
| * +-----------+-----------+------------------+ |
| * | USER ID | GROUP ID |FILE SERIAL NUMBER| |
| * +-----------+-----------+------------------+ |
| * 20 28 36 44 |
| */ |
| length = 44; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| mode_t mode; |
| int64_t uid; |
| int64_t gid; |
| |
| mode = archive_entry_mode(file->entry); |
| uid = archive_entry_uid(file->entry); |
| gid = archive_entry_gid(file->entry); |
| if (iso9660->opt.rr == OPT_RR_USEFUL) { |
| /* |
| * This action is similar to mkisofs -r option |
| * but our rockridge=useful option does not |
| * set a zero to uid and gid. |
| */ |
| /* set all read bit ON */ |
| mode |= 0444; |
| #if !defined(_WIN32) && !defined(__CYGWIN__) |
| if (mode & 0111) |
| #endif |
| /* set all exec bit ON */ |
| mode |= 0111; |
| /* clear all write bits. */ |
| mode &= ~0222; |
| /* clear setuid,setgid,sticky bits. */ |
| mode &= ~07000; |
| } |
| |
| bp[1] = 'P'; |
| bp[2] = 'X'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| /* file mode */ |
| set_num_733(bp+5, mode); |
| /* file links (stat.st_nlink) */ |
| set_num_733(bp+13, |
| archive_entry_nlink(file->entry)); |
| set_num_733(bp+21, (uint32_t)uid); |
| set_num_733(bp+29, (uint32_t)gid); |
| /* File Serial Number */ |
| if (pxent->dir) |
| set_num_733(bp+37, pxent->dir_location); |
| else if (file->hardlink_target != NULL) |
| set_num_733(bp+37, |
| file->hardlink_target->cur_content->location); |
| else |
| set_num_733(bp+37, |
| file->cur_content->location); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "SL" System Use Entry. */ |
| if (rr_flag & RR_USE_SL) { |
| /* |
| * "SL" Format: |
| * e.g. a symbolic name is 'foo/bar' |
| * len ver flg |
| * +----+----+----+----+----+------------+ |
| * | 'S'| 'L'| 0F | 01 | 00 | components | |
| * +----+----+----+----+----+-----+------+ |
| * 0 1 2 3 4 5 ...|... 15 |
| * <----------------- len --------+------> |
| * components : | |
| * cflg clen | |
| * +----+----+----+----+----+ | |
| * | 00 | 03 | 'f'| 'o'| 'o'| <---+ |
| * +----+----+----+----+----+ | |
| * 5 6 7 8 9 10 | |
| * cflg clen | |
| * +----+----+----+----+----+ | |
| * | 00 | 03 | 'b'| 'a'| 'r'| <---+ |
| * +----+----+----+----+----+ |
| * 10 11 12 13 14 15 |
| * |
| * - cflg : flag of component |
| * - clen : length of component |
| */ |
| const char *sl; |
| char sl_last; |
| |
| if (extra_space(&ctl) < 7) |
| bp = extra_next_record(&ctl, 7); |
| sl = file->symlink.s; |
| sl_last = '\0'; |
| if (bp != NULL) { |
| bp[1] = 'S'; |
| bp[2] = 'L'; |
| bp[4] = 1; /* version */ |
| } |
| for (;;) { |
| unsigned char *nc, *cf, *cl, cldmy = 0; |
| int sllen, slmax; |
| |
| slmax = extra_space(&ctl); |
| if (slmax > 0xff) |
| slmax = 0xff; |
| if (bp != NULL) |
| nc = &bp[6]; |
| else |
| nc = NULL; |
| cf = cl = NULL; |
| sllen = 0; |
| while (*sl && sllen + 11 < slmax) { |
| if (sl_last == '\0' && sl[0] == '/') { |
| /* |
| * flg len |
| * +----+----+ |
| * | 08 | 00 | ROOT component. |
| * +----+----+ ("/") |
| * |
| * Root component has to appear |
| * at the first component only. |
| */ |
| if (nc != NULL) { |
| cf = nc++; |
| *cf = 0x08; /* ROOT */ |
| *nc++ = 0; |
| } |
| sllen += 2; |
| sl++; |
| sl_last = '/'; |
| cl = NULL; |
| continue; |
| } |
| if (((sl_last == '\0' || sl_last == '/') && |
| sl[0] == '.' && sl[1] == '.' && |
| (sl[2] == '/' || sl[2] == '\0')) || |
| (sl[0] == '/' && |
| sl[1] == '.' && sl[2] == '.' && |
| (sl[3] == '/' || sl[3] == '\0'))) { |
| /* |
| * flg len |
| * +----+----+ |
| * | 04 | 00 | PARENT component. |
| * +----+----+ ("..") |
| */ |
| if (nc != NULL) { |
| cf = nc++; |
| *cf = 0x04; /* PARENT */ |
| *nc++ = 0; |
| } |
| sllen += 2; |
| if (sl[0] == '/') |
| sl += 3;/* skip "/.." */ |
| else |
| sl += 2;/* skip ".." */ |
| sl_last = '.'; |
| cl = NULL; |
| continue; |
| } |
| if (((sl_last == '\0' || sl_last == '/') && |
| sl[0] == '.' && |
| (sl[1] == '/' || sl[1] == '\0')) || |
| (sl[0] == '/' && sl[1] == '.' && |
| (sl[2] == '/' || sl[2] == '\0'))) { |
| /* |
| * flg len |
| * +----+----+ |
| * | 02 | 00 | CURRENT component. |
| * +----+----+ (".") |
| */ |
| if (nc != NULL) { |
| cf = nc++; |
| *cf = 0x02; /* CURRENT */ |
| *nc++ = 0; |
| } |
| sllen += 2; |
| if (sl[0] == '/') |
| sl += 2;/* skip "/." */ |
| else |
| sl ++; /* skip "." */ |
| sl_last = '.'; |
| cl = NULL; |
| continue; |
| } |
| if (sl[0] == '/' || cl == NULL) { |
| if (nc != NULL) { |
| cf = nc++; |
| *cf = 0; |
| cl = nc++; |
| *cl = 0; |
| } else |
| cl = &cldmy; |
| sllen += 2; |
| if (sl[0] == '/') { |
| sl_last = *sl++; |
| continue; |
| } |
| } |
| sl_last = *sl++; |
| if (nc != NULL) { |
| *nc++ = sl_last; |
| (*cl) ++; |
| } |
| sllen++; |
| } |
| if (*sl) { |
| length = 5 + sllen; |
| if (bp != NULL) { |
| /* |
| * Mark flg as CONTINUE component. |
| */ |
| *cf |= 0x01; |
| /* |
| * len ver flg |
| * +----+----+----+----+----+- |
| * | 'S'| 'L'| XX | 01 | 01 | |
| * +----+----+----+----+----+- |
| * ^ |
| * continues in next "SL" |
| */ |
| bp[3] = length; |
| bp[5] = 0x01;/* This Symbolic Link |
| * continues in next |
| * "SL" field */ |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| if (extra_space(&ctl) < 11) |
| bp = extra_next_record(&ctl, 11); |
| if (bp != NULL) { |
| /* Next 'SL' */ |
| bp[1] = 'S'; |
| bp[2] = 'L'; |
| bp[4] = 1; /* version */ |
| } |
| } else { |
| length = 5 + sllen; |
| if (bp != NULL) { |
| bp[3] = length; |
| bp[5] = 0; |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| break; |
| } |
| } |
| } |
| |
| /* Write "TF" System Use Entry. */ |
| if (rr_flag & RR_USE_TF) { |
| /* |
| * "TF" Format: |
| * len ver |
| * +----+----+----+----+-----+-------------+ |
| * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS | |
| * +----+----+----+----+-----+-------------+ |
| * 0 1 2 3 4 5 XX |
| * TIME STAMPS : ISO 9660 Standard 9.1.5. |
| * If TF_LONG_FORM FLAGS is set, |
| * use ISO9660 Standard 8.4.26.1. |
| */ |
| #define TF_CREATION 0x01 /* Creation time recorded */ |
| #define TF_MODIFY 0x02 /* Modification time recorded */ |
| #define TF_ACCESS 0x04 /* Last Access time recorded */ |
| #define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */ |
| #define TF_BACKUP 0x10 /* Last Backup time recorded */ |
| #define TF_EXPIRATION 0x20 /* Expiration time recorded */ |
| #define TF_EFFECTIVE 0x40 /* Effective time recorded */ |
| #define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */ |
| unsigned char tf_flags; |
| |
| length = 5; |
| tf_flags = 0; |
| #ifndef COMPAT_MKISOFS |
| if (archive_entry_birthtime_is_set(file->entry) && |
| archive_entry_birthtime(file->entry) <= |
| archive_entry_mtime(file->entry)) { |
| length += 7; |
| tf_flags |= TF_CREATION; |
| } |
| #endif |
| if (archive_entry_mtime_is_set(file->entry)) { |
| length += 7; |
| tf_flags |= TF_MODIFY; |
| } |
| if (archive_entry_atime_is_set(file->entry)) { |
| length += 7; |
| tf_flags |= TF_ACCESS; |
| } |
| if (archive_entry_ctime_is_set(file->entry)) { |
| length += 7; |
| tf_flags |= TF_ATTRIBUTES; |
| } |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'T'; |
| bp[2] = 'F'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| bp[5] = tf_flags; |
| bp += 5; |
| /* Creation time */ |
| if (tf_flags & TF_CREATION) { |
| set_time_915(bp+1, |
| archive_entry_birthtime(file->entry)); |
| bp += 7; |
| } |
| /* Modification time */ |
| if (tf_flags & TF_MODIFY) { |
| set_time_915(bp+1, |
| archive_entry_mtime(file->entry)); |
| bp += 7; |
| } |
| /* Last Access time */ |
| if (tf_flags & TF_ACCESS) { |
| set_time_915(bp+1, |
| archive_entry_atime(file->entry)); |
| bp += 7; |
| } |
| /* Last Attribute Change time */ |
| if (tf_flags & TF_ATTRIBUTES) { |
| set_time_915(bp+1, |
| archive_entry_ctime(file->entry)); |
| bp += 7; |
| } |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "RE" System Use Entry. */ |
| if (rr_flag & RR_USE_RE) { |
| /* |
| * "RE" Format: |
| * len ver |
| * +----+----+----+----+ |
| * | 'R'| 'E'| 04 | 01 | |
| * +----+----+----+----+ |
| * 0 1 2 3 4 |
| */ |
| length = 4; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'R'; |
| bp[2] = 'E'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "PL" System Use Entry. */ |
| if (rr_flag & RR_USE_PL) { |
| /* |
| * "PL" Format: |
| * len ver |
| * +----+----+----+----+------------+ |
| * | 'P'| 'L'| 0C | 01 | *LOCATION | |
| * +----+----+----+----+------------+ |
| * 0 1 2 3 4 12 |
| * *LOCATION: location of parent directory |
| */ |
| length = 12; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'P'; |
| bp[2] = 'L'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| set_num_733(bp + 5, |
| rr_parent->dir_location); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "CL" System Use Entry. */ |
| if (rr_flag & RR_USE_CL) { |
| /* |
| * "CL" Format: |
| * len ver |
| * +----+----+----+----+------------+ |
| * | 'C'| 'L'| 0C | 01 | *LOCATION | |
| * +----+----+----+----+------------+ |
| * 0 1 2 3 4 12 |
| * *LOCATION: location of child directory |
| */ |
| length = 12; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'C'; |
| bp[2] = 'L'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| set_num_733(bp + 5, |
| isoent->rr_child->dir_location); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "PN" System Use Entry. */ |
| if (rr_flag & RR_USE_PN) { |
| /* |
| * "PN" Format: |
| * len ver |
| * +----+----+----+----+------------+------------+ |
| * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low | |
| * +----+----+----+----+------------+------------+ |
| * 0 1 2 3 4 12 20 |
| */ |
| length = 20; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| uint64_t dev; |
| |
| bp[1] = 'P'; |
| bp[2] = 'N'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| dev = (uint64_t)archive_entry_rdev(file->entry); |
| set_num_733(bp + 5, (uint32_t)(dev >> 32)); |
| set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF)); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "ZF" System Use Entry. */ |
| if (file->zisofs.header_size) { |
| /* |
| * "ZF" Format: |
| * len ver |
| * +----+----+----+----+----+----+-------------+ |
| * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size | |
| * +----+----+----+----+----+----+-------------+ |
| * 0 1 2 3 4 5 6 7 |
| * +--------------------+-------------------+ |
| * | Log2 of block Size | Uncompressed Size | |
| * +--------------------+-------------------+ |
| * 7 8 16 |
| */ |
| length = 16; |
| if (extra_space(&ctl) < length) |
| bp = extra_next_record(&ctl, length); |
| if (bp != NULL) { |
| bp[1] = 'Z'; |
| bp[2] = 'F'; |
| bp[3] = length; |
| bp[4] = 1; /* version */ |
| bp[5] = 'p'; |
| bp[6] = 'z'; |
| bp[7] = file->zisofs.header_size; |
| bp[8] = file->zisofs.log2_bs; |
| set_num_733(bp + 9, file->zisofs.uncompressed_size); |
| bp += length; |
| } |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| /* Write "CE" System Use Entry. */ |
| if (t == DIR_REC_SELF && isoent == isoent->parent) { |
| length = RR_CE_SIZE; |
| if (bp != NULL) |
| set_SUSP_CE(bp+1, iso9660->location_rrip_er, |
| 0, RRIP_ER_SIZE); |
| extra_tell_used_size(&ctl, length); |
| } |
| |
| extra_close_record(&ctl, 0); |
| |
| return (ctl.dr_len); |
| } |
| |
| /* |
| * Write data of a Directory Record or calculate writing bytes itself. |
| * If parameter `p' is NULL, calculates the size of writing data, which |
| * a Directory Record needs to write, then it saved and return |
| * the calculated size. |
| * Parameter `n' is a remaining size of buffer. when parameter `p' is |
| * not NULL, check whether that `n' is not less than the saved size. |
| * if that `n' is small, return zero. |
| * |
| * This format of the Directory Record is according to |
| * ISO9660 Standard 9.1 |
| */ |
| static int |
| set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, |
| struct iso9660 *iso9660, enum dir_rec_type t, |
| enum vdd_type vdd_type) |
| { |
| unsigned char *bp; |
| size_t dr_len; |
| size_t fi_len; |
| |
| if (p != NULL) { |
| /* |
| * Check whether a write buffer size is less than the |
| * saved size which is needed to write this Directory |
| * Record. |
| */ |
| switch (t) { |
| case DIR_REC_VD: |
| dr_len = isoent->dr_len.vd; break; |
| case DIR_REC_SELF: |
| dr_len = isoent->dr_len.self; break; |
| case DIR_REC_PARENT: |
| dr_len = isoent->dr_len.parent; break; |
| case DIR_REC_NORMAL: |
| default: |
| dr_len = isoent->dr_len.normal; break; |
| } |
| if (dr_len > n) |
| return (0);/* Needs more buffer size. */ |
| } |
| |
| if (t == DIR_REC_NORMAL && isoent->identifier != NULL) |
| fi_len = isoent->id_len; |
| else |
| fi_len = 1; |
| |
| if (p != NULL) { |
| struct isoent *xisoent; |
| struct isofile *file; |
| unsigned char flag; |
| |
| if (t == DIR_REC_PARENT) |
| xisoent = isoent->parent; |
| else |
| xisoent = isoent; |
| file = isoent->file; |
| if (file->hardlink_target != NULL) |
| file = file->hardlink_target; |
| /* Make a file flag. */ |
| if (xisoent->dir) |
| flag = FILE_FLAG_DIRECTORY; |
| else { |
| if (file->cur_content->next != NULL) |
| flag = FILE_FLAG_MULTI_EXTENT; |
| else |
| flag = 0; |
| } |
| |
| bp = p -1; |
| /* Extended Attribute Record Length */ |
| set_num_711(bp+2, 0); |
| /* Location of Extent */ |
| if (xisoent->dir) |
| set_num_733(bp+3, xisoent->dir_location); |
| else |
| set_num_733(bp+3, file->cur_content->location); |
| /* Data Length */ |
| if (xisoent->dir) |
| set_num_733(bp+11, |
| xisoent->dir_block * LOGICAL_BLOCK_SIZE); |
| else |
| set_num_733(bp+11, (uint32_t)file->cur_content->size); |
| /* Recording Date and Time */ |
| /* NOTE: |
| * If a file type is symbolic link, you are seeing this |
| * field value is different from a value mkisofs makes. |
| * libarchive uses lstat to get this one, but it |
| * seems mkisofs uses stat to get. |
| */ |
| set_time_915(bp+19, |
| archive_entry_mtime(xisoent->file->entry)); |
| /* File Flags */ |
| bp[26] = flag; |
| /* File Unit Size */ |
| set_num_711(bp+27, 0); |
| /* Interleave Gap Size */ |
| set_num_711(bp+28, 0); |
| /* Volume Sequence Number */ |
| set_num_723(bp+29, iso9660->volume_sequence_number); |
| /* Length of File Identifier */ |
| set_num_711(bp+33, (unsigned char)fi_len); |
| /* File Identifier */ |
| switch (t) { |
| case DIR_REC_VD: |
| case DIR_REC_SELF: |
| set_num_711(bp+34, 0); |
| break; |
| case DIR_REC_PARENT: |
| set_num_711(bp+34, 1); |
| break; |
| case DIR_REC_NORMAL: |
| if (isoent->identifier != NULL) |
| memcpy(bp+34, isoent->identifier, fi_len); |
| else |
| set_num_711(bp+34, 0); |
| break; |
| } |
| } else |
| bp = NULL; |
| dr_len = 33 + fi_len; |
| /* Padding Field */ |
| if (dr_len & 0x01) { |
| dr_len ++; |
| if (p != NULL) |
| bp[dr_len] = 0; |
| } |
| |
| /* Volume Descriptor does not record extension. */ |
| if (t == DIR_REC_VD) { |
| if (p != NULL) |
| /* Length of Directory Record */ |
| set_num_711(p, (unsigned char)dr_len); |
| else |
| isoent->dr_len.vd = (int)dr_len; |
| return ((int)dr_len); |
| } |
| |
| /* Rockridge */ |
| if (iso9660->opt.rr && vdd_type != VDD_JOLIET) |
| dr_len = set_directory_record_rr(bp, (int)dr_len, |
| isoent, iso9660, t); |
| |
| if (p != NULL) |
| /* Length of Directory Record */ |
| set_num_711(p, (unsigned char)dr_len); |
| else { |
| /* |
| * Save the size which is needed to write this |
| * Directory Record. |
| */ |
| switch (t) { |
| case DIR_REC_VD: |
| /* This case does not come, but compiler |
| * complains that DIR_REC_VD not handled |
| * in switch .... */ |
| break; |
| case DIR_REC_SELF: |
| isoent->dr_len.self = (int)dr_len; break; |
| case DIR_REC_PARENT: |
| isoent->dr_len.parent = (int)dr_len; break; |
| case DIR_REC_NORMAL: |
| isoent->dr_len.normal = (int)dr_len; break; |
| } |
| } |
| |
| return ((int)dr_len); |
| } |
| |
| /* |
| * Calculate the size of a directory record. |
| */ |
| static inline int |
| get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent, |
| enum dir_rec_type t, enum vdd_type vdd_type) |
| { |
| |
| return (set_directory_record(NULL, SIZE_MAX, |
| isoent, iso9660, t, vdd_type)); |
| } |
| |
| /* |
| * Manage to write ISO-image data with wbuff to reduce calling |
| * __archive_write_output() for performance. |
| */ |
| |
| |
| static inline unsigned char * |
| wb_buffptr(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; |
| |
| return (&(iso9660->wbuff[sizeof(iso9660->wbuff) |
| - iso9660->wbuff_remaining])); |
| } |
| |
| static int |
| wb_write_out(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; |
| size_t wsize, nw; |
| int r; |
| |
| wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; |
| nw = wsize % LOGICAL_BLOCK_SIZE; |
| if (iso9660->wbuff_type == WB_TO_STREAM) |
| r = __archive_write_output(a, iso9660->wbuff, wsize - nw); |
| else |
| r = write_to_temp(a, iso9660->wbuff, wsize - nw); |
| /* Increase the offset. */ |
| iso9660->wbuff_offset += wsize - nw; |
| if (iso9660->wbuff_offset > iso9660->wbuff_written) |
| iso9660->wbuff_written = iso9660->wbuff_offset; |
| iso9660->wbuff_remaining = sizeof(iso9660->wbuff); |
| if (nw) { |
| iso9660->wbuff_remaining -= nw; |
| memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw); |
| } |
| return (r); |
| } |
| |
| static int |
| wb_consume(struct archive_write *a, size_t size) |
| { |
| struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; |
| |
| if (size > iso9660->wbuff_remaining || |
| iso9660->wbuff_remaining == 0) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Internal Programing error: iso9660:wb_consume()" |
| " size=%jd, wbuff_remaining=%jd", |
| (intmax_t)size, (intmax_t)iso9660->wbuff_remaining); |
| return (ARCHIVE_FATAL); |
| } |
| iso9660->wbuff_remaining -= size; |
| if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE) |
| return (wb_write_out(a)); |
| return (ARCHIVE_OK); |
| } |
| |
| #ifdef HAVE_ZLIB_H |
| |
| static int |
| wb_set_offset(struct archive_write *a, int64_t off) |
| { |
| struct iso9660 *iso9660 = (struct iso9660 *)a->format_data; |
| int64_t used, ext_bytes; |
| |
| if (iso9660->wbuff_type != WB_TO_TEMP) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Internal Programing error: iso9660:wb_set_offset()"); |
| return (ARCHIVE_FATAL); |
| } |
| |
| used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining; |
| if (iso9660->wbuff_offset + used > iso9660->wbuff_tail) |
| iso9660->wbuff_tail = iso9660->wbuff_offset + used; |
| if (iso9660->wbuff_offset < iso9660->wbuff_written) { |
| if (used > 0 && |
| write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->wbuff_offset = iso9660->wbuff_written; |
| lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET); |
| iso9660->wbuff_remaining = sizeof(iso9660->wbuff); |
| used = 0; |
| } |
| if (off < iso9660->wbuff_offset) { |
| /* |
| * Write out waiting data. |
| */ |
| if (used > 0) { |
| if (wb_write_out(a) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| lseek(iso9660->temp_fd, off, SEEK_SET); |
| iso9660->wbuff_offset = off; |
| iso9660->wbuff_remaining = sizeof(iso9660->wbuff); |
| } else if (off <= iso9660->wbuff_tail) { |
| iso9660->wbuff_remaining = (size_t) |
| (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset)); |
| } else { |
| ext_bytes = off - iso9660->wbuff_tail; |
| iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff) |
| - (iso9660->wbuff_tail - iso9660->wbuff_offset)); |
| while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) { |
| if (write_null(a, (size_t)iso9660->wbuff_remaining) |
| != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| ext_bytes -= iso9660->wbuff_remaining; |
| } |
| if (ext_bytes > 0) { |
| if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| } |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| #endif /* HAVE_ZLIB_H */ |
| |
| static int |
| write_null(struct archive_write *a, size_t size) |
| { |
| size_t remaining; |
| unsigned char *p, *old; |
| int r; |
| |
| remaining = wb_remaining(a); |
| p = wb_buffptr(a); |
| if (size <= remaining) { |
| memset(p, 0, size); |
| return (wb_consume(a, size)); |
| } |
| memset(p, 0, remaining); |
| r = wb_consume(a, remaining); |
| if (r != ARCHIVE_OK) |
| return (r); |
| size -= remaining; |
| old = p; |
| p = wb_buffptr(a); |
| memset(p, 0, old - p); |
| remaining = wb_remaining(a); |
| while (size) { |
| size_t wsize = size; |
| |
| if (wsize > remaining) |
| wsize = remaining; |
| r = wb_consume(a, wsize); |
| if (r != ARCHIVE_OK) |
| return (r); |
| size -= wsize; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Write Volume Descriptor Set Terminator |
| */ |
| static int |
| write_VD_terminator(struct archive_write *a) |
| { |
| unsigned char *bp; |
| |
| bp = wb_buffptr(a) -1; |
| set_VD_bp(bp, VDT_TERMINATOR, 1); |
| set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE); |
| |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| static int |
| set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc, |
| struct archive_write *a, struct vdd *vdd, struct archive_string *id, |
| const char *label, int leading_under, enum char_type char_type) |
| { |
| char identifier[256]; |
| struct isoent *isoent; |
| const char *ids; |
| size_t len; |
| int r; |
| |
| if (id->length > 0 && leading_under && id->s[0] != '_') { |
| if (char_type == A_CHAR) |
| r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc); |
| else |
| r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc); |
| } else if (id->length > 0) { |
| ids = id->s; |
| if (leading_under) |
| ids++; |
| isoent = isoent_find_entry(vdd->rootent, ids); |
| if (isoent == NULL) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Not Found %s `%s'.", |
| label, ids); |
| return (ARCHIVE_FATAL); |
| } |
| len = isoent->ext_off + isoent->ext_len; |
| if (vdd->vdd_type == VDD_JOLIET) { |
| if (len > sizeof(identifier)-2) |
| len = sizeof(identifier)-2; |
| } else { |
| if (len > sizeof(identifier)-1) |
| len = sizeof(identifier)-1; |
| } |
| memcpy(identifier, isoent->identifier, len); |
| identifier[len] = '\0'; |
| if (vdd->vdd_type == VDD_JOLIET) { |
| identifier[len+1] = 0; |
| vdc = VDC_UCS2_DIRECT; |
| } |
| if (char_type == A_CHAR) |
| r = set_str_a_characters_bp(a, bp, from, to, |
| identifier, vdc); |
| else |
| r = set_str_d_characters_bp(a, bp, from, to, |
| identifier, vdc); |
| } else { |
| if (char_type == A_CHAR) |
| r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc); |
| else |
| r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc); |
| } |
| return (r); |
| } |
| |
| /* |
| * Write Primary/Supplementary Volume Descriptor |
| */ |
| static int |
| write_VD(struct archive_write *a, struct vdd *vdd) |
| { |
| struct iso9660 *iso9660; |
| unsigned char *bp; |
| uint16_t volume_set_size = 1; |
| char identifier[256]; |
| enum VD_type vdt; |
| enum vdc vdc; |
| unsigned char vd_ver, fst_ver; |
| int r; |
| |
| iso9660 = a->format_data; |
| switch (vdd->vdd_type) { |
| case VDD_JOLIET: |
| vdt = VDT_SUPPLEMENTARY; |
| vd_ver = fst_ver = 1; |
| vdc = VDC_UCS2; |
| break; |
| case VDD_ENHANCED: |
| vdt = VDT_SUPPLEMENTARY; |
| vd_ver = fst_ver = 2; |
| vdc = VDC_LOWERCASE; |
| break; |
| case VDD_PRIMARY: |
| default: |
| vdt = VDT_PRIMARY; |
| vd_ver = fst_ver = 1; |
| #ifdef COMPAT_MKISOFS |
| vdc = VDC_LOWERCASE; |
| #else |
| vdc = VDC_STD; |
| #endif |
| break; |
| } |
| |
| bp = wb_buffptr(a) -1; |
| /* Volume Descriptor Type */ |
| set_VD_bp(bp, vdt, vd_ver); |
| /* Unused Field */ |
| set_unused_field_bp(bp, 8, 8); |
| /* System Identifier */ |
| get_system_identitier(identifier, sizeof(identifier)); |
| r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Volume Identifier */ |
| r = set_str_d_characters_bp(a, bp, 41, 72, |
| iso9660->volume_identifier.s, vdc); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Unused Field */ |
| set_unused_field_bp(bp, 73, 80); |
| /* Volume Space Size */ |
| set_num_733(bp+81, iso9660->volume_space_size); |
| if (vdd->vdd_type == VDD_JOLIET) { |
| /* Escape Sequences */ |
| bp[89] = 0x25;/* UCS-2 Level 3 */ |
| bp[90] = 0x2F; |
| bp[91] = 0x45; |
| memset(bp + 92, 0, 120 - 92 + 1); |
| } else { |
| /* Unused Field */ |
| set_unused_field_bp(bp, 89, 120); |
| } |
| /* Volume Set Size */ |
| set_num_723(bp+121, volume_set_size); |
| /* Volume Sequence Number */ |
| set_num_723(bp+125, iso9660->volume_sequence_number); |
| /* Logical Block Size */ |
| set_num_723(bp+129, LOGICAL_BLOCK_SIZE); |
| /* Path Table Size */ |
| set_num_733(bp+133, vdd->path_table_size); |
| /* Location of Occurrence of Type L Path Table */ |
| set_num_731(bp+141, vdd->location_type_L_path_table); |
| /* Location of Optional Occurrence of Type L Path Table */ |
| set_num_731(bp+145, 0); |
| /* Location of Occurrence of Type M Path Table */ |
| set_num_732(bp+149, vdd->location_type_M_path_table); |
| /* Location of Optional Occurrence of Type M Path Table */ |
| set_num_732(bp+153, 0); |
| /* Directory Record for Root Directory(BP 157 to 190) */ |
| set_directory_record(bp+157, 190-157+1, vdd->rootent, |
| iso9660, DIR_REC_VD, vdd->vdd_type); |
| /* Volume Set Identifier */ |
| r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Publisher Identifier */ |
| r = set_file_identifier(bp, 319, 446, vdc, a, vdd, |
| &(iso9660->publisher_identifier), |
| "Publisher File", 1, A_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Data Preparer Identifier */ |
| r = set_file_identifier(bp, 447, 574, vdc, a, vdd, |
| &(iso9660->data_preparer_identifier), |
| "Data Preparer File", 1, A_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Application Identifier */ |
| r = set_file_identifier(bp, 575, 702, vdc, a, vdd, |
| &(iso9660->application_identifier), |
| "Application File", 1, A_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Copyright File Identifier */ |
| r = set_file_identifier(bp, 703, 739, vdc, a, vdd, |
| &(iso9660->copyright_file_identifier), |
| "Copyright File", 0, D_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Abstract File Identifier */ |
| r = set_file_identifier(bp, 740, 776, vdc, a, vdd, |
| &(iso9660->abstract_file_identifier), |
| "Abstract File", 0, D_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Bibliographic File Identifier */ |
| r = set_file_identifier(bp, 777, 813, vdc, a, vdd, |
| &(iso9660->bibliographic_file_identifier), |
| "Bibliongraphic File", 0, D_CHAR); |
| if (r != ARCHIVE_OK) |
| return (r); |
| /* Volume Creation Date and Time */ |
| set_date_time(bp+814, iso9660->birth_time); |
| /* Volume Modification Date and Time */ |
| set_date_time(bp+831, iso9660->birth_time); |
| /* Volume Expiration Date and Time(obsolete) */ |
| set_date_time_null(bp+848); |
| /* Volume Effective Date and Time */ |
| set_date_time(bp+865, iso9660->birth_time); |
| /* File Structure Version */ |
| bp[882] = fst_ver; |
| /* Reserved */ |
| bp[883] = 0; |
| /* Application Use */ |
| memset(bp + 884, 0x20, 1395 - 884 + 1); |
| /* Reserved */ |
| set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE); |
| |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| /* |
| * Write Boot Record Volume Descriptor |
| */ |
| static int |
| write_VD_boot_record(struct archive_write *a) |
| { |
| struct iso9660 *iso9660; |
| unsigned char *bp; |
| |
| iso9660 = a->format_data; |
| bp = wb_buffptr(a) -1; |
| /* Volume Descriptor Type */ |
| set_VD_bp(bp, VDT_BOOT_RECORD, 1); |
| /* Boot System Identifier */ |
| memcpy(bp+8, "EL TORITO SPECIFICATION", 23); |
| set_unused_field_bp(bp, 8+23, 39); |
| /* Unused */ |
| set_unused_field_bp(bp, 40, 71); |
| /* Absolute pointer to first sector of Boot Catalog */ |
| set_num_731(bp+72, |
| iso9660->el_torito.catalog->file->content.location); |
| /* Unused */ |
| set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE); |
| |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| enum keytype { |
| KEY_FLG, |
| KEY_STR, |
| KEY_INT, |
| KEY_HEX |
| }; |
| static void |
| set_option_info(struct archive_string *info, int *opt, const char *key, |
| enum keytype type, ...) |
| { |
| va_list ap; |
| char prefix; |
| const char *s; |
| int d; |
| |
| prefix = (*opt==0)? ' ':','; |
| va_start(ap, type); |
| switch (type) { |
| case KEY_FLG: |
| d = va_arg(ap, int); |
| archive_string_sprintf(info, "%c%s%s", |
| prefix, (d == 0)?"!":"", key); |
| break; |
| case KEY_STR: |
| s = va_arg(ap, const char *); |
| archive_string_sprintf(info, "%c%s=%s", |
| prefix, key, s); |
| break; |
| case KEY_INT: |
| d = va_arg(ap, int); |
| archive_string_sprintf(info, "%c%s=%d", |
| prefix, key, d); |
| break; |
| case KEY_HEX: |
| d = va_arg(ap, int); |
| archive_string_sprintf(info, "%c%s=%x", |
| prefix, key, d); |
| break; |
| } |
| va_end(ap); |
| |
| *opt = 1; |
| } |
| |
| /* |
| * Make Non-ISO File System Information |
| */ |
| static int |
| write_information_block(struct archive_write *a) |
| { |
| struct iso9660 *iso9660; |
| char buf[128]; |
| const char *v; |
| int opt, r; |
| struct archive_string info; |
| size_t info_size = LOGICAL_BLOCK_SIZE * |
| NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK; |
| |
| iso9660 = (struct iso9660 *)a->format_data; |
| if (info_size > wb_remaining(a)) { |
| r = wb_write_out(a); |
| if (r != ARCHIVE_OK) |
| return (r); |
| } |
| archive_string_init(&info); |
| if (archive_string_ensure(&info, info_size) == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| memset(info.s, 0, info_size); |
| opt = 0; |
| #if defined(HAVE__CTIME64_S) |
| { |
| __time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits |
| _ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp)); |
| } |
| #elif defined(HAVE_CTIME_R) |
| ctime_r(&(iso9660->birth_time), buf); |
| #else |
| strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1); |
| buf[sizeof(buf)-1] = '\0'; |
| #endif |
| archive_string_sprintf(&info, |
| "INFO %s%s", buf, archive_version_string()); |
| if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT) |
| set_option_info(&info, &opt, "abstract-file", |
| KEY_STR, iso9660->abstract_file_identifier.s); |
| if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT) |
| set_option_info(&info, &opt, "application-id", |
| KEY_STR, iso9660->application_identifier.s); |
| if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT) |
| set_option_info(&info, &opt, "allow-vernum", |
| KEY_FLG, iso9660->opt.allow_vernum); |
| if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT) |
| set_option_info(&info, &opt, "biblio-file", |
| KEY_STR, iso9660->bibliographic_file_identifier.s); |
| if (iso9660->opt.boot != OPT_BOOT_DEFAULT) |
| set_option_info(&info, &opt, "boot", |
| KEY_STR, iso9660->el_torito.boot_filename.s); |
| if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT) |
| set_option_info(&info, &opt, "boot-catalog", |
| KEY_STR, iso9660->el_torito.catalog_filename.s); |
| if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT) |
| set_option_info(&info, &opt, "boot-info-table", |
| KEY_FLG, iso9660->opt.boot_info_table); |
| if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT) |
| set_option_info(&info, &opt, "boot-load-seg", |
| KEY_HEX, iso9660->el_torito.boot_load_seg); |
| if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT) |
| set_option_info(&info, &opt, "boot-load-size", |
| KEY_INT, iso9660->el_torito.boot_load_size); |
| if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) { |
| v = "no-emulation"; |
| if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD) |
| v = "fd"; |
| if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK) |
| v = "hard-disk"; |
| set_option_info(&info, &opt, "boot-type", |
| KEY_STR, v); |
| } |
| #ifdef HAVE_ZLIB_H |
| if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT) |
| set_option_info(&info, &opt, "compression-level", |
| KEY_INT, iso9660->zisofs.compression_level); |
| #endif |
| if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT) |
| set_option_info(&info, &opt, "copyright-file", |
| KEY_STR, iso9660->copyright_file_identifier.s); |
| if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT) |
| set_option_info(&info, &opt, "iso-level", |
| KEY_INT, iso9660->opt.iso_level); |
| if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) { |
| if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) |
| set_option_info(&info, &opt, "joliet", |
| KEY_STR, "long"); |
| else |
| set_option_info(&info, &opt, "joliet", |
| KEY_FLG, iso9660->opt.joliet); |
| } |
| if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT) |
| set_option_info(&info, &opt, "limit-depth", |
| KEY_FLG, iso9660->opt.limit_depth); |
| if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT) |
| set_option_info(&info, &opt, "limit-dirs", |
| KEY_FLG, iso9660->opt.limit_dirs); |
| if (iso9660->opt.pad != OPT_PAD_DEFAULT) |
| set_option_info(&info, &opt, "pad", |
| KEY_FLG, iso9660->opt.pad); |
| if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT) |
| set_option_info(&info, &opt, "publisher", |
| KEY_STR, iso9660->publisher_identifier.s); |
| if (iso9660->opt.rr != OPT_RR_DEFAULT) { |
| if (iso9660->opt.rr == OPT_RR_DISABLED) |
| set_option_info(&info, &opt, "rockridge", |
| KEY_FLG, iso9660->opt.rr); |
| else if (iso9660->opt.rr == OPT_RR_STRICT) |
| set_option_info(&info, &opt, "rockridge", |
| KEY_STR, "strict"); |
| else if (iso9660->opt.rr == OPT_RR_USEFUL) |
| set_option_info(&info, &opt, "rockridge", |
| KEY_STR, "useful"); |
| } |
| if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT) |
| set_option_info(&info, &opt, "volume-id", |
| KEY_STR, iso9660->volume_identifier.s); |
| if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT) |
| set_option_info(&info, &opt, "zisofs", |
| KEY_FLG, iso9660->opt.zisofs); |
| |
| memcpy(wb_buffptr(a), info.s, info_size); |
| archive_string_free(&info); |
| return (wb_consume(a, info_size)); |
| } |
| |
| static int |
| write_rr_ER(struct archive_write *a) |
| { |
| unsigned char *p; |
| |
| p = wb_buffptr(a); |
| |
| memset(p, 0, LOGICAL_BLOCK_SIZE); |
| p[0] = 'E'; |
| p[1] = 'R'; |
| p[3] = 0x01; |
| p[2] = RRIP_ER_SIZE; |
| p[4] = RRIP_ER_ID_SIZE; |
| p[5] = RRIP_ER_DSC_SIZE; |
| p[6] = RRIP_ER_SRC_SIZE; |
| p[7] = 0x01; |
| memcpy(&p[8], rrip_identifier, p[4]); |
| memcpy(&p[8+p[4]], rrip_descriptor, p[5]); |
| memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]); |
| |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| static void |
| calculate_path_table_size(struct vdd *vdd) |
| { |
| int depth, size; |
| struct path_table *pt; |
| |
| pt = vdd->pathtbl; |
| size = 0; |
| for (depth = 0; depth < vdd->max_depth; depth++) { |
| struct isoent **ptbl; |
| int i, cnt; |
| |
| if ((cnt = pt[depth].cnt) == 0) |
| break; |
| |
| ptbl = pt[depth].sorted; |
| for (i = 0; i < cnt; i++) { |
| int len; |
| |
| if (ptbl[i]->identifier == NULL) |
| len = 1; /* root directory */ |
| else |
| len = ptbl[i]->id_len; |
| if (len & 0x01) |
| len++; /* Padding Field */ |
| size += 8 + len; |
| } |
| } |
| vdd->path_table_size = size; |
| vdd->path_table_block = |
| ((size + PATH_TABLE_BLOCK_SIZE -1) / |
| PATH_TABLE_BLOCK_SIZE) * |
| (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE); |
| } |
| |
| static int |
| _write_path_table(struct archive_write *a, int type_m, int depth, |
| struct vdd *vdd) |
| { |
| unsigned char *bp, *wb; |
| struct isoent **ptbl; |
| size_t wbremaining; |
| int i, r, wsize; |
| |
| if (vdd->pathtbl[depth].cnt == 0) |
| return (0); |
| |
| wsize = 0; |
| wb = wb_buffptr(a); |
| wbremaining = wb_remaining(a); |
| bp = wb - 1; |
| ptbl = vdd->pathtbl[depth].sorted; |
| for (i = 0; i < vdd->pathtbl[depth].cnt; i++) { |
| struct isoent *np; |
| size_t len; |
| |
| np = ptbl[i]; |
| if (np->identifier == NULL) |
| len = 1; /* root directory */ |
| else |
| len = np->id_len; |
| if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) { |
| r = wb_consume(a, (bp+1) - wb); |
| if (r < 0) |
| return (r); |
| wb = wb_buffptr(a); |
| wbremaining = wb_remaining(a); |
| bp = wb -1; |
| } |
| /* Length of Directory Identifier */ |
| set_num_711(bp+1, (unsigned char)len); |
| /* Extended Attribute Record Length */ |
| set_num_711(bp+2, 0); |
| /* Location of Extent */ |
| if (type_m) |
| set_num_732(bp+3, np->dir_location); |
| else |
| set_num_731(bp+3, np->dir_location); |
| /* Parent Directory Number */ |
| if (type_m) |
| set_num_722(bp+7, np->parent->dir_number); |
| else |
| set_num_721(bp+7, np->parent->dir_number); |
| /* Directory Identifier */ |
| if (np->identifier == NULL) |
| bp[9] = 0; |
| else |
| memcpy(&bp[9], np->identifier, len); |
| if (len & 0x01) { |
| /* Padding Field */ |
| bp[9+len] = 0; |
| len++; |
| } |
| wsize += 8 + (int)len; |
| bp += 8 + len; |
| } |
| if ((bp + 1) > wb) { |
| r = wb_consume(a, (bp+1)-wb); |
| if (r < 0) |
| return (r); |
| } |
| return (wsize); |
| } |
| |
| static int |
| write_path_table(struct archive_write *a, int type_m, struct vdd *vdd) |
| { |
| int depth, r; |
| size_t path_table_size; |
| |
| r = ARCHIVE_OK; |
| path_table_size = 0; |
| for (depth = 0; depth < vdd->max_depth; depth++) { |
| r = _write_path_table(a, type_m, depth, vdd); |
| if (r < 0) |
| return (r); |
| path_table_size += r; |
| } |
| |
| /* Write padding data. */ |
| path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE; |
| if (path_table_size > 0) |
| r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size); |
| return (r); |
| } |
| |
| static int |
| calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd, |
| struct isoent *isoent, int depth) |
| { |
| struct isoent **enttbl; |
| int bs, block, i; |
| |
| block = 1; |
| bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type); |
| bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type); |
| |
| if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && |
| !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) |
| return (block); |
| |
| enttbl = isoent->children_sorted; |
| for (i = 0; i < isoent->children.cnt; i++) { |
| struct isoent *np = enttbl[i]; |
| struct isofile *file; |
| |
| file = np->file; |
| if (file->hardlink_target != NULL) |
| file = file->hardlink_target; |
| file->cur_content = &(file->content); |
| do { |
| int dr_l; |
| |
| dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL, |
| vdd->vdd_type); |
| if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) { |
| block ++; |
| bs = dr_l; |
| } else |
| bs += dr_l; |
| file->cur_content = file->cur_content->next; |
| } while (file->cur_content != NULL); |
| } |
| return (block); |
| } |
| |
| static int |
| _write_directory_descriptors(struct archive_write *a, struct vdd *vdd, |
| struct isoent *isoent, int depth) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isoent **enttbl; |
| unsigned char *p, *wb; |
| int i, r; |
| int dr_l; |
| |
| p = wb = wb_buffptr(a); |
| #define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb)) |
| p += set_directory_record(p, WD_REMAINING, isoent, |
| iso9660, DIR_REC_SELF, vdd->vdd_type); |
| p += set_directory_record(p, WD_REMAINING, isoent, |
| iso9660, DIR_REC_PARENT, vdd->vdd_type); |
| |
| if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET && |
| !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) { |
| memset(p, 0, WD_REMAINING); |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| enttbl = isoent->children_sorted; |
| for (i = 0; i < isoent->children.cnt; i++) { |
| struct isoent *np = enttbl[i]; |
| struct isofile *file = np->file; |
| |
| if (file->hardlink_target != NULL) |
| file = file->hardlink_target; |
| file->cur_content = &(file->content); |
| do { |
| dr_l = set_directory_record(p, WD_REMAINING, |
| np, iso9660, DIR_REC_NORMAL, |
| vdd->vdd_type); |
| if (dr_l == 0) { |
| memset(p, 0, WD_REMAINING); |
| r = wb_consume(a, LOGICAL_BLOCK_SIZE); |
| if (r < 0) |
| return (r); |
| p = wb = wb_buffptr(a); |
| dr_l = set_directory_record(p, |
| WD_REMAINING, np, iso9660, |
| DIR_REC_NORMAL, vdd->vdd_type); |
| } |
| p += dr_l; |
| file->cur_content = file->cur_content->next; |
| } while (file->cur_content != NULL); |
| } |
| memset(p, 0, WD_REMAINING); |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| static int |
| write_directory_descriptors(struct archive_write *a, struct vdd *vdd) |
| { |
| struct isoent *np; |
| int depth, r; |
| |
| depth = 0; |
| np = vdd->rootent; |
| do { |
| struct extr_rec *extr; |
| |
| r = _write_directory_descriptors(a, vdd, np, depth); |
| if (r < 0) |
| return (r); |
| if (vdd->vdd_type != VDD_JOLIET) { |
| /* |
| * This extract record is used by SUSP,RRIP. |
| * Not for joliet. |
| */ |
| for (extr = np->extr_rec_list.first; |
| extr != NULL; |
| extr = extr->next) { |
| unsigned char *wb; |
| |
| wb = wb_buffptr(a); |
| memcpy(wb, extr->buf, extr->offset); |
| memset(wb + extr->offset, 0, |
| LOGICAL_BLOCK_SIZE - extr->offset); |
| r = wb_consume(a, LOGICAL_BLOCK_SIZE); |
| if (r < 0) |
| return (r); |
| } |
| } |
| |
| if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { |
| /* Enter to sub directories. */ |
| np = np->subdirs.first; |
| depth++; |
| continue; |
| } |
| while (np != np->parent) { |
| if (np->drnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| depth--; |
| } else { |
| np = np->drnext; |
| break; |
| } |
| } |
| } while (np != np->parent); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Read file contents from the temporary file, and write it. |
| */ |
| static int |
| write_file_contents(struct archive_write *a, int64_t offset, int64_t size) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| int r; |
| |
| lseek(iso9660->temp_fd, offset, SEEK_SET); |
| |
| while (size) { |
| size_t rsize; |
| ssize_t rs; |
| unsigned char *wb; |
| |
| wb = wb_buffptr(a); |
| rsize = wb_remaining(a); |
| if (rsize > (size_t)size) |
| rsize = (size_t)size; |
| rs = read(iso9660->temp_fd, wb, rsize); |
| if (rs <= 0) { |
| archive_set_error(&a->archive, errno, |
| "Can't read temporary file(%jd)", (intmax_t)rs); |
| return (ARCHIVE_FATAL); |
| } |
| size -= rs; |
| r = wb_consume(a, rs); |
| if (r < 0) |
| return (r); |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| write_file_descriptors(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file; |
| int64_t blocks, offset; |
| int r; |
| |
| blocks = 0; |
| offset = 0; |
| |
| /* Make the boot catalog contents, and write it. */ |
| if (iso9660->el_torito.catalog != NULL) { |
| r = make_boot_catalog(a); |
| if (r < 0) |
| return (r); |
| } |
| |
| /* Write the boot file contents. */ |
| if (iso9660->el_torito.boot != NULL) { |
| file = iso9660->el_torito.boot->file; |
| blocks = file->content.blocks; |
| offset = file->content.offset_of_temp; |
| if (offset != 0) { |
| r = write_file_contents(a, offset, |
| blocks << LOGICAL_BLOCK_BITS); |
| if (r < 0) |
| return (r); |
| blocks = 0; |
| offset = 0; |
| } |
| } |
| |
| /* Write out all file contents. */ |
| for (file = iso9660->data_file_list.first; |
| file != NULL; file = file->datanext) { |
| |
| if (!file->write_content) |
| continue; |
| |
| if ((offset + (blocks << LOGICAL_BLOCK_BITS)) < |
| file->content.offset_of_temp) { |
| if (blocks > 0) { |
| r = write_file_contents(a, offset, |
| blocks << LOGICAL_BLOCK_BITS); |
| if (r < 0) |
| return (r); |
| } |
| blocks = 0; |
| offset = file->content.offset_of_temp; |
| } |
| |
| file->cur_content = &(file->content); |
| do { |
| blocks += file->cur_content->blocks; |
| /* Next fragment */ |
| file->cur_content = file->cur_content->next; |
| } while (file->cur_content != NULL); |
| } |
| |
| /* Flush out remaining blocks. */ |
| if (blocks > 0) { |
| r = write_file_contents(a, offset, |
| blocks << LOGICAL_BLOCK_BITS); |
| if (r < 0) |
| return (r); |
| } |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static void |
| isofile_init_entry_list(struct iso9660 *iso9660) |
| { |
| iso9660->all_file_list.first = NULL; |
| iso9660->all_file_list.last = &(iso9660->all_file_list.first); |
| } |
| |
| static void |
| isofile_add_entry(struct iso9660 *iso9660, struct isofile *file) |
| { |
| file->allnext = NULL; |
| *iso9660->all_file_list.last = file; |
| iso9660->all_file_list.last = &(file->allnext); |
| } |
| |
| static void |
| isofile_free_all_entries(struct iso9660 *iso9660) |
| { |
| struct isofile *file, *file_next; |
| |
| file = iso9660->all_file_list.first; |
| while (file != NULL) { |
| file_next = file->allnext; |
| isofile_free(file); |
| file = file_next; |
| } |
| } |
| |
| static void |
| isofile_init_entry_data_file_list(struct iso9660 *iso9660) |
| { |
| iso9660->data_file_list.first = NULL; |
| iso9660->data_file_list.last = &(iso9660->data_file_list.first); |
| } |
| |
| static void |
| isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file) |
| { |
| file->datanext = NULL; |
| *iso9660->data_file_list.last = file; |
| iso9660->data_file_list.last = &(file->datanext); |
| } |
| |
| |
| static struct isofile * |
| isofile_new(struct archive_write *a, struct archive_entry *entry) |
| { |
| struct isofile *file; |
| |
| file = calloc(1, sizeof(*file)); |
| if (file == NULL) |
| return (NULL); |
| |
| if (entry != NULL) |
| file->entry = archive_entry_clone(entry); |
| else |
| file->entry = archive_entry_new2(&a->archive); |
| if (file->entry == NULL) { |
| free(file); |
| return (NULL); |
| } |
| archive_string_init(&(file->parentdir)); |
| archive_string_init(&(file->basename)); |
| archive_string_init(&(file->basename_utf16)); |
| archive_string_init(&(file->symlink)); |
| file->cur_content = &(file->content); |
| |
| return (file); |
| } |
| |
| static void |
| isofile_free(struct isofile *file) |
| { |
| struct content *con, *tmp; |
| |
| con = file->content.next; |
| while (con != NULL) { |
| tmp = con; |
| con = con->next; |
| free(tmp); |
| } |
| archive_entry_free(file->entry); |
| archive_string_free(&(file->parentdir)); |
| archive_string_free(&(file->basename)); |
| archive_string_free(&(file->basename_utf16)); |
| archive_string_free(&(file->symlink)); |
| free(file); |
| } |
| |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| static int |
| cleanup_backslash_1(char *p) |
| { |
| int mb, dos; |
| |
| mb = dos = 0; |
| while (*p) { |
| if (*(unsigned char *)p > 127) |
| mb = 1; |
| if (*p == '\\') { |
| /* If we have not met any multi-byte characters, |
| * we can replace '\' with '/'. */ |
| if (!mb) |
| *p = '/'; |
| dos = 1; |
| } |
| p++; |
| } |
| if (!mb || !dos) |
| return (0); |
| return (-1); |
| } |
| |
| static void |
| cleanup_backslash_2(wchar_t *p) |
| { |
| |
| /* Convert a path-separator from '\' to '/' */ |
| while (*p != L'\0') { |
| if (*p == L'\\') |
| *p = L'/'; |
| p++; |
| } |
| } |
| #endif |
| |
| /* |
| * Generate a parent directory name and a base name from a pathname. |
| */ |
| static int |
| isofile_gen_utility_names(struct archive_write *a, struct isofile *file) |
| { |
| struct iso9660 *iso9660; |
| const char *pathname; |
| char *p, *dirname, *slash; |
| size_t len; |
| int ret = ARCHIVE_OK; |
| |
| iso9660 = a->format_data; |
| |
| archive_string_empty(&(file->parentdir)); |
| archive_string_empty(&(file->basename)); |
| archive_string_empty(&(file->basename_utf16)); |
| archive_string_empty(&(file->symlink)); |
| |
| pathname = archive_entry_pathname(file->entry); |
| if (pathname == NULL || pathname[0] == '\0') {/* virtual root */ |
| file->dircnt = 0; |
| return (ret); |
| } |
| |
| /* |
| * Make a UTF-16BE basename if Joliet extension enabled. |
| */ |
| if (iso9660->opt.joliet) { |
| const char *u16, *ulast; |
| size_t u16len, ulen_last; |
| |
| if (iso9660->sconv_to_utf16be == NULL) { |
| iso9660->sconv_to_utf16be = |
| archive_string_conversion_to_charset( |
| &(a->archive), "UTF-16BE", 1); |
| if (iso9660->sconv_to_utf16be == NULL) |
| /* Couldn't allocate memory */ |
| return (ARCHIVE_FATAL); |
| iso9660->sconv_from_utf16be = |
| archive_string_conversion_from_charset( |
| &(a->archive), "UTF-16BE", 1); |
| if (iso9660->sconv_from_utf16be == NULL) |
| /* Couldn't allocate memory */ |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* |
| * Convert a filename to UTF-16BE. |
| */ |
| if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len, |
| iso9660->sconv_to_utf16be)) { |
| if (errno == ENOMEM) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory for UTF-16BE"); |
| return (ARCHIVE_FATAL); |
| } |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "A filename cannot be converted to UTF-16BE;" |
| "You should disable making Joliet extension"); |
| ret = ARCHIVE_WARN; |
| } |
| |
| /* |
| * Make sure a path separator is not in the last; |
| * Remove trailing '/'. |
| */ |
| while (u16len >= 2) { |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| if (u16[u16len-2] == 0 && |
| (u16[u16len-1] == '/' || u16[u16len-1] == '\\')) |
| #else |
| if (u16[u16len-2] == 0 && u16[u16len-1] == '/') |
| #endif |
| { |
| u16len -= 2; |
| } else |
| break; |
| } |
| |
| /* |
| * Find a basename in UTF-16BE. |
| */ |
| ulast = u16; |
| u16len >>= 1; |
| ulen_last = u16len; |
| while (u16len > 0) { |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\')) |
| #else |
| if (u16[0] == 0 && u16[1] == '/') |
| #endif |
| { |
| ulast = u16 + 2; |
| ulen_last = u16len -1; |
| } |
| u16 += 2; |
| u16len --; |
| } |
| ulen_last <<= 1; |
| if (archive_string_ensure(&(file->basename_utf16), |
| ulen_last) == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory for UTF-16BE"); |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* |
| * Set UTF-16BE basename. |
| */ |
| memcpy(file->basename_utf16.s, ulast, ulen_last); |
| file->basename_utf16.length = ulen_last; |
| } |
| |
| archive_strcpy(&(file->parentdir), pathname); |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| /* |
| * Convert a path-separator from '\' to '/' |
| */ |
| if (cleanup_backslash_1(file->parentdir.s) != 0) { |
| const wchar_t *wp = archive_entry_pathname_w(file->entry); |
| struct archive_wstring ws; |
| |
| if (wp != NULL) { |
| int r; |
| archive_string_init(&ws); |
| archive_wstrcpy(&ws, wp); |
| cleanup_backslash_2(ws.s); |
| archive_string_empty(&(file->parentdir)); |
| r = archive_string_append_from_wcs(&(file->parentdir), |
| ws.s, ws.length); |
| archive_wstring_free(&ws); |
| if (r < 0 && errno == ENOMEM) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| } |
| } |
| #endif |
| |
| len = file->parentdir.length; |
| p = dirname = file->parentdir.s; |
| |
| /* |
| * Remove leading '/', '../' and './' elements |
| */ |
| while (*p) { |
| if (p[0] == '/') { |
| p++; |
| len--; |
| } else if (p[0] != '.') |
| break; |
| else if (p[1] == '.' && p[2] == '/') { |
| p += 3; |
| len -= 3; |
| } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) { |
| p += 2; |
| len -= 2; |
| } else if (p[1] == '\0') { |
| p++; |
| len--; |
| } else |
| break; |
| } |
| if (p != dirname) { |
| memmove(dirname, p, len+1); |
| p = dirname; |
| } |
| /* |
| * Remove "/","/." and "/.." elements from tail. |
| */ |
| while (len > 0) { |
| size_t ll = len; |
| |
| if (len > 0 && p[len-1] == '/') { |
| p[len-1] = '\0'; |
| len--; |
| } |
| if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { |
| p[len-2] = '\0'; |
| len -= 2; |
| } |
| if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && |
| p[len-1] == '.') { |
| p[len-3] = '\0'; |
| len -= 3; |
| } |
| if (ll == len) |
| break; |
| } |
| while (*p) { |
| if (p[0] == '/') { |
| if (p[1] == '/') |
| /* Convert '//' --> '/' */ |
| strcpy(p, p+1); |
| else if (p[1] == '.' && p[2] == '/') |
| /* Convert '/./' --> '/' */ |
| strcpy(p, p+2); |
| else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { |
| /* Convert 'dir/dir1/../dir2/' |
| * --> 'dir/dir2/' |
| */ |
| char *rp = p -1; |
| while (rp >= dirname) { |
| if (*rp == '/') |
| break; |
| --rp; |
| } |
| if (rp > dirname) { |
| strcpy(rp, p+3); |
| p = rp; |
| } else { |
| strcpy(dirname, p+4); |
| p = dirname; |
| } |
| } else |
| p++; |
| } else |
| p++; |
| } |
| p = dirname; |
| len = strlen(p); |
| |
| if (archive_entry_filetype(file->entry) == AE_IFLNK) { |
| /* Convert symlink name too. */ |
| pathname = archive_entry_symlink(file->entry); |
| archive_strcpy(&(file->symlink), pathname); |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| /* |
| * Convert a path-separator from '\' to '/' |
| */ |
| if (archive_strlen(&(file->symlink)) > 0 && |
| cleanup_backslash_1(file->symlink.s) != 0) { |
| const wchar_t *wp = |
| archive_entry_symlink_w(file->entry); |
| struct archive_wstring ws; |
| |
| if (wp != NULL) { |
| int r; |
| archive_string_init(&ws); |
| archive_wstrcpy(&ws, wp); |
| cleanup_backslash_2(ws.s); |
| archive_string_empty(&(file->symlink)); |
| r = archive_string_append_from_wcs( |
| &(file->symlink), |
| ws.s, ws.length); |
| archive_wstring_free(&ws); |
| if (r < 0 && errno == ENOMEM) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| } |
| } |
| #endif |
| } |
| /* |
| * - Count up directory elements. |
| * - Find out the position which points the last position of |
| * path separator('/'). |
| */ |
| slash = NULL; |
| file->dircnt = 0; |
| for (; *p != '\0'; p++) |
| if (*p == '/') { |
| slash = p; |
| file->dircnt++; |
| } |
| if (slash == NULL) { |
| /* The pathname doesn't have a parent directory. */ |
| file->parentdir.length = len; |
| archive_string_copy(&(file->basename), &(file->parentdir)); |
| archive_string_empty(&(file->parentdir)); |
| *file->parentdir.s = '\0'; |
| return (ret); |
| } |
| |
| /* Make a basename from dirname and slash */ |
| *slash = '\0'; |
| file->parentdir.length = slash - dirname; |
| archive_strcpy(&(file->basename), slash + 1); |
| if (archive_entry_filetype(file->entry) == AE_IFDIR) |
| file->dircnt ++; |
| return (ret); |
| } |
| |
| /* |
| * Register a entry to get a hardlink target. |
| */ |
| static int |
| isofile_register_hardlink(struct archive_write *a, struct isofile *file) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct hardlink *hl; |
| const char *pathname; |
| |
| archive_entry_set_nlink(file->entry, 1); |
| pathname = archive_entry_hardlink(file->entry); |
| if (pathname == NULL) { |
| /* This `file` is a hardlink target. */ |
| hl = malloc(sizeof(*hl)); |
| if (hl == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| hl->nlink = 1; |
| /* A hardlink target must be the first position. */ |
| file->hlnext = NULL; |
| hl->file_list.first = file; |
| hl->file_list.last = &(file->hlnext); |
| __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree), |
| (struct archive_rb_node *)hl); |
| } else { |
| hl = (struct hardlink *)__archive_rb_tree_find_node( |
| &(iso9660->hardlink_rbtree), pathname); |
| if (hl != NULL) { |
| /* Insert `file` entry into the tail. */ |
| file->hlnext = NULL; |
| *hl->file_list.last = file; |
| hl->file_list.last = &(file->hlnext); |
| hl->nlink++; |
| } |
| archive_entry_unset_size(file->entry); |
| } |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Hardlinked files have to have the same location of extent. |
| * We have to find out hardlink target entries for the entries |
| * which have a hardlink target name. |
| */ |
| static void |
| isofile_connect_hardlink_files(struct iso9660 *iso9660) |
| { |
| struct archive_rb_node *n; |
| struct hardlink *hl; |
| struct isofile *target, *nf; |
| |
| ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) { |
| hl = (struct hardlink *)n; |
| |
| /* The first entry must be a hardlink target. */ |
| target = hl->file_list.first; |
| archive_entry_set_nlink(target->entry, hl->nlink); |
| /* Set a hardlink target to reference entries. */ |
| for (nf = target->hlnext; |
| nf != NULL; nf = nf->hlnext) { |
| nf->hardlink_target = target; |
| archive_entry_set_nlink(nf->entry, hl->nlink); |
| } |
| } |
| } |
| |
| static int |
| isofile_hd_cmp_node(const struct archive_rb_node *n1, |
| const struct archive_rb_node *n2) |
| { |
| const struct hardlink *h1 = (const struct hardlink *)n1; |
| const struct hardlink *h2 = (const struct hardlink *)n2; |
| |
| return (strcmp(archive_entry_pathname(h1->file_list.first->entry), |
| archive_entry_pathname(h2->file_list.first->entry))); |
| } |
| |
| static int |
| isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key) |
| { |
| const struct hardlink *h = (const struct hardlink *)n; |
| |
| return (strcmp(archive_entry_pathname(h->file_list.first->entry), |
| (const char *)key)); |
| } |
| |
| static void |
| isofile_init_hardlinks(struct iso9660 *iso9660) |
| { |
| static const struct archive_rb_tree_ops rb_ops = { |
| isofile_hd_cmp_node, isofile_hd_cmp_key, |
| }; |
| |
| __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops); |
| } |
| |
| static void |
| isofile_free_hardlinks(struct iso9660 *iso9660) |
| { |
| struct archive_rb_node *n, *next; |
| |
| for (n = ARCHIVE_RB_TREE_MIN(&(iso9660->hardlink_rbtree)); n;) { |
| next = __archive_rb_tree_iterate(&(iso9660->hardlink_rbtree), |
| n, ARCHIVE_RB_DIR_RIGHT); |
| free(n); |
| n = next; |
| } |
| } |
| |
| static struct isoent * |
| isoent_new(struct isofile *file) |
| { |
| struct isoent *isoent; |
| static const struct archive_rb_tree_ops rb_ops = { |
| isoent_cmp_node, isoent_cmp_key, |
| }; |
| |
| isoent = calloc(1, sizeof(*isoent)); |
| if (isoent == NULL) |
| return (NULL); |
| isoent->file = file; |
| isoent->children.first = NULL; |
| isoent->children.last = &(isoent->children.first); |
| __archive_rb_tree_init(&(isoent->rbtree), &rb_ops); |
| isoent->subdirs.first = NULL; |
| isoent->subdirs.last = &(isoent->subdirs.first); |
| isoent->extr_rec_list.first = NULL; |
| isoent->extr_rec_list.last = &(isoent->extr_rec_list.first); |
| isoent->extr_rec_list.current = NULL; |
| if (archive_entry_filetype(file->entry) == AE_IFDIR) |
| isoent->dir = 1; |
| |
| return (isoent); |
| } |
| |
| static inline struct isoent * |
| isoent_clone(struct isoent *src) |
| { |
| return (isoent_new(src->file)); |
| } |
| |
| static void |
| _isoent_free(struct isoent *isoent) |
| { |
| struct extr_rec *er, *er_next; |
| |
| free(isoent->children_sorted); |
| free(isoent->identifier); |
| er = isoent->extr_rec_list.first; |
| while (er != NULL) { |
| er_next = er->next; |
| free(er); |
| er = er_next; |
| } |
| free(isoent); |
| } |
| |
| static void |
| isoent_free_all(struct isoent *isoent) |
| { |
| struct isoent *np, *np_temp; |
| |
| if (isoent == NULL) |
| return; |
| np = isoent; |
| for (;;) { |
| if (np->dir) { |
| if (np->children.first != NULL) { |
| /* Enter to sub directories. */ |
| np = np->children.first; |
| continue; |
| } |
| } |
| for (;;) { |
| np_temp = np; |
| if (np->chnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| _isoent_free(np_temp); |
| if (np == np_temp) |
| return; |
| } else { |
| np = np->chnext; |
| _isoent_free(np_temp); |
| break; |
| } |
| } |
| } |
| } |
| |
| static struct isoent * |
| isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname) |
| { |
| struct isofile *file; |
| struct isoent *isoent; |
| |
| file = isofile_new(a, NULL); |
| if (file == NULL) |
| return (NULL); |
| archive_entry_set_pathname(file->entry, pathname); |
| archive_entry_unset_mtime(file->entry); |
| archive_entry_unset_atime(file->entry); |
| archive_entry_unset_ctime(file->entry); |
| archive_entry_set_uid(file->entry, getuid()); |
| archive_entry_set_gid(file->entry, getgid()); |
| archive_entry_set_mode(file->entry, 0555 | AE_IFDIR); |
| archive_entry_set_nlink(file->entry, 2); |
| if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { |
| isofile_free(file); |
| return (NULL); |
| } |
| isofile_add_entry(iso9660, file); |
| |
| isoent = isoent_new(file); |
| if (isoent == NULL) |
| return (NULL); |
| isoent->dir = 1; |
| isoent->virtual = 1; |
| |
| return (isoent); |
| } |
| |
| static int |
| isoent_cmp_node(const struct archive_rb_node *n1, |
| const struct archive_rb_node *n2) |
| { |
| const struct isoent *e1 = (const struct isoent *)n1; |
| const struct isoent *e2 = (const struct isoent *)n2; |
| |
| return (strcmp(e1->file->basename.s, e2->file->basename.s)); |
| } |
| |
| static int |
| isoent_cmp_key(const struct archive_rb_node *n, const void *key) |
| { |
| const struct isoent *e = (const struct isoent *)n; |
| |
| return (strcmp(e->file->basename.s, (const char *)key)); |
| } |
| |
| static int |
| isoent_add_child_head(struct isoent *parent, struct isoent *child) |
| { |
| |
| if (!__archive_rb_tree_insert_node( |
| &(parent->rbtree), (struct archive_rb_node *)child)) |
| return (0); |
| if ((child->chnext = parent->children.first) == NULL) |
| parent->children.last = &(child->chnext); |
| parent->children.first = child; |
| parent->children.cnt++; |
| child->parent = parent; |
| |
| /* Add a child to a sub-directory chain */ |
| if (child->dir) { |
| if ((child->drnext = parent->subdirs.first) == NULL) |
| parent->subdirs.last = &(child->drnext); |
| parent->subdirs.first = child; |
| parent->subdirs.cnt++; |
| child->parent = parent; |
| } else |
| child->drnext = NULL; |
| return (1); |
| } |
| |
| static int |
| isoent_add_child_tail(struct isoent *parent, struct isoent *child) |
| { |
| |
| if (!__archive_rb_tree_insert_node( |
| &(parent->rbtree), (struct archive_rb_node *)child)) |
| return (0); |
| child->chnext = NULL; |
| *parent->children.last = child; |
| parent->children.last = &(child->chnext); |
| parent->children.cnt++; |
| child->parent = parent; |
| |
| /* Add a child to a sub-directory chain */ |
| child->drnext = NULL; |
| if (child->dir) { |
| *parent->subdirs.last = child; |
| parent->subdirs.last = &(child->drnext); |
| parent->subdirs.cnt++; |
| child->parent = parent; |
| } |
| return (1); |
| } |
| |
| static void |
| isoent_remove_child(struct isoent *parent, struct isoent *child) |
| { |
| struct isoent *ent; |
| |
| /* Remove a child entry from children chain. */ |
| ent = parent->children.first; |
| while (ent->chnext != child) |
| ent = ent->chnext; |
| if ((ent->chnext = ent->chnext->chnext) == NULL) |
| parent->children.last = &(ent->chnext); |
| parent->children.cnt--; |
| |
| if (child->dir) { |
| /* Remove a child entry from sub-directory chain. */ |
| ent = parent->subdirs.first; |
| while (ent->drnext != child) |
| ent = ent->drnext; |
| if ((ent->drnext = ent->drnext->drnext) == NULL) |
| parent->subdirs.last = &(ent->drnext); |
| parent->subdirs.cnt--; |
| } |
| |
| __archive_rb_tree_remove_node(&(parent->rbtree), |
| (struct archive_rb_node *)child); |
| } |
| |
| static int |
| isoent_clone_tree(struct archive_write *a, struct isoent **nroot, |
| struct isoent *root) |
| { |
| struct isoent *np, *xroot, *newent; |
| |
| np = root; |
| xroot = NULL; |
| do { |
| newent = isoent_clone(np); |
| if (newent == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| if (xroot == NULL) { |
| *nroot = xroot = newent; |
| newent->parent = xroot; |
| } else |
| isoent_add_child_tail(xroot, newent); |
| if (np->dir && np->children.first != NULL) { |
| /* Enter to sub directories. */ |
| np = np->children.first; |
| xroot = newent; |
| continue; |
| } |
| while (np != np->parent) { |
| if (np->chnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| xroot = xroot->parent; |
| } else { |
| np = np->chnext; |
| break; |
| } |
| } |
| } while (np != np->parent); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Setup directory locations. |
| */ |
| static void |
| isoent_setup_directory_location(struct iso9660 *iso9660, int location, |
| struct vdd *vdd) |
| { |
| struct isoent *np; |
| int depth; |
| |
| vdd->total_dir_block = 0; |
| depth = 0; |
| np = vdd->rootent; |
| do { |
| int block; |
| |
| np->dir_block = calculate_directory_descriptors( |
| iso9660, vdd, np, depth); |
| vdd->total_dir_block += np->dir_block; |
| np->dir_location = location; |
| location += np->dir_block; |
| block = extra_setup_location(np, location); |
| vdd->total_dir_block += block; |
| location += block; |
| |
| if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { |
| /* Enter to sub directories. */ |
| np = np->subdirs.first; |
| depth++; |
| continue; |
| } |
| while (np != np->parent) { |
| if (np->drnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| depth--; |
| } else { |
| np = np->drnext; |
| break; |
| } |
| } |
| } while (np != np->parent); |
| } |
| |
| static void |
| _isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent, |
| int *symlocation) |
| { |
| struct isoent **children; |
| int n; |
| |
| if (isoent->children.cnt == 0) |
| return; |
| |
| children = isoent->children_sorted; |
| for (n = 0; n < isoent->children.cnt; n++) { |
| struct isoent *np; |
| struct isofile *file; |
| |
| np = children[n]; |
| if (np->dir) |
| continue; |
| if (np == iso9660->el_torito.boot) |
| continue; |
| file = np->file; |
| if (file->boot || file->hardlink_target != NULL) |
| continue; |
| if (archive_entry_filetype(file->entry) == AE_IFLNK || |
| file->content.size == 0) { |
| /* |
| * Do not point a valid location. |
| * Make sure entry is not hardlink file. |
| */ |
| file->content.location = (*symlocation)--; |
| continue; |
| } |
| |
| file->write_content = 1; |
| } |
| } |
| |
| /* |
| * Setup file locations. |
| */ |
| static void |
| isoent_setup_file_location(struct iso9660 *iso9660, int location) |
| { |
| struct isoent *isoent; |
| struct isoent *np; |
| struct isofile *file; |
| size_t size; |
| int block; |
| int depth; |
| int joliet; |
| int symlocation; |
| int total_block; |
| |
| iso9660->total_file_block = 0; |
| if ((isoent = iso9660->el_torito.catalog) != NULL) { |
| isoent->file->content.location = location; |
| block = (int)((archive_entry_size(isoent->file->entry) + |
| LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS); |
| location += block; |
| iso9660->total_file_block += block; |
| } |
| if ((isoent = iso9660->el_torito.boot) != NULL) { |
| isoent->file->content.location = location; |
| size = fd_boot_image_size(iso9660->el_torito.media_type); |
| if (size == 0) |
| size = (size_t)archive_entry_size(isoent->file->entry); |
| block = ((int)size + LOGICAL_BLOCK_SIZE -1) |
| >> LOGICAL_BLOCK_BITS; |
| location += block; |
| iso9660->total_file_block += block; |
| isoent->file->content.blocks = block; |
| } |
| |
| depth = 0; |
| symlocation = -16; |
| if (!iso9660->opt.rr && iso9660->opt.joliet) { |
| joliet = 1; |
| np = iso9660->joliet.rootent; |
| } else { |
| joliet = 0; |
| np = iso9660->primary.rootent; |
| } |
| do { |
| _isoent_file_location(iso9660, np, &symlocation); |
| |
| if (np->subdirs.first != NULL && |
| (joliet || |
| ((iso9660->opt.rr == OPT_RR_DISABLED && |
| depth + 2 < iso9660->primary.max_depth) || |
| (iso9660->opt.rr && |
| depth + 1 < iso9660->primary.max_depth)))) { |
| /* Enter to sub directories. */ |
| np = np->subdirs.first; |
| depth++; |
| continue; |
| } |
| while (np != np->parent) { |
| if (np->drnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| depth--; |
| } else { |
| np = np->drnext; |
| break; |
| } |
| } |
| } while (np != np->parent); |
| |
| total_block = 0; |
| for (file = iso9660->data_file_list.first; |
| file != NULL; file = file->datanext) { |
| |
| if (!file->write_content) |
| continue; |
| |
| file->cur_content = &(file->content); |
| do { |
| file->cur_content->location = location; |
| location += file->cur_content->blocks; |
| total_block += file->cur_content->blocks; |
| /* Next fragment */ |
| file->cur_content = file->cur_content->next; |
| } while (file->cur_content != NULL); |
| } |
| iso9660->total_file_block += total_block; |
| } |
| |
| static int |
| get_path_component(char *name, size_t n, const char *fn) |
| { |
| char *p; |
| size_t l; |
| |
| p = strchr(fn, '/'); |
| if (p == NULL) { |
| if ((l = strlen(fn)) == 0) |
| return (0); |
| } else |
| l = p - fn; |
| if (l > n -1) |
| return (-1); |
| memcpy(name, fn, l); |
| name[l] = '\0'; |
| |
| return ((int)l); |
| } |
| |
| /* |
| * Add a new entry into the tree. |
| */ |
| static int |
| isoent_tree(struct archive_write *a, struct isoent **isoentpp) |
| { |
| #if defined(_WIN32) && !defined(__CYGWIN__) |
| char name[_MAX_FNAME];/* Included null terminator size. */ |
| #elif defined(NAME_MAX) && NAME_MAX >= 255 |
| char name[NAME_MAX+1]; |
| #else |
| char name[256]; |
| #endif |
| struct iso9660 *iso9660 = a->format_data; |
| struct isoent *dent, *isoent, *np; |
| struct isofile *f1, *f2; |
| const char *fn, *p; |
| int l; |
| |
| isoent = *isoentpp; |
| dent = iso9660->primary.rootent; |
| if (isoent->file->parentdir.length > 0) |
| fn = p = isoent->file->parentdir.s; |
| else |
| fn = p = ""; |
| |
| /* |
| * If the path of the parent directory of `isoent' entry is |
| * the same as the path of `cur_dirent', add isoent to |
| * `cur_dirent'. |
| */ |
| if (archive_strlen(&(iso9660->cur_dirstr)) |
| == archive_strlen(&(isoent->file->parentdir)) && |
| strcmp(iso9660->cur_dirstr.s, fn) == 0) { |
| if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) { |
| np = (struct isoent *)__archive_rb_tree_find_node( |
| &(iso9660->cur_dirent->rbtree), |
| isoent->file->basename.s); |
| goto same_entry; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| for (;;) { |
| l = get_path_component(name, sizeof(name), fn); |
| if (l == 0) { |
| np = NULL; |
| break; |
| } |
| if (l < 0) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "A name buffer is too small"); |
| _isoent_free(isoent); |
| return (ARCHIVE_FATAL); |
| } |
| |
| np = isoent_find_child(dent, name); |
| if (np == NULL || fn[0] == '\0') |
| break; |
| |
| /* Find next subdirectory. */ |
| if (!np->dir) { |
| /* NOT Directory! */ |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "`%s' is not directory, we cannot insert `%s' ", |
| archive_entry_pathname(np->file->entry), |
| archive_entry_pathname(isoent->file->entry)); |
| _isoent_free(isoent); |
| *isoentpp = NULL; |
| return (ARCHIVE_FAILED); |
| } |
| fn += l; |
| if (fn[0] == '/') |
| fn++; |
| dent = np; |
| } |
| if (np == NULL) { |
| /* |
| * Create virtual parent directories. |
| */ |
| while (fn[0] != '\0') { |
| struct isoent *vp; |
| struct archive_string as; |
| |
| archive_string_init(&as); |
| archive_strncat(&as, p, fn - p + l); |
| if (as.s[as.length-1] == '/') { |
| as.s[as.length-1] = '\0'; |
| as.length--; |
| } |
| vp = isoent_create_virtual_dir(a, iso9660, as.s); |
| if (vp == NULL) { |
| archive_string_free(&as); |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| _isoent_free(isoent); |
| *isoentpp = NULL; |
| return (ARCHIVE_FATAL); |
| } |
| archive_string_free(&as); |
| |
| if (vp->file->dircnt > iso9660->dircnt_max) |
| iso9660->dircnt_max = vp->file->dircnt; |
| isoent_add_child_tail(dent, vp); |
| np = vp; |
| |
| fn += l; |
| if (fn[0] == '/') |
| fn++; |
| l = get_path_component(name, sizeof(name), fn); |
| if (l < 0) { |
| archive_string_free(&as); |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "A name buffer is too small"); |
| _isoent_free(isoent); |
| *isoentpp = NULL; |
| return (ARCHIVE_FATAL); |
| } |
| dent = np; |
| } |
| |
| /* Found out the parent directory where isoent can be |
| * inserted. */ |
| iso9660->cur_dirent = dent; |
| archive_string_empty(&(iso9660->cur_dirstr)); |
| archive_string_ensure(&(iso9660->cur_dirstr), |
| archive_strlen(&(dent->file->parentdir)) + |
| archive_strlen(&(dent->file->basename)) + 2); |
| if (archive_strlen(&(dent->file->parentdir)) + |
| archive_strlen(&(dent->file->basename)) == 0) |
| iso9660->cur_dirstr.s[0] = 0; |
| else { |
| if (archive_strlen(&(dent->file->parentdir)) > 0) { |
| archive_string_copy(&(iso9660->cur_dirstr), |
| &(dent->file->parentdir)); |
| archive_strappend_char(&(iso9660->cur_dirstr), '/'); |
| } |
| archive_string_concat(&(iso9660->cur_dirstr), |
| &(dent->file->basename)); |
| } |
| |
| if (!isoent_add_child_tail(dent, isoent)) { |
| np = (struct isoent *)__archive_rb_tree_find_node( |
| &(dent->rbtree), isoent->file->basename.s); |
| goto same_entry; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| same_entry: |
| /* |
| * We have already has the entry the filename of which is |
| * the same. |
| */ |
| f1 = np->file; |
| f2 = isoent->file; |
| |
| /* If the file type of entries is different, |
| * we cannot handle it. */ |
| if (archive_entry_filetype(f1->entry) != |
| archive_entry_filetype(f2->entry)) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Found duplicate entries `%s' and its file type is " |
| "different", |
| archive_entry_pathname(f1->entry)); |
| _isoent_free(isoent); |
| *isoentpp = NULL; |
| return (ARCHIVE_FAILED); |
| } |
| |
| /* Swap file entries. */ |
| np->file = f2; |
| isoent->file = f1; |
| np->virtual = 0; |
| |
| _isoent_free(isoent); |
| *isoentpp = np; |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Find a entry from `isoent' |
| */ |
| static struct isoent * |
| isoent_find_child(struct isoent *isoent, const char *child_name) |
| { |
| struct isoent *np; |
| |
| np = (struct isoent *)__archive_rb_tree_find_node( |
| &(isoent->rbtree), child_name); |
| return (np); |
| } |
| |
| /* |
| * Find a entry full-path of which is specified by `fn' parameter, |
| * in the tree. |
| */ |
| static struct isoent * |
| isoent_find_entry(struct isoent *rootent, const char *fn) |
| { |
| #if defined(_WIN32) && !defined(__CYGWIN__) |
| char name[_MAX_FNAME];/* Included null terminator size. */ |
| #elif defined(NAME_MAX) && NAME_MAX >= 255 |
| char name[NAME_MAX+1]; |
| #else |
| char name[256]; |
| #endif |
| struct isoent *isoent, *np; |
| int l; |
| |
| isoent = rootent; |
| np = NULL; |
| for (;;) { |
| l = get_path_component(name, sizeof(name), fn); |
| if (l == 0) |
| break; |
| fn += l; |
| if (fn[0] == '/') |
| fn++; |
| |
| np = isoent_find_child(isoent, name); |
| if (np == NULL) |
| break; |
| if (fn[0] == '\0') |
| break;/* We found out the entry */ |
| |
| /* Try sub directory. */ |
| isoent = np; |
| np = NULL; |
| if (!isoent->dir) |
| break;/* Not directory */ |
| } |
| |
| return (np); |
| } |
| |
| /* |
| * Following idr_* functions are used for resolving duplicated filenames |
| * and unreceivable filenames to generate ISO9660/Joliet Identifiers. |
| */ |
| |
| static void |
| idr_relaxed_filenames(char *map) |
| { |
| int i; |
| |
| for (i = 0x21; i <= 0x2F; i++) |
| map[i] = 1; |
| for (i = 0x3A; i <= 0x41; i++) |
| map[i] = 1; |
| for (i = 0x5B; i <= 0x5E; i++) |
| map[i] = 1; |
| map[0x60] = 1; |
| for (i = 0x7B; i <= 0x7E; i++) |
| map[i] = 1; |
| } |
| |
| static void |
| idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr) |
| { |
| |
| idr->idrent_pool = NULL; |
| idr->pool_size = 0; |
| if (vdd->vdd_type != VDD_JOLIET) { |
| if (iso9660->opt.iso_level <= 3) { |
| memcpy(idr->char_map, d_characters_map, |
| sizeof(idr->char_map)); |
| } else { |
| memcpy(idr->char_map, d1_characters_map, |
| sizeof(idr->char_map)); |
| idr_relaxed_filenames(idr->char_map); |
| } |
| } |
| } |
| |
| static void |
| idr_cleanup(struct idr *idr) |
| { |
| free(idr->idrent_pool); |
| } |
| |
| static int |
| idr_ensure_poolsize(struct archive_write *a, struct idr *idr, |
| int cnt) |
| { |
| |
| if (idr->pool_size < cnt) { |
| void *p; |
| const int bk = (1 << 7) - 1; |
| int psize; |
| |
| psize = (cnt + bk) & ~bk; |
| p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize); |
| if (p == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| idr->idrent_pool = (struct idrent *)p; |
| idr->pool_size = psize; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax, |
| int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops) |
| { |
| int r; |
| |
| (void)ffmax; /* UNUSED */ |
| |
| r = idr_ensure_poolsize(a, idr, cnt); |
| if (r != ARCHIVE_OK) |
| return (r); |
| __archive_rb_tree_init(&(idr->rbtree), rbt_ops); |
| idr->wait_list.first = NULL; |
| idr->wait_list.last = &(idr->wait_list.first); |
| idr->pool_idx = 0; |
| idr->num_size = num_size; |
| idr->null_size = null_size; |
| return (ARCHIVE_OK); |
| } |
| |
| static void |
| idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff) |
| { |
| struct idrent *idrent, *n; |
| |
| idrent = &(idr->idrent_pool[idr->pool_idx++]); |
| idrent->wnext = idrent->avail = NULL; |
| idrent->isoent = isoent; |
| idrent->weight = weight; |
| idrent->noff = noff; |
| idrent->rename_num = 0; |
| |
| if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) { |
| n = (struct idrent *)__archive_rb_tree_find_node( |
| &(idr->rbtree), idrent->isoent); |
| if (n != NULL) { |
| /* this `idrent' needs to rename. */ |
| idrent->avail = n; |
| *idr->wait_list.last = idrent; |
| idr->wait_list.last = &(idrent->wnext); |
| } |
| } |
| } |
| |
| static void |
| idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize) |
| { |
| unsigned char *p; |
| int wnp_ext_off; |
| |
| wnp_ext_off = wnp->isoent->ext_off; |
| if (wnp->noff + numsize != wnp_ext_off) { |
| p = (unsigned char *)wnp->isoent->identifier; |
| /* Extend the filename; foo.c --> foo___.c */ |
| memmove(p + wnp->noff + numsize, p + wnp_ext_off, |
| wnp->isoent->ext_len + nullsize); |
| wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize; |
| wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len; |
| } |
| } |
| |
| static void |
| idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num)) |
| { |
| struct idrent *n; |
| unsigned char *p; |
| |
| for (n = idr->wait_list.first; n != NULL; n = n->wnext) { |
| idr_extend_identifier(n, idr->num_size, idr->null_size); |
| p = (unsigned char *)n->isoent->identifier + n->noff; |
| do { |
| fsetnum(p, n->avail->rename_num++); |
| } while (!__archive_rb_tree_insert_node( |
| &(idr->rbtree), &(n->rbnode))); |
| } |
| } |
| |
| static void |
| idr_set_num(unsigned char *p, int num) |
| { |
| static const char xdig[] = { |
| '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', |
| 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', |
| 'U', 'V', 'W', 'X', 'Y', 'Z' |
| }; |
| |
| num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig); |
| p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))]; |
| num %= sizeof(xdig) * sizeof(xdig); |
| p[1] = xdig[ (num / sizeof(xdig))]; |
| num %= sizeof(xdig); |
| p[2] = xdig[num]; |
| } |
| |
| static void |
| idr_set_num_beutf16(unsigned char *p, int num) |
| { |
| static const uint16_t xdig[] = { |
| 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, |
| 0x0036, 0x0037, 0x0038, 0x0039, |
| 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, |
| 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, |
| 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, |
| 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, |
| 0x0059, 0x005A |
| }; |
| #define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0])) |
| |
| num %= XDIG_CNT * XDIG_CNT * XDIG_CNT; |
| archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]); |
| num %= XDIG_CNT * XDIG_CNT; |
| archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]); |
| num %= XDIG_CNT; |
| archive_be16enc(p+4, xdig[num]); |
| } |
| |
| /* |
| * Generate ISO9660 Identifier. |
| */ |
| static int |
| isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, |
| struct idr *idr) |
| { |
| struct iso9660 *iso9660; |
| struct isoent *np; |
| char *p; |
| int l, r; |
| const char *char_map; |
| char allow_ldots, allow_multidot, allow_period, allow_vernum; |
| int fnmax, ffmax, dnmax; |
| static const struct archive_rb_tree_ops rb_ops = { |
| isoent_cmp_node_iso9660, isoent_cmp_key_iso9660 |
| }; |
| |
| if (isoent->children.cnt == 0) |
| return (0); |
| |
| iso9660 = a->format_data; |
| char_map = idr->char_map; |
| if (iso9660->opt.iso_level <= 3) { |
| allow_ldots = 0; |
| allow_multidot = 0; |
| allow_period = 1; |
| allow_vernum = iso9660->opt.allow_vernum; |
| if (iso9660->opt.iso_level == 1) { |
| fnmax = 8; |
| ffmax = 12;/* fnmax + '.' + 3 */ |
| dnmax = 8; |
| } else { |
| fnmax = 30; |
| ffmax = 31; |
| dnmax = 31; |
| } |
| } else { |
| allow_ldots = allow_multidot = 1; |
| allow_period = allow_vernum = 0; |
| if (iso9660->opt.rr) |
| /* |
| * MDR : The maximum size of Directory Record(254). |
| * DRL : A Directory Record Length(33). |
| * CE : A size of SUSP CE System Use Entry(28). |
| * MDR - DRL - CE = 254 - 33 - 28 = 193. |
| */ |
| fnmax = ffmax = dnmax = 193; |
| else |
| /* |
| * XA : CD-ROM XA System Use Extension |
| * Information(14). |
| * MDR - DRL - XA = 254 - 33 -14 = 207. |
| */ |
| fnmax = ffmax = dnmax = 207; |
| } |
| |
| r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops); |
| if (r < 0) |
| return (r); |
| |
| for (np = isoent->children.first; np != NULL; np = np->chnext) { |
| char *dot, *xdot; |
| int ext_off, noff, weight; |
| |
| l = (int)np->file->basename.length; |
| p = malloc(l+31+2+1); |
| if (p == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| memcpy(p, np->file->basename.s, l); |
| p[l] = '\0'; |
| np->identifier = p; |
| |
| dot = xdot = NULL; |
| if (!allow_ldots) { |
| /* |
| * If there is a '.' character at the first byte, |
| * it has to be replaced by '_' character. |
| */ |
| if (*p == '.') |
| *p++ = '_'; |
| } |
| for (;*p; p++) { |
| if (*p & 0x80) { |
| *p = '_'; |
| continue; |
| } |
| if (char_map[(unsigned char)*p]) { |
| /* if iso-level is '4', a character '.' is |
| * allowed by char_map. */ |
| if (*p == '.') { |
| xdot = dot; |
| dot = p; |
| } |
| continue; |
| } |
| if (*p >= 'a' && *p <= 'z') { |
| *p -= 'a' - 'A'; |
| continue; |
| } |
| if (*p == '.') { |
| xdot = dot; |
| dot = p; |
| if (allow_multidot) |
| continue; |
| } |
| *p = '_'; |
| } |
| p = np->identifier; |
| weight = -1; |
| if (dot == NULL) { |
| int nammax; |
| |
| if (np->dir) |
| nammax = dnmax; |
| else |
| nammax = fnmax; |
| |
| if (l > nammax) { |
| p[nammax] = '\0'; |
| weight = nammax; |
| ext_off = nammax; |
| } else |
| ext_off = l; |
| } else { |
| *dot = '.'; |
| ext_off = (int)(dot - p); |
| |
| if (iso9660->opt.iso_level == 1) { |
| if (dot - p <= 8) { |
| if (strlen(dot) > 4) { |
| /* A length of a file extension |
| * must be less than 4 */ |
| dot[4] = '\0'; |
| weight = 0; |
| } |
| } else { |
| p[8] = dot[0]; |
| p[9] = dot[1]; |
| p[10] = dot[2]; |
| p[11] = dot[3]; |
| p[12] = '\0'; |
| weight = 8; |
| ext_off = 8; |
| } |
| } else if (np->dir) { |
| if (l > dnmax) { |
| p[dnmax] = '\0'; |
| weight = dnmax; |
| if (ext_off > dnmax) |
| ext_off = dnmax; |
| } |
| } else if (l > ffmax) { |
| int extlen = (int)strlen(dot); |
| int xdoff; |
| |
| if (xdot != NULL) |
| xdoff = (int)(xdot - p); |
| else |
| xdoff = 0; |
| |
| if (extlen > 1 && xdoff < fnmax-1) { |
| int off; |
| |
| if (extlen > ffmax) |
| extlen = ffmax; |
| off = ffmax - extlen; |
| if (off == 0) { |
| /* A dot('.') character |
| * doesn't place to the first |
| * byte of identifier. */ |
| off ++; |
| extlen --; |
| } |
| memmove(p+off, dot, extlen); |
| p[ffmax] = '\0'; |
| ext_off = off; |
| weight = off; |
| #ifdef COMPAT_MKISOFS |
| } else if (xdoff >= fnmax-1) { |
| /* Simulate a bug(?) of mkisofs. */ |
| p[fnmax-1] = '\0'; |
| ext_off = fnmax-1; |
| weight = fnmax-1; |
| #endif |
| } else { |
| p[fnmax] = '\0'; |
| ext_off = fnmax; |
| weight = fnmax; |
| } |
| } |
| } |
| /* Save an offset of a file name extension to sort files. */ |
| np->ext_off = ext_off; |
| np->ext_len = (int)strlen(&p[ext_off]); |
| np->id_len = l = ext_off + np->ext_len; |
| |
| /* Make an offset of the number which is used to be set |
| * hexadecimal number to avoid duplicate identifier. */ |
| if (iso9660->opt.iso_level == 1) { |
| if (ext_off >= 5) |
| noff = 5; |
| else |
| noff = ext_off; |
| } else { |
| if (l == ffmax) |
| noff = ext_off - 3; |
| else if (l == ffmax-1) |
| noff = ext_off - 2; |
| else if (l == ffmax-2) |
| noff = ext_off - 1; |
| else |
| noff = ext_off; |
| } |
| /* Register entry to the identifier resolver. */ |
| idr_register(idr, np, weight, noff); |
| } |
| |
| /* Resolve duplicate identifier. */ |
| idr_resolve(idr, idr_set_num); |
| |
| /* Add a period and a version number to identifiers. */ |
| for (np = isoent->children.first; np != NULL; np = np->chnext) { |
| if (!np->dir && np->rr_child == NULL) { |
| p = np->identifier + np->ext_off + np->ext_len; |
| if (np->ext_len == 0 && allow_period) { |
| *p++ = '.'; |
| np->ext_len = 1; |
| } |
| if (np->ext_len == 1 && !allow_period) { |
| *--p = '\0'; |
| np->ext_len = 0; |
| } |
| np->id_len = np->ext_off + np->ext_len; |
| if (allow_vernum) { |
| *p++ = ';'; |
| *p++ = '1'; |
| np->id_len += 2; |
| } |
| *p = '\0'; |
| } else |
| np->id_len = np->ext_off + np->ext_len; |
| np->mb_len = np->id_len; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Generate Joliet Identifier. |
| */ |
| static int |
| isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, |
| struct idr *idr) |
| { |
| struct iso9660 *iso9660; |
| struct isoent *np; |
| unsigned char *p; |
| size_t l; |
| int r; |
| size_t ffmax, parent_len; |
| static const struct archive_rb_tree_ops rb_ops = { |
| isoent_cmp_node_joliet, isoent_cmp_key_joliet |
| }; |
| |
| if (isoent->children.cnt == 0) |
| return (0); |
| |
| iso9660 = a->format_data; |
| if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME) |
| ffmax = 206; |
| else |
| ffmax = 128; |
| |
| r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops); |
| if (r < 0) |
| return (r); |
| |
| parent_len = 1; |
| for (np = isoent; np->parent != np; np = np->parent) |
| parent_len += np->mb_len + 1; |
| |
| for (np = isoent->children.first; np != NULL; np = np->chnext) { |
| unsigned char *dot; |
| int ext_off, noff, weight; |
| size_t lt; |
| |
| if ((l = np->file->basename_utf16.length) > ffmax) |
| l = ffmax; |
| |
| p = malloc((l+1)*2); |
| if (p == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| memcpy(p, np->file->basename_utf16.s, l); |
| p[l] = 0; |
| p[l+1] = 0; |
| |
| np->identifier = (char *)p; |
| lt = l; |
| dot = p + l; |
| weight = 0; |
| while (lt > 0) { |
| if (!joliet_allowed_char(p[0], p[1])) |
| archive_be16enc(p, 0x005F); /* '_' */ |
| else if (p[0] == 0 && p[1] == 0x2E) /* '.' */ |
| dot = p; |
| p += 2; |
| lt -= 2; |
| } |
| ext_off = (int)(dot - (unsigned char *)np->identifier); |
| np->ext_off = ext_off; |
| np->ext_len = (int)l - ext_off; |
| np->id_len = (int)l; |
| |
| /* |
| * Get a length of MBS of a full-pathname. |
| */ |
| if (np->file->basename_utf16.length > ffmax) { |
| if (archive_strncpy_l(&iso9660->mbs, |
| (const char *)np->identifier, l, |
| iso9660->sconv_from_utf16be) != 0 && |
| errno == ENOMEM) { |
| archive_set_error(&a->archive, errno, |
| "No memory"); |
| return (ARCHIVE_FATAL); |
| } |
| np->mb_len = (int)iso9660->mbs.length; |
| if (np->mb_len != (int)np->file->basename.length) |
| weight = np->mb_len; |
| } else |
| np->mb_len = (int)np->file->basename.length; |
| |
| /* If a length of full-pathname is longer than 240 bytes, |
| * it violates Joliet extensions regulation. */ |
| if (parent_len > 240 |
| || np->mb_len > 240 |
| || parent_len + np->mb_len > 240) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "The regulation of Joliet extensions;" |
| " A length of a full-pathname of `%s' is " |
| "longer than 240 bytes, (p=%d, b=%d)", |
| archive_entry_pathname(np->file->entry), |
| (int)parent_len, (int)np->mb_len); |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* Make an offset of the number which is used to be set |
| * hexadecimal number to avoid duplicate identifier. */ |
| if (l == ffmax) |
| noff = ext_off - 6; |
| else if (l == ffmax-2) |
| noff = ext_off - 4; |
| else if (l == ffmax-4) |
| noff = ext_off - 2; |
| else |
| noff = ext_off; |
| /* Register entry to the identifier resolver. */ |
| idr_register(idr, np, weight, noff); |
| } |
| |
| /* Resolve duplicate identifier with Joliet Volume. */ |
| idr_resolve(idr, idr_set_num_beutf16); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * This comparing rule is according to ISO9660 Standard 9.3 |
| */ |
| static int |
| isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2) |
| { |
| const char *s1, *s2; |
| int cmp; |
| int l; |
| |
| s1 = p1->identifier; |
| s2 = p2->identifier; |
| |
| /* Compare File Name */ |
| l = p1->ext_off; |
| if (l > p2->ext_off) |
| l = p2->ext_off; |
| cmp = memcmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| if (p1->ext_off < p2->ext_off) { |
| s2 += l; |
| l = p2->ext_off - p1->ext_off; |
| while (l--) |
| if (0x20 != *s2++) |
| return (0x20 |
| - *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_off > p2->ext_off) { |
| s1 += l; |
| l = p1->ext_off - p2->ext_off; |
| while (l--) |
| if (0x20 != *s1++) |
| return (*(const unsigned char *)(s1 - 1) |
| - 0x20); |
| } |
| /* Compare File Name Extension */ |
| if (p1->ext_len == 0 && p2->ext_len == 0) |
| return (0); |
| if (p1->ext_len == 1 && p2->ext_len == 1) |
| return (0); |
| if (p1->ext_len <= 1) |
| return (-1); |
| if (p2->ext_len <= 1) |
| return (1); |
| l = p1->ext_len; |
| if (l > p2->ext_len) |
| l = p2->ext_len; |
| s1 = p1->identifier + p1->ext_off; |
| s2 = p2->identifier + p2->ext_off; |
| if (l > 1) { |
| cmp = memcmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| } |
| if (p1->ext_len < p2->ext_len) { |
| s2 += l; |
| l = p2->ext_len - p1->ext_len; |
| while (l--) |
| if (0x20 != *s2++) |
| return (0x20 |
| - *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_len > p2->ext_len) { |
| s1 += l; |
| l = p1->ext_len - p2->ext_len; |
| while (l--) |
| if (0x20 != *s1++) |
| return (*(const unsigned char *)(s1 - 1) |
| - 0x20); |
| } |
| /* Compare File Version Number */ |
| /* No operation. The File Version Number is always one. */ |
| |
| return (cmp); |
| } |
| |
| static int |
| isoent_cmp_node_iso9660(const struct archive_rb_node *n1, |
| const struct archive_rb_node *n2) |
| { |
| const struct idrent *e1 = (const struct idrent *)n1; |
| const struct idrent *e2 = (const struct idrent *)n2; |
| |
| return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent)); |
| } |
| |
| static int |
| isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key) |
| { |
| const struct isoent *isoent = (const struct isoent *)key; |
| const struct idrent *idrent = (const struct idrent *)node; |
| |
| return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent)); |
| } |
| |
| static int |
| isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2) |
| { |
| const unsigned char *s1, *s2; |
| int cmp; |
| int l; |
| |
| s1 = (const unsigned char *)p1->identifier; |
| s2 = (const unsigned char *)p2->identifier; |
| |
| /* Compare File Name */ |
| l = p1->ext_off; |
| if (l > p2->ext_off) |
| l = p2->ext_off; |
| cmp = memcmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| if (p1->ext_off < p2->ext_off) { |
| s2 += l; |
| l = p2->ext_off - p1->ext_off; |
| while (l--) |
| if (0 != *s2++) |
| return (- *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_off > p2->ext_off) { |
| s1 += l; |
| l = p1->ext_off - p2->ext_off; |
| while (l--) |
| if (0 != *s1++) |
| return (*(const unsigned char *)(s1 - 1)); |
| } |
| /* Compare File Name Extension */ |
| if (p1->ext_len == 0 && p2->ext_len == 0) |
| return (0); |
| if (p1->ext_len == 2 && p2->ext_len == 2) |
| return (0); |
| if (p1->ext_len <= 2) |
| return (-1); |
| if (p2->ext_len <= 2) |
| return (1); |
| l = p1->ext_len; |
| if (l > p2->ext_len) |
| l = p2->ext_len; |
| s1 = (unsigned char *)(p1->identifier + p1->ext_off); |
| s2 = (unsigned char *)(p2->identifier + p2->ext_off); |
| if (l > 1) { |
| cmp = memcmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| } |
| if (p1->ext_len < p2->ext_len) { |
| s2 += l; |
| l = p2->ext_len - p1->ext_len; |
| while (l--) |
| if (0 != *s2++) |
| return (- *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_len > p2->ext_len) { |
| s1 += l; |
| l = p1->ext_len - p2->ext_len; |
| while (l--) |
| if (0 != *s1++) |
| return (*(const unsigned char *)(s1 - 1)); |
| } |
| /* Compare File Version Number */ |
| /* No operation. The File Version Number is always one. */ |
| |
| return (cmp); |
| } |
| |
| static int |
| isoent_cmp_node_joliet(const struct archive_rb_node *n1, |
| const struct archive_rb_node *n2) |
| { |
| const struct idrent *e1 = (const struct idrent *)n1; |
| const struct idrent *e2 = (const struct idrent *)n2; |
| |
| return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent)); |
| } |
| |
| static int |
| isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key) |
| { |
| const struct isoent *isoent = (const struct isoent *)key; |
| const struct idrent *idrent = (const struct idrent *)node; |
| |
| return (isoent_cmp_joliet_identifier(isoent, idrent->isoent)); |
| } |
| |
| static int |
| isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent, |
| struct idr *idr) |
| { |
| struct archive_rb_node *rn; |
| struct isoent **children; |
| |
| children = malloc(isoent->children.cnt * sizeof(struct isoent *)); |
| if (children == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| isoent->children_sorted = children; |
| |
| ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) { |
| struct idrent *idrent = (struct idrent *)rn; |
| *children ++ = idrent->isoent; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * - Generate ISO9660 and Joliet identifiers from basenames. |
| * - Sort files by each directory. |
| */ |
| static int |
| isoent_traverse_tree(struct archive_write *a, struct vdd* vdd) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isoent *np; |
| struct idr idr; |
| int depth; |
| int r; |
| int (*genid)(struct archive_write *, struct isoent *, struct idr *); |
| |
| idr_init(iso9660, vdd, &idr); |
| np = vdd->rootent; |
| depth = 0; |
| if (vdd->vdd_type == VDD_JOLIET) |
| genid = isoent_gen_joliet_identifier; |
| else |
| genid = isoent_gen_iso9660_identifier; |
| do { |
| if (np->virtual && |
| !archive_entry_mtime_is_set(np->file->entry)) { |
| /* Set properly times to virtual directory */ |
| archive_entry_set_mtime(np->file->entry, |
| iso9660->birth_time, 0); |
| archive_entry_set_atime(np->file->entry, |
| iso9660->birth_time, 0); |
| archive_entry_set_ctime(np->file->entry, |
| iso9660->birth_time, 0); |
| } |
| if (np->children.first != NULL) { |
| if (vdd->vdd_type != VDD_JOLIET && |
| !iso9660->opt.rr && depth + 1 >= vdd->max_depth) { |
| if (np->children.cnt > 0) |
| iso9660->directories_too_deep = np; |
| } else { |
| /* Generate Identifier */ |
| r = genid(a, np, &idr); |
| if (r < 0) |
| goto exit_traverse_tree; |
| r = isoent_make_sorted_files(a, np, &idr); |
| if (r < 0) |
| goto exit_traverse_tree; |
| |
| if (np->subdirs.first != NULL && |
| depth + 1 < vdd->max_depth) { |
| /* Enter to sub directories. */ |
| np = np->subdirs.first; |
| depth++; |
| continue; |
| } |
| } |
| } |
| while (np != np->parent) { |
| if (np->drnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| depth--; |
| } else { |
| np = np->drnext; |
| break; |
| } |
| } |
| } while (np != np->parent); |
| |
| r = ARCHIVE_OK; |
| exit_traverse_tree: |
| idr_cleanup(&idr); |
| |
| return (r); |
| } |
| |
| /* |
| * Collect directory entries into path_table by a directory depth. |
| */ |
| static int |
| isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth) |
| { |
| struct isoent *np; |
| |
| if (rootent == NULL) |
| rootent = vdd->rootent; |
| np = rootent; |
| do { |
| /* Register current directory to pathtable. */ |
| path_table_add_entry(&(vdd->pathtbl[depth]), np); |
| |
| if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) { |
| /* Enter to sub directories. */ |
| np = np->subdirs.first; |
| depth++; |
| continue; |
| } |
| while (np != rootent) { |
| if (np->drnext == NULL) { |
| /* Return to the parent directory. */ |
| np = np->parent; |
| depth--; |
| } else { |
| np = np->drnext; |
| break; |
| } |
| } |
| } while (np != rootent); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * The entry whose number of levels in a directory hierarchy is |
| * large than eight relocate to rr_move directory. |
| */ |
| static int |
| isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved, |
| struct isoent *curent, struct isoent **newent) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isoent *rrmoved, *mvent, *np; |
| |
| if ((rrmoved = *rr_moved) == NULL) { |
| struct isoent *rootent = iso9660->primary.rootent; |
| /* There isn't rr_move entry. |
| * Create rr_move entry and insert it into the root entry. |
| */ |
| rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved"); |
| if (rrmoved == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| /* Add "rr_moved" entry to the root entry. */ |
| isoent_add_child_head(rootent, rrmoved); |
| archive_entry_set_nlink(rootent->file->entry, |
| archive_entry_nlink(rootent->file->entry) + 1); |
| /* Register "rr_moved" entry to second level pathtable. */ |
| path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved); |
| /* Save rr_moved. */ |
| *rr_moved = rrmoved; |
| } |
| /* |
| * Make a clone of curent which is going to be relocated |
| * to rr_moved. |
| */ |
| mvent = isoent_clone(curent); |
| if (mvent == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| /* linking.. and use for creating "CL", "PL" and "RE" */ |
| mvent->rr_parent = curent->parent; |
| curent->rr_child = mvent; |
| /* |
| * Move subdirectories from the curent to mvent |
| */ |
| if (curent->children.first != NULL) { |
| *mvent->children.last = curent->children.first; |
| mvent->children.last = curent->children.last; |
| } |
| for (np = mvent->children.first; np != NULL; np = np->chnext) |
| np->parent = mvent; |
| mvent->children.cnt = curent->children.cnt; |
| curent->children.cnt = 0; |
| curent->children.first = NULL; |
| curent->children.last = &curent->children.first; |
| |
| if (curent->subdirs.first != NULL) { |
| *mvent->subdirs.last = curent->subdirs.first; |
| mvent->subdirs.last = curent->subdirs.last; |
| } |
| mvent->subdirs.cnt = curent->subdirs.cnt; |
| curent->subdirs.cnt = 0; |
| curent->subdirs.first = NULL; |
| curent->subdirs.last = &curent->subdirs.first; |
| |
| /* |
| * The mvent becomes a child of the rr_moved entry. |
| */ |
| isoent_add_child_tail(rrmoved, mvent); |
| archive_entry_set_nlink(rrmoved->file->entry, |
| archive_entry_nlink(rrmoved->file->entry) + 1); |
| /* |
| * This entry which relocated to the rr_moved directory |
| * has to set the flag as a file. |
| * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry. |
| */ |
| curent->dir = 0; |
| |
| *newent = mvent; |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| isoent_rr_move(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct path_table *pt; |
| struct isoent *rootent, *rr_moved; |
| struct isoent *np, *last; |
| int r; |
| |
| pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]); |
| /* There aren't level 8 directories reaching a deeper level. */ |
| if (pt->cnt == 0) |
| return (ARCHIVE_OK); |
| |
| rootent = iso9660->primary.rootent; |
| /* If "rr_moved" directory is already existing, |
| * we have to use it. */ |
| rr_moved = isoent_find_child(rootent, "rr_moved"); |
| if (rr_moved != NULL && |
| rr_moved != rootent->children.first) { |
| /* |
| * It's necessary that rr_move is the first entry |
| * of the root. |
| */ |
| /* Remove "rr_moved" entry from children chain. */ |
| isoent_remove_child(rootent, rr_moved); |
| |
| /* Add "rr_moved" entry into the head of children chain. */ |
| isoent_add_child_head(rootent, rr_moved); |
| } |
| |
| /* |
| * Check level 8 path_table. |
| * If find out sub directory entries, that entries move to rr_move. |
| */ |
| np = pt->first; |
| while (np != NULL) { |
| last = path_table_last_entry(pt); |
| for (; np != NULL; np = np->ptnext) { |
| struct isoent *mvent; |
| struct isoent *newent; |
| |
| if (!np->dir) |
| continue; |
| for (mvent = np->subdirs.first; |
| mvent != NULL; mvent = mvent->drnext) { |
| r = isoent_rr_move_dir(a, &rr_moved, |
| mvent, &newent); |
| if (r < 0) |
| return (r); |
| isoent_collect_dirs(&(iso9660->primary), |
| newent, 2); |
| } |
| } |
| /* If new entries are added to level 8 path_talbe, |
| * its sub directory entries move to rr_move too. |
| */ |
| np = last->ptnext; |
| } |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * This comparing rule is according to ISO9660 Standard 6.9.1 |
| */ |
| static int |
| _compare_path_table(const void *v1, const void *v2) |
| { |
| const struct isoent *p1, *p2; |
| const char *s1, *s2; |
| int cmp, l; |
| |
| p1 = *((const struct isoent **)(uintptr_t)v1); |
| p2 = *((const struct isoent **)(uintptr_t)v2); |
| |
| /* Compare parent directory number */ |
| cmp = p1->parent->dir_number - p2->parent->dir_number; |
| if (cmp != 0) |
| return (cmp); |
| |
| /* Compare identifier */ |
| s1 = p1->identifier; |
| s2 = p2->identifier; |
| l = p1->ext_off; |
| if (l > p2->ext_off) |
| l = p2->ext_off; |
| cmp = strncmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| if (p1->ext_off < p2->ext_off) { |
| s2 += l; |
| l = p2->ext_off - p1->ext_off; |
| while (l--) |
| if (0x20 != *s2++) |
| return (0x20 |
| - *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_off > p2->ext_off) { |
| s1 += l; |
| l = p1->ext_off - p2->ext_off; |
| while (l--) |
| if (0x20 != *s1++) |
| return (*(const unsigned char *)(s1 - 1) |
| - 0x20); |
| } |
| return (0); |
| } |
| |
| static int |
| _compare_path_table_joliet(const void *v1, const void *v2) |
| { |
| const struct isoent *p1, *p2; |
| const unsigned char *s1, *s2; |
| int cmp, l; |
| |
| p1 = *((const struct isoent **)(uintptr_t)v1); |
| p2 = *((const struct isoent **)(uintptr_t)v2); |
| |
| /* Compare parent directory number */ |
| cmp = p1->parent->dir_number - p2->parent->dir_number; |
| if (cmp != 0) |
| return (cmp); |
| |
| /* Compare identifier */ |
| s1 = (const unsigned char *)p1->identifier; |
| s2 = (const unsigned char *)p2->identifier; |
| l = p1->ext_off; |
| if (l > p2->ext_off) |
| l = p2->ext_off; |
| cmp = memcmp(s1, s2, l); |
| if (cmp != 0) |
| return (cmp); |
| if (p1->ext_off < p2->ext_off) { |
| s2 += l; |
| l = p2->ext_off - p1->ext_off; |
| while (l--) |
| if (0 != *s2++) |
| return (- *(const unsigned char *)(s2 - 1)); |
| } else if (p1->ext_off > p2->ext_off) { |
| s1 += l; |
| l = p1->ext_off - p2->ext_off; |
| while (l--) |
| if (0 != *s1++) |
| return (*(const unsigned char *)(s1 - 1)); |
| } |
| return (0); |
| } |
| |
| static inline void |
| path_table_add_entry(struct path_table *pathtbl, struct isoent *ent) |
| { |
| ent->ptnext = NULL; |
| *pathtbl->last = ent; |
| pathtbl->last = &(ent->ptnext); |
| pathtbl->cnt ++; |
| } |
| |
| static inline struct isoent * |
| path_table_last_entry(struct path_table *pathtbl) |
| { |
| if (pathtbl->first == NULL) |
| return (NULL); |
| return (((struct isoent *)(void *) |
| ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext)))); |
| } |
| |
| /* |
| * Sort directory entries in path_table |
| * and assign directory number to each entries. |
| */ |
| static int |
| isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd, |
| int depth, int *dir_number) |
| { |
| struct isoent *np; |
| struct isoent **enttbl; |
| struct path_table *pt; |
| int i; |
| |
| pt = &vdd->pathtbl[depth]; |
| if (pt->cnt == 0) { |
| pt->sorted = NULL; |
| return (ARCHIVE_OK); |
| } |
| enttbl = malloc(pt->cnt * sizeof(struct isoent *)); |
| if (enttbl == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| pt->sorted = enttbl; |
| for (np = pt->first; np != NULL; np = np->ptnext) |
| *enttbl ++ = np; |
| enttbl = pt->sorted; |
| |
| switch (vdd->vdd_type) { |
| case VDD_PRIMARY: |
| case VDD_ENHANCED: |
| #ifdef __COMPAR_FN_T |
| qsort(enttbl, pt->cnt, sizeof(struct isoent *), |
| (__compar_fn_t)_compare_path_table); |
| #else |
| qsort(enttbl, pt->cnt, sizeof(struct isoent *), |
| _compare_path_table); |
| #endif |
| break; |
| case VDD_JOLIET: |
| #ifdef __COMPAR_FN_T |
| qsort(enttbl, pt->cnt, sizeof(struct isoent *), |
| (__compar_fn_t)_compare_path_table_joliet); |
| #else |
| qsort(enttbl, pt->cnt, sizeof(struct isoent *), |
| _compare_path_table_joliet); |
| #endif |
| break; |
| } |
| for (i = 0; i < pt->cnt; i++) |
| enttbl[i]->dir_number = (*dir_number)++; |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd, |
| int max_depth) |
| { |
| int i; |
| |
| vdd->max_depth = max_depth; |
| vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth); |
| if (vdd->pathtbl == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| for (i = 0; i < vdd->max_depth; i++) { |
| vdd->pathtbl[i].first = NULL; |
| vdd->pathtbl[i].last = &(vdd->pathtbl[i].first); |
| vdd->pathtbl[i].sorted = NULL; |
| vdd->pathtbl[i].cnt = 0; |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * Make Path Tables |
| */ |
| static int |
| isoent_make_path_table(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| int depth, r; |
| int dir_number; |
| |
| /* |
| * Init Path Table. |
| */ |
| if (iso9660->dircnt_max >= MAX_DEPTH && |
| (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4)) |
| r = isoent_alloc_path_table(a, &(iso9660->primary), |
| iso9660->dircnt_max + 1); |
| else |
| /* The number of levels in the hierarchy cannot exceed |
| * eight. */ |
| r = isoent_alloc_path_table(a, &(iso9660->primary), |
| MAX_DEPTH); |
| if (r < 0) |
| return (r); |
| if (iso9660->opt.joliet) { |
| r = isoent_alloc_path_table(a, &(iso9660->joliet), |
| iso9660->dircnt_max + 1); |
| if (r < 0) |
| return (r); |
| } |
| |
| /* Step 0. |
| * - Collect directories for primary and joliet. |
| */ |
| isoent_collect_dirs(&(iso9660->primary), NULL, 0); |
| if (iso9660->opt.joliet) |
| isoent_collect_dirs(&(iso9660->joliet), NULL, 0); |
| /* |
| * Rockridge; move deeper depth directories to rr_moved. |
| */ |
| if (iso9660->opt.rr) { |
| r = isoent_rr_move(a); |
| if (r < 0) |
| return (r); |
| } |
| |
| /* Update nlink. */ |
| isofile_connect_hardlink_files(iso9660); |
| |
| /* Step 1. |
| * - Renew a value of the depth of that directories. |
| * - Resolve hardlinks. |
| * - Convert pathnames to ISO9660 name or UCS2(joliet). |
| * - Sort files by each directory. |
| */ |
| r = isoent_traverse_tree(a, &(iso9660->primary)); |
| if (r < 0) |
| return (r); |
| if (iso9660->opt.joliet) { |
| r = isoent_traverse_tree(a, &(iso9660->joliet)); |
| if (r < 0) |
| return (r); |
| } |
| |
| /* Step 2. |
| * - Sort directories. |
| * - Assign all directory number. |
| */ |
| dir_number = 1; |
| for (depth = 0; depth < iso9660->primary.max_depth; depth++) { |
| r = isoent_make_path_table_2(a, &(iso9660->primary), |
| depth, &dir_number); |
| if (r < 0) |
| return (r); |
| } |
| if (iso9660->opt.joliet) { |
| dir_number = 1; |
| for (depth = 0; depth < iso9660->joliet.max_depth; depth++) { |
| r = isoent_make_path_table_2(a, &(iso9660->joliet), |
| depth, &dir_number); |
| if (r < 0) |
| return (r); |
| } |
| } |
| if (iso9660->opt.limit_dirs && dir_number > 0xffff) { |
| /* |
| * Maximum number of directories is 65535(0xffff) |
| * doe to size(16bit) of Parent Directory Number of |
| * the Path Table. |
| * See also ISO9660 Standard 9.4. |
| */ |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Too many directories(%d) over 65535.", dir_number); |
| return (ARCHIVE_FATAL); |
| } |
| |
| /* Get the size of the Path Table. */ |
| calculate_path_table_size(&(iso9660->primary)); |
| if (iso9660->opt.joliet) |
| calculate_path_table_size(&(iso9660->joliet)); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| |
| /* Find a isoent of the boot file. */ |
| iso9660->el_torito.boot = isoent_find_entry(rootent, |
| iso9660->el_torito.boot_filename.s); |
| if (iso9660->el_torito.boot == NULL) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Can't find the boot image file ``%s''", |
| iso9660->el_torito.boot_filename.s); |
| return (ARCHIVE_FATAL); |
| } |
| iso9660->el_torito.boot->file->boot = BOOT_IMAGE; |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file; |
| struct isoent *isoent; |
| struct archive_entry *entry; |
| |
| (void)rootent; /* UNUSED */ |
| /* |
| * Create the entry which is the "boot.catalog" file. |
| */ |
| file = isofile_new(a, NULL); |
| if (file == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| archive_entry_set_pathname(file->entry, |
| iso9660->el_torito.catalog_filename.s); |
| archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE); |
| archive_entry_set_mtime(file->entry, iso9660->birth_time, 0); |
| archive_entry_set_atime(file->entry, iso9660->birth_time, 0); |
| archive_entry_set_ctime(file->entry, iso9660->birth_time, 0); |
| archive_entry_set_uid(file->entry, getuid()); |
| archive_entry_set_gid(file->entry, getgid()); |
| archive_entry_set_mode(file->entry, AE_IFREG | 0444); |
| archive_entry_set_nlink(file->entry, 1); |
| |
| if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) { |
| isofile_free(file); |
| return (ARCHIVE_FATAL); |
| } |
| file->boot = BOOT_CATALOG; |
| file->content.size = LOGICAL_BLOCK_SIZE; |
| isofile_add_entry(iso9660, file); |
| |
| isoent = isoent_new(file); |
| if (isoent == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| isoent->virtual = 1; |
| |
| /* Add the "boot.catalog" entry into tree */ |
| if (isoent_tree(a, &isoent) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| iso9660->el_torito.catalog = isoent; |
| /* |
| * Get a boot media type. |
| */ |
| switch (iso9660->opt.boot_type) { |
| default: |
| case OPT_BOOT_TYPE_AUTO: |
| /* Try detecting a media type of the boot image. */ |
| entry = iso9660->el_torito.boot->file->entry; |
| if (archive_entry_size(entry) == FD_1_2M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_1_2M_DISKETTE; |
| else if (archive_entry_size(entry) == FD_1_44M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_1_44M_DISKETTE; |
| else if (archive_entry_size(entry) == FD_2_88M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_2_88M_DISKETTE; |
| else |
| /* We cannot decide whether the boot image is |
| * hard-disk. */ |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_NO_EMULATION; |
| break; |
| case OPT_BOOT_TYPE_NO_EMU: |
| iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION; |
| break; |
| case OPT_BOOT_TYPE_HARD_DISK: |
| iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK; |
| break; |
| case OPT_BOOT_TYPE_FD: |
| entry = iso9660->el_torito.boot->file->entry; |
| if (archive_entry_size(entry) <= FD_1_2M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_1_2M_DISKETTE; |
| else if (archive_entry_size(entry) <= FD_1_44M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_1_44M_DISKETTE; |
| else if (archive_entry_size(entry) <= FD_2_88M_SIZE) |
| iso9660->el_torito.media_type = |
| BOOT_MEDIA_2_88M_DISKETTE; |
| else { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Boot image file(``%s'') size is too big " |
| "for fd type.", |
| iso9660->el_torito.boot_filename.s); |
| return (ARCHIVE_FATAL); |
| } |
| break; |
| } |
| |
| /* |
| * Get a system type. |
| * TODO: `El Torito' specification says "A copy of byte 5 from the |
| * Partition Table found in the boot image". |
| */ |
| iso9660->el_torito.system_type = 0; |
| |
| /* |
| * Get an ID. |
| */ |
| if (iso9660->opt.publisher) |
| archive_string_copy(&(iso9660->el_torito.id), |
| &(iso9660->publisher_identifier)); |
| |
| |
| return (ARCHIVE_OK); |
| } |
| |
| /* |
| * If a media type is floppy, return its image size. |
| * otherwise return 0. |
| */ |
| static size_t |
| fd_boot_image_size(int media_type) |
| { |
| switch (media_type) { |
| case BOOT_MEDIA_1_2M_DISKETTE: |
| return (FD_1_2M_SIZE); |
| case BOOT_MEDIA_1_44M_DISKETTE: |
| return (FD_1_44M_SIZE); |
| case BOOT_MEDIA_2_88M_DISKETTE: |
| return (FD_2_88M_SIZE); |
| default: |
| return (0); |
| } |
| } |
| |
| /* |
| * Make a boot catalog image data. |
| */ |
| static int |
| make_boot_catalog(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| unsigned char *block; |
| unsigned char *p; |
| uint16_t sum, *wp; |
| |
| block = wb_buffptr(a); |
| memset(block, 0, LOGICAL_BLOCK_SIZE); |
| p = block; |
| /* |
| * Validation Entry |
| */ |
| /* Header ID */ |
| p[0] = 1; |
| /* Platform ID */ |
| p[1] = iso9660->el_torito.platform_id; |
| /* Reserved */ |
| p[2] = p[3] = 0; |
| /* ID */ |
| if (archive_strlen(&(iso9660->el_torito.id)) > 0) |
| strncpy((char *)p+4, iso9660->el_torito.id.s, 23); |
| p[27] = 0; |
| /* Checksum */ |
| p[28] = p[29] = 0; |
| /* Key */ |
| p[30] = 0x55; |
| p[31] = 0xAA; |
| |
| sum = 0; |
| wp = (uint16_t *)block; |
| while (wp < (uint16_t *)&block[32]) |
| sum += archive_le16dec(wp++); |
| set_num_721(&block[28], (~sum) + 1); |
| |
| /* |
| * Initial/Default Entry |
| */ |
| p = &block[32]; |
| /* Boot Indicator */ |
| p[0] = 0x88; |
| /* Boot media type */ |
| p[1] = iso9660->el_torito.media_type; |
| /* Load Segment */ |
| if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) |
| set_num_721(&p[2], iso9660->el_torito.boot_load_seg); |
| else |
| set_num_721(&p[2], 0); |
| /* System Type */ |
| p[4] = iso9660->el_torito.system_type; |
| /* Unused */ |
| p[5] = 0; |
| /* Sector Count */ |
| if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION) |
| set_num_721(&p[6], iso9660->el_torito.boot_load_size); |
| else |
| set_num_721(&p[6], 1); |
| /* Load RBA */ |
| set_num_731(&p[8], |
| iso9660->el_torito.boot->file->content.location); |
| /* Unused */ |
| memset(&p[12], 0, 20); |
| |
| return (wb_consume(a, LOGICAL_BLOCK_SIZE)); |
| } |
| |
| static int |
| setup_boot_information(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isoent *np; |
| int64_t size; |
| uint32_t sum; |
| unsigned char buff[4096]; |
| |
| np = iso9660->el_torito.boot; |
| lseek(iso9660->temp_fd, |
| np->file->content.offset_of_temp + 64, SEEK_SET); |
| size = archive_entry_size(np->file->entry) - 64; |
| if (size <= 0) { |
| archive_set_error(&a->archive, errno, |
| "Boot file(%jd) is too small", (intmax_t)size + 64); |
| return (ARCHIVE_FATAL); |
| } |
| sum = 0; |
| while (size > 0) { |
| size_t rsize; |
| ssize_t i, rs; |
| |
| if (size > (int64_t)sizeof(buff)) |
| rsize = sizeof(buff); |
| else |
| rsize = (size_t)size; |
| |
| rs = read(iso9660->temp_fd, buff, rsize); |
| if (rs <= 0) { |
| archive_set_error(&a->archive, errno, |
| "Can't read temporary file(%jd)", |
| (intmax_t)rs); |
| return (ARCHIVE_FATAL); |
| } |
| for (i = 0; i < rs; i += 4) |
| sum += archive_le32dec(buff + i); |
| size -= rs; |
| } |
| /* Set the location of Primary Volume Descriptor. */ |
| set_num_731(buff, SYSTEM_AREA_BLOCK); |
| /* Set the location of the boot file. */ |
| set_num_731(buff+4, np->file->content.location); |
| /* Set the size of the boot file. */ |
| size = fd_boot_image_size(iso9660->el_torito.media_type); |
| if (size == 0) |
| size = archive_entry_size(np->file->entry); |
| set_num_731(buff+8, (uint32_t)size); |
| /* Set the sum of the boot file. */ |
| set_num_731(buff+12, sum); |
| /* Clear reserved bytes. */ |
| memset(buff+16, 0, 40); |
| |
| /* Overwrite the boot file. */ |
| lseek(iso9660->temp_fd, |
| np->file->content.offset_of_temp + 8, SEEK_SET); |
| return (write_to_temp(a, buff, 56)); |
| } |
| |
| #ifdef HAVE_ZLIB_H |
| |
| static int |
| zisofs_init_zstream(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| int r; |
| |
| iso9660->zisofs.stream.next_in = NULL; |
| iso9660->zisofs.stream.avail_in = 0; |
| iso9660->zisofs.stream.total_in = 0; |
| iso9660->zisofs.stream.total_out = 0; |
| if (iso9660->zisofs.stream_valid) |
| r = deflateReset(&(iso9660->zisofs.stream)); |
| else { |
| r = deflateInit(&(iso9660->zisofs.stream), |
| iso9660->zisofs.compression_level); |
| iso9660->zisofs.stream_valid = 1; |
| } |
| switch (r) { |
| case Z_OK: |
| break; |
| default: |
| case Z_STREAM_ERROR: |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Internal error initializing " |
| "compression library: invalid setup parameter"); |
| return (ARCHIVE_FATAL); |
| case Z_MEM_ERROR: |
| archive_set_error(&a->archive, ENOMEM, |
| "Internal error initializing " |
| "compression library"); |
| return (ARCHIVE_FATAL); |
| case Z_VERSION_ERROR: |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Internal error initializing " |
| "compression library: invalid library version"); |
| return (ARCHIVE_FATAL); |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| #endif /* HAVE_ZLIB_H */ |
| |
| static int |
| zisofs_init(struct archive_write *a, struct isofile *file) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| #ifdef HAVE_ZLIB_H |
| uint64_t tsize; |
| size_t _ceil, bpsize; |
| int r; |
| #endif |
| |
| iso9660->zisofs.detect_magic = 0; |
| iso9660->zisofs.making = 0; |
| |
| if (!iso9660->opt.rr || !iso9660->opt.zisofs) |
| return (ARCHIVE_OK); |
| |
| if (archive_entry_size(file->entry) >= 24 && |
| archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) { |
| /* Acceptable file size for zisofs. */ |
| iso9660->zisofs.detect_magic = 1; |
| iso9660->zisofs.magic_cnt = 0; |
| } |
| if (!iso9660->zisofs.detect_magic) |
| return (ARCHIVE_OK); |
| |
| #ifdef HAVE_ZLIB_H |
| /* The number of Logical Blocks which uncompressed data |
| * will use in iso-image file is the same as the number of |
| * Logical Blocks which zisofs(compressed) data will use |
| * in ISO-image file. It won't reduce iso-image file size. */ |
| if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE) |
| return (ARCHIVE_OK); |
| |
| /* Initialize compression library */ |
| r = zisofs_init_zstream(a); |
| if (r != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */ |
| file->zisofs.header_size = ZF_HEADER_SIZE >> 2; |
| file->zisofs.log2_bs = ZF_LOG2_BS; |
| file->zisofs.uncompressed_size = |
| (uint32_t)archive_entry_size(file->entry); |
| |
| /* Calculate a size of Block Pointers of zisofs. */ |
| _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) |
| >> file->zisofs.log2_bs; |
| iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; |
| iso9660->zisofs.block_pointers_idx = 0; |
| |
| /* Ensure a buffer size used for Block Pointers */ |
| bpsize = iso9660->zisofs.block_pointers_cnt * |
| sizeof(iso9660->zisofs.block_pointers[0]); |
| if (iso9660->zisofs.block_pointers_allocated < bpsize) { |
| free(iso9660->zisofs.block_pointers); |
| iso9660->zisofs.block_pointers = malloc(bpsize); |
| if (iso9660->zisofs.block_pointers == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "Can't allocate data"); |
| return (ARCHIVE_FATAL); |
| } |
| iso9660->zisofs.block_pointers_allocated = bpsize; |
| } |
| |
| /* |
| * Skip zisofs header and Block Pointers, which we will write |
| * after all compressed data of a file written to the temporary |
| * file. |
| */ |
| tsize = ZF_HEADER_SIZE + bpsize; |
| if (write_null(a, (size_t)tsize) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* |
| * Initialize some variables to make zisofs. |
| */ |
| archive_le32enc(&(iso9660->zisofs.block_pointers[0]), |
| (uint32_t)tsize); |
| iso9660->zisofs.remaining = file->zisofs.uncompressed_size; |
| iso9660->zisofs.making = 1; |
| iso9660->zisofs.allzero = 1; |
| iso9660->zisofs.block_offset = tsize; |
| iso9660->zisofs.total_size = tsize; |
| iso9660->cur_file->cur_content->size = tsize; |
| #endif |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static void |
| zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file = iso9660->cur_file; |
| const unsigned char *p, *endp; |
| const unsigned char *magic_buff; |
| uint32_t uncompressed_size; |
| unsigned char header_size; |
| unsigned char log2_bs; |
| size_t _ceil, doff; |
| uint32_t bst, bed; |
| int magic_max; |
| int64_t entry_size; |
| |
| entry_size = archive_entry_size(file->entry); |
| if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size) |
| magic_max = (int)entry_size; |
| else |
| magic_max = sizeof(iso9660->zisofs.magic_buffer); |
| |
| if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max) |
| /* It's unnecessary we copy buffer. */ |
| magic_buff = buff; |
| else { |
| if (iso9660->zisofs.magic_cnt < magic_max) { |
| size_t l; |
| |
| l = sizeof(iso9660->zisofs.magic_buffer) |
| - iso9660->zisofs.magic_cnt; |
| if (l > s) |
| l = s; |
| memcpy(iso9660->zisofs.magic_buffer |
| + iso9660->zisofs.magic_cnt, buff, l); |
| iso9660->zisofs.magic_cnt += (int)l; |
| if (iso9660->zisofs.magic_cnt < magic_max) |
| return; |
| } |
| magic_buff = iso9660->zisofs.magic_buffer; |
| } |
| iso9660->zisofs.detect_magic = 0; |
| p = magic_buff; |
| |
| /* Check the magic code of zisofs. */ |
| if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) |
| /* This is not zisofs file which made by mkzftree. */ |
| return; |
| p += sizeof(zisofs_magic); |
| |
| /* Read a zisofs header. */ |
| uncompressed_size = archive_le32dec(p); |
| header_size = p[4]; |
| log2_bs = p[5]; |
| if (uncompressed_size < 24 || header_size != 4 || |
| log2_bs > 30 || log2_bs < 7) |
| return;/* Invalid or not supported header. */ |
| |
| /* Calculate a size of Block Pointers of zisofs. */ |
| _ceil = (uncompressed_size + |
| (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs; |
| doff = (_ceil + 1) * 4 + 16; |
| if (entry_size < (int64_t)doff) |
| return;/* Invalid data. */ |
| |
| /* Check every Block Pointer has valid value. */ |
| p = magic_buff + 16; |
| endp = magic_buff + magic_max; |
| while (_ceil && p + 8 <= endp) { |
| bst = archive_le32dec(p); |
| if (bst != doff) |
| return;/* Invalid data. */ |
| p += 4; |
| bed = archive_le32dec(p); |
| if (bed < bst || bed > entry_size) |
| return;/* Invalid data. */ |
| doff += bed - bst; |
| _ceil--; |
| } |
| |
| file->zisofs.uncompressed_size = uncompressed_size; |
| file->zisofs.header_size = header_size; |
| file->zisofs.log2_bs = log2_bs; |
| |
| /* Disable making a zisofs image. */ |
| iso9660->zisofs.making = 0; |
| } |
| |
| #ifdef HAVE_ZLIB_H |
| |
| /* |
| * Compress data and write it to a temporary file. |
| */ |
| static int |
| zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file = iso9660->cur_file; |
| const unsigned char *b; |
| z_stream *zstrm; |
| size_t avail, csize; |
| int flush, r; |
| |
| zstrm = &(iso9660->zisofs.stream); |
| zstrm->next_out = wb_buffptr(a); |
| zstrm->avail_out = (uInt)wb_remaining(a); |
| b = (const unsigned char *)buff; |
| do { |
| avail = ZF_BLOCK_SIZE - zstrm->total_in; |
| if (s < avail) { |
| avail = s; |
| flush = Z_NO_FLUSH; |
| } else |
| flush = Z_FINISH; |
| iso9660->zisofs.remaining -= avail; |
| if (iso9660->zisofs.remaining <= 0) |
| flush = Z_FINISH; |
| |
| zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b; |
| zstrm->avail_in = (uInt)avail; |
| |
| /* |
| * Check if current data block are all zero. |
| */ |
| if (iso9660->zisofs.allzero) { |
| const unsigned char *nonzero = b; |
| const unsigned char *nonzeroend = b + avail; |
| |
| while (nonzero < nonzeroend) |
| if (*nonzero++) { |
| iso9660->zisofs.allzero = 0; |
| break; |
| } |
| } |
| b += avail; |
| s -= avail; |
| |
| /* |
| * If current data block are all zero, we do not use |
| * compressed data. |
| */ |
| if (flush == Z_FINISH && iso9660->zisofs.allzero && |
| avail + zstrm->total_in == ZF_BLOCK_SIZE) { |
| if (iso9660->zisofs.block_offset != |
| file->cur_content->size) { |
| int64_t diff; |
| |
| r = wb_set_offset(a, |
| file->cur_content->offset_of_temp + |
| iso9660->zisofs.block_offset); |
| if (r != ARCHIVE_OK) |
| return (r); |
| diff = file->cur_content->size - |
| iso9660->zisofs.block_offset; |
| file->cur_content->size -= diff; |
| iso9660->zisofs.total_size -= diff; |
| } |
| zstrm->avail_in = 0; |
| } |
| |
| /* |
| * Compress file data. |
| */ |
| while (zstrm->avail_in > 0) { |
| csize = zstrm->total_out; |
| r = deflate(zstrm, flush); |
| switch (r) { |
| case Z_OK: |
| case Z_STREAM_END: |
| csize = zstrm->total_out - csize; |
| if (wb_consume(a, csize) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->zisofs.total_size += csize; |
| iso9660->cur_file->cur_content->size += csize; |
| zstrm->next_out = wb_buffptr(a); |
| zstrm->avail_out = (uInt)wb_remaining(a); |
| break; |
| default: |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_MISC, |
| "Compression failed:" |
| " deflate() call returned status %d", |
| r); |
| return (ARCHIVE_FATAL); |
| } |
| } |
| |
| if (flush == Z_FINISH) { |
| /* |
| * Save the information of one zisofs block. |
| */ |
| iso9660->zisofs.block_pointers_idx ++; |
| archive_le32enc(&(iso9660->zisofs.block_pointers[ |
| iso9660->zisofs.block_pointers_idx]), |
| (uint32_t)iso9660->zisofs.total_size); |
| r = zisofs_init_zstream(a); |
| if (r != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| iso9660->zisofs.allzero = 1; |
| iso9660->zisofs.block_offset = file->cur_content->size; |
| } |
| } while (s); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| zisofs_finish_entry(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file = iso9660->cur_file; |
| unsigned char buff[16]; |
| size_t s; |
| int64_t tail; |
| |
| /* Direct temp file stream to zisofs temp file stream. */ |
| archive_entry_set_size(file->entry, iso9660->zisofs.total_size); |
| |
| /* |
| * Save a file pointer which points the end of current zisofs data. |
| */ |
| tail = wb_offset(a); |
| |
| /* |
| * Make a header. |
| * |
| * +-----------------+----------------+-----------------+ |
| * | Header 16 bytes | Block Pointers | Compressed data | |
| * +-----------------+----------------+-----------------+ |
| * 0 16 +X |
| * Block Pointers : |
| * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1) |
| * |
| * Write zisofs header. |
| * Magic number |
| * +----+----+----+----+----+----+----+----+ |
| * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 | |
| * +----+----+----+----+----+----+----+----+ |
| * 0 1 2 3 4 5 6 7 8 |
| * |
| * +------------------------+------------------+ |
| * | Uncompressed file size | header_size >> 2 | |
| * +------------------------+------------------+ |
| * 8 12 13 |
| * |
| * +-----------------+----------------+ |
| * | log2 block_size | Reserved(0000) | |
| * +-----------------+----------------+ |
| * 13 14 16 |
| */ |
| memcpy(buff, zisofs_magic, 8); |
| set_num_731(buff+8, file->zisofs.uncompressed_size); |
| buff[12] = file->zisofs.header_size; |
| buff[13] = file->zisofs.log2_bs; |
| buff[14] = buff[15] = 0;/* Reserved */ |
| |
| /* Move to the right position to write the header. */ |
| wb_set_offset(a, file->content.offset_of_temp); |
| |
| /* Write the header. */ |
| if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* |
| * Write zisofs Block Pointers. |
| */ |
| s = iso9660->zisofs.block_pointers_cnt * |
| sizeof(iso9660->zisofs.block_pointers[0]); |
| if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s) |
| != ARCHIVE_OK) |
| return (ARCHIVE_FATAL); |
| |
| /* Set a file pointer back to the end of the temporary file. */ |
| wb_set_offset(a, tail); |
| |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| zisofs_free(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| int ret = ARCHIVE_OK; |
| |
| free(iso9660->zisofs.block_pointers); |
| if (iso9660->zisofs.stream_valid && |
| deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Failed to clean up compressor"); |
| ret = ARCHIVE_FATAL; |
| } |
| iso9660->zisofs.block_pointers = NULL; |
| iso9660->zisofs.stream_valid = 0; |
| return (ret); |
| } |
| |
| struct zisofs_extract { |
| int pz_log2_bs; /* Log2 of block size */ |
| uint64_t pz_uncompressed_size; |
| size_t uncompressed_buffer_size; |
| |
| int initialized:1; |
| int header_passed:1; |
| |
| uint32_t pz_offset; |
| unsigned char *block_pointers; |
| size_t block_pointers_size; |
| size_t block_pointers_avail; |
| size_t block_off; |
| uint32_t block_avail; |
| |
| z_stream stream; |
| int stream_valid; |
| }; |
| |
| static ssize_t |
| zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, |
| const unsigned char *p, size_t bytes) |
| { |
| size_t avail = bytes; |
| size_t _ceil, xsize; |
| |
| /* Allocate block pointers buffer. */ |
| _ceil = (size_t)((zisofs->pz_uncompressed_size + |
| (((int64_t)1) << zisofs->pz_log2_bs) - 1) |
| >> zisofs->pz_log2_bs); |
| xsize = (_ceil + 1) * 4; |
| if (zisofs->block_pointers == NULL) { |
| size_t alloc = ((xsize >> 10) + 1) << 10; |
| zisofs->block_pointers = malloc(alloc); |
| if (zisofs->block_pointers == NULL) { |
| archive_set_error(&a->archive, ENOMEM, |
| "No memory for zisofs decompression"); |
| return (ARCHIVE_FATAL); |
| } |
| } |
| zisofs->block_pointers_size = xsize; |
| |
| /* Allocate uncompressed data buffer. */ |
| zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs; |
| |
| /* |
| * Read the file header, and check the magic code of zisofs. |
| */ |
| if (!zisofs->header_passed) { |
| int err = 0; |
| if (avail < 16) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_FILE_FORMAT, |
| "Illegal zisofs file body"); |
| return (ARCHIVE_FATAL); |
| } |
| |
| if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0) |
| err = 1; |
| else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size) |
| err = 1; |
| else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs) |
| err = 1; |
| if (err) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_FILE_FORMAT, |
| "Illegal zisofs file body"); |
| return (ARCHIVE_FATAL); |
| } |
| avail -= 16; |
| p += 16; |
| zisofs->header_passed = 1; |
| } |
| |
| /* |
| * Read block pointers. |
| */ |
| if (zisofs->header_passed && |
| zisofs->block_pointers_avail < zisofs->block_pointers_size) { |
| xsize = zisofs->block_pointers_size |
| - zisofs->block_pointers_avail; |
| if (avail < xsize) |
| xsize = avail; |
| memcpy(zisofs->block_pointers |
| + zisofs->block_pointers_avail, p, xsize); |
| zisofs->block_pointers_avail += xsize; |
| avail -= xsize; |
| if (zisofs->block_pointers_avail |
| == zisofs->block_pointers_size) { |
| /* We've got all block pointers and initialize |
| * related variables. */ |
| zisofs->block_off = 0; |
| zisofs->block_avail = 0; |
| /* Complete a initialization */ |
| zisofs->initialized = 1; |
| } |
| } |
| return ((ssize_t)avail); |
| } |
| |
| static ssize_t |
| zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, |
| const unsigned char *p, size_t bytes) |
| { |
| size_t avail; |
| int r; |
| |
| if (!zisofs->initialized) { |
| ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes); |
| if (rs < 0) |
| return (rs); |
| if (!zisofs->initialized) { |
| /* We need more data. */ |
| zisofs->pz_offset += (uint32_t)bytes; |
| return (bytes); |
| } |
| avail = rs; |
| p += bytes - avail; |
| } else |
| avail = bytes; |
| |
| /* |
| * Get block offsets from block pointers. |
| */ |
| if (zisofs->block_avail == 0) { |
| uint32_t bst, bed; |
| |
| if (zisofs->block_off + 4 >= zisofs->block_pointers_size) { |
| /* There isn't a pair of offsets. */ |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_FILE_FORMAT, |
| "Illegal zisofs block pointers"); |
| return (ARCHIVE_FATAL); |
| } |
| bst = archive_le32dec( |
| zisofs->block_pointers + zisofs->block_off); |
| if (bst != zisofs->pz_offset + (bytes - avail)) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_FILE_FORMAT, |
| "Illegal zisofs block pointers(cannot seek)"); |
| return (ARCHIVE_FATAL); |
| } |
| bed = archive_le32dec( |
| zisofs->block_pointers + zisofs->block_off + 4); |
| if (bed < bst) { |
| archive_set_error(&a->archive, |
| ARCHIVE_ERRNO_FILE_FORMAT, |
| "Illegal zisofs block pointers"); |
| return (ARCHIVE_FATAL); |
| } |
| zisofs->block_avail = bed - bst; |
| zisofs->block_off += 4; |
| |
| /* Initialize compression library for new block. */ |
| if (zisofs->stream_valid) |
| r = inflateReset(&zisofs->stream); |
| else |
| r = inflateInit(&zisofs->stream); |
| if (r != Z_OK) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Can't initialize zisofs decompression."); |
| return (ARCHIVE_FATAL); |
| } |
| zisofs->stream_valid = 1; |
| zisofs->stream.total_in = 0; |
| zisofs->stream.total_out = 0; |
| } |
| |
| /* |
| * Make uncompressed data. |
| */ |
| if (zisofs->block_avail == 0) { |
| /* |
| * It's basically 32K bytes NUL data. |
| */ |
| unsigned char *wb; |
| size_t size, wsize; |
| |
| size = zisofs->uncompressed_buffer_size; |
| while (size) { |
| wb = wb_buffptr(a); |
| if (size > wb_remaining(a)) |
| wsize = wb_remaining(a); |
| else |
| wsize = size; |
| memset(wb, 0, wsize); |
| r = wb_consume(a, wsize); |
| if (r < 0) |
| return (r); |
| size -= wsize; |
| } |
| } else { |
| zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; |
| if (avail > zisofs->block_avail) |
| zisofs->stream.avail_in = zisofs->block_avail; |
| else |
| zisofs->stream.avail_in = (uInt)avail; |
| zisofs->stream.next_out = wb_buffptr(a); |
| zisofs->stream.avail_out = (uInt)wb_remaining(a); |
| |
| r = inflate(&zisofs->stream, 0); |
| switch (r) { |
| case Z_OK: /* Decompressor made some progress.*/ |
| case Z_STREAM_END: /* Found end of stream. */ |
| break; |
| default: |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "zisofs decompression failed (%d)", r); |
| return (ARCHIVE_FATAL); |
| } |
| avail -= zisofs->stream.next_in - p; |
| zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); |
| r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out); |
| if (r < 0) |
| return (r); |
| } |
| zisofs->pz_offset += (uint32_t)bytes; |
| return (bytes - avail); |
| } |
| |
| static int |
| zisofs_rewind_boot_file(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| struct isofile *file; |
| unsigned char *rbuff; |
| ssize_t r; |
| size_t remaining, rbuff_size; |
| struct zisofs_extract zext; |
| int64_t read_offset, write_offset, new_offset; |
| int fd, ret = ARCHIVE_OK; |
| |
| file = iso9660->el_torito.boot->file; |
| /* |
| * There is nothing to do if this boot file does not have |
| * zisofs header. |
| */ |
| if (file->zisofs.header_size == 0) |
| return (ARCHIVE_OK); |
| |
| /* |
| * Uncompress the zisofs'ed file contents. |
| */ |
| memset(&zext, 0, sizeof(zext)); |
| zext.pz_uncompressed_size = file->zisofs.uncompressed_size; |
| zext.pz_log2_bs = file->zisofs.log2_bs; |
| |
| fd = iso9660->temp_fd; |
| new_offset = wb_offset(a); |
| read_offset = file->content.offset_of_temp; |
| remaining = (size_t)file->content.size; |
| if (remaining > 1024 * 32) |
| rbuff_size = 1024 * 32; |
| else |
| rbuff_size = remaining; |
| |
| rbuff = malloc(rbuff_size); |
| if (rbuff == NULL) { |
| archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); |
| return (ARCHIVE_FATAL); |
| } |
| while (remaining) { |
| size_t rsize; |
| ssize_t rs; |
| |
| /* Get the current file pointer. */ |
| write_offset = lseek(fd, 0, SEEK_CUR); |
| |
| /* Change the file pointer to read. */ |
| lseek(fd, read_offset, SEEK_SET); |
| |
| rsize = rbuff_size; |
| if (rsize > remaining) |
| rsize = remaining; |
| rs = read(iso9660->temp_fd, rbuff, rsize); |
| if (rs <= 0) { |
| archive_set_error(&a->archive, errno, |
| "Can't read temporary file(%jd)", (intmax_t)rs); |
| ret = ARCHIVE_FATAL; |
| break; |
| } |
| remaining -= rs; |
| read_offset += rs; |
| |
| /* Put the file pointer back to write. */ |
| lseek(fd, write_offset, SEEK_SET); |
| |
| r = zisofs_extract(a, &zext, rbuff, rs); |
| if (r < 0) { |
| ret = (int)r; |
| break; |
| } |
| } |
| |
| if (ret == ARCHIVE_OK) { |
| /* |
| * Change the boot file content from zisofs'ed data |
| * to plain data. |
| */ |
| file->content.offset_of_temp = new_offset; |
| file->content.size = file->zisofs.uncompressed_size; |
| archive_entry_set_size(file->entry, file->content.size); |
| /* Set to be no zisofs. */ |
| file->zisofs.header_size = 0; |
| file->zisofs.log2_bs = 0; |
| file->zisofs.uncompressed_size = 0; |
| r = wb_write_padding_to_temp(a, file->content.size); |
| if (r < 0) |
| ret = ARCHIVE_FATAL; |
| } |
| |
| /* |
| * Free the resource we used in this function only. |
| */ |
| free(rbuff); |
| free(zext.block_pointers); |
| if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Failed to clean up compressor"); |
| ret = ARCHIVE_FATAL; |
| } |
| |
| return (ret); |
| } |
| |
| #else |
| |
| static int |
| zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) |
| { |
| (void)buff; /* UNUSED */ |
| (void)s; /* UNUSED */ |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programing error"); |
| return (ARCHIVE_FATAL); |
| } |
| |
| static int |
| zisofs_rewind_boot_file(struct archive_write *a) |
| { |
| struct iso9660 *iso9660 = a->format_data; |
| |
| if (iso9660->el_torito.boot->file->zisofs.header_size != 0) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "We cannot extract the zisofs imaged boot file;" |
| " this may not boot in being zisofs imaged"); |
| return (ARCHIVE_FAILED); |
| } |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| zisofs_finish_entry(struct archive_write *a) |
| { |
| (void)a; /* UNUSED */ |
| return (ARCHIVE_OK); |
| } |
| |
| static int |
| zisofs_free(struct archive_write *a) |
| { |
| (void)a; /* UNUSED */ |
| return (ARCHIVE_OK); |
| } |
| |
| #endif /* HAVE_ZLIB_H */ |
| |