Initial import

This commit is contained in:
Mario Danic
2006-08-15 20:37:04 +00:00
commit 2f2c115e08
96 changed files with 15010 additions and 0 deletions

4
libisofs/Makefile Executable file
View File

@ -0,0 +1,4 @@
all clean:
$(MAKE) -C .. -$(MAKEFLAGS) $@
.PHONY: all clean

44
libisofs/Makefile.am Executable file
View File

@ -0,0 +1,44 @@
pkgconfigdir=$(libdir)/pkgconfig
libincludedir=$(includedir)/libburn/@BURN_MAJOR_VERSION@
lib_LTLIBRARIES = libisofs.la
libisofs_la_SOURCES = \
errors.h \
errors.c \
tree.h \
tree.c \
volume.h \
volume.c \
util.h \
util.c \
ecma119.c \
ecma119.h \
struct.h \
struct.c \
susp.h \
susp.c \
rockridge.h \
rockridge.c
libinclude_HEADERS = libisofs.h
noinst_PROGRAMS = test
test_SOURCES = test.c
test_LDADD = $(libisofs_la_OBJECTS)
INCLUDES = -I..
## ========================================================================= ##
indent_files = $(libisofs_la_SOURCES)
indent: $(indent_files)
indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \
-cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \
-lp -saf -sai -nprs -npsl -saw -sob -ss -ut \
-sbi0 -nsc -ts8 -npcs -ncdb -fca \
$^
.PHONY: indent
## ========================================================================= ##

1506
libisofs/ecma119.c Executable file

File diff suppressed because it is too large Load Diff

220
libisofs/ecma119.h Executable file
View File

@ -0,0 +1,220 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file ecma119.h
*
* Structures and definitions used for writing an emca119 (ISO9660) compatible
* volume.
*/
#ifndef __ISO_ECMA119
#define __ISO_ECMA119
#include <stdio.h>
#include <sys/time.h>
#include "susp.h"
/**
* Persistent data for writing directories according to the ecma119 standard.
*/
struct dir_write_info
{
struct susp_info susp; /**< \see node_write_info */
struct susp_info self_susp; /**< SUSP data for this directory's
* "." entry.
*/
struct susp_info parent_susp; /**< SUSP data for this directory's
* ".." entry.
*/
int len; /**< The combined length of all children's
* Directory Record lengths. This includes
* the System Use areas.
*/
int susp_len; /**< The combined length of all children's
* SUSP Continuation Areas.
*/
/* the parent/child information prior to relocation */
struct iso_tree_dir *real_parent;
int real_nchildren;
struct iso_tree_dir **real_children;
int real_depth;
/* joliet information */
int joliet_block; /**< The block at which the Joliet version of
* this directory will be written.
*/
int joliet_len; /**< The combined length of all children's
* Joliet Directory Record lengths.
*/
};
/**
* Persistent data for writing files according to the ecma119 standard.
*/
struct file_write_info
{
struct susp_info susp; /**< \see node_write_info */
struct iso_tree_dir *real_me; /**< If this is non-NULL, the file is
* a placeholder for a relocated
* directory and this field points to
* that relocated directory.
*/
};
/**
* The fields in common between file_write_info and dir_write_info.
*/
struct node_write_info
{
struct susp_info susp; /**< The SUSP data for this file. */
};
/**
* The possible states that the ecma119 writer can be in.
*/
enum ecma119_write_state
{
ECMA119_WRITE_BEFORE,
ECMA119_WRITE_SYSTEM_AREA,
ECMA119_WRITE_PRI_VOL_DESC,
ECMA119_WRITE_SUP_VOL_DESC_JOLIET,
ECMA119_WRITE_VOL_DESC_TERMINATOR,
ECMA119_WRITE_L_PATH_TABLE,
ECMA119_WRITE_M_PATH_TABLE,
ECMA119_WRITE_L_PATH_TABLE_JOLIET,
ECMA119_WRITE_M_PATH_TABLE_JOLIET,
ECMA119_WRITE_DIR_RECORDS,
ECMA119_WRITE_DIR_RECORDS_JOLIET,
ECMA119_WRITE_FILES,
ECMA119_WRITE_DONE
};
/**
* Data describing the state of the ecma119 writer. Everything here should be
* considered private!
*/
struct ecma119_write_target
{
struct iso_volset *volset;
int volnum;
time_t now; /**< Time at which writing began. */
int total_size; /**< Total size of the output. This only
* includes the current volume. */
uint32_t vol_space_size;
unsigned int rockridge:1;
unsigned int joliet:1;
unsigned int iso_level:2;
int curblock;
uint16_t block_size;
uint32_t path_table_size;
uint32_t path_table_size_joliet;
uint32_t l_path_table_pos;
uint32_t m_path_table_pos;
uint32_t l_path_table_pos_joliet;
uint32_t m_path_table_pos_joliet;
struct iso_tree_dir **dirlist; /* A pre-order list of directories
* (this is the order in which we write
* out directory records).
*/
struct iso_tree_dir **pathlist; /* A breadth-first list of directories.
* This is used for writing out the path
* tables.
*/
int dirlist_len; /* The length of the previous 2 lists.
*/
struct iso_tree_file **filelist;/* A pre-order list of files with
* non-NULL paths and non-zero sizes.
*/
int filelist_len; /* Length of the previous list. */
int curfile; /* Used as a helper field for writing
out filelist and dirlist */
/* Joliet versions of the above lists. Since Joliet doesn't require
* directory relocation, the order of these list might be different from
* the lists above. */
struct iso_tree_dir **dirlist_joliet;
struct iso_tree_dir **pathlist_joliet;
enum ecma119_write_state state; /* The current state of the writer. */
/* persistent data for the various states. Each struct should not be
* touched except for the writer of the relevant stage. When the writer
* of the relevant stage is finished, it should set all fields to 0.
*/
union
{
struct
{
int blocks;
unsigned char *data;
} path_table;
struct
{
size_t pos; /* The number of bytes we have written
* so far in the current directory.
*/
size_t data_len;/* The number of bytes in the current
* directory.
*/
unsigned char *data; /* The data (combined Directory
* Records and susp_CE areas) of the
* current directory.
*/
int dir; /* The index in dirlist that we are
* currently writing. */
} dir_records;
struct
{
size_t pos; /* The number of bytes we have written
* so far in the current file.
*/
size_t data_len;/* The number of bytes in the currently
* open file.
*/
FILE *fd; /* The currently open file. */
int file; /* The index in filelist that we are
* currently writing. */
} files;
} state_data;
};
/**
* Create a new ecma119_write_target from the given volume number of the
* given volume set.
*
* \pre \p volnum is less than \p volset-\>volset_size.
* \post For each node in the tree, writer_data has been allocated.
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
*/
struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
int volnum);
/** Macros to help with casting between node_write_info and dir/file_write_info.
*/
#define DIR_INF(a) ( (struct dir_write_info*) (a) )
#define FILE_INF(a) ( (struct file_write_info*) (a) )
#define NODE_INF(a) ( (struct node_write_info*) (a) )
#define GET_DIR_INF(a) ( (struct dir_write_info*) (a)->writer_data )
#define GET_FILE_INF(a) ( (struct file_write_info*) (a)->writer_data )
#define GET_NODE_INF(a) ( (struct node_write_info*) (a)->writer_data )
#define TARGET_ROOT(t) ( (t)->volset->volume[(t)->volnum]->root )
#define NODE_NAMELEN(n,i) strlen(iso_tree_node_get_name(ISO_NODE(n), i))
#define NODE_JOLLEN(n) ucslen(iso_tree_node_get_name(ISO_NODE(n), \
ISO_NAME_JOLIET))
#endif /* __ISO_ECMA119 */

14
libisofs/errors.c Executable file
View File

@ -0,0 +1,14 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#include "errors.h"
#include <stdio.h>
void iso_warn(enum iso_warnings w)
{
printf("WARNING: %u\n", w);
}
void iso_error(enum iso_errors e)
{
printf("ERROR: %u\n", e);
}

19
libisofs/errors.h Executable file
View File

@ -0,0 +1,19 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#ifndef __ERRORS
#define __ERRORS
enum iso_warnings
{
ISO_WARNING_FOO
};
enum iso_errors
{
ISO_ERROR_FOO
};
void iso_warn(enum iso_warnings w);
void iso_error(enum iso_errors e);
#endif /* __ERRORS */

243
libisofs/libisofs.h Executable file
View File

@ -0,0 +1,243 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
* Usage is easy:
* - Create a new volume.
* - Add files and directories.
* - Write the volume to a file or create a burn source for use with Libburn.
*/
#ifndef __LIBISOFS
#define __LIBISOFS
#include "libburn/libburn.h"
/**
* Data volume.
* @see volume.h for details.
*/
struct iso_volume;
/**
* A set of data volumes.
* @see volume.h for details.
*/
struct iso_volset;
/**
* Directory on a volume.
* @see tree.h for details.
*/
struct iso_tree_dir;
/**
* File on a volume.
* @see tree.h for details.
*/
struct iso_tree_file;
/**
* Either a file or a directory.
* \see tree.h
*/
struct iso_tree_node;
/**
* Possible versions of a file or directory name or identifier.
*/
enum iso_name_version {
ISO_NAME_FULL, /**< In the current locale. */
ISO_NAME_ISO, /**< Current ISO level identifier. */
ISO_NAME_ISO_L1, /**< ISO level 1 identifier. */
ISO_NAME_ISO_L2, /**< ISO level 2 identifier. */
ISO_NAME_ROCKRIDGE, /**< Rock Ridge file or directory name. */
ISO_NAME_JOLIET /**< Joliet identifier. */
};
enum ecma119_extension_flag {
ECMA119_ROCKRIDGE = (1<<0),
ECMA119_JOLIET = (1<<1)
};
/**
* Create a new volume.
* The parameters can be set to NULL if you wish to set them later.
*/
struct iso_volume *iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id);
/**
* Free a volume.
*/
void iso_volume_free(struct iso_volume *volume);
/**
* Get the root directory for a volume.
*/
struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume);
/**
* Fill in the volume identifier for a volume.
*/
void iso_volume_set_volume_id(struct iso_volume *volume,
const char *volume_id);
/**
* Fill in the publisher for a volume.
*/
void iso_volume_set_publisher_id(struct iso_volume *volume,
const char *publisher_id);
/**
* Fill in the data preparer for a volume.
*/
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
const char *data_preparer_id);
/**
* Get the current ISO level for a volume.
*/
int iso_volume_get_iso_level(const struct iso_volume *volume);
/**
* Set the current ISO level for a volume.
* ISO level must be 1 or 2.
*/
void iso_volume_set_iso_level(struct iso_volume *volume, int level);
/**
* See if Rock Ridge (POSIX) is enabled for a volume.
*/
int iso_volume_get_rockridge(const struct iso_volume *volume);
/**
* Enable or disable Rock Ridge (POSIX) for a volume.
*/
void iso_volume_set_rockridge(struct iso_volume *volume, int rockridge);
/**
* See if Joliet (Unicode) is enabled for a volume.
*/
int iso_volume_get_joliet(const struct iso_volume *volume);
/**
* Enable or disable Joliet (Unicode) for a volume.
*/
void iso_volume_set_joliet(struct iso_volume *volume, int joliet);
/**
* Create a new Volume Set consisting of only one volume.
* @param volume The first and only volume for the volset to contain.
* @param volset_id The Volume Set ID.
* @return A new iso_volset.
*/
struct iso_volset *iso_volset_new(struct iso_volume *volume,
const char *volset_id);
/**
* Add a file to a directory.
*
* \param path The path, on the local filesystem, of the file.
*
* \pre \p parent is non-NULL
* \pre \p path is non-NULL and is a valid path to a non-directory on the local
* filesystem.
* \return An iso_tree_file whose path is \p path and whose parent is \p parent.
*/
struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent,
const char *path);
/**
* Add a directory from the local filesystem to the tree.
* Warning: this only adds the directory itself, no files or subdirectories.
*
* \param path The path, on the local filesystem, of the directory.
*
* \pre \p parent is non-NULL
* \pre \p path is non-NULL and is a valid path to a directory on the local
* filesystem.
* \return a pointer to the newly created directory.
*/
struct iso_tree_dir *iso_tree_add_dir(struct iso_tree_dir *parent,
const char *path);
/**
* Recursively add an existing directory to the tree.
* Warning: when using this, you'll lose pointers to files or subdirectories.
* If you want to have pointers to all files and directories,
* use iso_tree_add_file and iso_tree_add_dir.
*
* \param path The path, on the local filesystem, of the directory to add.
*
* \pre \p parent is non-NULL
* \pre \p path is non-NULL and is a valid path to a directory on the local
* filesystem.
* \return a pointer to the newly created directory.
*/
struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent,
const char *path);
/**
* Creates a new, empty directory on the volume.
*
* \pre \p parent is non-NULL
* \pre \p name is unique among the children and files belonging to \p parent.
* Also, it doesn't contain '/' characters.
*
* \post \p parent contains a child directory whose name is \p name and whose
* POSIX attributes are the same as \p parent's.
* \return a pointer to the newly created directory.
*/
struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent,
const char *name);
/**
* Get the name of a node.
*/
const char *iso_tree_node_get_name(const struct iso_tree_node *node,
enum iso_name_version ver);
/**
* Set the name of a file.
* The name you input here will be the full name and will be used to derive the
* ISO, RockRidge and Joliet names.
*/
void iso_tree_file_set_name(struct iso_tree_file *file, const char *name);
/**
* Set the name of a directory.
* The name you input here will be the full name and will be used to derive the
* ISO, RockRidge and Joliet names.
*/
void iso_tree_dir_set_name(struct iso_tree_dir *dir, const char *name);
/**
* Recursively print a directory to stdout.
* \param spaces The initial number of spaces on the left. Set to 0 if you
* supply a root directory.
*/
void iso_tree_print(const struct iso_tree_dir *root, int spaces);
/** Create a burn_source which can be used as a data source for a track
*
* The volume set used to create the libburn_source can _not_ be modified
* until the libburn_source is freed.
*
* \param volumeset The volume set from which you want to write
* \param volnum The volume in the set which you want to write (usually 0)
* \param level ISO level to write at.
* \param flags Which extensions to support.
*
* \pre \p volumeset is non-NULL
* \pre \p volnum is less than \p volset->volset_size.
* \return A burn_source to be used for the data source for a track
*/
struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset,
int volnum,
int level,
int flags);
#endif /* __LIBISOFS */

299
libisofs/rockridge.c Executable file
View File

@ -0,0 +1,299 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "rockridge.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "ecma119.h"
#include "susp.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
/* create a PX field from the permissions on the current node. */
unsigned char *rrip_make_PX(struct ecma119_write_target *t,
struct iso_tree_node *node)
{
unsigned char *PX = malloc(44);
PX[0] = 'P';
PX[1] = 'X';
PX[2] = 44;
PX[3] = 1;
iso_bb(&PX[4], node->attrib.st_mode, 4);
iso_bb(&PX[12], node->attrib.st_nlink, 4);
iso_bb(&PX[20], node->attrib.st_uid, 4);
iso_bb(&PX[28], node->attrib.st_gid, 4);
iso_bb(&PX[36], node->attrib.st_ino, 4);
return PX;
}
/** See IEEE 1282 4.1.1 */
void rrip_add_PX(struct ecma119_write_target *t, struct iso_tree_node *node)
{
susp_append(t, node, rrip_make_PX(t, node));
}
void rrip_add_PX_dir(struct ecma119_write_target *t, struct iso_tree_dir *dir)
{
susp_append(t, ISO_NODE(dir), rrip_make_PX(t, ISO_NODE(dir)));
susp_append_self(t, dir, rrip_make_PX(t, ISO_NODE(dir)));
susp_append_parent(t, dir, rrip_make_PX(t, ISO_NODE(dir)));
}
void rrip_add_PN(struct ecma119_write_target *t, struct iso_tree_node *node)
{
unsigned char *PN = malloc(20);
PN[0] = 'P';
PN[1] = 'N';
PN[2] = 20;
PN[3] = 1;
iso_bb(&PN[4], node->attrib.st_dev >> 32, 4);
iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4);
susp_append(t, node, PN);
}
static void rrip_SL_append_comp(int *n, unsigned char ***comps,
char *s, int size, char fl)
{
unsigned char *comp = malloc(size + 2);
(*n)++;
comp[0] = fl;
comp[1] = size;
*comps = realloc(*comps, (*n) * sizeof(void*));
(*comps)[(*n) - 1] = comp;
if (size) {
memcpy(&comp[2], s, size);
}
}
static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
unsigned char ***comps)
{
int size = cur - prev;
if (size == 0) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
return;
}
if (size == 1 && prev[0] == '.') {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
return;
}
if (size == 2 && !strncmp(prev, "..", 2)) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
return;
}
/* we can't make a component any bigger than 250 (is this really a
problem)? because then it won't fit inside the SL field */
while (size > 248) {
size -= 248;
rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
}
rrip_SL_append_comp(n_comp, comps, prev, size, 0);
}
void rrip_add_SL(struct ecma119_write_target *t, struct iso_tree_node *node)
{
int ret, pathsize = 0;
char *path = NULL, *cur, *prev;
struct iso_tree_file *file = (struct iso_tree_file *)node;
int i, j;
unsigned char **comp = NULL;
int n_comp = 0;
int total_comp_len = 0;
int written = 0, pos;
unsigned char *SL;
do {
pathsize += 128;
path = realloc(path, pathsize);
ret = readlink(file->path, path, pathsize);
} while (ret == pathsize);
if (ret == -1) {
fprintf(stderr, "Error: couldn't read symlink: %s\n",
strerror(errno));
return;
}
path[ret] = '\0';
prev = path;
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
rrip_SL_add_component(prev, cur, &n_comp, &comp);
cur++;
prev = cur;
}
/* if there was no trailing '/', we need to add the last component. */
if (prev == path || prev != &path[ret - 1]) {
rrip_SL_add_component(prev, &path[ret], &n_comp, &comp);
}
for (i = 0; i < n_comp; i++) {
total_comp_len += comp[i][1] + 2;
if (total_comp_len > 250) {
total_comp_len -= comp[i][1] + 2;
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 1; /* CONTINUE */
pos = 5;
for (j = written; j < i; j++) {
memcpy(&SL[pos], comp[j], comp[j][2]);
pos += comp[j][2];
}
susp_append(t, node, SL);
written = i - 1;
total_comp_len = comp[i][1];
}
}
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 0;
pos = 5;
for (j = written; j < n_comp; j++) {
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
pos += comp[j][1] + 2;
}
susp_append(t, node, SL);
free(path);
/* free the components */
for (i = 0; i < n_comp; i++) {
free(comp[i]);
}
free(comp);
}
static void rrip_add_NM_single(struct ecma119_write_target *t,
struct iso_tree_node *node,
char *name, int size, int flags)
{
unsigned char *NM = malloc(size + 5);
NM[0] = 'N';
NM[1] = 'M';
NM[2] = size + 5;
NM[3] = 1;
NM[4] = flags;
if (size) {
memcpy(&NM[5], name, size);
}
susp_append(t, node, NM);
}
void rrip_add_NM(struct ecma119_write_target *t, struct iso_tree_node *node)
{
struct iso_tree_file *file = (struct iso_tree_file *)node;
int len = strlen(file->name.rockridge);
char *pos = file->name.rockridge;
if (len == 1 && pos[0] == '.') {
rrip_add_NM_single(t, node, pos, 0, 1 << 1);
return;
}
if (len == 2 && !strncmp(pos, "..", 2)) {
rrip_add_NM_single(t, node, pos, 0, 1 << 2);
return;
}
while (len > 250) {
rrip_add_NM_single(t, node, pos, 250, 1);
len -= 250;
pos += 250;
}
rrip_add_NM_single(t, node, pos, len, 0);
}
void rrip_add_CL(struct ecma119_write_target *t, struct iso_tree_node *node)
{
unsigned char *CL = calloc(1, 12);
CL[0] = 'C';
CL[1] = 'L';
CL[2] = 12;
CL[3] = 1;
susp_append(t, node, CL);
}
void rrip_add_PL(struct ecma119_write_target *t, struct iso_tree_dir *node)
{
unsigned char *PL = calloc(1, 12);
PL[0] = 'P';
PL[1] = 'L';
PL[2] = 12;
PL[3] = 1;
susp_append_parent(t, node, PL);
}
void rrip_add_RE(struct ecma119_write_target *t, struct iso_tree_node *node)
{
unsigned char *RE = malloc(4);
RE[0] = 'R';
RE[1] = 'E';
RE[2] = 4;
RE[3] = 1;
susp_append(t, node, RE);
}
void rrip_add_TF(struct ecma119_write_target *t, struct iso_tree_node *node)
{
unsigned char *TF = malloc(5 + 3 * 17);
TF[0] = 'T';
TF[1] = 'F';
TF[2] = 5 + 3 * 17;
TF[3] = 1;
TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7);
iso_datetime_17(&TF[5], node->attrib.st_mtime);
iso_datetime_17(&TF[22], node->attrib.st_atime);
iso_datetime_17(&TF[39], node->attrib.st_ctime);
susp_append(t, node, TF);
}
void rrip_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir)
{
struct dir_write_info *inf;
struct file_write_info *finf;
int i;
inf = dir->writer_data;
if (dir->parent != inf->real_parent) {
unsigned char *PL = susp_find(&inf->parent_susp, "PL");
iso_bb(&PL[4], inf->real_parent->block, 4);
}
for (i = 0; i < dir->nfiles; i++) {
finf = dir->files[i]->writer_data;
if (finf->real_me) {
unsigned char *CL = susp_find(&finf->susp, "CL");
iso_bb(&CL[4], finf->real_me->block, 4);
}
}
for (i = 0; i < dir->nchildren; i++) {
rrip_finalize(t, dir->children[i]);
}
}

30
libisofs/rockridge.h Executable file
View File

@ -0,0 +1,30 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for Rock Ridge support. */
#ifndef __ISO_ROCKRIDGE
#define __ISO_ROCKRIDGE
struct ecma119_write_target;
struct iso_tree_node;
struct iso_tree_dir;
void rrip_add_PX(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_PN(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_SL(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_NM(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_CL(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_RE(struct ecma119_write_target *, struct iso_tree_node *);
void rrip_add_TF(struct ecma119_write_target *, struct iso_tree_node *);
/* This is special because it doesn't modify the susp fields of the directory
* that gets passed to it; it modifies the susp fields of the ".." entry in
* that directory. */
void rrip_add_PL(struct ecma119_write_target *, struct iso_tree_dir *);
/* Add a PX field to the susp, self_susp and parent_susp entries */
void rrip_add_PX_dir(struct ecma119_write_target *, struct iso_tree_dir *);
void rrip_finalize(struct ecma119_write_target *, struct iso_tree_dir *);
#endif /* __ISO_ROCKRIDGE */

340
libisofs/struct.c Executable file
View File

@ -0,0 +1,340 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "struct.h"
#include "util.h"
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct struct_element {
uint8_t ch;
int bytes; /* The number of bytes in the value to convert
* from/to. */
uint8_t end; /* The endianness specifier. */
int mul; /* The number of values to convert. */
union { /* Pointer to the value. */
uint8_t *val8;
uint16_t *val16;
uint32_t *val32;
time_t *time;
} val;
};
/* check if a character is a valid endian-ness specifier */
#define isend(a) ((a) == '=' || (a) == '<' || (a) == '>')
static int iso_struct_element_make(struct struct_element *elem,
int mul,
char end,
char ch)
{
if (!end) {
#ifdef WORDS_BIGENDIAN
elem->end = '>'; /* default endianness is native */
#else
elem->end = '<';
#endif
} else {
elem->end = end;
}
elem->ch = ch;
elem->mul = mul;
elem->val.val8 = NULL;
switch(toupper(ch)) {
case 'X':
case 'B':
elem->bytes = 1;
break;
case 'H':
elem->bytes = 2;
break;
case 'L':
elem->bytes = 4;
break;
case 'S':
elem->bytes = 7;
elem->end = '<';
break;
case 'T':
elem->bytes = 17;
elem->end = '<';
break;
default:
elem->bytes = -1;
break;
}
return elem->bytes * elem->mul * ((elem->end == '=') ? 2 : 1);
}
static int iso_struct_element_make_v(struct struct_element *elem,
va_list *ap)
{
int mul = va_arg(*ap, int);
int end = va_arg(*ap, int);
int ch = va_arg(*ap, int);
return iso_struct_element_make(elem, mul, end, ch);
}
static int iso_struct_element_parse(const char **ffmt,
struct struct_element *elem)
{
off_t pos;
const char *fmt = *ffmt;
int mul;
char end = 0;
mul = 1;
for (pos=0; isdigit(fmt[pos]) || isend(fmt[pos]); pos++) {
if (isdigit(fmt[pos])) {
mul = atoi( fmt + pos );
while (isdigit(fmt[pos+1])) pos++;
} else {
end = fmt[pos];
}
}
(*ffmt) += pos + 1;
return iso_struct_element_make(elem, mul, end, fmt[pos]);
}
/* read a single integer from data[i] to elem[i], interpreting the endian-ness
* and offset appropriately. */
static uint32_t iso_struct_element_read_int(struct struct_element *elem,
const uint8_t *data,
int i)
{
uint32_t el;
switch(elem->end) {
case '>':
el = iso_read_msb(data + i*elem->bytes, elem->bytes);
break;
case '<':
el = iso_read_lsb(data + i*elem->bytes, elem->bytes);
break;
case '=':
el = iso_read_bb(data + i*elem->bytes*2, elem->bytes);
}
switch(elem->bytes) {
case 1:
elem->val.val8[i] = el;
break;
case 2:
elem->val.val16[i] = el;
break;
case 4:
elem->val.val32[i] = el;
break;
}
return el;
}
/* write a single integer from elem[i] to data[i]. */
static uint32_t iso_struct_element_write1(struct struct_element *elem,
uint8_t *data,
int i)
{
uint32_t el;
switch(elem->bytes) {
case 1:
el = elem->val.val8[i];
break;
case 2:
el = elem->val.val16[i];
break;
case 4:
el = elem->val.val32[i];
break;
}
switch(elem->end) {
case '>':
iso_msb(data + i*elem->bytes, el, elem->bytes);
break;
case '<':
iso_lsb(data + i*elem->bytes, el, elem->bytes);
break;
case '=':
iso_bb(data + i*elem->bytes*2, el, elem->bytes);
}
return el;
}
static int iso_struct_element_read(struct struct_element *elem,
const uint8_t *data)
{
int size = elem->bytes * ((elem->end == '=') ? 2 : 1);
int i;
if (elem->ch == 'x') {
return size * elem->mul;
}
for (i=0; i<elem->mul; i++) {
switch(toupper(elem->ch)) {
case 'S':
/*
elem->val.time[i] = iso_datetime_read_7(&data[i*7]);
*/
break;
case 'T':
/*
elem->val.time[i] = iso_datetime_read_17(&data[i*17]);
*/
break;
default:
iso_struct_element_read_int(elem, data, i);
}
}
return size * elem->mul;
}
static int iso_struct_element_write(struct struct_element *elem,
uint8_t *data)
{
int size = elem->bytes * ((elem->end == '=') ? 2 : 1);
int i;
uint32_t ret;
if (elem->ch == 'x') {
return size*elem->mul;
}
for (i=0; i<elem->mul; i++) {
switch(toupper(elem->ch)) {
case 'S':
iso_datetime_7(&data[i*7], elem->val.time[i]);
ret = elem->val.time[i];
break;
case 'T':
iso_datetime_17(&data[i*17], elem->val.time[i]);
ret = elem->val.time[i];
break;
default:
ret = iso_struct_element_write1(elem, data, i);
break;
}
if (islower(elem->ch) && ret == 0) {
memset(data + size*i, 0, size*(elem->mul-i));
break;
}
}
return size * elem->mul;
}
int iso_struct_unpack(const char *fmt, const uint8_t *data, ...)
{
int num_conv;
int ret;
va_list ap;
struct struct_element elem;
off_t off;
va_start(ap, data);
num_conv = 0;
off = 0;
while(*fmt) {
ret = iso_struct_element_parse(&fmt, &elem);
if (ret < 0) {
va_end(ap);
return -1;
}
if (elem.ch != 'x') {
elem.val.val8 = va_arg(ap, void*);
}
off += iso_struct_element_read(&elem, data + off);
num_conv++;
}
va_end(ap);
return num_conv;
}
int iso_struct_pack(const char *fmt, uint8_t *data, ...)
{
int num_conv;
int ret;
va_list ap;
struct struct_element elem;
off_t off;
va_start(ap, data);
num_conv = 0;
off = 0;
while(*fmt) {
ret = iso_struct_element_parse(&fmt, &elem);
if (ret < 0) {
va_end(ap);
return -1;
}
if (elem.ch != 'x') {
elem.val.val8 = va_arg(ap, void*);
}
off += iso_struct_element_write(&elem, data + off);
num_conv++;
}
va_end(ap);
return num_conv;
}
int iso_struct_pack_long(uint8_t *data, ...)
{
int num_conv;
int ret;
int i, j;
va_list ap;
struct struct_element *elem = NULL;
off_t off;
va_start(ap, data);
num_conv = 0;
off = 0;
elem = calloc(1, sizeof(struct struct_element));
i=0;
while ((ret = iso_struct_element_make_v(&elem[i], &ap) > 0)) {
elem = realloc(elem, (++i + 1) * sizeof(struct struct_element));
}
for (j=0; j<i; j++) {
if (elem[j].ch != 'x') {
elem[j].val.val8 = va_arg(ap, void*);
}
off += iso_struct_element_write(&elem[j], data + off);
}
va_end(ap);
if (ret < 0) {
return -1;
}
return num_conv;
}
int iso_struct_calcsize(const char *fmt)
{
int ret, total;
struct struct_element elem;
total = 0;
while (*fmt) {
ret = iso_struct_element_parse(&fmt, &elem);
if (ret < 0) {
return -1;
}
total += ret;
}
return total;
}

77
libisofs/struct.h Executable file
View File

@ -0,0 +1,77 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file struct.h
* Add functionality similar to the python "struct" module to make it easier
* to read and write .iso structures.
*
* The following conversions are supported:
* B uint8_t, the arg should be (uint8_t*)
* H uint16_t, the arg should be (uint16_t*)
* L uint32_t, the arg should be (uint32_t*)
* S a 7-byte timestamp, the arg should be (time_t*)
* T a 17-byte timestamp, the arg should be (time_t*)
* x ignored field, no arg should be specified
*
* Any of the first 3 conversions may be preceded by a endian specifier:
* < little-endian
* > big-endian
* = both-endian (ie. according to ecma119 7.2.3 or 7.3.3)
*
* Each conversion specifier may also be preceded by a length specifier. For
* example, "<5L" specifies an array of 5 little-endian 32-bit integers. Note
* that "=L" takes 8 bytes while "<L" and ">L" each take 4.
*
* You can use a lower-case conversion specifier instead of an upper-case one
* to signify that the (multi-element) conversion should stop when a zero is
* reached. This is useful for writing out NULL-terminated strings. Note that
* this has no effect when unpacking data from a struct.
*/
#ifndef __ISO_STRUCT
#define __ISO_STRUCT
#include <stdint.h>
/**
* Unpack a struct into its components. The list of components is a list of
* pointers to the variables to write.
*
* For example:
* uint8_t byte1, byte2;
* uint16_t uint;
* iso_struct_unpack("BB=H", data, &byte1, &byte2, &uint);
*
* \return The number of conversions performed, or -1 on error.
*/
int iso_struct_unpack(const char *fmt, const uint8_t *data, ...);
/**
* Write out a struct from its components. The list of components is a list of
* pointers to the variables to write and the buffer to which to write
* is assumed to be large
* enough to take the data.
*
* \return The number of conversions performed, or -1 on error.
*/
int iso_struct_pack(const char *fmt, uint8_t *data, ...);
/**
* Achieves the same effect as iso_struct_pack(), but the format is passed as
* a sequence of (int, char, char) triples. This list is terminated by
* (0, 0, 0) and the list of parameters follows.
*
* Example: iso_struct_pack_long(data, 4, '=', 'H', 0, 0, 0, &val) is the same
* as iso_struct_pack("4=H", 0, 0, 0, &val)
*/
int iso_struct_pack_long(uint8_t *data, ...);
/**
* Calculate the size of a given format string.
*
* \return The sum of the length of all formats in the string, in bytes. Return
* -1 on error.
*/
int iso_struct_calcsize(const char *fmt);
#endif

312
libisofs/susp.c Executable file
View File

@ -0,0 +1,312 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "susp.h"
#include "tree.h"
#include "util.h"
#include "ecma119.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
static void susp_insert_direct(struct ecma119_write_target *t,
struct susp_info *susp, unsigned char *data,
int pos)
{
int i;
assert(pos <= susp->n_susp_fields);
susp->n_susp_fields++;
susp->susp_fields = realloc(susp->susp_fields,
sizeof(void*) * susp->n_susp_fields);
for (i = susp->n_susp_fields-1; i > pos; i--) {
susp->susp_fields[i] = susp->susp_fields[i - 1];
}
susp->susp_fields[pos] = data;
}
void susp_append(struct ecma119_write_target *t,
struct iso_tree_node *node, unsigned char *data)
{
struct dir_write_info *inf = node->writer_data;
struct susp_info *susp = &inf->susp;
susp_insert_direct(t, susp, data, susp->n_susp_fields);
}
void susp_append_self(struct ecma119_write_target *t,
struct iso_tree_dir *dir, unsigned char *data)
{
struct dir_write_info *inf = dir->writer_data;
struct susp_info *susp = &inf->self_susp;
susp_insert_direct(t, susp, data, susp->n_susp_fields);
}
void susp_append_parent(struct ecma119_write_target *t,
struct iso_tree_dir *dir, unsigned char *data)
{
struct dir_write_info *inf = dir->writer_data;
struct susp_info *susp = &inf->parent_susp;
susp_insert_direct(t, susp, data, susp->n_susp_fields);
}
void susp_insert(struct ecma119_write_target *t,
struct iso_tree_node *node, unsigned char *data, int pos)
{
struct dir_write_info *inf = node->writer_data;
struct susp_info *susp = &inf->susp;
susp_insert_direct(t, susp, data, pos);
}
void susp_insert_self(struct ecma119_write_target *t,
struct iso_tree_dir *dir, unsigned char *data, int pos)
{
struct dir_write_info *inf = dir->writer_data;
struct susp_info *susp = &inf->self_susp;
susp_insert_direct(t, susp, data, pos);
}
void susp_insert_parent(struct ecma119_write_target *t,
struct iso_tree_dir *dir, unsigned char *data, int pos)
{
struct dir_write_info *inf = dir->writer_data;
struct susp_info *susp = &inf->parent_susp;
susp_insert_direct(t, susp, data, pos);
}
unsigned char *susp_find(struct susp_info *susp, const char *name)
{
int i;
for (i = 0; i < susp->n_susp_fields; i++) {
if (!strncmp((char *)susp->susp_fields[i], name, 2)) {
return susp->susp_fields[i];
}
}
return NULL;
}
/* utility function for susp_add_CE because susp_add_CE needs to act 3 times
* on directories (for the "." and ".." entries. */
#define CE_LEN 28
static unsigned char *susp_add_single_CE(struct ecma119_write_target *t,
struct susp_info *susp, int len)
{
int susp_length = 0, tmp_len;
int i;
unsigned char *CE;
for (i = 0; i < susp->n_susp_fields; i++) {
susp_length += susp->susp_fields[i][2];
}
if (susp_length <= len) {
/* no need for a CE field */
susp->non_CE_len = susp_length;
susp->n_fields_fit = susp->n_susp_fields;
return NULL;
}
tmp_len = susp_length;
for (i = susp->n_susp_fields - 1; i >= 0; i--) {
tmp_len -= susp->susp_fields[i][2];
if (tmp_len + CE_LEN <= len) {
susp->non_CE_len = tmp_len + CE_LEN;
susp->CE_len = susp_length - tmp_len;
/* i+1 because we have to count the CE field */
susp->n_fields_fit = i + 1;
CE = calloc(1, CE_LEN);
/* we don't fill in the BLOCK LOCATION or OFFSET
fields yet. */
CE[0] = 'C';
CE[1] = 'E';
CE[2] = (char)CE_LEN;
CE[3] = (char)1;
iso_bb(&CE[20], susp_length - tmp_len, 4);
return CE;
}
}
assert(0);
return NULL;
}
/** See IEEE P1281 Draft Version 1.12/5.2. Because this function depends on the
* length of the other SUSP fields, it should always be calculated last. */
void susp_add_CE(struct ecma119_write_target *t, struct iso_tree_node *node)
{
struct dir_write_info *inf = node->writer_data;
unsigned char *CE;
CE = susp_add_single_CE(t, &inf->susp, 255 - node->dirent_len);
if (CE)
susp_insert(t, node, CE, inf->susp.n_fields_fit - 1);
if (S_ISDIR(node->attrib.st_mode)) {
CE = susp_add_single_CE(t, &inf->self_susp, 255 - 34);
if (CE)
susp_insert_self(t, (struct iso_tree_dir *)node, CE,
inf->self_susp.n_fields_fit - 1);
CE = susp_add_single_CE(t, &inf->parent_susp, 255 - 34);
if (CE)
susp_insert_parent(t, (struct iso_tree_dir *)node, CE,
inf->parent_susp.n_fields_fit - 1);
}
}
/** See IEEE P1281 Draft Version 1.12/5.3 */
void susp_add_SP(struct ecma119_write_target *t, struct iso_tree_dir *dir)
{
unsigned char *SP = malloc(7);
SP[0] = 'S';
SP[1] = 'P';
SP[2] = (char)7;
SP[3] = (char)1;
SP[4] = 0xbe;
SP[5] = 0xef;
SP[6] = 0;
susp_append_self(t, dir, SP);
}
#if 0
/** See IEEE P1281 Draft Version 1.12/5.4 */
static void susp_add_ST(struct ecma119_write_target *t,
struct iso_tree_node *node)
{
unsigned char *ST = malloc(4);
ST[0] = 'S';
ST[1] = 'T';
ST[2] = (char)4;
ST[3] = (char)1;
susp_append(t, node, ST);
}
#endif
/** See IEEE P1281 Draft Version 1.12/5.5 */
void susp_add_ER(struct ecma119_write_target *t, struct iso_tree_dir *dir)
{
unsigned char *ER = malloc(182);
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
susp_append_self(t, dir, ER);
}
static void susp_fin_CE(struct ecma119_write_target *t,
struct iso_tree_dir *dir)
{
struct dir_write_info *inf = (struct dir_write_info *)
dir->writer_data;
struct node_write_info *cinf;
unsigned char *CE;
int i;
int CE_offset = inf->len;
if (inf->self_susp.CE_len) {
CE = inf->self_susp.susp_fields[inf->self_susp.n_fields_fit -
1];
iso_bb(&CE[4], dir->block + CE_offset / 2048, 4);
iso_bb(&CE[12], CE_offset % 2048, 4);
CE_offset += inf->self_susp.CE_len;
}
if (inf->parent_susp.CE_len) {
CE = inf->parent_susp.susp_fields[inf->parent_susp.
n_fields_fit - 1];
iso_bb(&CE[4], dir->block + CE_offset / 2048, 4);
iso_bb(&CE[12], CE_offset % 2048, 4);
CE_offset += inf->parent_susp.CE_len;
}
for (i = 0; i < dir->nchildren; i++) {
cinf = dir->children[i]->writer_data;
if (!cinf->susp.CE_len) {
continue;
}
CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1];
iso_bb(&CE[4], dir->block + CE_offset / 2048, 4);
iso_bb(&CE[12], CE_offset % 2048, 4);
CE_offset += cinf->susp.CE_len;
}
for (i = 0; i < dir->nfiles; i++) {
cinf = dir->files[i]->writer_data;
if (!cinf->susp.CE_len) {
continue;
}
CE = cinf->susp.susp_fields[cinf->susp.n_fields_fit - 1];
iso_bb(&CE[4], dir->block + CE_offset / 2048, 4);
iso_bb(&CE[12], CE_offset % 2048, 4);
CE_offset += cinf->susp.CE_len;
}
assert(CE_offset == inf->len + inf->susp_len);
}
void susp_finalize(struct ecma119_write_target *t, struct iso_tree_dir *dir)
{
int i;
if (dir->depth != 1) {
susp_fin_CE(t, dir);
}
for (i = 0; i < dir->nchildren; i++) {
susp_finalize(t, dir->children[i]);
}
}
void susp_write(struct ecma119_write_target *t, struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = 0; i < susp->n_fields_fit; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_free_fields(struct susp_info *susp)
{
int i;
for (i=0; i<susp->n_susp_fields; i++) {
free(susp->susp_fields[i]);
}
if (susp->susp_fields) {
free(susp->susp_fields);
}
memset(susp, 0, sizeof(struct susp_info));
}

75
libisofs/susp.h Executable file
View File

@ -0,0 +1,75 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for SUSP (IEEE 1281).
*/
#ifndef __ISO_SUSP
#define __ISO_SUSP
/* SUSP is only present in standard ecma119 */
struct ecma119_write_target;
struct iso_tree_node;
struct iso_tree_dir;
/** This contains the information that needs to go in the SUSP area of a file.
*/
struct susp_info
{
int n_susp_fields; /**< Number of SUSP fields */
unsigned char **susp_fields; /**< Data for each SUSP field */
/* the next 3 relate to CE and are filled out by susp_add_CE. */
int n_fields_fit; /**< How many of the above SUSP fields fit
* within this node's dirent. */
int non_CE_len; /**< Length of the part of the SUSP area that
* fits in the dirent. */
int CE_len; /**< Length of the part of the SUSP area that
* will go in a CE area. */
};
void susp_add_CE(struct ecma119_write_target *, struct iso_tree_node *);
/* these next 2 are special because they don't modify the susp fields of the
* directory that gets passed to them; they modify the susp fields of the
* "." entry in the directory. */
void susp_add_SP(struct ecma119_write_target *, struct iso_tree_dir *);
void susp_add_ER(struct ecma119_write_target *, struct iso_tree_dir *);
/** Once all the directories and files are laid out, recurse through the tree
* and finalize all SUSP CE entries. */
void susp_finalize(struct ecma119_write_target *, struct iso_tree_dir *);
void susp_append(struct ecma119_write_target *,
struct iso_tree_node *,
unsigned char *);
void susp_append_self(struct ecma119_write_target *,
struct iso_tree_dir *,
unsigned char *);
void susp_append_parent(struct ecma119_write_target *,
struct iso_tree_dir *,
unsigned char *);
void susp_insert(struct ecma119_write_target *,
struct iso_tree_node *,
unsigned char *,
int pos);
void susp_insert_self(struct ecma119_write_target *,
struct iso_tree_dir *,
unsigned char *,
int pos);
void susp_insert_parent(struct ecma119_write_target *,
struct iso_tree_dir *,
unsigned char *,
int pos);
unsigned char *susp_find(struct susp_info *,
const char *);
void susp_write(struct ecma119_write_target *,
struct susp_info *,
unsigned char *);
void susp_write_CE(struct ecma119_write_target *,
struct susp_info *,
unsigned char *);
void susp_free_fields(struct susp_info *);
#endif /* __ISO_SUSP */

138
libisofs/test.c Executable file
View File

@ -0,0 +1,138 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#define _GNU_SOURCE
#include "libisofs/libisofs.h"
#include "libburn/libburn.h"
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#define SECSIZE 2048
const char * const optstring = "JRL:h";
extern char *optarg;
extern int optind;
void usage()
{
printf("test [OPTIONS] DIRECTORY OUTPUT\n");
}
void help()
{
printf(
"Options:\n"
" -J Add Joliet support\n"
" -R Add Rock Ridge support\n"
" -L <num> Set the ISO level (1 or 2)\n"
" -h Print this message\n"
);
}
int main(int argc, char **argv)
{
struct iso_volset *volset;
struct iso_volume *volume;
struct burn_source *src;
unsigned char buf[2048];
FILE *fd;
int c;
int level=1, flags=0;
DIR *dir;
struct dirent *ent;
while ((c = getopt(argc, argv, optstring)) != -1) {
switch(c) {
case 'h':
usage();
help();
exit(0);
break;
case 'J':
flags |= ECMA119_JOLIET;
break;
case 'R':
flags |= ECMA119_ROCKRIDGE;
break;
case 'L':
level = atoi(optarg);
break;
case '?':
usage();
exit(1);
break;
}
}
if (argc < 2) {
printf ("must pass directory to build iso from\n");
usage();
return 1;
}
if (argc < 3) {
printf ("must supply output file\n");
usage();
return 1;
}
fd = fopen(argv[optind+1], "w");
if (!fd) {
perror("error opening output file");
exit(1);
}
volume = iso_volume_new( "VOLID", "PUBID", "PREPID" );
volset = iso_volset_new( volume, "VOLSETID" );
dir = opendir(argv[optind]);
if (!dir) {
perror("error opening input directory");
exit(1);
}
while ( (ent = readdir(dir)) ) {
struct stat st;
char *name;
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
continue;
}
name = malloc(strlen(argv[optind]) + strlen(ent->d_name) + 2);
strcpy(name, argv[optind]);
strcat(name, "/");
strcat(name, ent->d_name);
if (lstat(name, &st) == -1) {
fprintf(stderr, "error opening file %s: %s\n",
name, strerror(errno));
exit(1);
}
if (S_ISDIR(st.st_mode)) {
iso_tree_radd_dir(iso_volume_get_root(volume), name);
} else {
iso_tree_add_file(iso_volume_get_root(volume), name);
}
free(name);
}
iso_tree_print(iso_volume_get_root(volume), 0);
src = iso_source_new_ecma119(volset, 0, level, flags);
while (src->read(src, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
return 0;
}

409
libisofs/tree.c Executable file
View File

@ -0,0 +1,409 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include "libisofs.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume)
{
struct iso_tree_dir *dir;
assert(volume);
dir = calloc(1, sizeof(struct iso_tree_dir));
dir->attrib.st_mode = S_IFDIR;
dir->volume = volume;
return dir;
}
void iso_tree_free(struct iso_tree_dir *root)
{
int i;
assert(root);
/* Free names. */
free(root->name.full);
free(root->name.iso1);
free(root->name.iso2);
free(root->name.rockridge);
free(root->name.joliet);
/* Free the children. */
for (i = 0; i < root->nchildren; i++)
iso_tree_free(root->children[i]);
free(root->children);
/* Free all files. */
for (i = 0; i < root->nfiles; i++) {
struct iso_tree_file *file = root->files[i];
free(file->path);
free(file->name.full);
free(file->name.iso1);
free(file->name.iso2);
free(file->name.rockridge);
free(file->name.joliet);
free(file);
}
free(root->files);
if (root->writer_data) {
fprintf(stderr, "Warning: freeing a directory with non-NULL "
"writer_data\n");
}
/* Free ourself. */
free(root);
}
struct iso_tree_file *iso_tree_add_new_file(struct iso_tree_dir *parent,
const char *name)
{
struct iso_tree_file *file;
assert(parent && name);
file = calloc(1, sizeof(struct iso_tree_file));
file->path = calloc(1, 1);
file->parent = parent;
file->volume = parent->volume;
file->attrib = parent->attrib;
file->attrib.st_mode &= ~S_IFMT;
file->attrib.st_mode |= S_IFREG;
file->attrib.st_size = 0;
iso_tree_file_set_name(file, name);
/* Add the new file to the parent directory */
parent->nfiles++;
parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles);
parent->files[parent->nfiles - 1] = file;
return file;
}
struct iso_tree_file *iso_tree_add_file(struct iso_tree_dir *parent,
const char *path)
{
struct iso_tree_file *file;
struct stat st;
int statret;
const char *name;
assert( parent != NULL && path != NULL );
statret = lstat(path, &st);
if (statret == -1) {
fprintf(stderr, "couldn't stat file %s: %s\n",
path, strerror(errno));
return NULL;
}
/* Set up path, parent and volume. */
file = calloc(1, sizeof(struct iso_tree_file));
file->path = strdup(path);
file->parent = parent;
file->volume = parent->volume;
file->attrib = st;
/* find the last component in the path */
name = strrchr(path, '/');
if (name == NULL) {
name = path;
} else {
name++;
}
iso_tree_file_set_name(file, name);
if (!S_ISREG(st.st_mode)) {
file->attrib.st_size = 0;
}
/* Add the new file to the parent directory */
parent->nfiles++;
parent->files = realloc(parent->files, sizeof(void *) * parent->nfiles);
parent->files[parent->nfiles - 1] = file;
return file;
}
struct iso_tree_dir *iso_tree_add_dir(struct iso_tree_dir *parent,
const char *path)
{
struct iso_tree_dir *dir;
struct stat st;
int statret;
char *pathcpy;
char *name;
assert( parent && path );
statret = stat(path, &st);
if (statret == -1) {
fprintf(stderr, "couldn't stat directory %s: %s\n",
path, strerror(errno));
return NULL;
}
dir = calloc(1, sizeof(struct iso_tree_dir));
dir->parent = parent;
dir->volume = parent->volume;
dir->attrib = st;
/* find the last component in the path. We need to copy the path because
* we modify it if there is a trailing slash. */
pathcpy = strdup(path);
name = strrchr(pathcpy, '/');
if (name == &pathcpy[strlen(pathcpy) - 1]) {
/* get rid of the trailing slash */
*name = '\0';
name = strrchr(pathcpy, '/');
}
if (name == NULL) {
name = pathcpy;
} else {
name++;
}
iso_tree_dir_set_name(dir, name);
free(pathcpy);
parent->nchildren++;
parent->children = realloc(parent->children,
parent->nchildren * sizeof(void*));
parent->children[parent->nchildren - 1] = dir;
return dir;
}
struct iso_tree_dir *iso_tree_radd_dir(struct iso_tree_dir *parent,
const char *path)
{
struct iso_tree_dir *nparent;
struct stat st;
int statret;
DIR *dir;
struct dirent *ent;
assert( parent && path );
statret = stat(path, &st);
nparent = iso_tree_add_dir(parent, path);
/* Open the directory for reading and iterate over the directory
entries. */
dir = opendir(path);
if (!dir) {
fprintf(stderr, "couldn't open directory %s: %s\n",
path, strerror(errno));
return NULL;
}
while ((ent = readdir(dir))) {
char *child;
/* Skip current and parent directory entries. */
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
/* Build the child's full pathname. */
child = iso_pathname(path, ent->d_name);
/* Skip to the next entry on errors. */
if (stat(child, &st) != 0)
continue;
if (S_ISDIR(st.st_mode)) {
iso_tree_radd_dir(nparent, child);
} else {
iso_tree_add_file(nparent, child);
}
free(child);
}
closedir(dir);
return nparent;
}
struct iso_tree_dir *iso_tree_add_new_dir(struct iso_tree_dir *parent,
const char *name)
{
struct iso_tree_dir *dir;
assert( parent && name );
dir = calloc(1, sizeof(struct iso_tree_dir));
dir->parent = parent;
dir->volume = parent->volume;
iso_tree_dir_set_name(dir, name);
dir->attrib = parent->attrib;
dir->attrib.st_mtime = time(NULL);
dir->attrib.st_atime = time(NULL);
dir->attrib.st_ctime = time(NULL);
/* add the new directory to parent */
parent->nchildren++;
parent->children = realloc(parent->children,
parent->nchildren * sizeof(void*));
parent->children[parent->nchildren - 1] = dir;
return dir;
}
void iso_tree_file_set_name(struct iso_tree_file *file, const char *name)
{
file->name.full = strdup(name);
file->name.iso1 = iso_1_fileid(name);
file->name.iso2 = iso_2_fileid(name);
file->name.rockridge = iso_p_filename(name);
file->name.joliet = iso_j_id(file->name.full);
}
char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node,
enum iso_name_version ver)
{
if (ver == ISO_NAME_ISO) {
if (node->volume->iso_level == 1) {
return node->name.iso1;
} else {
return node->name.iso2;
}
}
switch (ver) {
case ISO_NAME_FULL:
return node->name.full;
case ISO_NAME_ISO:
if (node->volume->iso_level == 1) {
return node->name.iso1;
} else {
return node->name.iso2;
}
case ISO_NAME_ISO_L1:
return node->name.iso1;
case ISO_NAME_ISO_L2:
return node->name.iso2;
case ISO_NAME_ROCKRIDGE:
return node->name.rockridge;
case ISO_NAME_JOLIET:
return (char*) node->name.joliet;
}
assert(0);
return NULL; /* just to shut up warnings */
}
const char *iso_tree_node_get_name(const struct iso_tree_node *node,
enum iso_name_version ver)
{
return iso_tree_node_get_name_nconst(node, ver);
}
void iso_tree_dir_set_name(struct iso_tree_dir *dir, const char *name)
{
dir->name.full = strdup(name);
/* Level 1 directory is a string of d-characters of maximum size 8. */
dir->name.iso1 = iso_d_str(name, 8, 0);
/* Level 2 directory is a string of d-characters of maximum size 31. */
dir->name.iso2 = iso_d_str(name, 31, 0);
dir->name.rockridge = iso_p_dirname(name);
dir->name.joliet = iso_j_id(dir->name.full);
}
/* Compares file names for use with qsort. */
int iso_node_cmp(const void *v1, const void *v2)
{
struct iso_tree_node **f1 = (struct iso_tree_node **)v1;
struct iso_tree_node **f2 = (struct iso_tree_node **)v2;
return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_FULL),
iso_tree_node_get_name(*f2, ISO_NAME_FULL));
}
int iso_node_cmp_iso(const void *v1, const void *v2)
{
struct iso_tree_node **f1 = (struct iso_tree_node **)v1;
struct iso_tree_node **f2 = (struct iso_tree_node **)v2;
return strcmp(iso_tree_node_get_name(*f1, ISO_NAME_ISO),
iso_tree_node_get_name(*f2, ISO_NAME_ISO));
}
int iso_node_cmp_joliet(const void *v1, const void *v2)
{
struct iso_tree_node **f1 = (struct iso_tree_node **)v1;
struct iso_tree_node **f2 = (struct iso_tree_node **)v2;
return ucscmp((uint16_t*)iso_tree_node_get_name(*f1, ISO_NAME_JOLIET),
(uint16_t*)iso_tree_node_get_name(*f2, ISO_NAME_JOLIET));
}
void iso_tree_sort(struct iso_tree_dir *root)
{
int i;
qsort(root->files, root->nfiles, sizeof(struct iso_tree_file *),
iso_node_cmp);
qsort(root->children, root->nchildren, sizeof(struct iso_tree_dir *),
iso_node_cmp);
for (i = 0; i < root->nchildren; i++)
iso_tree_sort(root->children[i]);
}
void iso_tree_print(const struct iso_tree_dir *root, int spaces)
{
iso_tree_print_verbose(root, NULL, NULL, NULL, spaces);
}
void iso_tree_print_verbose(const struct iso_tree_dir *root,
print_dir_callback dc, print_file_callback fc,
void *data, int spaces)
{
int i, j;
for (i = 0; i < spaces; i++)
printf(" ");
/* Root directory doesn't have a name. */
if (root->name.full != NULL)
printf("%s", iso_tree_node_get_name(ISO_NODE(root),
ISO_NAME_ISO));
printf("/\n");
if (dc) dc(root, data, spaces);
spaces += 2;
for (j = 0; j < root->nchildren; j++) {
iso_tree_print_verbose(root->children[j], dc, fc, data,
spaces);
}
for (j = 0; j < root->nfiles; j++) {
for (i = 0; i < spaces; i++)
printf(" ");
printf("%s\n",
iso_tree_node_get_name(ISO_NODE(root->files[j]),
ISO_NAME_ISO));
if (fc) fc(root->files[j], data, spaces);
}
}

259
libisofs/tree.h Executable file
View File

@ -0,0 +1,259 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file tree.h
*
* Extra declarations for use with the iso_tree_dir and iso_tree_file
* structures.
*/
#ifndef __ISO_TREE
#define __ISO_TREE
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdint.h>
#include "libisofs.h"
/**
* File or directory names or identifiers.
*/
struct iso_names
{
char *full; /**< Full version: either part of the path or
user input. */
char *iso1; /**< ISO level 1 identifier. */
char *iso2; /**< ISO level 2 identifier. */
char *rockridge; /**< Rock Ridge file or directory name. */
uint16_t *joliet; /**< Joliet identifier. */
};
/**
* Directory on a volume.
*/
struct iso_tree_dir
{
struct iso_tree_dir *parent; /**< \see iso_tree_node */
struct iso_volume *volume; /**< \see iso_tree_node */
struct iso_names name; /**< \see iso_tree_node */
struct stat attrib; /**< \see iso_tree_node */
off_t block; /**< \see iso_tree_node */
uint8_t dirent_len; /**< \see iso_tree_node */
void *writer_data; /**< \see iso_tree_node */
int depth; /**< The depth of this directory in the
* Directory Heirarchy. This is 1 for
* the root directory.
*/
int nchildren; /**< Number of child directories. */
int nfiles; /**< Number of files in this directory.
*/
struct iso_tree_dir **children; /**< Child directories. */
struct iso_tree_file **files; /**< Files in this directory. */
};
/**
* File on a volume.
*/
struct iso_tree_file
{
struct iso_tree_dir *parent; /**< \see iso_tree_node */
struct iso_volume *volume; /**< \see iso_tree_node */
struct iso_names name; /**< \see iso_tree_node */
struct stat attrib; /**< \see iso_tree_node */
off_t block; /**< \see iso_tree_node */
uint8_t dirent_len; /**< \see iso_tree_node */
void *writer_data; /**< \see iso_tree_node */
char *path; /**< Location of the file in the
* local filesystem. This can be a
* full or relative path. If this is
* NULL, then the file doesn't exist
* in the local filesystem and its
* size must be zero.
*
* FIXME: Allow references to files
* on other volumes belonging to the
* same volset as this file.
*/
};
/**
* Fields in common between iso_tree_file and iso_tree_dir.
*/
struct iso_tree_node
{
struct iso_tree_dir *parent; /**< The parent of this node. Must be
* non-NULL unless we are the
* root directory on a volume.
*/
struct iso_volume *volume; /**< The volume to which this node
* belongs.
*/
struct iso_names name; /**< The name of this node in its parent
* directory. Must be non-NULL unless
* we are the root directory on a
* volume.
*/
struct stat attrib; /**< The POSIX attributes of this
* node as documented in "man 2 stat".
*
* Any node that is not a regular
* file or a directory must have
* \p attrib.st_size set to zero. Any
* node that is a directory will have
* \p attrib.st_size filled out by the
* writer.
*/
/* information used for writing */
off_t block; /**< The block at which this node's
* data will be written.
*/
uint8_t dirent_len; /**< The size of this node's
* Directory Record in its parent.
* This does not include System Use
* fields, if present.
*/
void *writer_data; /**< This is writer-specific data. It
* must be set to NULL when a node
* is created and it should be NULL
* again when the node is freed.
*/
};
/** A macro to simplify casting between nodes and files/directories. */
#define ISO_NODE(a) ( (struct iso_tree_node*) (a) )
/** A macro to simplify casting between nodes and directories. */
#define ISO_DIR(a) ( (struct iso_tree_dir*) (a) )
/** A macro to simplify casting between nodes and files. */
#define ISO_FILE(a) ( (struct iso_tree_file*) (a) )
/**
* Create a new root directory for a volume.
*
* \param volume The volume for which to create a new root directory.
*
* \pre \p volume is non-NULL.
* \post \p volume has a non-NULL, empty root directory.
* \return \p volume's new non-NULL, empty root directory.
*/
struct iso_tree_dir *iso_tree_new_root(struct iso_volume *volume);
/**
* Create a new, empty, file.
*
* \param parent The parent of the new file.
* \param name The name of the new file, encoded in the current locale.
*
* \pre \p parent is non-NULL.
* \pre \p name is non-NULL and it does not match any other file or directory
* name in \p parent.
* \post \p parent contains a file with the following properties:
* - the file's (full) name is \p name
* - the file's POSIX permissions are the same as \p parent's
* - the file is a regular file
* - the file is empty
*
* \return \p parent's newly created file.
*/
struct iso_tree_file *iso_tree_add_new_file(struct iso_tree_dir *parent,
const char *name);
/**
* Recursively free a directory.
*
* \param root The root of the directory heirarchy to free.
*
* \pre \p root is non-NULL.
*/
void iso_tree_free(struct iso_tree_dir *root);
/**
* Recursively sort all the files and child directories in a directory.
*
* \param root The root of the directory heirarchy to sort.
*
* \pre \p root is non-NULL.
* \post For each directory \p dir in the directory heirarchy descended fro
* root, the fields in \p dir.children and \p dir.files are alphabetically
* sorted according to \p name.full.
*
* \see iso_names
*/
void iso_tree_sort(struct iso_tree_dir *root);
/**
* Compare the names of 2 nodes, \p *v1 and \p *v2. This is compatible with
* qsort.
*/
int iso_node_cmp(const void *v1, const void *v2);
/**
* Compare the joliet names of 2 nodes, compatible with qsort.
*/
int iso_node_cmp_joliet(const void *v1, const void *v2);
/**
* Compare the iso names of 2 nodes, compatible with qsort.
*/
int iso_node_cmp_iso(const void *v1, const void *v2);
/**
* A function that prints verbose information about a directory.
*
* \param dir The directory about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_dir_callback) (const struct iso_tree_dir *dir,
void *data,
int spaces);
/**
* A function that prints verbose information about a file.
*
* \param dir The file about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_file_callback) (const struct iso_tree_file *file,
void *data,
int spaces);
/**
* Recursively print a directory heirarchy. For each node in the directory
* heirarchy, call a callback function to print information more verbosely.
*
* \param root The root of the directory heirarchy to print.
* \param dir The callback function to call for each directory in the tree.
* \param file The callback function to call for each file in the tree.
* \param callback_data The data to pass to the callback functions.
* \param spaces The number of spaces to prepend to the output.
*
* \pre \p root is not NULL.
* \pre Neither of the callback functions modifies the directory heirarchy.
*/
void iso_tree_print_verbose(const struct iso_tree_dir *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces);
/**
* Get a non-const version of the node name. This is used for name-mangling
* iso names. It should only be used internally in libisofs; all other users
* should only access the const name.
*/
char *iso_tree_node_get_name_nconst(const struct iso_tree_node *node,
enum iso_name_version ver);
#endif /* __ISO_TREE */

710
libisofs/util.c Executable file
View File

@ -0,0 +1,710 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <ctype.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdint.h>
#include <langinfo.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include "util.h"
int valid_d_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
}
int valid_a_char(char c)
{
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?') || (c >= 'A' &&
c <= 'Z')
|| (c == '_');
}
int valid_j_char(char msb, char lsb)
{
return !(msb == '\0' &&
(lsb < ' ' || lsb == '*' || lsb == '/' || lsb == ':' ||
lsb == ';' || lsb == '?' || lsb == '\\'));
}
int valid_p_char(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' &&
c <= 'z')
|| (c == '.') || (c == '_') || (c == '-');
}
char *iso_d_str(const char *src, int size, int pad)
{
char *dest = strlen(src) > size ||
pad ? malloc(size + 1) : malloc(strlen(src) + 1);
int i;
/* Try converting to upper-case before validating. */
for (i = 0; i < size && src[i]; i++) {
char c = toupper(src[i]);
dest[i] = valid_d_char(c) ? c : '_';
}
/* Optionally pad with spaces and terminate with NULL. */
if (pad)
while (i < size)
dest[i++] = ' ';
dest[i] = '\0';
return dest;
}
char *iso_a_str(const char *src, int size)
{
char *dest = malloc(size + 1);
int i;
for (i = 0; i < size && src[i]; i++) {
char c = toupper(src[i]);
dest[i] = valid_a_char(c) ? c : '_';
}
while (i < size)
dest[i++] = ' ';
dest[i] = '\0';
return dest;
}
uint16_t *iso_j_str(const char *src)
{
int size = strlen(src) * 2;
uint16_t *dest = malloc(size + 2);
char *cpy, *in, *out;
size_t inleft, outleft;
iconv_t cd;
/* If the conversion is unavailable, return NULL. Obviously,
nl_langinfo(..) requires the locale to be set correctly. */
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
return NULL;
}
/* In, out, inleft and outleft will be updated by iconv, that's why we
need separate variables. */
cpy = strdup(src); /* iconv doesn't take const * chars, so we need our
* own copy. */
in = cpy;
out = (char*)dest;
inleft = strlen(cpy);
outleft = size;
iconv(cd, &in, &inleft, &out, &outleft);
/* Since we need to pad with NULLs, we can pad up to and including the
terminating NULLs. */
outleft += 2;
while (outleft) {
*out++ = '\0';
outleft--;
}
iconv_close(cd);
free(cpy);
return dest;
}
char *iso_1_fileid(const char *src)
{
char *dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
(;1) + 1 (\0) */
char *dot = strrchr(src, '.'); /* Position of the last dot in the
filename, will be used to calculate
lname and lext. */
int lname, lext, pos, i;
lext = dot ? strlen(dot + 1) : 0;
lname = strlen(src) - lext - (dot ? 1 : 0);
/* If we can't build a filename, return NULL. */
if (lname == 0 && lext == 0) {
free(dest);
return NULL;
}
pos = 0;
/* Convert up to 8 characters of the filename. */
for (i = 0; i < lname && i < 8; i++) {
char c = toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
/* This dot is mandatory, even if there is no extension. */
dest[pos++] = '.';
/* Convert up to 3 characters of the extension, if any. */
for (i = 0; i < lext && i < 3; i++) {
char c = toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
/* File versions are mandatory, even if they aren't used. */
dest[pos++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
char *iso_2_fileid(const char *src)
{
char *dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
(;1) + 1 (\0) */
char *dot = strrchr(src, '.');
int lname, lext, lnname, lnext, pos, i;
/* Since the maximum length can be divided freely over the name and
extension, we need to calculate their new lengths (lnname and
lnext). If the original filename is too long, we start by trimming
the extension, but keep a minimum extension length of 3. */
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 30) ? 30 : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 31 &&
lext > 3) ? (lname < 27 ? 30 - lname : 3) : lext;
lnname = (strlen(src) > 31) ? 30 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
free(dest);
return NULL;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
char c = toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = '.';
/* Convert up to lnext characters of the extension, if any. */
for (i = 0; i < lnext; i++) {
char c = toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = ';';
dest[pos++] = '1';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
uint16_t *iso_j_id(char *src)
{
char *dest = malloc(136); /* 136 = 128 (name + ext) + 2 (\0 .) +
4 (\0 ; \0 1) + 2 (\0 \0) */
char *dot = strrchr(src, '.');
int lname, lext, lcname, lcext, lnname, lnext, pos, i;
size_t inleft, outleft;
char *cname, *cext, *in, *out;
iconv_t cd;
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
}
if (lname == 0 && lext == 0) {
free(dest);
return NULL;
}
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
return NULL;
}
/* We need to convert the name and extension first, in order to
calculate the number of characters they have. */
cname = malloc(lname * 2);
in = src;
out = cname;
inleft = lname;
outleft = lname * 2;
iconv(cd, &in, &inleft, &out, &outleft);
lcname = (lname * 2) - outleft;
iconv_close(cd);
if (lext) {
cd = iconv_open("UCS-2BE", nl_langinfo(CODESET));
if (cd == (iconv_t) - 1) {
free(dest);
free(cname);
return NULL;
}
cext = malloc(lext * 2);
in = dot + 1;
out = cext;
inleft = lext;
outleft = lext * 2;
iconv(cd, &in, &inleft, &out, &outleft);
lcext = (lext * 2) - outleft;
iconv_close(cd);
} else
lcext = 0;
/* Again, divide the available characters over name and extension, but
keep a minimum of three characters for the new extension. */
lnext = (lcname + lcext > 128 &&
lcext > 6) ? (lcname < 122 ? 128 - lcname : 6) : lcext;
lnname = (lcname + lcext > 128) ? 128 - lnext : lcname;
pos = 0;
/* Copy up to lnname bytes from the converted filename. */
for (i = 0; i < lnname; i = i + 2)
if (valid_j_char(cname[i], cname[i + 1])) {
dest[pos++] = cname[i];
dest[pos++] = cname[i + 1];
} else {
dest[pos++] = '\0';
dest[pos++] = '_';
}
/* Dot is now a 16 bit separator. */
dest[pos++] = '\0';
dest[pos++] = '.';
/* Copy up to lnext bytes from the converted extension, if any. */
for (i = 0; i < lnext; i = i + 2)
if (valid_j_char(cext[i], cext[i + 1])) {
dest[pos++] = cext[i];
dest[pos++] = cext[i + 1];
} else {
dest[pos++] = '\0';
dest[pos++] = '_';
}
/* Again, 2 bytes per character. */
dest[pos++] = '\0';
dest[pos++] = ';';
dest[pos++] = '\0';
dest[pos++] = '1';
dest[pos++] = '\0';
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(cname);
if (lext)
free(cext);
/* Fill in the size in bytes (including the terminating NULLs) of the
destination string. */
return (uint16_t *) dest;
}
char *iso_p_filename(const char *src)
{
char *dest = malloc(251); /* We can fit up to 250 characters in
a Rock Ridge name entry. */
char *dot = strrchr(src, '.');
int lname, lext, lnname, lnext, pos, i;
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > 250) ? 250 : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > 250 &&
lext > 3) ? (lname < 246 ? 249 - lname : 3) : lext;
lnname = (strlen(src) > 250) ? 249 - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
free(dest);
return NULL;
}
pos = 0;
for (i = 0; i < lnname; i++)
dest[pos++] = valid_p_char(src[i]) ? src[i] : '_';
if (lnext) {
dest[pos++] = '.';
for (i = 0; i < lnext; i++)
dest[pos++] =
valid_p_char(src[lname + 1 + i]) ?
src[lname + 1 + i] : '_';
}
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
return dest;
}
char *iso_p_dirname(const char *src)
{
char *dest = strlen(src) > 250 ? malloc(251) : malloc(strlen(src) + 1);
int i;
if (strlen(src) == 0) {
free(dest);
return NULL;
}
for (i = 0; i < 250 && src[i]; i++)
dest[i] = valid_p_char(src[i]) ? src[i] : '_';
dest[i] = '\0';
return dest;
}
char *iso_pathname(const char *dir, const char *file)
{
char *path = malloc(strlen(dir) + strlen(file) + 2);
strcpy(path, dir);
path[strlen(dir)] = '/';
strcpy(path + strlen(dir) + 1, file);
return path;
}
void iso_a_strcpy(unsigned char *dest, int size, const char *src)
{
int i = 0, d = 0;
if (src)
for (; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_a_char(s))
dest[d++] = s;
else
dest[d++] = '_';
}
for (; i < size; ++i) {
/* pad with spaces */
dest[d++] = ' ';
}
}
void iso_d_strcpy(unsigned char *dest, int size, const char *src)
{
int i = 0, d = 0;
if (src)
for (; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_d_char(s))
dest[d++] = s;
else
dest[d++] = '_';
}
for (; i < size; ++i) {
/* pad with spaces */
dest[d++] = ' ';
}
}
char *iso_a_strndup(const char *src, int size)
{
int i, d;
char *out;
out = malloc(size + 1);
for (i = d = 0; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_a_char(s))
out[d++] = s;
else
out[d++] = '_';
}
out[d++] = '\0';
out = realloc(out, d); /* shrink the buffer to what we used */
return out;
}
char *iso_d_strndup(const char *src, int size)
{
int i, d;
char *out;
out = malloc(size + 1);
for (i = d = 0; i < size && *src; ++i, ++src) {
char s = toupper(*src);
if (valid_d_char(s))
out[d++] = s;
else
out[d++] = '_';
}
out[d++] = '\0';
out = realloc(out, d); /* shrink the buffer to what we used */
return out;
}
char *iso_strconcat(char sep, const char *a, const char *b)
{
char *out;
int la, lb;
la = strlen(a);
lb = strlen(b);
out = malloc(la + lb + 1 + (sep ? 1 : 0));
memcpy(out, a, la);
memcpy(out + la + (sep ? 1 : 0), b, lb);
if (sep)
out[la] = sep;
out[la + lb + (sep ? 1 : 0)] = '\0';
return out;
}
char *iso_strdup(const char *src)
{
return src ? strdup(src) : NULL;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[i] = (num >> (8 * i)) & 0xff;
}
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
}
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
{
iso_lsb(buf, num, bytes);
iso_msb(buf+bytes, num, bytes);
}
void iso_datetime_7(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
if (!tzsetup) {
tzset();
tzoffset = -timezone / 60 / 15;
if (tzoffset < -48)
tzoffset += 101;
tzsetup = 1;
}
localtime_r(&t, &tm);
buf[0] = tm.tm_year;
buf[1] = tm.tm_mon + 1;
buf[2] = tm.tm_mday;
buf[3] = tm.tm_hour;
buf[4] = tm.tm_min;
buf[5] = tm.tm_sec;
buf[6] = tzoffset;
}
time_t iso_datetime_read_7(const uint8_t *buf)
{
struct tm tm;
tm.tm_year = buf[0];
tm.tm_mon = buf[1] + 1;
tm.tm_mday = buf[2];
tm.tm_hour = buf[3];
tm.tm_min = buf[4];
tm.tm_sec = buf[5];
return mktime(&tm) - buf[6] * 60 * 60;
}
void iso_datetime_17(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
char c[5];
if (t == (time_t) - 1) {
/* unspecified time */
memset(buf, '0', 16);
buf[16] = 0;
} else {
if (!tzsetup) {
tzset();
tzoffset = -timezone / 60 / 15;
if (tzoffset < -48)
tzoffset += 101;
tzsetup = 1;
}
localtime_r(&t, &tm);
/* year */
sprintf(c, "%04d", tm.tm_year + 1900);
memcpy(&buf[0], c, 4);
/* month */
sprintf(c, "%02d", tm.tm_mon + 1);
memcpy(&buf[4], c, 2);
/* day */
sprintf(c, "%02d", tm.tm_mday);
memcpy(&buf[6], c, 2);
/* hour */
sprintf(c, "%02d", tm.tm_hour);
memcpy(&buf[8], c, 2);
/* minute */
sprintf(c, "%02d", tm.tm_min);
memcpy(&buf[10], c, 2);
/* second */
sprintf(c, "%02d", MIN(59, tm.tm_sec));
memcpy(&buf[12], c, 2);
/* hundreths */
memcpy(&buf[14], "00", 2);
/* timezone */
buf[16] = tzoffset;
}
}
time_t iso_datetime_read_17(const uint8_t *buf)
{
struct tm tm;
sscanf((char*)&buf[0], "%4d", &tm.tm_year);
sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
sscanf((char*)&buf[10], "%2d", &tm.tm_min);
sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
return mktime(&tm) - buf[16] * 60 * 60;
}
void iso_split_filename(char *name, char **ext)
{
char *r;
r = strrchr(name, '.');
if (r) {
*r = '\0';
*ext = r + 1;
} else
*ext = "";
}
void iso_filecpy(unsigned char *buf, int size, const char *name, int version)
{
char v[6];
int nl, vl;
assert(version >= 0);
assert(version < 0x8000);
nl = strlen(name);
memcpy(buf, name, nl);
if (!version)
assert(size >= strlen(name));
else {
sprintf(v, "%d", version);
vl = strlen(v);
assert(size >= nl + vl + 1);
buf[nl] = ';';
memcpy(&buf[nl + 1], v, vl);
nl += vl + 1;
}
/* pad with spaces */
if (nl < size)
memset(&buf[nl], ' ', size - nl);
}
size_t ucslen(const uint16_t *str)
{
int i;
for (i=0; str[i]; i++)
;
return i;
}
int ucscmp(const uint16_t *s1, const uint16_t *s2)
{
int i;
for (i=0; 1; i++) {
if (s1[i] < s2[i]) {
return -1;
} else if (s1[i] > s2[i]) {
return 1;
} else if (s1[i] == 0 && s2[i] == 0) {
break;
}
}
return 0;
}
uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[i]) << (i*8);
}
return ret;
}
uint32_t iso_read_msb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[bytes-i-1]) << (i*8);
}
return ret;
}
uint32_t iso_read_bb(const uint8_t *buf, int bytes)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
assert(v1 == v2);
return v1;
}

156
libisofs/util.h Executable file
View File

@ -0,0 +1,156 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/**
* Utility functions for the Libisofs library.
*/
#ifndef __ISO_UTIL
#define __ISO_UTIL
#include <stdint.h>
/**
* Checks if the given character is a valid d-character.
*/
int valid_d_char(char c);
/**
* Checks if the given character is a valid a-character.
*/
int valid_a_char(char c);
/**
* Checks if the given character is part of the Joliet Allowed Character Set.
*/
int valid_j_char(char msb, char lsb);
/**
* Checks if the given character is part of the POSIX Portable Filename Character Set.
*/
int valid_p_char(char c);
/**
* Build a string of d-characters of maximum size 'size', optionally padded with spaces if necessary.
* Can be used to create directory identifiers if padding is turned off.
*/
char *iso_d_str(const char *src, int size, int pad);
/**
* Build a string of a-characters of maximum size 'size', padded with spaces if necessary.
*/
char *iso_a_str(const char *src, int size);
/**
* Build a string of j-characters of maximum size 'size', padded with NULLs if necessary.
* Requires the locale to be set correctly.
* @return NULL if the conversion from the current codeset to UCS-2BE is not available.
*/
uint16_t *iso_j_str(const char *src);
/**
* Create a level 1 file identifier that consists of a name, extension and version number.
* The resulting string will have a file name of maximum length 8, followed by a separator (.),
* an optional extension of maximum length 3, followed by a separator (;) and a version number (digit 1).
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_1_fileid(const char *src);
/**
* Create a level 2 file identifier that consists of a name, extension and version number.
* The combined file name and extension length will not exceed 30, the name and extension will be separated (.),
* and the extension will be followed by a separator (;) and a version number (digit 1).
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_2_fileid(const char *src);
/**
* Create a Joliet file or directory identifier that consists of a name, extension and version number.
* The combined name and extension length will not exceed 128 bytes, the name and extension will be separated (.),
* and the extension will be followed by a separator (;) and a version number (digit 1).
* All characters consist of 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL.
* Requires the locale to be set correctly.
* @param size will be set to the size (in bytes) of the identifier.
* @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available.
*/
uint16_t *iso_j_id(char *src);
/**
* Create a POSIX portable file name that consists of a name and extension.
* The resulting file name will not exceed 250 characters.
* @return NULL if the original name and extension both are of length 0.
*/
char *iso_p_filename(const char *src);
/**
* Create a POSIX portable directory name.
* The resulting directory name will not exceed 250 characters.
* @return NULL if the original name is of length 0.
*/
char *iso_p_dirname(const char *src);
/**
* Build a pathname out of a directory and file name.
*/
char *iso_pathname(const char *dir, const char *file);
#include <time.h>
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
/** Copy a string of a-characters, padding the unused part of the
destination buffer */
void iso_a_strcpy(unsigned char *dest, int size, const char *src);
/** Copy a string of d-characters, padding the unused part of the
destination buffer */
void iso_d_strcpy(unsigned char *dest, int size, const char *src);
/** Returns a null terminated string containing the first 'size' a-characters
from the source */
char *iso_a_strndup(const char *src, int size);
/** Returns a null terminated string containing the first 'size' d-characters
from the source */
char *iso_d_strndup(const char *src, int size);
char *iso_strconcat(char sep, const char *a, const char *b);
char *iso_strdup(const char *src);
void iso_lsb(uint8_t *buf, uint32_t num, int bytes);
void iso_msb(uint8_t *buf, uint32_t num, int bytes);
void iso_bb(uint8_t *buf, uint32_t num, int bytes);
uint32_t iso_read_lsb(const uint8_t *buf, int bytes);
uint32_t iso_read_msb(const uint8_t *buf, int bytes);
uint32_t iso_read_bb(const uint8_t *buf, int bytes);
/** Records the date/time into a 7 byte buffer (9.1.5) */
void iso_datetime_7(unsigned char *buf, time_t t);
/** Records the date/time into a 17 byte buffer (8.4.26.1) */
void iso_datetime_17(unsigned char *buf, time_t t);
time_t iso_datetime_read_7(const uint8_t *buf);
time_t iso_datetime_read_17(const uint8_t *buf);
/** Removes the extension from the name, and returns the extension. The
returned extension should _not_ be freed! */
void iso_split_filename(char *name, char **ext);
void iso_filecpy(unsigned char *buf, int size, const char *name, int version);
int iso_filename_len(const char *name, int iso_level);
/* replacement for strlen for joliet names (wcslen doesn't work on machines
* with 32-bit wchar_t */
size_t ucslen(const uint16_t *);
/* replacement for strcmp for joliet names */
int ucscmp(const uint16_t*, const uint16_t*);
#define DIV_UP(n,div) ( ((n)+(div)-1) / (div) )
#define ROUND_UP(n,mul) ( DIV_UP(n,mul) * (mul) )
#endif /* __ISO_UTIL */

72
libisofs/volume.c Executable file
View File

@ -0,0 +1,72 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <stdlib.h>
#include "libisofs.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
#include <string.h>
struct iso_volset *iso_volset_new(struct iso_volume *vol, const char *id)
{
struct iso_volset *volset = calloc(1, sizeof(struct iso_volset));
volset->volset_size = 1;
volset->volume = malloc(sizeof(void *));
volset->volume[0] = vol;
volset->volset_id.cstr = strdup(id);
volset->volset_id.jstr = iso_j_str(id);
return volset;
}
struct iso_volume *iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id)
{
struct iso_volume *volume;
volume = calloc(1, sizeof(struct iso_volume));
volume->refcount = 1;
/* Get a new root directory. */
volume->root = iso_tree_new_root(volume);
/* Set these fields, if given. */
if (volume_id != NULL) {
volume->volume_id.cstr = strdup(volume_id);
volume->volume_id.jstr = iso_j_str(volume_id);
}
if (publisher_id != NULL) {
volume->publisher_id.cstr = strdup(publisher_id);
volume->publisher_id.jstr = iso_j_str(publisher_id);
}
if (data_preparer_id != NULL) {
volume->data_preparer_id.cstr = strdup(data_preparer_id);
volume->data_preparer_id.jstr = iso_j_str(data_preparer_id);
}
return volume;
}
void iso_volume_free(struct iso_volume *volume)
{
/* Only free if no references are in use. */
if (--volume->refcount < 1) {
iso_tree_free(volume->root);
free(volume->volume_id.cstr);
free(volume->volume_id.jstr);
free(volume->publisher_id.cstr);
free(volume->publisher_id.jstr);
free(volume->data_preparer_id.cstr);
free(volume->data_preparer_id.jstr);
free(volume);
}
}
struct iso_tree_dir *iso_volume_get_root(const struct iso_volume *volume)
{
return volume->root;
}

55
libisofs/volume.h Executable file
View File

@ -0,0 +1,55 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet sts=8 ts=8 sw=8 : */
/**
* Extra declarations for use with the iso_volume structure.
*/
#ifndef __ISO_VOLUME
#define __ISO_VOLUME
#include "libisofs.h"
struct iso_string
{
char *cstr;
uint16_t *jstr;
};
/**
* Data volume.
*/
struct iso_volume
{
int refcount; /**< Number of used references to th
volume. */
struct iso_tree_dir *root; /**< Root of the directory tree for the
volume. */
unsigned rockridge:1;
unsigned joliet:1;
unsigned iso_level:2;
struct iso_string volume_id; /**< Volume identifier. */
struct iso_string publisher_id; /**< Volume publisher. */
struct iso_string data_preparer_id; /**< Volume data preparer. */
};
/**
* A set of data volumes.
*/
struct iso_volset
{
int refcount;
struct iso_volume **volume; /**< The volumes belonging to this
volume set. */
int volset_size; /**< The number of volumes in this
volume set. */
struct iso_string volset_id; /**< The id of this volume set, encoded
in the current locale. */
};
#endif /* __ISO_VOLUME */

1178
libisofs/writer.c Executable file

File diff suppressed because it is too large Load Diff

103
libisofs/writer.h Executable file
View File

@ -0,0 +1,103 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#ifndef __WRITER
#define __WRITER
#include "libisofs.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
enum iso_write_state
{
ISO_WRITE_BEFORE,
ISO_WRITE_SYSTEM_AREA,
ISO_WRITE_PRI_VOL_DESC,
ISO_WRITE_VOL_DESC_TERMINATOR,
ISO_WRITE_L_PATH_TABLE,
ISO_WRITE_M_PATH_TABLE,
ISO_WRITE_DIR_RECORDS,
ISO_WRITE_ER_AREA,
ISO_WRITE_FILES,
ISO_WRITE_DONE
};
/** File Flags (9.1.6) */
enum
{
ISO_FILE_FLAG_NORMAL = 0,
ISO_FILE_FLAG_HIDDEN = 1 << 0,
ISO_FILE_FLAG_DIRECTORY = 1 << 1,
ISO_FILE_FLAG_ASSOCIATED = 1 << 2,
ISO_FILE_FLAG_RECORD = 1 << 3,
ISO_FILE_FLAG_PROTECTION = 1 << 4,
ISO_FILE_FLAG_MULTIEXTENT = 1 << 7
};
struct iso_write_target
{
struct iso_volumeset *volset;
int volume;
/* the time at which the writing began */
time_t now;
/* size of a physical sector on the target disc */
int phys_sector_size;
/* size of the total output */
int total_size;
/* when compiling the iso, this is the next available logical block.
when writing the iso, this is the next block to write. */
int logical_block;
/* The number of Logical Blocks for the Volume Space */
int volume_space_size;
/* The Logical Block size */
int logical_block_size;
/* The Path Table size */
int path_table_size;
/* Locations of Type L Path Table (Logical Block Number) */
int l_path_table_pos;
/* Locations of Type M Path Table (Logical Block Number) */
int m_path_table_pos;
/* Location of the SUSP ER area (Logical Block Number) */
int susp_er_pos;
/* Current file being written in iso_write_files() */
struct iso_tree_file **current_file;
FILE *current_fd;
/* what we're doing when the generate function gets called next */
enum iso_write_state state;
union
{
struct iso_state_system_area
{
/* how many sectors in the system area have been
written */
int sectors;
} system_area;
struct iso_state_path_tables
{
/* how many sectors in the path table area have been
written */
int sectors;
} path_tables;
struct iso_state_dir_records
{
/* how many sectors in the directory records area have
been written */
int sectors;
} dir_records;
struct iso_state_files
{
/* how many sectors in the current file have been
written */
int sectors;
} files;
} state_data;
};
#endif /* __WRITER */