| /*- |
| * 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 <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) { |
| /* |
|