blob: c0ca435d15c7421007f6b468d2f7180f66c6d527 [file] [log] [blame]
/*-
* 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) {
/*