Initial import
This commit is contained in:
4
libisofs/Makefile
Executable file
4
libisofs/Makefile
Executable file
@ -0,0 +1,4 @@
|
||||
all clean:
|
||||
$(MAKE) -C .. -$(MAKEFLAGS) $@
|
||||
|
||||
.PHONY: all clean
|
44
libisofs/Makefile.am
Executable file
44
libisofs/Makefile.am
Executable 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
1506
libisofs/ecma119.c
Executable file
File diff suppressed because it is too large
Load Diff
220
libisofs/ecma119.h
Executable file
220
libisofs/ecma119.h
Executable 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
14
libisofs/errors.c
Executable 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
19
libisofs/errors.h
Executable 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
243
libisofs/libisofs.h
Executable 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
299
libisofs/rockridge.c
Executable 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
30
libisofs/rockridge.h
Executable 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
340
libisofs/struct.c
Executable 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
77
libisofs/struct.h
Executable 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
312
libisofs/susp.c
Executable 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
75
libisofs/susp.h
Executable 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
138
libisofs/test.c
Executable 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
409
libisofs/tree.c
Executable 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
259
libisofs/tree.h
Executable 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
710
libisofs/util.c
Executable 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
156
libisofs/util.h
Executable 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
72
libisofs/volume.c
Executable 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
55
libisofs/volume.h
Executable 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
1178
libisofs/writer.c
Executable file
File diff suppressed because it is too large
Load Diff
103
libisofs/writer.h
Executable file
103
libisofs/writer.h
Executable 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 */
|
Reference in New Issue
Block a user