Marked libisofs SVN copy as outdated (use bzr on lp)

This commit is contained in:
2010-02-08 11:10:47 +00:00
parent 7d413dcb12
commit 517adbd8e5
476 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,328 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* Synchronized ring buffer, works with a writer thread and a read thread.
*
* TODO #00010 : optimize ring buffer
* - write/read at the end of buffer requires a second mutex_lock, even if
* there's enought space/data at the beginning
* - pre-buffer for writes < BLOCK_SIZE
*
*/
#include "buffer.h"
#include "libburn/libburn.h"
#include "ecma119.h"
#include <pthread.h>
#include <string.h>
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
struct iso_ring_buffer
{
uint8_t *buf;
/*
* Max number of bytes in buffer
*/
size_t cap;
/*
* Number of bytes available.
*/
size_t size;
/* position for reading and writing, offset from buf */
size_t rpos;
size_t wpos;
/*
* flags to report if read or writer threads ends execution
* 0 not finished, 1 finished ok, 2 finish with error
*/
unsigned int rend :2;
unsigned int wend :2;
/* just for statistical purposes */
unsigned int times_full;
unsigned int times_empty;
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t full;
};
/**
* Create a new buffer.
*
* The created buffer should be freed with iso_ring_buffer_free()
*
* @param size
* Number of blocks in buffer. You should supply a number >= 32, otherwise
* size will be ignored and 32 will be used by default, which leads to a
* 64 KiB buffer.
* @return
* 1 success, < 0 error
*/
int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf)
{
IsoRingBuffer *buffer;
if (rbuf == NULL) {
return ISO_NULL_POINTER;
}
buffer = malloc(sizeof(IsoRingBuffer));
if (buffer == NULL) {
return ISO_OUT_OF_MEM;
}
buffer->cap = (size > 32 ? size : 32) * BLOCK_SIZE;
buffer->buf = malloc(buffer->cap);
if (buffer->buf == NULL) {
free(buffer);
return ISO_OUT_OF_MEM;
}
buffer->size = 0;
buffer->wpos = 0;
buffer->rpos = 0;
buffer->times_full = 0;
buffer->times_empty = 0;
buffer->rend = buffer->wend = 0;
/* init mutex and waiting queues */
pthread_mutex_init(&buffer->mutex, NULL);
pthread_cond_init(&buffer->empty, NULL);
pthread_cond_init(&buffer->full, NULL);
*rbuf = buffer;
return ISO_SUCCESS;
}
void iso_ring_buffer_free(IsoRingBuffer *buf)
{
if (buf == NULL) {
return;
}
free(buf->buf);
pthread_mutex_destroy(&buf->mutex);
pthread_cond_destroy(&buf->empty);
pthread_cond_destroy(&buf->full);
free(buf);
}
/**
* Write count bytes into buffer. It blocks until all bytes where written or
* reader close the buffer.
*
* @param buf
* the buffer
* @param data
* pointer to a memory region of at least coun bytes, from which data
* will be read.
* @param
* Number of bytes to write
* @return
* 1 succes, 0 read finished, < 0 error
*/
int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count)
{
size_t len;
int bytes_write = 0;
if (buf == NULL || data == NULL) {
return ISO_NULL_POINTER;
}
while (bytes_write < count) {
pthread_mutex_lock(&buf->mutex);
while (buf->size == buf->cap) {
/*
* Note. There's only a writer, so we have no race conditions.
* Thus, the while(buf->size == buf->cap) is used here
* only to propertly detect the reader has been cancelled
*/
if (buf->rend) {
/* the read procces has been finished */
pthread_mutex_unlock(&buf->mutex);
return 0;
}
buf->times_full++;
/* wait until space available */
pthread_cond_wait(&buf->full, &buf->mutex);
}
len = MIN(count - bytes_write, buf->cap - buf->size);
if (buf->wpos + len > buf->cap) {
len = buf->cap - buf->wpos;
}
memcpy(buf->buf + buf->wpos, data + bytes_write, len);
buf->wpos = (buf->wpos + len) % (buf->cap);
bytes_write += len;
buf->size += len;
/* wake up reader */
pthread_cond_signal(&buf->empty);
pthread_mutex_unlock(&buf->mutex);
}
return ISO_SUCCESS;
}
/**
* Read count bytes from the buffer into dest. It blocks until the desired
* bytes has been read. If the writer finishes before outputting enought
* bytes, 0 (EOF) is returned, the number of bytes already read remains
* unknown.
*
* @return
* 1 success, 0 EOF, < 0 error
*/
int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count)
{
size_t len;
int bytes_read = 0;
if (buf == NULL || dest == NULL) {
return ISO_NULL_POINTER;
}
while (bytes_read < count) {
pthread_mutex_lock(&buf->mutex);
while (buf->size == 0) {
/*
* Note. There's only a reader, so we have no race conditions.
* Thus, the while(buf->size == 0) is used here just to ensure
* a reader detects the EOF propertly if the writer has been
* canceled while the reader was waiting
*/
if (buf->wend) {
/* the writer procces has been finished */
pthread_mutex_unlock(&buf->mutex);
return 0; /* EOF */
}
buf->times_empty++;
/* wait until data available */
pthread_cond_wait(&buf->empty, &buf->mutex);
}
len = MIN(count - bytes_read, buf->size);
if (buf->rpos + len > buf->cap) {
len = buf->cap - buf->rpos;
}
memcpy(dest + bytes_read, buf->buf + buf->rpos, len);
buf->rpos = (buf->rpos + len) % (buf->cap);
bytes_read += len;
buf->size -= len;
/* wake up the writer */
pthread_cond_signal(&buf->full);
pthread_mutex_unlock(&buf->mutex);
}
return ISO_SUCCESS;
}
void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error)
{
pthread_mutex_lock(&buf->mutex);
buf->wend = error ? 2 : 1;
/* ensure no reader is waiting */
pthread_cond_signal(&buf->empty);
pthread_mutex_unlock(&buf->mutex);
}
void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error)
{
pthread_mutex_lock(&buf->mutex);
if (buf->rend) {
/* reader already closed */
pthread_mutex_unlock(&buf->mutex);
return;
}
buf->rend = error ? 2 : 1;
/* ensure no writer is waiting */
pthread_cond_signal(&buf->full);
pthread_mutex_unlock(&buf->mutex);
}
/**
* Get the times the buffer was full.
*/
unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf)
{
return buf->times_full;
}
/**
* Get the times the buffer was empty.
*/
unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf)
{
return buf->times_empty;
}
/**
* Get the status of the buffer used by a burn_source.
*
* @param b
* A burn_source previously obtained with
* iso_image_create_burn_source().
* @param size
* Will be filled with the total size of the buffer, in bytes
* @param free_bytes
* Will be filled with the bytes currently available in buffer
* @return
* < 0 error, > 0 state:
* 1="active" : input and consumption are active
* 2="ending" : input has ended without error
* 3="failing" : input had error and ended,
* 5="abandoned" : consumption has ended prematurely
* 6="ended" : consumption has ended without input error
* 7="aborted" : consumption has ended after input error
*/
int iso_ring_buffer_get_status(struct burn_source *b, size_t *size,
size_t *free_bytes)
{
int ret;
IsoRingBuffer *buf;
if (b == NULL) {
return ISO_NULL_POINTER;
}
buf = ((Ecma119Image*)(b->data))->buffer;
/* get mutex */
pthread_mutex_lock(&buf->mutex);
if (size) {
*size = buf->cap;
}
if (free_bytes) {
*free_bytes = buf->cap - buf->size;
}
ret = (buf->rend ? 4 : 0) + (buf->wend + 1);
pthread_mutex_unlock(&buf->mutex);
return ret;
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_BUFFER_H_
#define LIBISO_BUFFER_H_
#include <stdlib.h>
#include <stdint.h>
#define BLOCK_SIZE 2048
typedef struct iso_ring_buffer IsoRingBuffer;
/**
* Create a new buffer.
*
* The created buffer should be freed with iso_ring_buffer_free()
*
* @param size
* Number of blocks in buffer. You should supply a number >= 32, otherwise
* size will be ignored and 32 will be used by default, which leads to a
* 64 KiB buffer.
* @return
* 1 success, < 0 error
*/
int iso_ring_buffer_new(size_t size, IsoRingBuffer **rbuf);
/**
* Free a given buffer
*/
void iso_ring_buffer_free(IsoRingBuffer *buf);
/**
* Write count bytes into buffer. It blocks until all bytes where written or
* reader close the buffer.
*
* @param buf
* the buffer
* @param data
* pointer to a memory region of at least coun bytes, from which data
* will be read.
* @param
* Number of bytes to write
* @return
* 1 succes, 0 read finished, < 0 error
*/
int iso_ring_buffer_write(IsoRingBuffer *buf, uint8_t *data, size_t count);
/**
* Read count bytes from the buffer into dest. It blocks until the desired
* bytes has been read. If the writer finishes before outputting enought
* bytes, 0 (EOF) is returned, the number of bytes already read remains
* unknown.
*
* @return
* 1 success, 0 EOF, < 0 error
*/
int iso_ring_buffer_read(IsoRingBuffer *buf, uint8_t *dest, size_t count);
/**
* Close the buffer (to be called by the writer).
* You have to explicity close the buffer when you don't have more data to
* write, otherwise reader will be waiting forever.
*
* @param error
* Writer has finished prematurely due to an error
*/
void iso_ring_buffer_writer_close(IsoRingBuffer *buf, int error);
/**
* Close the buffer (to be called by the reader).
* If for any reason you don't want to read more data, you need to call this
* to let the writer thread finish.
*
* @param error
* Reader has finished prematurely due to an error
*/
void iso_ring_buffer_reader_close(IsoRingBuffer *buf, int error);
/**
* Get the times the buffer was full.
*/
unsigned int iso_ring_buffer_get_times_full(IsoRingBuffer *buf);
/**
* Get the times the buffer was empty.
*/
unsigned int iso_ring_buffer_get_times_empty(IsoRingBuffer *buf);
#endif /*LIBISO_BUFFER_H_*/

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "builder.h"
#include "node.h"
#include "fsource.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
void iso_node_builder_ref(IsoNodeBuilder *builder)
{
++builder->refcount;
}
void iso_node_builder_unref(IsoNodeBuilder *builder)
{
if (--builder->refcount == 0) {
/* free private data */
builder->free(builder);
free(builder);
}
}
static
int default_create_file(IsoNodeBuilder *builder, IsoImage *image,
IsoFileSource *src, IsoFile **file)
{
int ret;
struct stat info;
IsoStream *stream;
IsoFile *node;
char *name;
if (builder == NULL || src == NULL || file == NULL) {
return ISO_NULL_POINTER;
}
ret = iso_file_source_stat(src, &info);
if (ret < 0) {
return ret;
}
/* this will fail if src is a dir, is not accessible... */
ret = iso_file_source_stream_new(src, &stream);
if (ret < 0) {
return ret;
}
/* take a ref to the src, as stream has taken our ref */
iso_file_source_ref(src);
name = iso_file_source_get_name(src);
ret = iso_node_new_file(name, stream, &node);
if (ret < 0) {
iso_stream_unref(stream);
free(name);
return ret;
}
/* fill node fields */
iso_node_set_permissions((IsoNode*)node, info.st_mode);
iso_node_set_uid((IsoNode*)node, info.st_uid);
iso_node_set_gid((IsoNode*)node, info.st_gid);
iso_node_set_atime((IsoNode*)node, info.st_atime);
iso_node_set_mtime((IsoNode*)node, info.st_mtime);
iso_node_set_ctime((IsoNode*)node, info.st_ctime);
iso_node_set_uid((IsoNode*)node, info.st_uid);
*file = node;
return ISO_SUCCESS;
}
static
int default_create_node(IsoNodeBuilder *builder, IsoImage *image,
IsoFileSource *src, IsoNode **node)
{
int ret;
struct stat info;
IsoNode *new;
char *name;
if (builder == NULL || src == NULL || node == NULL) {
return ISO_NULL_POINTER;
}
/* get info about source */
if (iso_tree_get_follow_symlinks(image)) {
ret = iso_file_source_stat(src, &info);
} else {
ret = iso_file_source_lstat(src, &info);
}
if (ret < 0) {
return ret;
}
name = iso_file_source_get_name(src);
new = NULL;
switch (info.st_mode & S_IFMT) {
case S_IFREG:
{
/* source is a regular file */
IsoStream *stream;
IsoFile *file;
ret = iso_file_source_stream_new(src, &stream);
if (ret < 0) {
break;
}
/* take a ref to the src, as stream has taken our ref */
iso_file_source_ref(src);
/* create the file */
ret = iso_node_new_file(name, stream, &file);
if (ret < 0) {
iso_stream_unref(stream);
}
new = (IsoNode*) file;
}
break;
case S_IFDIR:
{
/* source is a directory */
IsoDir *dir;
ret = iso_node_new_dir(name, &dir);
new = (IsoNode*)dir;
}
break;
case S_IFLNK:
{
/* source is a symbolic link */
char dest[PATH_MAX];
IsoSymlink *link;
ret = iso_file_source_readlink(src, dest, PATH_MAX);
if (ret < 0) {
break;
}
ret = iso_node_new_symlink(name, strdup(dest), &link);
new = (IsoNode*) link;
}
break;
case S_IFSOCK:
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
{
/* source is an special file */
IsoSpecial *special;
ret = iso_node_new_special(name, info.st_mode, info.st_rdev,
&special);
new = (IsoNode*) special;
}
break;
}
if (ret < 0) {
free(name);
return ret;
}
/* fill fields */
iso_node_set_permissions(new, info.st_mode);
iso_node_set_uid(new, info.st_uid);
iso_node_set_gid(new, info.st_gid);
iso_node_set_atime(new, info.st_atime);
iso_node_set_mtime(new, info.st_mtime);
iso_node_set_ctime(new, info.st_ctime);
iso_node_set_uid(new, info.st_uid);
*node = new;
return ISO_SUCCESS;
}
static
void default_free(IsoNodeBuilder *builder)
{
return;
}
int iso_node_basic_builder_new(IsoNodeBuilder **builder)
{
IsoNodeBuilder *b;
if (builder == NULL) {
return ISO_NULL_POINTER;
}
b = malloc(sizeof(IsoNodeBuilder));
if (b == NULL) {
return ISO_OUT_OF_MEM;
}
b->refcount = 1;
b->create_file_data = NULL;
b->create_node_data = NULL;
b->create_file = default_create_file;
b->create_node = default_create_node;
b->free = default_free;
*builder = b;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_BUILDER_H_
#define LIBISO_BUILDER_H_
/*
* Definitions for IsoNode builders.
*/
/*
* Some functions here will be moved to libisofs.h when we expose
* Builder.
*/
#include "libisofs.h"
#include "fsource.h"
typedef struct Iso_Node_Builder IsoNodeBuilder;
struct Iso_Node_Builder
{
/**
* Create a new IsoFile from an IsoFileSource. Name, permissions
* and other attributes are taken from src, but a regular file will
* always be created, even if src is another kind of file.
*
* In that case, if the implementation can't do the conversion, it
* should fail propertly.
*
* Note that the src is never unref, so you need to free it.
*
* @return
* 1 on success, < 0 on error
*/
int (*create_file)(IsoNodeBuilder *builder, IsoImage *image,
IsoFileSource *src, IsoFile **file);
/**
* Create a new IsoNode from a IsoFileSource. The type of the node to be
* created is determined from the type of the file source. Name,
* permissions and other attributes are taken from source file.
*
* Note that the src is never unref, so you need to free it.
*
* @return
* 1 on success, < 0 on error
*/
int (*create_node)(IsoNodeBuilder *builder, IsoImage *image,
IsoFileSource *src, IsoNode **node);
/**
* Free implementation specific data. Should never be called by user.
* Use iso_node_builder_unref() instead.
*/
void (*free)(IsoNodeBuilder *builder);
int refcount;
void *create_file_data;
void *create_node_data;
};
void iso_node_builder_ref(IsoNodeBuilder *builder);
void iso_node_builder_unref(IsoNodeBuilder *builder);
/**
* Create a new basic builder ...
*
* @return
* 1 success, < 0 error
*/
int iso_node_basic_builder_new(IsoNodeBuilder **builder);
#endif /*LIBISO_BUILDER_H_*/

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/**
* Private data for File IsoDataSource
*/
struct file_data_src
{
char *path;
int fd;
};
/**
* Increments the reference counting of the given IsoDataSource.
*/
void iso_data_source_ref(IsoDataSource *src)
{
src->refcount++;
}
/**
* Decrements the reference counting of the given IsoDataSource, freeing it
* if refcount reach 0.
*/
void iso_data_source_unref(IsoDataSource *src)
{
if (--src->refcount == 0) {
src->free_data(src);
free(src);
}
}
static
int ds_open(IsoDataSource *src)
{
int fd;
struct file_data_src *data;
if (src == NULL || src->data == NULL) {
return ISO_NULL_POINTER;
}
data = (struct file_data_src*) src->data;
if (data->fd != -1) {
return ISO_FILE_ALREADY_OPENED;
}
fd = open(data->path, O_RDONLY);
if (fd == -1) {
return ISO_FILE_ERROR;
}
data->fd = fd;
return ISO_SUCCESS;
}
static
int ds_close(IsoDataSource *src)
{
int ret;
struct file_data_src *data;
if (src == NULL || src->data == NULL) {
return ISO_NULL_POINTER;
}
data = (struct file_data_src*) src->data;
if (data->fd == -1) {
return ISO_FILE_NOT_OPENED;
}
/* close can fail if fd is not valid, but that should never happen */
ret = close(data->fd);
/* in any case we mark file as closed */
data->fd = -1;
return ret == 0 ? ISO_SUCCESS : ISO_FILE_ERROR;
}
static int ds_read_block(IsoDataSource *src, uint32_t lba, uint8_t *buffer)
{
struct file_data_src *data;
if (src == NULL || src->data == NULL || buffer == NULL) {
return ISO_NULL_POINTER;
}
data = (struct file_data_src*) src->data;
if (data->fd == -1) {
return ISO_FILE_NOT_OPENED;
}
/* goes to requested block */
if (lseek(data->fd, (off_t)lba * (off_t)2048, SEEK_SET) == (off_t) -1) {
return ISO_FILE_SEEK_ERROR;
}
/* TODO #00008 : guard against partial reads. */
if (read(data->fd, buffer, 2048) != 2048) {
return ISO_FILE_READ_ERROR;
}
return ISO_SUCCESS;
}
static
void ds_free_data(IsoDataSource *src)
{
struct file_data_src *data;
data = (struct file_data_src*)src->data;
/* close the file if needed */
if (data->fd != -1) {
close(data->fd);
}
free(data->path);
free(data);
}
/**
* Create a new IsoDataSource from a local file. This is suitable for
* accessing regular .iso images, or to acces drives via its block device
* and standard POSIX I/O calls.
*
* @param path
* The path of the file
* @param src
* Will be filled with the pointer to the newly created data source.
* @return
* 1 on success, < 0 on error.
*/
int iso_data_source_new_from_file(const char *path, IsoDataSource **src)
{
int ret;
struct file_data_src *data;
IsoDataSource *ds;
if (path == NULL || src == NULL) {
return ISO_NULL_POINTER;
}
/* ensure we have read access to the file */
ret = iso_eaccess(path);
if (ret < 0) {
return ret;
}
data = malloc(sizeof(struct file_data_src));
if (data == NULL) {
return ISO_OUT_OF_MEM;
}
ds = malloc(sizeof(IsoDataSource));
if (ds == NULL) {
free(data);
return ISO_OUT_OF_MEM;
}
/* fill data fields */
data->path = strdup(path);
if (data->path == NULL) {
free(data);
free(ds);
return ISO_OUT_OF_MEM;
}
data->fd = -1;
ds->version = 0;
ds->refcount = 1;
ds->data = data;
ds->open = ds_open;
ds->close = ds_close;
ds->read_block = ds_read_block;
ds->free_data = ds_free_data;
*src = ds;
return ISO_SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,476 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_ECMA119_H_
#define LIBISO_ECMA119_H_
#include "libisofs.h"
#include "util.h"
#include "buffer.h"
#include <stdint.h>
#include <pthread.h>
#define BLOCK_SIZE 2048
/**
* Holds the options for the image generation.
*/
struct iso_write_opts {
int level; /**< ISO level to write at. (ECMA-119, 10) */
/** Which extensions to support. */
unsigned int rockridge :1;
unsigned int joliet :1;
unsigned int iso1999 :1;
/* allways write timestamps in GMT */
unsigned int always_gmt :1;
/*
* Relaxed constraints. Setting any of these to 1 break the specifications,
* but it is supposed to work on most moderns systems. Use with caution.
*/
/**
* Omit the version number (";1") at the end of the ISO-9660 identifiers.
* Version numbers are usually not used.
*/
unsigned int omit_version_numbers :1;
/**
* Allow ISO-9660 directory hierarchy to be deeper than 8 levels.
*/
unsigned int allow_deep_paths :1;
/**
* Allow path in the ISO-9660 tree to have more than 255 characters.
*/
unsigned int allow_longer_paths :1;
/**
* Allow a single file or directory hierarchy to have up to 37 characters.
* This is larger than the 31 characters allowed by ISO level 2, and the
* extra space is taken from the version number, so this also forces
* omit_version_numbers.
*/
unsigned int max_37_char_filenames :1;
/**
* ISO-9660 forces filenames to have a ".", that separates file name from
* extension. libisofs adds it if original filename doesn't has one. Set
* this to 1 to prevent this behavior
*/
unsigned int no_force_dots :1;
/**
* Allow lowercase characters in ISO-9660 filenames. By default, only
* uppercase characters, numbers and a few other characters are allowed.
*/
unsigned int allow_lowercase :1;
/**
* Allow all ASCII characters to be appear on an ISO-9660 filename. Note
* that "/" and "\0" characters are never allowed, even in RR names.
*/
unsigned int allow_full_ascii :1;
/**
* Allow all characters to be part of Volume and Volset identifiers on
* the Primary Volume Descriptor. This breaks ISO-9660 contraints, but
* should work on modern systems.
*/
unsigned int relaxed_vol_atts :1;
/**
* Allow paths in the Joliet tree to have more than 240 characters.
*/
unsigned int joliet_longer_paths :1;
/** If files should be sorted based on their weight. */
unsigned int sort_files :1;
/**
* The following options set the default values for files and directory
* permissions, gid and uid. All these take one of three values: 0, 1 or 2.
* If 0, the corresponding attribute will be kept as setted in the IsoNode.
* Unless you have changed it, it corresponds to the value on disc, so it
* is suitable for backup purposes. If set to 1, the corresponding attrib.
* will be changed by a default suitable value. Finally, if you set it to
* 2, the attrib. will be changed with the value specified in the options
* below. Note that for mode attributes, only the permissions are set, the
* file type remains unchanged.
*/
unsigned int replace_dir_mode :2;
unsigned int replace_file_mode :2;
unsigned int replace_uid :2;
unsigned int replace_gid :2;
mode_t dir_mode; /** Mode to use on dirs when replace_dir_mode == 2. */
mode_t file_mode; /** Mode to use on files when replace_file_mode == 2. */
uid_t uid; /** uid to use when replace_uid == 2. */
gid_t gid; /** gid to use when replace_gid == 2. */
/**
* 0 to use IsoNode timestamps, 1 to use recording time, 2 to use
* values from timestamp field. This has only meaning if RR extensions
* are enabled.
*/
unsigned int replace_timestamps :2;
time_t timestamp;
/**
* Charset for the RR filenames that will be created.
* NULL to use default charset, the locale one.
*/
char *output_charset;
/**
* This flags control the type of the image to create. Libisofs support
* two kind of images: stand-alone and appendable.
*
* A stand-alone image is an image that is valid alone, and that can be
* mounted by its own. This is the kind of image you will want to create
* in most cases. A stand-alone image can be burned in an empty CD or DVD,
* or write to an .iso file for future burning or distribution.
*
* On the other side, an appendable image is not self contained, it refers
* to serveral files that are stored outside the image. Its usage is for
* multisession discs, where you add data in a new session, while the
* previous session data can still be accessed. In those cases, the old
* data is not written again. Instead, the new image refers to it, and thus
* it's only valid when appended to the original. Note that in those cases
* the image will be written after the original, and thus you will want
* to use a ms_block greater than 0.
*
* Note that if you haven't import a previous image (by means of
* iso_image_import()), the image will always be a stand-alone image, as
* there is no previous data to refer to.
*/
unsigned int appendable : 1;
/**
* Start block of the image. It is supposed to be the lba where the first
* block of the image will be written on disc. All references inside the
* ISO image will take this into account, thus providing a mountable image.
*
* For appendable images, that are written to a new session, you should
* pass here the lba of the next writable address on disc.
*
* In stand alone images this is usually 0. However, you may want to
* provide a different ms_block if you don't plan to burn the image in the
* first session on disc, such as in some CD-Extra disc whether the data
* image is written in a new session after some audio tracks.
*/
uint32_t ms_block;
/**
* When not NULL, it should point to a buffer of at least 64KiB, where
* libisofs will write the contents that should be written at the beginning
* of a overwriteable media, to grow the image. The growing of an image is
* a way, used by first time in growisofs by Andy Polyakov, to allow the
* appending of new data to non-multisession media, such as DVD+RW, in the
* same way you append a new session to a multisession disc, i.e., without
* need to write again the contents of the previous image.
*
* Note that if you want this kind of image growing, you will also need to
* set appendable to "1" and provide a valid ms_block after the previous
* image.
*
* You should initialize the buffer either with 0s, or with the contents of
* the first blocks of the image you're growing. In most cases, 0 is good
* enought.
*/
uint8_t *overwrite;
/**
* Size, in number of blocks, of the FIFO buffer used between the writer
* thread and the burn_source. You have to provide at least a 32 blocks
* buffer.
*/
size_t fifo_size;
};
typedef struct ecma119_image Ecma119Image;
typedef struct ecma119_node Ecma119Node;
typedef struct joliet_node JolietNode;
typedef struct iso1999_node Iso1999Node;
typedef struct Iso_File_Src IsoFileSrc;
typedef struct Iso_Image_Writer IsoImageWriter;
struct ecma119_image
{
IsoImage *image;
Ecma119Node *root;
unsigned int iso_level :2;
/* extensions */
unsigned int rockridge :1;
unsigned int joliet :1;
unsigned int eltorito :1;
unsigned int iso1999 :1;
/* allways write timestamps in GMT */
unsigned int always_gmt :1;
/* relaxed constraints */
unsigned int omit_version_numbers :1;
unsigned int allow_deep_paths :1;
unsigned int allow_longer_paths :1;
unsigned int max_37_char_filenames :1;
unsigned int no_force_dots :1;
unsigned int allow_lowercase :1;
unsigned int allow_full_ascii :1;
unsigned int relaxed_vol_atts : 1;
/** Allow paths on Joliet tree to be larger than 240 bytes */
unsigned int joliet_longer_paths :1;
/*
* Mode replace. If one of these flags is set, the correspodent values are
* replaced with values below.
*/
unsigned int replace_uid :1;
unsigned int replace_gid :1;
unsigned int replace_file_mode :1;
unsigned int replace_dir_mode :1;
unsigned int replace_timestamps :1;
uid_t uid;
gid_t gid;
mode_t file_mode;
mode_t dir_mode;
time_t timestamp;
/**
* if sort files or not. Sorting is based of the weight of each file
*/
int sort_files;
/**
* In the CD, each file must have an unique inode number. So each
* time we add a new file, this is incremented.
*/
ino_t ino;
char *input_charset;
char *output_charset;
unsigned int appendable : 1;
uint32_t ms_block; /**< start block for a ms image */
time_t now; /**< Time at which writing began. */
/** Total size of the output. This only includes the current volume. */
off_t total_size;
uint32_t vol_space_size;
/* Bytes already written, just for progress notification */
off_t bytes_written;
int percent_written;
/*
* Block being processed, either during image writing or structure
* size calculation.
*/
uint32_t curblock;
/*
* number of dirs in ECMA-119 tree, computed together with dir position,
* and needed for path table computation in a efficient way
*/
size_t ndirs;
uint32_t path_table_size;
uint32_t l_path_table_pos;
uint32_t m_path_table_pos;
/*
* Joliet related information
*/
JolietNode *joliet_root;
size_t joliet_ndirs;
uint32_t joliet_path_table_size;
uint32_t joliet_l_path_table_pos;
uint32_t joliet_m_path_table_pos;
/*
* ISO 9660:1999 related information
*/
Iso1999Node *iso1999_root;
size_t iso1999_ndirs;
uint32_t iso1999_path_table_size;
uint32_t iso1999_l_path_table_pos;
uint32_t iso1999_m_path_table_pos;
/*
* El-Torito related information
*/
struct el_torito_boot_catalog *catalog;
IsoFileSrc *cat; /**< location of the boot catalog in the new image */
IsoFileSrc *bootimg; /**< location of the boot image in the new image */
/*
* Number of pad blocks that we need to write. Padding blocks are blocks
* filled by 0s that we put between the directory structures and the file
* data. These padding blocks are added by libisofs to improve the handling
* of image growing. The idea is that the first blocks in the image are
* overwritten with the volume descriptors of the new image. These first
* blocks usually correspond to the volume descriptors and directory
* structure of the old image, and can be safety overwritten. However,
* with very small images they might correspond to valid data. To ensure
* this never happens, what we do is to add padding bytes, to ensure no
* file data is written in the first 64 KiB, that are the bytes we usually
* overwrite.
*/
uint32_t pad_blocks;
size_t nwriters;
IsoImageWriter **writers;
/* tree of files sources */
IsoRBTree *files;
/* Buffer for communication between burn_source and writer thread */
IsoRingBuffer *buffer;
/* writer thread descriptor */
pthread_t wthread;
pthread_attr_t th_attr;
};
#define BP(a,b) [(b) - (a) + 1]
/* ECMA-119, 8.4 */
struct ecma119_pri_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t unused1 BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t unused3 BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
/* ECMA-119, 8.5 */
struct ecma119_sup_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t vol_flags BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t esc_sequences BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
/* ECMA-119, 8.2 */
struct ecma119_boot_rec_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t boot_sys_id BP(8, 39);
uint8_t boot_id BP(40, 71);
uint8_t boot_catalog BP(72, 75);
uint8_t unused BP(76, 2048);
};
/* ECMA-119, 9.1 */
struct ecma119_dir_record
{
uint8_t len_dr BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 10);
uint8_t length BP(11, 18);
uint8_t recording_time BP(19, 25);
uint8_t flags BP(26, 26);
uint8_t file_unit_size BP(27, 27);
uint8_t interleave_gap_size BP(28, 28);
uint8_t vol_seq_number BP(29, 32);
uint8_t len_fi BP(33, 33);
uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
/* padding field (if len_fi is even) */
/* system use (len_dr - len_su + 1 to len_dr) */
};
/* ECMA-119, 9.4 */
struct ecma119_path_table_record
{
uint8_t len_di BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 6);
uint8_t parent BP(7, 8);
uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
/* padding field (if len_di is odd) */
};
/* ECMA-119, 8.3 */
struct ecma119_vol_desc_terminator
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t reserved BP(8, 2048);
};
#endif /*LIBISO_ECMA119_H_*/

View File

@ -0,0 +1,846 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "ecma119_tree.h"
#include "ecma119.h"
#include "node.h"
#include "util.h"
#include "filesrc.h"
#include "messages.h"
#include "image.h"
#include "stream.h"
#include "eltorito.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static
int get_iso_name(Ecma119Image *img, IsoNode *iso, char **name)
{
int ret, relaxed;
char *ascii_name;
char *isoname= NULL;
if (iso->name == NULL) {
/* it is not necessarily an error, it can be the root */
return ISO_SUCCESS;
}
ret = str2ascii(img->input_charset, iso->name, &ascii_name);
if (ret < 0) {
iso_msg_submit(img->image->id, ret, 0, "Can't convert %s", iso->name);
return ret;
}
if (img->allow_full_ascii) {
relaxed = 2;
} else {
relaxed = (int)img->allow_lowercase;
}
if (iso->type == LIBISO_DIR) {
if (img->max_37_char_filenames) {
isoname = iso_r_dirid(ascii_name, 37, relaxed);
} else if (img->iso_level == 1) {
if (relaxed) {
isoname = iso_r_dirid(ascii_name, 8, relaxed);
} else {
isoname = iso_1_dirid(ascii_name);
}
} else {
if (relaxed) {
isoname = iso_r_dirid(ascii_name, 8, relaxed);
} else {
isoname = iso_2_dirid(ascii_name);
}
}
} else {
if (img->max_37_char_filenames) {
isoname = iso_r_fileid(ascii_name, 36, relaxed,
img->no_force_dots ? 0 : 1);
} else if (img->iso_level == 1) {
if (relaxed) {
isoname = iso_r_fileid(ascii_name, 11, relaxed,
img->no_force_dots ? 0 : 1);
} else {
isoname = iso_1_fileid(ascii_name);
}
} else {
if (relaxed) {
isoname = iso_r_fileid(ascii_name, 30, relaxed,
img->no_force_dots ? 0 : 1);
} else {
isoname = iso_2_fileid(ascii_name);
}
}
}
free(ascii_name);
if (isoname != NULL) {
*name = isoname;
return ISO_SUCCESS;
} else {
/*
* only possible if mem error, as check for empty names is done
* in public tree
*/
return ISO_OUT_OF_MEM;
}
}
static
int create_ecma119_node(Ecma119Image *img, IsoNode *iso, Ecma119Node **node)
{
Ecma119Node *ecma;
ecma = calloc(1, sizeof(Ecma119Node));
if (ecma == NULL) {
return ISO_OUT_OF_MEM;
}
/* take a ref to the IsoNode */
ecma->node = iso;
iso_node_ref(iso);
/* TODO #00009 : add true support for harlinks and inode numbers */
ecma->nlink = 1;
ecma->ino = ++img->ino;
*node = ecma;
return ISO_SUCCESS;
}
/**
* Create a new ECMA-119 node representing a directory from a iso directory
* node.
*/
static
int create_dir(Ecma119Image *img, IsoDir *iso, Ecma119Node **node)
{
int ret;
Ecma119Node **children;
struct ecma119_dir_info *dir_info;
children = calloc(1, sizeof(void*) * iso->nchildren);
if (children == NULL) {
return ISO_OUT_OF_MEM;
}
dir_info = calloc(1, sizeof(struct ecma119_dir_info));
if (dir_info == NULL) {
free(children);
return ISO_OUT_OF_MEM;
}
ret = create_ecma119_node(img, (IsoNode*)iso, node);
if (ret < 0) {
free(children);
free(dir_info);
return ret;
}
(*node)->type = ECMA119_DIR;
(*node)->info.dir = dir_info;
(*node)->info.dir->nchildren = 0;
(*node)->info.dir->children = children;
return ISO_SUCCESS;
}
/**
* Create a new ECMA-119 node representing a regular file from a iso file
* node.
*/
static
int create_file(Ecma119Image *img, IsoFile *iso, Ecma119Node **node)
{
int ret;
IsoFileSrc *src;
off_t size;
size = iso_stream_get_size(iso->stream);
if (size > (off_t)0xffffffff) {
return iso_msg_submit(img->image->id, ISO_FILE_TOO_BIG, 0,
"File \"%s\" can't be added to image because "
"is greater than 4GB", iso->node.name);
}
ret = iso_file_src_create(img, iso, &src);
if (ret < 0) {
return ret;
}
ret = create_ecma119_node(img, (IsoNode*)iso, node);
if (ret < 0) {
/*
* the src doesn't need to be freed, it is free together with
* the Ecma119Image
*/
return ret;
}
(*node)->type = ECMA119_FILE;
(*node)->info.file = src;
return ret;
}
/**
* Create a new ECMA-119 node representing a regular file from an El-Torito
* boot catalog
*/
static
int create_boot_cat(Ecma119Image *img, IsoBoot *iso, Ecma119Node **node)
{
int ret;
IsoFileSrc *src;
ret = el_torito_catalog_file_src_create(img, &src);
if (ret < 0) {
return ret;
}
ret = create_ecma119_node(img, (IsoNode*)iso, node);
if (ret < 0) {
/*
* the src doesn't need to be freed, it is free together with
* the Ecma119Image
*/
return ret;
}
(*node)->type = ECMA119_FILE;
(*node)->info.file = src;
return ret;
}
/**
* Create a new ECMA-119 node representing a symbolic link from a iso symlink
* node.
*/
static
int create_symlink(Ecma119Image *img, IsoSymlink *iso, Ecma119Node **node)
{
int ret;
ret = create_ecma119_node(img, (IsoNode*)iso, node);
if (ret < 0) {
return ret;
}
(*node)->type = ECMA119_SYMLINK;
return ISO_SUCCESS;
}
/**
* Create a new ECMA-119 node representing a special file.
*/
static
int create_special(Ecma119Image *img, IsoSpecial *iso, Ecma119Node **node)
{
int ret;
ret = create_ecma119_node(img, (IsoNode*)iso, node);
if (ret < 0) {
return ret;
}
(*node)->type = ECMA119_SPECIAL;
return ISO_SUCCESS;
}
void ecma119_node_free(Ecma119Node *node)
{
if (node == NULL) {
return;
}
if (node->type == ECMA119_DIR) {
int i;
for (i = 0; i < node->info.dir->nchildren; i++) {
ecma119_node_free(node->info.dir->children[i]);
}
free(node->info.dir->children);
free(node->info.dir);
}
free(node->iso_name);
iso_node_unref(node->node);
free(node);
}
/**
*
* @return
* 1 success, 0 node ignored, < 0 error
*
*/
static
int create_tree(Ecma119Image *image, IsoNode *iso, Ecma119Node **tree,
int depth, int pathlen)
{
int ret;
Ecma119Node *node;
int max_path;
char *iso_name= NULL;
if (image == NULL || iso == NULL || tree == NULL) {
return ISO_NULL_POINTER;
}
if (iso->hidden & LIBISO_HIDE_ON_RR) {
/* file will be ignored */
return 0;
}
ret = get_iso_name(image, iso, &iso_name);
if (ret < 0) {
return ret;
}
max_path = pathlen + 1 + (iso_name ? strlen(iso_name) : 0);
if (!image->rockridge) {
if ((iso->type == LIBISO_DIR && depth > 8) && !image->allow_deep_paths) {
free(iso_name);
return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0,
"File \"%s\" can't be added, because directory depth "
"is greater than 8.", iso->name);
} else if (max_path > 255 && !image->allow_longer_paths) {
free(iso_name);
return iso_msg_submit(image->image->id, ISO_FILE_IMGPATH_WRONG, 0,
"File \"%s\" can't be added, because path length "
"is greater than 255 characters", iso->name);
}
}
switch (iso->type) {
case LIBISO_FILE:
ret = create_file(image, (IsoFile*)iso, &node);
break;
case LIBISO_SYMLINK:
if (image->rockridge) {
ret = create_symlink(image, (IsoSymlink*)iso, &node);
} else {
/* symlinks are only supported when RR is enabled */
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0,
"File \"%s\" ignored. Symlinks need RockRidge extensions.",
iso->name);
}
break;
case LIBISO_SPECIAL:
if (image->rockridge) {
ret = create_special(image, (IsoSpecial*)iso, &node);
} else {
/* symlinks are only supported when RR is enabled */
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0,
"File \"%s\" ignored. Special files need RockRidge extensions.",
iso->name);
}
break;
case LIBISO_BOOT:
if (image->eltorito) {
ret = create_boot_cat(image, (IsoBoot*)iso, &node);
} else {
/* log and ignore */
ret = iso_msg_submit(image->image->id, ISO_FILE_IGNORED, 0,
"El-Torito catalog found on a image without El-Torito.",
iso->name);
}
break;
case LIBISO_DIR:
{
IsoNode *pos;
IsoDir *dir = (IsoDir*)iso;
ret = create_dir(image, dir, &node);
if (ret < 0) {
return ret;
}
pos = dir->children;
while (pos) {
int cret;
Ecma119Node *child;
cret = create_tree(image, pos, &child, depth + 1, max_path);
if (cret < 0) {
/* error */
ecma119_node_free(node);
ret = cret;
break;
} else if (cret == ISO_SUCCESS) {
/* add child to this node */
int nchildren = node->info.dir->nchildren++;
node->info.dir->children[nchildren] = child;
child->parent = node;
}
pos = pos->next;
}
}
break;
default:
/* should never happen */
return ISO_ASSERT_FAILURE;
}
if (ret <= 0) {
free(iso_name);
return ret;
}
node->iso_name = iso_name;
*tree = node;
return ISO_SUCCESS;
}
/**
* Compare the iso name of two ECMA-119 nodes
*/
static
int cmp_node_name(const void *f1, const void *f2)
{
Ecma119Node *f = *((Ecma119Node**)f1);
Ecma119Node *g = *((Ecma119Node**)f2);
return strcmp(f->iso_name, g->iso_name);
}
/**
* Sorts a the children of each directory in the ECMA-119 tree represented
* by \p root, acording to the order specified in ECMA-119, section 9.3.
*/
static
void sort_tree(Ecma119Node *root)
{
size_t i;
qsort(root->info.dir->children, root->info.dir->nchildren, sizeof(void*),
cmp_node_name);
for (i = 0; i < root->info.dir->nchildren; i++) {
if (root->info.dir->children[i]->type == ECMA119_DIR)
sort_tree(root->info.dir->children[i]);
}
}
/**
* Ensures that the ISO name of each children of the given dir is unique,
* changing some of them if needed.
* It also ensures that resulting filename is always <= than given
* max_name_len, including extension. If needed, the extension will be reduced,
* but never under 3 characters.
*/
static
int mangle_single_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len,
int max_dir_len)
{
int ret;
int i, nchildren;
Ecma119Node **children;
IsoHTable *table;
int need_sort = 0;
nchildren = dir->info.dir->nchildren;
children = dir->info.dir->children;
/* a hash table will temporary hold the names, for fast searching */
ret = iso_htable_create((nchildren * 100) / 80, iso_str_hash,
(compare_function_t)strcmp, &table);
if (ret < 0) {
return ret;
}
for (i = 0; i < nchildren; ++i) {
char *name = children[i]->iso_name;
ret = iso_htable_add(table, name, name);
if (ret < 0) {
goto mangle_cleanup;
}
}
for (i = 0; i < nchildren; ++i) {
char *name, *ext;
char full_name[40];
int max; /* computed max len for name, without extension */
int j = i;
int digits = 1; /* characters to change per name */
/* first, find all child with same name */
while (j + 1 < nchildren && !cmp_node_name(children + i, children + j
+ 1)) {
++j;
}
if (j == i) {
/* name is unique */
continue;
}
/*
* A max of 7 characters is good enought, it allows handling up to
* 9,999,999 files with same name. We can increment this to
* max_name_len, but the int_pow() function must then be modified
* to return a bigger integer.
*/
while (digits < 8) {
int ok, k;
char *dot;
int change = 0; /* number to be written */
/* copy name to buffer */
strcpy(full_name, children[i]->iso_name);
/* compute name and extension */
dot = strrchr(full_name, '.');
if (dot != NULL && children[i]->type != ECMA119_DIR) {
/*
* File (not dir) with extension
* Note that we don't need to check for placeholders, as
* tree reparent happens later, so no placeholders can be
* here at this time.
*/
int extlen;
full_name[dot - full_name] = '\0';
name = full_name;
ext = dot + 1;
/*
* For iso level 1 we force ext len to be 3, as name
* can't grow on the extension space
*/
extlen = (max_file_len == 12) ? 3 : strlen(ext);
max = max_file_len - extlen - 1 - digits;
if (max <= 0) {
/* this can happen if extension is too long */
if (extlen + max > 3) {
/*
* reduce extension len, to give name an extra char
* note that max is negative or 0
*/
extlen = extlen + max - 1;
ext[extlen] = '\0';
max = max_file_len - extlen - 1 - digits;
} else {
/*
* error, we don't support extensions < 3
* This can't happen with current limit of digits.
*/
ret = ISO_ERROR;
goto mangle_cleanup;
}
}
/* ok, reduce name by digits */
if (name + max < dot) {
name[max] = '\0';
}
} else {
/* Directory, or file without extension */
if (children[i]->type == ECMA119_DIR) {
max = max_dir_len - digits;
dot = NULL; /* dots have no meaning in dirs */
} else {
max = max_file_len - digits;
}
name = full_name;
if (max < strlen(name)) {
name[max] = '\0';
}
/* let ext be an empty string */
ext = name + strlen(name);
}
ok = 1;
/* change name of each file */
for (k = i; k <= j; ++k) {
char tmp[40];
char fmt[16];
if (dot != NULL) {
sprintf(fmt, "%%s%%0%dd.%%s", digits);
} else {
sprintf(fmt, "%%s%%0%dd%%s", digits);
}
while (1) {
sprintf(tmp, fmt, name, change, ext);
++change;
if (change > int_pow(10, digits)) {
ok = 0;
break;
}
if (!iso_htable_get(table, tmp, NULL)) {
/* the name is unique, so it can be used */
break;
}
}
if (ok) {
char *new = strdup(tmp);
if (new == NULL) {
ret = ISO_OUT_OF_MEM;
goto mangle_cleanup;
}
iso_msg_debug(img->image->id, "\"%s\" renamed to \"%s\"",
children[k]->iso_name, new);
iso_htable_remove_ptr(table, children[k]->iso_name, NULL);
free(children[k]->iso_name);
children[k]->iso_name = new;
iso_htable_add(table, new, new);
/*
* if we change a name we need to sort again children
* at the end
*/
need_sort = 1;
} else {
/* we need to increment digits */
break;
}
}
if (ok) {
break;
} else {
++digits;
}
}
if (digits == 8) {
ret = ISO_MANGLE_TOO_MUCH_FILES;
goto mangle_cleanup;
}
i = j;
}
/*
* If needed, sort again the files inside dir
*/
if (need_sort) {
qsort(children, nchildren, sizeof(void*), cmp_node_name);
}
ret = ISO_SUCCESS;
mangle_cleanup : ;
iso_htable_destroy(table, NULL);
return ret;
}
static
int mangle_dir(Ecma119Image *img, Ecma119Node *dir, int max_file_len,
int max_dir_len)
{
int ret;
size_t i;
ret = mangle_single_dir(img, dir, max_file_len, max_dir_len);
if (ret < 0) {
return ret;
}
/* recurse */
for (i = 0; i < dir->info.dir->nchildren; ++i) {
if (dir->info.dir->children[i]->type == ECMA119_DIR) {
ret = mangle_dir(img, dir->info.dir->children[i], max_file_len,
max_dir_len);
if (ret < 0) {
/* error */
return ret;
}
}
}
return ISO_SUCCESS;
}
static
int mangle_tree(Ecma119Image *img, int recurse)
{
int max_file, max_dir;
if (img->max_37_char_filenames) {
max_file = max_dir = 37;
} else if (img->iso_level == 1) {
max_file = 12; /* 8 + 3 + 1 */
max_dir = 8;
} else {
max_file = max_dir = 31;
}
if (recurse) {
return mangle_dir(img, img->root, max_file, max_dir);
} else {
return mangle_single_dir(img, img->root, max_file, max_dir);
}
}
/**
* Create a new ECMA-119 node representing a placeholder for a relocated
* dir.
*
* See IEEE P1282, section 4.1.5 for details
*/
static
int create_placeholder(Ecma119Node *parent, Ecma119Node *real,
Ecma119Node **node)
{
Ecma119Node *ret;
ret = calloc(1, sizeof(Ecma119Node));
if (ret == NULL) {
return ISO_OUT_OF_MEM;
}
/*
* TODO
* If real is a dir, while placeholder is a file, ISO name restricctions
* are different, what to do?
*/
ret->iso_name = strdup(real->iso_name);
if (ret->iso_name == NULL) {
free(ret);
return ISO_OUT_OF_MEM;
}
/* take a ref to the IsoNode */
ret->node = real->node;
iso_node_ref(real->node);
ret->parent = parent;
ret->type = ECMA119_PLACEHOLDER;
ret->info.real_me = real;
ret->ino = real->ino;
ret->nlink = real->nlink;
*node = ret;
return ISO_SUCCESS;
}
static
size_t max_child_name_len(Ecma119Node *dir)
{
size_t ret = 0, i;
for (i = 0; i < dir->info.dir->nchildren; i++) {
size_t len = strlen(dir->info.dir->children[i]->iso_name);
ret = MAX(ret, len);
}
return ret;
}
/**
* Relocates a directory, as specified in Rock Ridge Specification
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels
* on a directory hierarchy exceeds 8, or the length of a path is higher
* than 255 characters, as specified in ECMA-119, section 6.8.2.1
*/
static
int reparent(Ecma119Node *child, Ecma119Node *parent)
{
int ret;
size_t i;
Ecma119Node *placeholder;
/* replace the child in the original parent with a placeholder */
for (i = 0; i < child->parent->info.dir->nchildren; i++) {
if (child->parent->info.dir->children[i] == child) {
ret = create_placeholder(child->parent, child, &placeholder);
if (ret < 0) {
return ret;
}
child->parent->info.dir->children[i] = placeholder;
break;
}
}
/* just for debug, this should never happen... */
if (i == child->parent->info.dir->nchildren) {
return ISO_ASSERT_FAILURE;
}
/* keep track of the real parent */
child->info.dir->real_parent = child->parent;
/* add the child to its new parent */
child->parent = parent;
parent->info.dir->nchildren++;
parent->info.dir->children = realloc(parent->info.dir->children,
sizeof(void*) * parent->info.dir->nchildren);
parent->info.dir->children[parent->info.dir->nchildren - 1] = child;
return ISO_SUCCESS;
}
/**
* Reorder the tree, if necessary, to ensure that
* - the depth is at most 8
* - each path length is at most 255 characters
* This restriction is imposed by ECMA-119 specification (ECMA-119, 6.8.2.1).
*
* @param dir
* Dir we are currently processing
* @param level
* Level of the directory in the hierarchy
* @param pathlen
* Length of the path until dir, including it
* @return
* 1 success, < 0 error
*/
static
int reorder_tree(Ecma119Image *img, Ecma119Node *dir, int level, int pathlen)
{
int ret;
size_t max_path;
max_path = pathlen + 1 + max_child_name_len(dir);
if (level > 8 || max_path > 255) {
ret = reparent(dir, img->root);
if (ret < 0) {
return ret;
}
/*
* we are appended to the root's children now, so there is no
* need to recurse (the root will hit us again)
*/
} else {
size_t i;
for (i = 0; i < dir->info.dir->nchildren; i++) {
Ecma119Node *child = dir->info.dir->children[i];
if (child->type == ECMA119_DIR) {
int newpathlen = pathlen + 1 + strlen(child->iso_name);
ret = reorder_tree(img, child, level + 1, newpathlen);
if (ret < 0) {
return ret;
}
}
}
}
return ISO_SUCCESS;
}
int ecma119_tree_create(Ecma119Image *img)
{
int ret;
Ecma119Node *root;
ret = create_tree(img, (IsoNode*)img->image->root, &root, 1, 0);
if (ret <= 0) {
if (ret == 0) {
/* unexpected error, root ignored!! This can't happen */
ret = ISO_ASSERT_FAILURE;
}
return ret;
}
img->root = root;
iso_msg_debug(img->image->id, "Sorting the low level tree...");
sort_tree(root);
iso_msg_debug(img->image->id, "Mangling names...");
ret = mangle_tree(img, 1);
if (ret < 0) {
return ret;
}
if (img->rockridge && !img->allow_deep_paths) {
/* reorder the tree, acording to RRIP, 4.1.5 */
ret = reorder_tree(img, img->root, 1, 0);
if (ret < 0) {
return ret;
}
/*
* and we need to remangle the root directory, as the function
* above could insert new directories into the root.
* Note that recurse = 0, as we don't need to recurse.
*/
ret = mangle_tree(img, 0);
if (ret < 0) {
return ret;
}
}
return ISO_SUCCESS;
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_ECMA119_TREE_H_
#define LIBISO_ECMA119_TREE_H_
#include "libisofs.h"
#include "ecma119.h"
enum ecma119_node_type {
ECMA119_FILE,
ECMA119_DIR,
ECMA119_SYMLINK,
ECMA119_SPECIAL,
ECMA119_PLACEHOLDER
};
/**
* Struct with info about a node representing a directory
*/
struct ecma119_dir_info
{
/* Block where the directory entries will be written on image */
size_t block;
size_t nchildren;
Ecma119Node **children;
/*
* Size of the dir, i.e., sum of the lengths of all directory records.
* It is computed by calc_dir_size() [ecma119.c].
* Note that this don't include the length of any SUSP Continuation
* Area needed by the dir, but it includes the size of the SUSP entries
* than fit in the directory records System Use Field.
*/
size_t len;
/**
* Real parent if the dir has been reallocated. NULL otherwise.
*/
Ecma119Node *real_parent;
};
/**
* A node for a tree containing all the information necessary for writing
* an ISO9660 volume.
*/
struct ecma119_node
{
/**
* Name in ASCII, conforming to selected ISO level.
* Version number is not include, it is added on the fly
*/
char *iso_name;
Ecma119Node *parent;
IsoNode *node; /*< reference to the iso node */
/* TODO #00009 : add true support for harlinks and inode numbers */
ino_t ino;
nlink_t nlink;
/**< file, symlink, special, directory or placeholder */
enum ecma119_node_type type;
union
{
IsoFileSrc *file;
struct ecma119_dir_info *dir;
/** this field points to the relocated directory. */
Ecma119Node *real_me;
} info;
};
/**
*
*/
int ecma119_tree_create(Ecma119Image *img);
/**
* Free an Ecma119Node, and its children if node is a dir
*/
void ecma119_node_free(Ecma119Node *node);
#endif /*LIBISO_ECMA119_TREE_H_*/

View File

@ -0,0 +1,908 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "eltorito.h"
#include "stream.h"
#include "fsource.h"
#include "filesrc.h"
#include "image.h"
#include "messages.h"
#include "writer.h"
#include <stdlib.h>
#include <string.h>
/**
* This table should be written with accuracy values at offset
* 8 of boot image, when used ISOLINUX boot loader
*/
struct boot_info_table {
uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */
uint8_t bi_file BP(5, 8); /* LBA of boot file */
uint8_t bi_length BP(9, 12); /* Length of boot file */
uint8_t bi_csum BP(13, 16); /* Checksum of boot file */
uint8_t bi_reserved BP(17, 56); /* Reserved */
};
/**
* Structure for each one of the four entries in a partition table on a
* hard disk image.
*/
struct partition_desc {
uint8_t boot_ind;
uint8_t begin_chs[3];
uint8_t type;
uint8_t end_chs[3];
uint8_t start[4];
uint8_t size[4];
};
/**
* Structures for a Master Boot Record of a hard disk image.
*/
struct hard_disc_mbr {
uint8_t code_area[440];
uint8_t opt_disk_sg[4];
uint8_t pad[2];
struct partition_desc partition[4];
uint8_t sign1;
uint8_t sign2;
};
/**
* Sets the load segment for the initial boot image. This is only for
* no emulation boot images, and is a NOP for other image types.
*/
void el_torito_set_load_seg(ElToritoBootImage *bootimg, short segment)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_seg = segment;
}
/**
* Sets the number of sectors (512b) to be load at load segment during
* the initial boot procedure. This is only for no emulation boot images,
* and is a NOP for other image types.
*/
void el_torito_set_load_size(ElToritoBootImage *bootimg, short sectors)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_size = sectors;
}
/**
* Marks the specified boot image as not bootable
*/
void el_torito_set_no_bootable(ElToritoBootImage *bootimg)
{
bootimg->bootable = 0;
}
/**
* Specifies that this image needs to be patched. This involves the writting
* of a 56 bytes boot information table at offset 8 of the boot image file.
* The original boot image file won't be modified.
* This is needed for isolinux boot images.
*/
void el_torito_patch_isolinux_image(ElToritoBootImage *bootimg)
{
bootimg->isolinux = 1;
}
static
int iso_tree_add_boot_node(IsoDir *parent, const char *name, IsoBoot **boot)
{
IsoBoot *node;
IsoNode **pos;
time_t now;
if (parent == NULL || name == NULL || boot == NULL) {
return ISO_NULL_POINTER;
}
if (boot) {
*boot = NULL;
}
/* check if the name is valid */
if (!iso_node_is_valid_name(name)) {
return ISO_WRONG_ARG_VALUE;
}
/* find place where to insert */
pos = &(parent->children);
while (*pos != NULL && strcmp((*pos)->name, name) < 0) {
pos = &((*pos)->next);
}
if (*pos != NULL && !strcmp((*pos)->name, name)) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
node = calloc(1, sizeof(IsoBoot));
if (node == NULL) {
return ISO_OUT_OF_MEM;
}
node->node.refcount = 1;
node->node.type = LIBISO_BOOT;
node->node.name = strdup(name);
if (node->node.name == NULL) {
free(node);
return ISO_OUT_OF_MEM;
}
/* atributes from parent */
node->node.mode = S_IFREG | (parent->node.mode & 0444);
node->node.uid = parent->node.uid;
node->node.gid = parent->node.gid;
node->node.hidden = parent->node.hidden;
/* current time */
now = time(NULL);
node->node.atime = now;
node->node.ctime = now;
node->node.mtime = now;
/* add to dir */
node->node.parent = parent;
node->node.next = *pos;
*pos = (IsoNode*)node;
if (boot) {
*boot = node;
}
return ++parent->nchildren;
}
static
int create_image(IsoImage *image, const char *image_path,
enum eltorito_boot_media_type type,
struct el_torito_boot_image **bootimg)
{
int ret;
struct el_torito_boot_image *boot;
int boot_media_type = 0;
int load_sectors = 0; /* number of sector to load */
unsigned char partition_type = 0;
IsoNode *imgfile;
IsoStream *stream;
ret = iso_tree_path_to_node(image, image_path, &imgfile);
if (ret < 0) {
return ret;
}
if (ret == 0) {
return ISO_NODE_DOESNT_EXIST;
}
if (imgfile->type != LIBISO_FILE) {
return ISO_BOOT_IMAGE_NOT_VALID;
}
stream = ((IsoFile*)imgfile)->stream;
/* we need to read the image at least two times */
if (!iso_stream_is_repeatable(stream)) {
return ISO_BOOT_IMAGE_NOT_VALID;
}
switch (type) {
case ELTORITO_FLOPPY_EMUL:
switch (iso_stream_get_size(stream)) {
case 1200 * 1024:
boot_media_type = 1; /* 1.2 meg diskette */
break;
case 1440 * 1024:
boot_media_type = 2; /* 1.44 meg diskette */
break;
case 2880 * 1024:
boot_media_type = 3; /* 2.88 meg diskette */
break;
default:
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Invalid image size %d Kb. Must be one of 1.2, 1.44"
"or 2.88 Mb", iso_stream_get_size(stream) / 1024);
return ISO_BOOT_IMAGE_NOT_VALID;
break;
}
/* it seems that for floppy emulation we need to load
* a single sector (512b) */
load_sectors = 1;
break;
case ELTORITO_HARD_DISC_EMUL:
{
size_t i;
struct hard_disc_mbr mbr;
int used_partition;
/* read the MBR on disc and get the type of the partition */
ret = iso_stream_open(stream);
if (ret < 0) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, ret,
"Can't open image file.");
return ret;
}
ret = iso_stream_read(stream, &mbr, sizeof(mbr));
iso_stream_close(stream);
if (ret != sizeof(mbr)) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Can't read MBR from image file.");
return ret < 0 ? ret : ISO_FILE_READ_ERROR;
}
/* check valid MBR signature */
if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Invalid MBR. Wrong signature.");
return ISO_BOOT_IMAGE_NOT_VALID;
}
/* ensure single partition */
used_partition = -1;
for (i = 0; i < 4; ++i) {
if (mbr.partition[i].type != 0) {
/* it's an used partition */
if (used_partition != -1) {
iso_msg_submit(image->id, ISO_BOOT_IMAGE_NOT_VALID, 0,
"Invalid MBR. At least 2 partitions: %d and "
"%d, are being used\n", used_partition, i);
return ISO_BOOT_IMAGE_NOT_VALID;
} else
used_partition = i;
}
}
partition_type = mbr.partition[used_partition].type;
}
boot_media_type = 4;
/* only load the MBR */
load_sectors = 1;
break;
case ELTORITO_NO_EMUL:
boot_media_type = 0;
break;
}
boot = calloc(1, sizeof(struct el_torito_boot_image));
if (boot == NULL) {
return ISO_OUT_OF_MEM;
}
boot->image = (IsoFile*)imgfile;
iso_node_ref(imgfile); /* get our ref */
boot->bootable = 1;
boot->type = boot_media_type;
boot->load_size = load_sectors;
boot->partition_type = partition_type;
if (bootimg) {
*bootimg = boot;
}
return ISO_SUCCESS;
}
int iso_image_set_boot_image(IsoImage *image, const char *image_path,
enum eltorito_boot_media_type type,
const char *catalog_path,
ElToritoBootImage **boot)
{
int ret;
struct el_torito_boot_catalog *catalog;
ElToritoBootImage *boot_image= NULL;
IsoBoot *cat_node= NULL;
if (image == NULL || image_path == NULL || catalog_path == NULL) {
return ISO_NULL_POINTER;
}
if (image->bootcat != NULL) {
return ISO_IMAGE_ALREADY_BOOTABLE;
}
/* create the node for the catalog */
{
IsoDir *parent;
char *catdir = NULL, *catname = NULL;
catdir = strdup(catalog_path);
if (catdir == NULL) {
return ISO_OUT_OF_MEM;
}
/* get both the dir and the name */
catname = strrchr(catdir, '/');
if (catname == NULL) {
free(catdir);
return ISO_WRONG_ARG_VALUE;
}
if (catname == catdir) {
/* we are apending catalog to root node */
parent = image->root;
} else {
IsoNode *p;
catname[0] = '\0';
ret = iso_tree_path_to_node(image, catdir, &p);
if (ret <= 0) {
free(catdir);
return ret < 0 ? ret : ISO_NODE_DOESNT_EXIST;
}
if (p->type != LIBISO_DIR) {
free(catdir);
return ISO_WRONG_ARG_VALUE;
}
parent = (IsoDir*)p;
}
catname++;
ret = iso_tree_add_boot_node(parent, catname, &cat_node);
free(catdir);
if (ret < 0) {
return ret;
}
}
/* create the boot image */
ret = create_image(image, image_path, type, &boot_image);
if (ret < 0) {
goto boot_image_cleanup;
}
/* creates the catalog with the given image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
if (catalog == NULL) {
ret = ISO_OUT_OF_MEM;
goto boot_image_cleanup;
}
catalog->image = boot_image;
catalog->node = cat_node;
iso_node_ref((IsoNode*)cat_node);
image->bootcat = catalog;
if (boot) {
*boot = boot_image;
}
return ISO_SUCCESS;
boot_image_cleanup:;
if (cat_node) {
iso_node_take((IsoNode*)cat_node);
iso_node_unref((IsoNode*)cat_node);
}
if (boot_image) {
iso_node_unref((IsoNode*)boot_image->image);
free(boot_image);
}
return ret;
}
/**
* Get El-Torito boot image of an ISO image, if any.
*
* This can be useful, for example, to check if a volume read from a previous
* session or an existing image is bootable. It can also be useful to get
* the image and catalog tree nodes. An application would want those, for
* example, to prevent the user removing it.
*
* Both nodes are owned by libisofs and should not be freed. You can get your
* own ref with iso_node_ref(). You can can also check if the node is already
* on the tree by getting its parent (note that when reading El-Torito info
* from a previous image, the nodes might not be on the tree even if you haven't
* removed them). Remember that you'll need to get a new ref
* (with iso_node_ref()) before inserting them again to the tree, and probably
* you will also need to set the name or permissions.
*
* @param image
* The image from which to get the boot image.
* @param boot
* If not NULL, it will be filled with a pointer to the boot image, if
* any. That object is owned by the IsoImage and should not be freed by
* the user, nor dereferenced once the last reference to the IsoImage was
* disposed via iso_image_unref().
* @param imgnode
* When not NULL, it will be filled with the image tree node. No extra ref
* is added, you can use iso_node_ref() to get one if you need it.
* @param catnode
* When not NULL, it will be filled with the catnode tree node. No extra
* ref is added, you can use iso_node_ref() to get one if you need it.
* @return
* 1 on success, 0 is the image is not bootable (i.e., it has no El-Torito
* image), < 0 error.
*/
int iso_image_get_boot_image(IsoImage *image, ElToritoBootImage **boot,
IsoFile **imgnode, IsoBoot **catnode)
{
if (image == NULL) {
return ISO_NULL_POINTER;
}
if (image->bootcat == NULL) {
return 0;
}
/* ok, image is bootable */
if (boot) {
*boot = image->bootcat->image;
}
if (imgnode) {
*imgnode = image->bootcat->image->image;
}
if (catnode) {
*catnode = image->bootcat->node;
}
return ISO_SUCCESS;
}
/**
* Removes the El-Torito bootable image.
*
* The IsoBoot node that acts as placeholder for the catalog is also removed
* for the image tree, if there.
* If the image is not bootable (don't have el-torito boot image) this function
* just returns.
*/
void iso_image_remove_boot_image(IsoImage *image)
{
if (image == NULL || image->bootcat == NULL)
return;
/*
* remove catalog node from its parent
* (the reference will be disposed next)
*/
iso_node_take((IsoNode*)image->bootcat->node);
/* free boot catalog and image, including references to nodes */
el_torito_boot_catalog_free(image->bootcat);
image->bootcat = NULL;
}
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat)
{
struct el_torito_boot_image *image;
if (cat == NULL) {
return;
}
image = cat->image;
iso_node_unref((IsoNode*)image->image);
free(image);
iso_node_unref((IsoNode*)cat->node);
free(cat);
}
/**
* Stream that generates the contents of a El-Torito catalog.
*/
struct catalog_stream
{
Ecma119Image *target;
uint8_t buffer[BLOCK_SIZE];
int offset; /* -1 if stream is not openned */
};
static void
write_validation_entry(uint8_t *buf)
{
size_t i;
int checksum;
struct el_torito_validation_entry *ve =
(struct el_torito_validation_entry*)buf;
ve->header_id[0] = 1;
ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */
ve->key_byte1[0] = 0x55;
ve->key_byte2[0] = 0xAA;
/* calculate the checksum, to ensure sum of all words is 0 */
checksum = 0;
for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) {
checksum -= (int16_t) ((buf[i+1] << 8) | buf[i]);
}
iso_lsb(ve->checksum, checksum, 2);
}
/**
* Write one section entry.
* Currently this is used only for default image (the only supported just now)
*/
static void
write_section_entry(uint8_t *buf, Ecma119Image *t)
{
struct el_torito_boot_image *img;
struct el_torito_section_entry *se =
(struct el_torito_section_entry*)buf;
img = t->catalog->image;
se->boot_indicator[0] = img->bootable ? 0x88 : 0x00;
se->boot_media_type[0] = img->type;
iso_lsb(se->load_seg, img->load_seg, 2);
se->system_type[0] = img->partition_type;
iso_lsb(se->sec_count, img->load_size, 2);
iso_lsb(se->block, t->bootimg->block, 4);
}
static
int catalog_open(IsoStream *stream)
{
struct catalog_stream *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
if (data->offset != -1) {
return ISO_FILE_ALREADY_OPENED;
}
memset(data->buffer, 0, BLOCK_SIZE);
/* fill the buffer with the catalog contents */
write_validation_entry(data->buffer);
/* write default entry */
write_section_entry(data->buffer + 32, data->target);
data->offset = 0;
return ISO_SUCCESS;
}
static
int catalog_close(IsoStream *stream)
{
struct catalog_stream *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
if (data->offset == -1) {
return ISO_FILE_NOT_OPENED;
}
data->offset = -1;
return ISO_SUCCESS;
}
static
off_t catalog_get_size(IsoStream *stream)
{
return BLOCK_SIZE;
}
static
int catalog_read(IsoStream *stream, void *buf, size_t count)
{
size_t len;
struct catalog_stream *data;
if (stream == NULL || buf == NULL) {
return ISO_NULL_POINTER;
}
if (count == 0) {
return ISO_WRONG_ARG_VALUE;
}
data = stream->data;
if (data->offset == -1) {
return ISO_FILE_NOT_OPENED;
}
len = MIN(count, BLOCK_SIZE - data->offset);
memcpy(buf, data->buffer + data->offset, len);
return len;
}
static
int catalog_is_repeatable(IsoStream *stream)
{
return 1;
}
/**
* fs_id will be the id reserved for El-Torito
* dev_id will be 0 for catalog, 1 for boot image (if needed)
* we leave ino_id for future use when we support multiple boot images
*/
static
void catalog_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
ino_t *ino_id)
{
*fs_id = ISO_ELTORITO_FS_ID;
*dev_id = 0;
*ino_id = 0;
}
static
void catalog_free(IsoStream *stream)
{
free(stream->data);
}
IsoStreamIface catalog_stream_class = {
0,
"boot",
catalog_open,
catalog_close,
catalog_get_size,
catalog_read,
catalog_is_repeatable,
catalog_get_id,
catalog_free
};
/**
* Create an IsoStream for writing El-Torito catalog for a given target.
*/
static
int catalog_stream_new(Ecma119Image *target, IsoStream **stream)
{
IsoStream *str;
struct catalog_stream *data;
if (target == NULL || stream == NULL || target->catalog == NULL) {
return ISO_NULL_POINTER;
}
str = malloc(sizeof(IsoStream));
if (str == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(struct catalog_stream));
if (str == NULL) {
free(str);
return ISO_OUT_OF_MEM;
}
/* fill data */
data->target = target;
data->offset = -1;
str->refcount = 1;
str->data = data;
str->class = &catalog_stream_class;
*stream = str;
return ISO_SUCCESS;
}
int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src)
{
int ret;
IsoFileSrc *file;
IsoStream *stream;
if (target == NULL || src == NULL || target->catalog == NULL) {
return ISO_OUT_OF_MEM;
}
if (target->cat != NULL) {
/* catalog file src already created */
*src = target->cat;
return ISO_SUCCESS;
}
file = malloc(sizeof(IsoFileSrc));
if (file == NULL) {
return ISO_OUT_OF_MEM;
}
ret = catalog_stream_new(target, &stream);
if (ret < 0) {
free(file);
return ret;
}
/* fill fields */
file->prev_img = 0; /* TODO allow copy of old img catalog???? */
file->block = 0; /* to be filled later */
file->sort_weight = 1000; /* slightly high */
file->stream = stream;
ret = iso_file_src_add(target, file, src);
if (ret <= 0) {
iso_stream_unref(stream);
free(file);
} else {
target->cat = *src;
}
return ret;
}
/******************* EL-TORITO WRITER *******************************/
static
int eltorito_writer_compute_data_blocks(IsoImageWriter *writer)
{
/* nothing to do, the files are written by the file writer */
return ISO_SUCCESS;
}
/**
* Write the Boot Record Volume Descriptor (ECMA-119, 8.2)
*/
static
int eltorito_writer_write_vol_desc(IsoImageWriter *writer)
{
Ecma119Image *t;
struct el_torito_boot_catalog *cat;
struct ecma119_boot_rec_vol_desc vol;
if (writer == NULL) {
return ISO_NULL_POINTER;
}
t = writer->target;
cat = t->catalog;
iso_msg_debug(t->image->id, "Write El-Torito boot record");
memset(&vol, 0, sizeof(struct ecma119_boot_rec_vol_desc));
vol.vol_desc_type[0] = 0;
memcpy(vol.std_identifier, "CD001", 5);
vol.vol_desc_version[0] = 1;
memcpy(vol.boot_sys_id, "EL TORITO SPECIFICATION", 23);
iso_lsb(vol.boot_catalog, t->cat->block, 4);
return iso_write(t, &vol, sizeof(struct ecma119_boot_rec_vol_desc));
}
/**
* Patch an isolinux boot image.
*
* @return
* 1 on success, 0 error (but continue), < 0 error
*/
static
int patch_boot_image(uint8_t *buf, Ecma119Image *t, size_t imgsize)
{
struct boot_info_table *info;
uint32_t checksum;
size_t offset;
if (imgsize < 64) {
return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0,
"Isolinux image too small. We won't patch it.");
}
/* compute checksum, as the the sum of all 32 bit words in boot image
* from offset 64 */
checksum = 0;
offset = (size_t) 64;
while (offset <= imgsize - 4) {
checksum += iso_read_lsb(buf + offset, 4);
offset += 4;
}
if (offset != imgsize) {
/* file length not multiple of 4 */
return iso_msg_submit(t->image->id, ISO_ISOLINUX_CANT_PATCH, 0,
"Unexpected isolinux image length. Patch might not work.");
}
/* patch boot info table */
info = (struct boot_info_table*)(buf + 8);
/*memset(info, 0, sizeof(struct boot_info_table));*/
iso_lsb(info->bi_pvd, t->ms_block + 16, 4);
iso_lsb(info->bi_file, t->bootimg->block, 4);
iso_lsb(info->bi_length, imgsize, 4);
iso_lsb(info->bi_csum, checksum, 4);
return ISO_SUCCESS;
}
static
int eltorito_writer_write_data(IsoImageWriter *writer)
{
/*
* We have nothing to write, but if we need to patch an isolinux image,
* this is a good place to do so.
*/
Ecma119Image *t;
int ret;
if (writer == NULL) {
return ISO_NULL_POINTER;
}
t = writer->target;
if (t->catalog->image->isolinux) {
/* we need to patch the image */
size_t size;
uint8_t *buf;
IsoStream *new = NULL;
IsoStream *original = t->bootimg->stream;
size = (size_t) iso_stream_get_size(original);
buf = malloc(size);
if (buf == NULL) {
return ISO_OUT_OF_MEM;
}
ret = iso_stream_open(original);
if (ret < 0) {
return ret;
}
ret = iso_stream_read(original, buf, size);
iso_stream_close(original);
if (ret != size) {
return (ret < 0) ? ret : ISO_FILE_READ_ERROR;
}
/* ok, patch the read buffer */
ret = patch_boot_image(buf, t, size);
if (ret < 0) {
return ret;
}
/* replace the original stream with a memory stream that reads from
* the patched buffer */
ret = iso_memory_stream_new(buf, size, &new);
if (ret < 0) {
return ret;
}
t->bootimg->stream = new;
iso_stream_unref(original);
}
return ISO_SUCCESS;
}
static
int eltorito_writer_free_data(IsoImageWriter *writer)
{
/* nothing to do */
return ISO_SUCCESS;
}
int eltorito_writer_create(Ecma119Image *target)
{
int ret;
IsoImageWriter *writer;
IsoFile *bootimg;
IsoFileSrc *src;
writer = malloc(sizeof(IsoImageWriter));
if (writer == NULL) {
return ISO_OUT_OF_MEM;
}
writer->compute_data_blocks = eltorito_writer_compute_data_blocks;
writer->write_vol_desc = eltorito_writer_write_vol_desc;
writer->write_data = eltorito_writer_write_data;
writer->free_data = eltorito_writer_free_data;
writer->data = NULL;
writer->target = target;
/* add this writer to image */
target->writers[target->nwriters++] = writer;
/*
* get catalog and image file sources.
* Note that the catalog may be already added, when creating the low
* level ECMA-119 tree.
*/
if (target->cat == NULL) {
ret = el_torito_catalog_file_src_create(target, &src);
if (ret < 0) {
return ret;
}
}
bootimg = target->catalog->image->image;
ret = iso_file_src_create(target, bootimg, &src);
if (ret < 0) {
return ret;
}
target->bootimg = src;
/* if we have selected to patch the image, it needs to be copied always */
if (target->catalog->image->isolinux) {
src->prev_img = 0;
}
/* we need the bootable volume descriptor */
target->curblock++;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/**
* Declare El-Torito related structures.
* References:
* "El Torito" Bootable CD-ROM Format Specification Version 1.0 (1995)
*/
#ifndef LIBISO_ELTORITO_H
#define LIBISO_ELTORITO_H
#include "ecma119.h"
#include "node.h"
/**
* A node that acts as a placeholder for an El-Torito catalog.
*/
struct Iso_Boot
{
IsoNode node;
};
struct el_torito_boot_catalog {
IsoBoot *node; /* node of the catalog */
struct el_torito_boot_image *image; /* default boot image */
};
struct el_torito_boot_image {
IsoFile *image;
unsigned int bootable:1; /**< If the entry is bootable. */
unsigned int isolinux:1; /**< If the image will be patched */
unsigned char type; /**< The type of image */
unsigned char partition_type; /**< type of partition for HD-emul images */
short load_seg; /**< Load segment for the initial boot image. */
short load_size; /**< Number of sectors to load. */
};
/** El-Torito, 2.1 */
struct el_torito_validation_entry {
uint8_t header_id BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t reserved BP(3, 4);
uint8_t id_string BP(5, 28);
uint8_t checksum BP(29, 30);
uint8_t key_byte1 BP(31, 31);
uint8_t key_byte2 BP(32, 32);
};
/** El-Torito, 2.2 */
struct el_torito_default_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t unused2 BP(13, 32);
};
/** El-Torito, 2.3 */
struct el_torito_section_header {
uint8_t header_indicator BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t number BP(3, 4);
uint8_t character BP(5, 32);
};
/** El-Torito, 2.4 */
struct el_torito_section_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t selec_criteria BP(13, 13);
uint8_t vendor_sc BP(14, 32);
};
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat);
/**
* Create a IsoFileSrc for writing the el-torito catalog for the given
* target, and add it to target. If the target already has a src for the
* catalog, it just returns.
*/
int el_torito_catalog_file_src_create(Ecma119Image *target, IsoFileSrc **src);
/**
* Create a writer for el-torito information.
*/
int eltorito_writer_create(Ecma119Image *target);
#endif /* LIBISO_ELTORITO_H */

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "filesrc.h"
#include "node.h"
#include "util.h"
#include "writer.h"
#include "messages.h"
#include "image.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
int iso_file_src_cmp(const void *n1, const void *n2)
{
const IsoFileSrc *f1, *f2;
unsigned int fs_id1, fs_id2;
dev_t dev_id1, dev_id2;
ino_t ino_id1, ino_id2;
f1 = (const IsoFileSrc *)n1;
f2 = (const IsoFileSrc *)n2;
iso_stream_get_id(f1->stream, &fs_id1, &dev_id1, &ino_id1);
iso_stream_get_id(f2->stream, &fs_id2, &dev_id2, &ino_id2);
if (fs_id1 < fs_id2) {
return -1;
} else if (fs_id1 > fs_id2) {
return 1;
} else {
/* files belong to the same fs */
if (dev_id1 > dev_id2) {
return -1;
} else if (dev_id1 < dev_id2) {
return 1;
} else {
/* files belong to same device in same fs */
return (ino_id1 < ino_id2) ? -1 : (ino_id1 > ino_id2) ? 1 : 0;
}
}
}
int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src)
{
int ret;
IsoFileSrc *fsrc;
unsigned int fs_id;
dev_t dev_id;
ino_t ino_id;
if (img == NULL || file == NULL || src == NULL) {
return ISO_NULL_POINTER;
}
iso_stream_get_id(file->stream, &fs_id, &dev_id, &ino_id);
fsrc = malloc(sizeof(IsoFileSrc));
if (fsrc == NULL) {
return ISO_OUT_OF_MEM;
}
/* fill key and other atts */
fsrc->prev_img = file->msblock ? 1 : 0;
fsrc->block = file->msblock;
fsrc->sort_weight = file->sort_weight;
fsrc->stream = file->stream;
/* insert the filesrc in the tree */
ret = iso_rbtree_insert(img->files, fsrc, (void**)src);
if (ret <= 0) {
free(fsrc);
return ret;
}
iso_stream_ref(fsrc->stream);
return ISO_SUCCESS;
}
/**
* Add a given IsoFileSrc to the given image target.
*
* The IsoFileSrc will be cached in a tree to prevent the same file for
* being written several times to image. If you call again this function
* with a node that refers to the same source file, the previously
* created one will be returned.
*
* @param img
* The image where this file is to be written
* @param new
* The IsoFileSrc to add
* @param src
* Will be filled with a pointer to the IsoFileSrc really present in
* the tree. It could be different than new if the same file already
* exists in the tree.
* @return
* 1 on success, 0 if file already exists on tree, < 0 error
*/
int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src)
{
int ret;
if (img == NULL || new == NULL || src == NULL) {
return ISO_NULL_POINTER;
}
/* insert the filesrc in the tree */
ret = iso_rbtree_insert(img->files, new, (void**)src);
return ret;
}
void iso_file_src_free(void *node)
{
iso_stream_unref(((IsoFileSrc*)node)->stream);
free(node);
}
off_t iso_file_src_get_size(IsoFileSrc *file)
{
return iso_stream_get_size(file->stream);
}
static int cmp_by_weight(const void *f1, const void *f2)
{
IsoFileSrc *f = *((IsoFileSrc**)f1);
IsoFileSrc *g = *((IsoFileSrc**)f2);
/* higher weighted first */
return g->sort_weight - f->sort_weight;
}
static
int is_ms_file(void *arg)
{
IsoFileSrc *f = (IsoFileSrc *)arg;
return f->prev_img ? 0 : 1;
}
static
int filesrc_writer_compute_data_blocks(IsoImageWriter *writer)
{
size_t i, size;
Ecma119Image *t;
IsoFileSrc **filelist;
int (*inc_item)(void *);
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
}
t = writer->target;
/* on appendable images, ms files shouldn't be included */
if (t->appendable) {
inc_item = is_ms_file;
} else {
inc_item = NULL;
}
/* store the filesrcs in a array */
filelist = (IsoFileSrc**)iso_rbtree_to_array(t->files, inc_item, &size);
if (filelist == NULL) {
return ISO_OUT_OF_MEM;
}
/* sort files by weight, if needed */
if (t->sort_files) {
qsort(filelist, size, sizeof(void*), cmp_by_weight);
}
/* fill block value */
for (i = 0; i < size; ++i) {
IsoFileSrc *file = filelist[i];
file->block = t->curblock;
t->curblock += DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE);
}
/* the list is only needed by this writer, store locally */
writer->data = filelist;
return ISO_SUCCESS;
}
static
int filesrc_writer_write_vol_desc(IsoImageWriter *writer)
{
/* nothing needed */
return ISO_SUCCESS;
}
/* open a file, i.e., its Stream */
static inline
int filesrc_open(IsoFileSrc *file)
{
return iso_stream_open(file->stream);
}
static inline
int filesrc_close(IsoFileSrc *file)
{
return iso_stream_close(file->stream);
}
/**
* @return
* 1 ok, 0 EOF, < 0 error
*/
static
int filesrc_read(IsoFileSrc *file, char *buf, size_t count)
{
size_t bytes = 0;
/* loop to ensure the full buffer is filled */
do {
ssize_t result;
result = iso_stream_read(file->stream, buf + bytes, count - bytes);
if (result < 0) {
/* fill buffer with 0s and return */
memset(buf + bytes, 0, count - bytes);
return result;
}
if (result == 0)
break;
bytes += result;
} while (bytes < count);
if (bytes < count) {
/* eof */
memset(buf + bytes, 0, count - bytes);
return 0;
} else {
return 1;
}
}
static
int filesrc_writer_write_data(IsoImageWriter *writer)
{
int res;
size_t i, b;
Ecma119Image *t;
IsoFileSrc *file;
IsoFileSrc **filelist;
char name[PATH_MAX];
char buffer[BLOCK_SIZE];
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
}
t = writer->target;
filelist = writer->data;
iso_msg_debug(t->image->id, "Writing Files...");
i = 0;
while ((file = filelist[i++]) != NULL) {
/*
* TODO WARNING
* when we allow files greater than 4GB, current DIV_UP implementation
* can overflow!!
*/
uint32_t nblocks = DIV_UP(iso_file_src_get_size(file), BLOCK_SIZE);
res = filesrc_open(file);
iso_stream_get_file_name(file->stream, name);
if (res < 0) {
/*
* UPS, very ugly error, the best we can do is just to write
* 0's to image
*/
iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0);
res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res,
"File \"%s\" can't be opened. Filling with 0s.", name);
if (res < 0) {
return res; /* aborted due to error severity */
}
memset(buffer, 0, BLOCK_SIZE);
for (b = 0; b < nblocks; ++b) {
res = iso_write(t, buffer, BLOCK_SIZE);
if (res < 0) {
/* ko, writer error, we need to go out! */
return res;
}
}
continue;
} else if (res > 1) {
iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0);
res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0,
"Size of file \"%s\" has changed. It will be %s", name,
(res == 2 ? "truncated" : "padded with 0's"));
if (res < 0) {
filesrc_close(file);
return res; /* aborted due to error severity */
}
}
#ifdef LIBISOFS_VERBOSE_DEBUG
else {
iso_msg_debug(t->image->id, "Writing file %s", name);
}
#endif
/* write file contents to image */
for (b = 0; b < nblocks; ++b) {
int wres;
res = filesrc_read(file, buffer, BLOCK_SIZE);
if (res < 0) {
/* read error */
break;
}
wres = iso_write(t, buffer, BLOCK_SIZE);
if (wres < 0) {
/* ko, writer error, we need to go out! */
filesrc_close(file);
return wres;
}
}
filesrc_close(file);
if (b < nblocks) {
/* premature end of file, due to error or eof */
iso_report_errfile(name, ISO_FILE_CANT_WRITE, 0, 0);
if (res < 0) {
/* error */
res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res,
"Read error in file %s.", name);
} else {
/* eof */
res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0,
"Premature end of file %s.", name);
}
if (res < 0) {
return res; /* aborted due error severity */
}
/* fill with 0s */
iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, 0,
"Filling with 0");
memset(buffer, 0, BLOCK_SIZE);
while (b++ < nblocks) {
res = iso_write(t, buffer, BLOCK_SIZE);
if (res < 0) {
/* ko, writer error, we need to go out! */
return res;
}
}
}
}
return ISO_SUCCESS;
}
static
int filesrc_writer_free_data(IsoImageWriter *writer)
{
/* free the list of files (contents are free together with the tree) */
free(writer->data);
return ISO_SUCCESS;
}
int iso_file_src_writer_create(Ecma119Image *target)
{
IsoImageWriter *writer;
writer = malloc(sizeof(IsoImageWriter));
if (writer == NULL) {
return ISO_ASSERT_FAILURE;
}
writer->compute_data_blocks = filesrc_writer_compute_data_blocks;
writer->write_vol_desc = filesrc_writer_write_vol_desc;
writer->write_data = filesrc_writer_write_data;
writer->free_data = filesrc_writer_free_data;
writer->data = NULL;
writer->target = target;
/* add this writer to image */
target->writers[target->nwriters++] = writer;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_FILESRC_H_
#define LIBISO_FILESRC_H_
#include "libisofs.h"
#include "stream.h"
#include "ecma119.h"
#include <stdint.h>
struct Iso_File_Src
{
unsigned int prev_img :1; /**< if the file comes from a previous image */
uint32_t block; /**< Block where this file will be written on image */
int sort_weight;
IsoStream *stream;
};
int iso_file_src_cmp(const void *n1, const void *n2);
/**
* Create a new IsoFileSrc to get data from a specific IsoFile.
*
* The IsoFileSrc will be cached in a tree to prevent the same file for
* being written several times to image. If you call again this function
* with a node that refers to the same source file, the previously
* created one will be returned. No new IsoFileSrc is created in that case.
*
* @param img
* The image where this file is to be written
* @param file
* The IsoNode we want to write
* @param src
* Will be filled with a pointer to the IsoFileSrc
* @return
* 1 on success, < 0 on error
*/
int iso_file_src_create(Ecma119Image *img, IsoFile *file, IsoFileSrc **src);
/**
* Add a given IsoFileSrc to the given image target.
*
* The IsoFileSrc will be cached in a tree to prevent the same file for
* being written several times to image. If you call again this function
* with a node that refers to the same source file, the previously
* created one will be returned.
*
* @param img
* The image where this file is to be written
* @param new
* The IsoFileSrc to add
* @param src
* Will be filled with a pointer to the IsoFileSrc really present in
* the tree. It could be different than new if the same file already
* exists in the tree.
* @return
* 1 on success, 0 if file already exists on tree, < 0 error
*/
int iso_file_src_add(Ecma119Image *img, IsoFileSrc *new, IsoFileSrc **src);
/**
* Free the IsoFileSrc especific data
*/
void iso_file_src_free(void *node);
/**
* Get the size of the file this IsoFileSrc represents
*/
off_t iso_file_src_get_size(IsoFileSrc *file);
/**
* Create a Writer for file contents.
*
* It takes care of written the files in the correct order.
*/
int iso_file_src_writer_create(Ecma119Image *target);
#endif /*LIBISO_FILESRC_H_*/

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2008 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "filter.h"
#include "node.h"
void iso_filter_ref(FilterContext *filter)
{
++filter->refcount;
}
void iso_filter_unref(FilterContext *filter)
{
if (--filter->refcount == 0) {
filter->free(filter);
free(filter);
}
}
int iso_file_add_filter(IsoFile *file, FilterContext *filter, int flag)
{
int ret;
IsoStream *original, *filtered;
if (file == NULL || filter == NULL) {
return ISO_NULL_POINTER;
}
original = file->stream;
if (!iso_stream_is_repeatable(original)) {
/* TODO use custom error */
return ISO_WRONG_ARG_VALUE;
}
ret = filter->get_filter(filter, original, &filtered);
if (ret < 0) {
return ret;
}
iso_stream_unref(original);
file->stream = filtered;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2008 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_FILTER_H_
#define LIBISO_FILTER_H_
/*
* Definitions of filters.
*/
/* dev_id for stream identification */
#define XOR_ENCRYPT_DEV_ID 1
typedef struct filter_context FilterContext;
struct filter_context {
int version; /* reserved for future usage, set to 0 */
int refcount;
/** filter specific shared data */
void *data;
/**
* Factory method to create a filtered stream from another stream.
*
* @param original
* The original stream to be filtered. If the filter needs a ref to
* it (most cases), it should take a ref to it with iso_stream_ref().
* @param filtered
* Will be filled with the filtered IsoStream (reference belongs to
* caller).
* @return
* 1 on success, < 0 on error
*/
int (*get_filter)(FilterContext *filter, IsoStream *original,
IsoStream **filtered);
/**
* Free implementation specific data. Should never be called by user.
* Use iso_filter_unref() instead.
*/
void (*free)(FilterContext *filter);
};
/**
*
* @param flag
* Reserved for future usage, pass always 0 for now.
* TODO in a future a different value can mean filter caching, where
* the filter is applied once and the filtered file is stored in a temp
* dir. This prevent filter to be applied several times.
*/
int iso_file_add_filter(IsoFile *file, FilterContext *filter, int flag);
void iso_filter_ref(FilterContext *filter);
void iso_filter_unref(FilterContext *filter);
#endif /*LIBISO_FILTER_H_*/

View File

@ -0,0 +1,187 @@
/*
* Copyright (c) 2008 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "../libisofs.h"
#include "../filter.h"
#include "../fsource.h"
/*
* A simple Filter implementation for example purposes. It encrypts a file
* by XORing each byte by a given key.
*/
static ino_t xor_ino_id = 0;
typedef struct
{
IsoStream *orig;
uint8_t key;
ino_t id;
} XorEncryptStreamData;
static
int xor_encrypt_stream_open(IsoStream *stream)
{
XorEncryptStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = (XorEncryptStreamData*)stream->data;
return iso_stream_open(data->orig);
}
static
int xor_encrypt_stream_close(IsoStream *stream)
{
XorEncryptStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
return iso_stream_close(data->orig);
}
static
off_t xor_encrypt_stream_get_size(IsoStream *stream)
{
XorEncryptStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
return iso_stream_get_size(data->orig);
}
static
int xor_encrypt_stream_read(IsoStream *stream, void *buf, size_t count)
{
int ret, len;
XorEncryptStreamData *data;
uint8_t *buffer = buf;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
ret = iso_stream_read(data->orig, buf, count);
if (ret < 0) {
return ret;
}
/* xor */
for (len = 0; len < ret; ++len) {
buffer[len] = buffer[len] ^ data->key;
}
return ret;
}
static
int xor_encrypt_stream_is_repeatable(IsoStream *stream)
{
/* the filter can't be created if underlying stream is not repeatable */
return 1;
}
static
void xor_encrypt_stream_get_id(IsoStream *stream, unsigned int *fs_id,
dev_t *dev_id, ino_t *ino_id)
{
XorEncryptStreamData *data = stream->data;
*fs_id = ISO_FILTER_FS_ID;
*dev_id = XOR_ENCRYPT_DEV_ID;
*ino_id = data->id;
}
static
void xor_encrypt_stream_free(IsoStream *stream)
{
XorEncryptStreamData *data = stream->data;
iso_stream_unref(data->orig);
free(data);
}
IsoStreamIface xor_encrypt_stream_class = {
0,
"xorf",
xor_encrypt_stream_open,
xor_encrypt_stream_close,
xor_encrypt_stream_get_size,
xor_encrypt_stream_read,
xor_encrypt_stream_is_repeatable,
xor_encrypt_stream_get_id,
xor_encrypt_stream_free
};
static
void xor_encrypt_filter_free(FilterContext *filter)
{
free(filter->data);
}
static
int xor_encrypt_filter_get_filter(FilterContext *filter, IsoStream *original,
IsoStream **filtered)
{
IsoStream *str;
XorEncryptStreamData *data;
if (filter == NULL || original == NULL || filtered == NULL) {
return ISO_NULL_POINTER;
}
str = malloc(sizeof(IsoStream));
if (str == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(XorEncryptStreamData));
if (str == NULL) {
free(str);
return ISO_OUT_OF_MEM;
}
/* fill data */
data->key = *((uint8_t*)filter->data);
data->id = xor_ino_id++;
/* get reference to the source */
data->orig = original;
iso_stream_ref(original);
str->refcount = 1;
str->data = data;
str->class = &xor_encrypt_stream_class;
*filtered = str;
return ISO_SUCCESS;
}
int create_xor_encrypt_filter(uint8_t key, FilterContext **filter)
{
FilterContext *f;
uint8_t *data;
f = calloc(1, sizeof(FilterContext));
if (f == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(uint8_t));
if (data == NULL) {
free(f);
return ISO_OUT_OF_MEM;
}
f->refcount = 1;
f->version = 0;
*data = key;
f->data = data;
f->free = xor_encrypt_filter_free;
f->get_filter = xor_encrypt_filter_get_filter;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,629 @@
/*
* Copyright (c) 2008 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "node.h"
#include <fnmatch.h>
#include <string.h>
struct iso_find_condition
{
/*
* Check whether the given node matches this condition.
*
* @param cond
* The condition to check
* @param node
* The node that should be checked
* @return
* 1 if the node matches the condition, 0 if not
*/
int (*matches)(IsoFindCondition *cond, IsoNode *node);
/**
* Free condition specific data
*/
void (*free)(IsoFindCondition*);
/** condition specific data */
void *data;
};
struct find_iter_data
{
IsoDirIter *iter;
IsoFindCondition *cond;
};
static
int find_iter_next(IsoDirIter *iter, IsoNode **node)
{
int ret;
IsoNode *n;
struct find_iter_data *data = iter->data;
while ((ret = iso_dir_iter_next(data->iter, &n)) == 1) {
if (data->cond->matches(data->cond, n)) {
*node = n;
break;
}
}
return ret;
}
static
int find_iter_has_next(IsoDirIter *iter)
{
struct find_iter_data *data = iter->data;
/*
* FIXME wrong implementation!!!! the underlying iter may have more nodes,
* but they may not match find conditions
*/
return iso_dir_iter_has_next(data->iter);
}
static
void find_iter_free(IsoDirIter *iter)
{
struct find_iter_data *data = iter->data;
data->cond->free(data->cond);
free(data->cond);
iso_dir_iter_free(data->iter);
free(iter->data);
}
static
int find_iter_take(IsoDirIter *iter)
{
struct find_iter_data *data = iter->data;
return iso_dir_iter_take(data->iter);
}
static
int find_iter_remove(IsoDirIter *iter)
{
struct find_iter_data *data = iter->data;
return iso_dir_iter_remove(data->iter);
}
void find_notify_child_taken(IsoDirIter *iter, IsoNode *node)
{
/* nothing to do */
return;
}
static
struct iso_dir_iter_iface find_iter_class = {
find_iter_next,
find_iter_has_next,
find_iter_free,
find_iter_take,
find_iter_remove,
find_notify_child_taken
};
int iso_dir_find_children(IsoDir* dir, IsoFindCondition *cond,
IsoDirIter **iter)
{
int ret;
IsoDirIter *children;
IsoDirIter *it;
struct find_iter_data *data;
if (dir == NULL || cond == NULL || iter == NULL) {
return ISO_NULL_POINTER;
}
it = malloc(sizeof(IsoDirIter));
if (it == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(struct find_iter_data));
if (data == NULL) {
free(it);
return ISO_OUT_OF_MEM;
}
ret = iso_dir_get_children(dir, &children);
if (ret < 0) {
free(it);
free(data);
return ret;
}
it->class = &find_iter_class;
it->dir = (IsoDir*)dir;
data->iter = children;
data->cond = cond;
it->data = data;
if (iso_dir_iter_register(it) < 0) {
free(it);
return ISO_OUT_OF_MEM;
}
*iter = it;
return ISO_SUCCESS;
}
/*************** find by name wildcard condition *****************/
static
int cond_name_matches(IsoFindCondition *cond, IsoNode *node)
{
char *pattern = (char*) cond->data;
int ret = fnmatch(pattern, node->name, 0);
return ret == 0 ? 1 : 0;
}
static
void cond_name_free(IsoFindCondition *cond)
{
free(cond->data);
}
/**
* Create a new condition that checks if the node name matches the given
* wildcard.
*
* @param wildcard
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_name(const char *wildcard)
{
IsoFindCondition *cond;
if (wildcard == NULL) {
return NULL;
}
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
cond->data = strdup(wildcard);
cond->free = cond_name_free;
cond->matches = cond_name_matches;
return cond;
}
/*************** find by mode condition *****************/
static
int cond_mode_matches(IsoFindCondition *cond, IsoNode *node)
{
mode_t *mask = (mode_t*) cond->data;
return node->mode & *mask ? 1 : 0;
}
static
void cond_mode_free(IsoFindCondition *cond)
{
free(cond->data);
}
/**
* Create a new condition that checks the node mode against a mode mask. It
* can be used to check both file type and permissions.
*
* For example:
*
* iso_new_find_conditions_mode(S_IFREG) : search for regular files
* iso_new_find_conditions_mode(S_IFCHR | S_IWUSR) : search for character
* devices where owner has write permissions.
*
* @param mask
* Mode mask to AND against node mode.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_mode(mode_t mask)
{
IsoFindCondition *cond;
mode_t *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(mode_t));
if (data == NULL) {
free(cond);
return NULL;
}
*data = mask;
cond->data = data;
cond->free = cond_mode_free;
cond->matches = cond_mode_matches;
return cond;
}
/*************** find by gid condition *****************/
static
int cond_gid_matches(IsoFindCondition *cond, IsoNode *node)
{
gid_t *gid = (gid_t*) cond->data;
return node->gid == *gid ? 1 : 0;
}
static
void cond_gid_free(IsoFindCondition *cond)
{
free(cond->data);
}
/**
* Create a new condition that checks the node gid.
*
* @param gid
* Desired Group Id.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_gid(gid_t gid)
{
IsoFindCondition *cond;
gid_t *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(gid_t));
if (data == NULL) {
free(cond);
return NULL;
}
*data = gid;
cond->data = data;
cond->free = cond_gid_free;
cond->matches = cond_gid_matches;
return cond;
}
/*************** find by uid condition *****************/
static
int cond_uid_matches(IsoFindCondition *cond, IsoNode *node)
{
uid_t *uid = (uid_t*) cond->data;
return node->uid == *uid ? 1 : 0;
}
static
void cond_uid_free(IsoFindCondition *cond)
{
free(cond->data);
}
/**
* Create a new condition that checks the node uid.
*
* @param uid
* Desired User Id.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_uid(uid_t uid)
{
IsoFindCondition *cond;
uid_t *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(uid_t));
if (data == NULL) {
free(cond);
return NULL;
}
*data = uid;
cond->data = data;
cond->free = cond_uid_free;
cond->matches = cond_uid_matches;
return cond;
}
/*************** find by timestamps condition *****************/
struct cond_times
{
time_t time;
int what_time; /* 0 atime, 1 mtime, 2 ctime */
enum iso_find_comparisons comparison;
};
static
int cond_time_matches(IsoFindCondition *cond, IsoNode *node)
{
time_t node_time;
struct cond_times *data = cond->data;
switch (data->what_time) {
case 0: node_time = node->atime; break;
case 1: node_time = node->mtime; break;
default: node_time = node->ctime; break;
}
switch (data->comparison) {
case ISO_FIND_COND_GREATER:
return node_time > data->time ? 1 : 0;
case ISO_FIND_COND_GREATER_OR_EQUAL:
return node_time >= data->time ? 1 : 0;
case ISO_FIND_COND_EQUAL:
return node_time == data->time ? 1 : 0;
case ISO_FIND_COND_LESS:
return node_time < data->time ? 1 : 0;
case ISO_FIND_COND_LESS_OR_EQUAL:
return node_time <= data->time ? 1 : 0;
}
/* should never happen */
return 0;
}
static
void cond_time_free(IsoFindCondition *cond)
{
free(cond->data);
}
/**
* Create a new condition that checks the time of last access.
*
* @param time
* Time to compare against IsoNode atime.
* @param comparison
* Comparison to be done between IsoNode atime and submitted time.
* Note that ISO_FIND_COND_GREATER, for example, is true if the node
* time is greater than the submitted time.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_atime(time_t time,
enum iso_find_comparisons comparison)
{
IsoFindCondition *cond;
struct cond_times *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(struct cond_times));
if (data == NULL) {
free(cond);
return NULL;
}
data->time = time;
data->comparison = comparison;
data->what_time = 0; /* atime */
cond->data = data;
cond->free = cond_time_free;
cond->matches = cond_time_matches;
return cond;
}
/**
* Create a new condition that checks the time of last modification.
*
* @param time
* Time to compare against IsoNode mtime.
* @param comparison
* Comparison to be done between IsoNode mtime and submitted time.
* Note that ISO_FIND_COND_GREATER, for example, is true if the node
* time is greater than the submitted time.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_mtime(time_t time,
enum iso_find_comparisons comparison)
{
IsoFindCondition *cond;
struct cond_times *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(struct cond_times));
if (data == NULL) {
free(cond);
return NULL;
}
data->time = time;
data->comparison = comparison;
data->what_time = 1; /* mtime */
cond->data = data;
cond->free = cond_time_free;
cond->matches = cond_time_matches;
return cond;
}
/**
* Create a new condition that checks the time of last status change.
*
* @param time
* Time to compare against IsoNode ctime.
* @param comparison
* Comparison to be done between IsoNode ctime and submitted time.
* Note that ISO_FIND_COND_GREATER, for example, is true if the node
* time is greater than the submitted time.
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_ctime(time_t time,
enum iso_find_comparisons comparison)
{
IsoFindCondition *cond;
struct cond_times *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(struct cond_times));
if (data == NULL) {
free(cond);
return NULL;
}
data->time = time;
data->comparison = comparison;
data->what_time = 2; /* ctime */
cond->data = data;
cond->free = cond_time_free;
cond->matches = cond_time_matches;
return cond;
}
/*************** logical operations on conditions *****************/
struct logical_binary_conditions {
IsoFindCondition *a;
IsoFindCondition *b;
};
static
void cond_logical_binary_free(IsoFindCondition *cond)
{
struct logical_binary_conditions *data;
data = cond->data;
data->a->free(data->a);
free(data->a);
data->b->free(data->b);
free(data->b);
free(cond->data);
}
static
int cond_logical_and_matches(IsoFindCondition *cond, IsoNode *node)
{
struct logical_binary_conditions *data = cond->data;
return data->a->matches(data->a, node) && data->b->matches(data->b, node);
}
/**
* Create a new condition that check if the two given conditions are
* valid.
*
* @param a
* @param b
* IsoFindCondition to compare
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_and(IsoFindCondition *a,
IsoFindCondition *b)
{
IsoFindCondition *cond;
struct logical_binary_conditions *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(struct logical_binary_conditions));
if (data == NULL) {
free(cond);
return NULL;
}
data->a = a;
data->b = b;
cond->data = data;
cond->free = cond_logical_binary_free;
cond->matches = cond_logical_and_matches;
return cond;
}
static
int cond_logical_or_matches(IsoFindCondition *cond, IsoNode *node)
{
struct logical_binary_conditions *data = cond->data;
return data->a->matches(data->a, node) || data->b->matches(data->b, node);
}
/**
* Create a new condition that check if at least one the two given conditions
* is valid.
*
* @param a
* @param b
* IsoFindCondition to compare
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_or(IsoFindCondition *a,
IsoFindCondition *b)
{
IsoFindCondition *cond;
struct logical_binary_conditions *data;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
data = malloc(sizeof(struct logical_binary_conditions));
if (data == NULL) {
free(cond);
return NULL;
}
data->a = a;
data->b = b;
cond->data = data;
cond->free = cond_logical_binary_free;
cond->matches = cond_logical_or_matches;
return cond;
}
static
void cond_not_free(IsoFindCondition *cond)
{
IsoFindCondition *negate = cond->data;
negate->free(negate);
free(negate);
}
static
int cond_not_matches(IsoFindCondition *cond, IsoNode *node)
{
IsoFindCondition *negate = cond->data;
return !(negate->matches(negate, node));
}
/**
* Create a new condition that check if the given conditions is false.
*
* @param negate
* @result
* The created IsoFindCondition, NULL on error.
*
* @since 0.6.4
*/
IsoFindCondition *iso_new_find_conditions_not(IsoFindCondition *negate)
{
IsoFindCondition *cond;
cond = malloc(sizeof(IsoFindCondition));
if (cond == NULL) {
return NULL;
}
cond->data = negate;
cond->free = cond_not_free;
cond->matches = cond_not_matches;
return cond;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,692 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* Filesystem/FileSource implementation to access the local filesystem.
*/
#include "fsource.h"
#include "util.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
static
int iso_file_source_new_lfs(IsoFileSource *parent, const char *name,
IsoFileSource **src);
/*
* We can share a local filesystem object, as it has no private atts.
*/
IsoFilesystem *lfs= NULL;
typedef struct
{
/** reference to the parent (if root it points to itself) */
IsoFileSource *parent;
char *name;
unsigned int openned :2; /* 0: not openned, 1: file, 2:dir */
union
{
int fd;
DIR *dir;
} info;
} _LocalFsFileSource;
static
char* lfs_get_path(IsoFileSource *src)
{
_LocalFsFileSource *data;
data = src->data;
if (data->parent == src) {
return strdup("/");
} else {
char *path = lfs_get_path(data->parent);
int pathlen = strlen(path);
path = realloc(path, pathlen + strlen(data->name) + 2);
if (pathlen != 1) {
/* pathlen can only be 1 for root */
path[pathlen] = '/';
path[pathlen + 1] = '\0';
}
return strcat(path, data->name);
}
}
static
char* lfs_get_name(IsoFileSource *src)
{
_LocalFsFileSource *data;
data = src->data;
return strdup(data->name);
}
static
int lfs_lstat(IsoFileSource *src, struct stat *info)
{
_LocalFsFileSource *data;
char *path;
if (src == NULL || info == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
path = lfs_get_path(src);
if (lstat(path, info) != 0) {
int err;
/* error, choose an appropriate return code */
switch (errno) {
case EACCES:
err = ISO_FILE_ACCESS_DENIED;
break;
case ENOTDIR:
case ENAMETOOLONG:
case ELOOP:
err = ISO_FILE_BAD_PATH;
break;
case ENOENT:
err = ISO_FILE_DOESNT_EXIST;
break;
case EFAULT:
case ENOMEM:
err = ISO_OUT_OF_MEM;
break;
default:
err = ISO_FILE_ERROR;
break;
}
return err;
}
free(path);
return ISO_SUCCESS;
}
static
int lfs_stat(IsoFileSource *src, struct stat *info)
{
_LocalFsFileSource *data;
char *path;
if (src == NULL || info == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
path = lfs_get_path(src);
if (stat(path, info) != 0) {
int err;
/* error, choose an appropriate return code */
switch (errno) {
case EACCES:
err = ISO_FILE_ACCESS_DENIED;
break;
case ENOTDIR:
case ENAMETOOLONG:
case ELOOP:
err = ISO_FILE_BAD_PATH;
break;
case ENOENT:
err = ISO_FILE_DOESNT_EXIST;
break;
case EFAULT:
case ENOMEM:
err = ISO_OUT_OF_MEM;
break;
default:
err = ISO_FILE_ERROR;
break;
}
return err;
}
free(path);
return ISO_SUCCESS;
}
static
int lfs_access(IsoFileSource *src)
{
int ret;
_LocalFsFileSource *data;
char *path;
if (src == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
path = lfs_get_path(src);
ret = iso_eaccess(path);
free(path);
return ret;
}
static
int lfs_open(IsoFileSource *src)
{
int err;
struct stat info;
_LocalFsFileSource *data;
char *path;
if (src == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
if (data->openned) {
return ISO_FILE_ALREADY_OPENED;
}
/* is a file or a dir ? */
err = lfs_stat(src, &info);
if (err < 0) {
return err;
}
path = lfs_get_path(src);
if (S_ISDIR(info.st_mode)) {
data->info.dir = opendir(path);
data->openned = data->info.dir ? 2 : 0;
} else {
data->info.fd = open(path, O_RDONLY);
data->openned = data->info.fd != -1 ? 1 : 0;
}
free(path);
/*
* check for possible errors, note that many of possible ones are
* parsed in the lstat call above
*/
if (data->openned == 0) {
switch (errno) {
case EACCES:
err = ISO_FILE_ACCESS_DENIED;
break;
case EFAULT:
case ENOMEM:
err = ISO_OUT_OF_MEM;
break;
default:
err = ISO_FILE_ERROR;
break;
}
return err;
}
return ISO_SUCCESS;
}
static
int lfs_close(IsoFileSource *src)
{
int ret;
_LocalFsFileSource *data;
if (src == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
switch (data->openned) {
case 1: /* not dir */
ret = close(data->info.fd) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR;
break;
case 2: /* directory */
ret = closedir(data->info.dir) == 0 ? ISO_SUCCESS : ISO_FILE_ERROR;
break;
default:
ret = ISO_FILE_NOT_OPENED;
break;
}
if (ret == ISO_SUCCESS) {
data->openned = 0;
}
return ret;
}
static
int lfs_read(IsoFileSource *src, void *buf, size_t count)
{
_LocalFsFileSource *data;
if (src == NULL || buf == NULL) {
return ISO_NULL_POINTER;
}
if (count == 0) {
return ISO_WRONG_ARG_VALUE;
}
data = src->data;
switch (data->openned) {
case 1: /* not dir */
{
int ret;
ret = read(data->info.fd, buf, count);
if (ret < 0) {
/* error on read */
switch (errno) {
case EINTR:
ret = ISO_INTERRUPTED;
break;
case EFAULT:
ret = ISO_OUT_OF_MEM;
break;
case EIO:
ret = ISO_FILE_READ_ERROR;
break;
default:
ret = ISO_FILE_ERROR;
break;
}
}
return ret;
}
case 2: /* directory */
return ISO_FILE_IS_DIR;
default:
return ISO_FILE_NOT_OPENED;
}
}
static
off_t lfs_lseek(IsoFileSource *src, off_t offset, int flag)
{
_LocalFsFileSource *data;
int whence;
if (src == NULL) {
return (off_t)ISO_NULL_POINTER;
}
switch (flag) {
case 0:
whence = SEEK_SET; break;
case 1:
whence = SEEK_CUR; break;
case 2:
whence = SEEK_END; break;
default:
return (off_t)ISO_WRONG_ARG_VALUE;
}
data = src->data;
switch (data->openned) {
case 1: /* not dir */
{
off_t ret;
ret = lseek(data->info.fd, offset, whence);
if (ret < 0) {
/* error on read */
switch (errno) {
case ESPIPE:
ret = (off_t)ISO_FILE_ERROR;
break;
default:
ret = (off_t)ISO_ERROR;
break;
}
}
return ret;
}
case 2: /* directory */
return (off_t)ISO_FILE_IS_DIR;
default:
return (off_t)ISO_FILE_NOT_OPENED;
}
}
static
int lfs_readdir(IsoFileSource *src, IsoFileSource **child)
{
_LocalFsFileSource *data;
if (src == NULL || child == NULL) {
return ISO_NULL_POINTER;
}
data = src->data;
switch (data->openned) {
case 1: /* not dir */
return ISO_FILE_IS_NOT_DIR;
case 2: /* directory */
{
struct dirent *entry;
int ret;
/* while to skip "." and ".." dirs */
while (1) {
entry = readdir(data->info.dir);
if (entry == NULL) {
if (errno == EBADF)
return ISO_FILE_ERROR;
else
return 0; /* EOF */
}
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
break;
}
}
/* create the new FileSrc */
ret = iso_file_source_new_lfs(src, entry->d_name, child);
return ret;
}
default:
return ISO_FILE_NOT_OPENED;
}
}
static
int lfs_readlink(IsoFileSource *src, char *buf, size_t bufsiz)
{
int size;
_LocalFsFileSource *data;
char *path;
if (src == NULL || buf == NULL) {
return ISO_NULL_POINTER;
}
if (bufsiz <= 0) {
return ISO_WRONG_ARG_VALUE;
}
data = src->data;
path = lfs_get_path(src);
/*
* invoke readlink, with bufsiz -1 to reserve an space for
* the NULL character
*/
size = readlink(path, buf, bufsiz - 1);
free(path);
if (size < 0) {
/* error */
switch (errno) {
case EACCES:
return ISO_FILE_ACCESS_DENIED;
case ENOTDIR:
case ENAMETOOLONG:
case ELOOP:
return ISO_FILE_BAD_PATH;
case ENOENT:
return ISO_FILE_DOESNT_EXIST;
case EINVAL:
return ISO_FILE_IS_NOT_SYMLINK;
case EFAULT:
case ENOMEM:
return ISO_OUT_OF_MEM;
default:
return ISO_FILE_ERROR;
}
}
/* NULL-terminate the buf */
buf[size] = '\0';
return ISO_SUCCESS;
}
static
IsoFilesystem* lfs_get_filesystem(IsoFileSource *src)
{
return src == NULL ? NULL : lfs;
}
static
void lfs_free(IsoFileSource *src)
{
_LocalFsFileSource *data;
data = src->data;
/* close the file if it is already openned */
if (data->openned) {
src->class->close(src);
}
if (data->parent != src) {
iso_file_source_unref(data->parent);
}
free(data->name);
free(data);
iso_filesystem_unref(lfs);
}
IsoFileSourceIface lfs_class = {
0, /* version */
lfs_get_path,
lfs_get_name,
lfs_lstat,
lfs_stat,
lfs_access,
lfs_open,
lfs_close,
lfs_read,
lfs_readdir,
lfs_readlink,
lfs_get_filesystem,
lfs_free,
lfs_lseek
};
/**
*
* @return
* 1 success, < 0 error
*/
static
int iso_file_source_new_lfs(IsoFileSource *parent, const char *name,
IsoFileSource **src)
{
IsoFileSource *lfs_src;
_LocalFsFileSource *data;
if (src == NULL) {
return ISO_NULL_POINTER;
}
if (lfs == NULL) {
/* this should never happen */
return ISO_ASSERT_FAILURE;
}
/* allocate memory */
data = malloc(sizeof(_LocalFsFileSource));
if (data == NULL) {
return ISO_OUT_OF_MEM;
}
lfs_src = malloc(sizeof(IsoFileSource));
if (lfs_src == NULL) {
free(data);
return ISO_OUT_OF_MEM;
}
/* fill struct */
data->name = name ? strdup(name) : NULL;
data->openned = 0;
if (parent) {
data->parent = parent;
iso_file_source_ref(parent);
} else {
data->parent = lfs_src;
}
lfs_src->refcount = 1;
lfs_src->data = data;
lfs_src->class = &lfs_class;
/* take a ref to local filesystem */
iso_filesystem_ref(lfs);
/* return */
*src = lfs_src;
return ISO_SUCCESS;
}
static
int lfs_get_root(IsoFilesystem *fs, IsoFileSource **root)
{
if (fs == NULL || root == NULL) {
return ISO_NULL_POINTER;
}
return iso_file_source_new_lfs(NULL, NULL, root);
}
static
int lfs_get_by_path(IsoFilesystem *fs, const char *path, IsoFileSource **file)
{
int ret;
IsoFileSource *src;
struct stat info;
char *ptr, *brk_info, *component;
if (fs == NULL || path == NULL || file == NULL) {
return ISO_NULL_POINTER;
}
/*
* first of all check that it is a valid path.
*/
if (lstat(path, &info) != 0) {
int err;
/* error, choose an appropriate return code */
switch (errno) {
case EACCES:
err = ISO_FILE_ACCESS_DENIED;
break;
case ENOTDIR:
case ENAMETOOLONG:
case ELOOP:
err = ISO_FILE_BAD_PATH;
break;
case ENOENT:
err = ISO_FILE_DOESNT_EXIST;
break;
case EFAULT:
case ENOMEM:
err = ISO_OUT_OF_MEM;
break;
default:
err = ISO_FILE_ERROR;
break;
}
return err;
}
/* ok, path is valid. create the file source */
ret = lfs_get_root(fs, &src);
if (ret < 0) {
return ret;
}
if (!strcmp(path, "/")) {
/* we are looking for root */
*file = src;
return ISO_SUCCESS;
}
ptr = strdup(path);
if (ptr == NULL) {
iso_file_source_unref(src);
return ISO_OUT_OF_MEM;
}
component = strtok_r(ptr, "/", &brk_info);
while (component) {
IsoFileSource *child = NULL;
if (!strcmp(component, ".")) {
child = src;
} else if (!strcmp(component, "..")) {
child = ((_LocalFsFileSource*)src->data)->parent;
iso_file_source_ref(child);
iso_file_source_unref(src);
} else {
ret = iso_file_source_new_lfs(src, component, &child);
iso_file_source_unref(src);
if (ret < 0) {
break;
}
}
src = child;
component = strtok_r(NULL, "/", &brk_info);
}
free(ptr);
if (ret > 0) {
*file = src;
}
return ret;
}
static
unsigned int lfs_get_id(IsoFilesystem *fs)
{
return ISO_LOCAL_FS_ID;
}
static
int lfs_fs_open(IsoFilesystem *fs)
{
/* open() operation is not needed */
return ISO_SUCCESS;
}
static
int lfs_fs_close(IsoFilesystem *fs)
{
/* close() operation is not needed */
return ISO_SUCCESS;
}
static
void lfs_fs_free(IsoFilesystem *fs)
{
lfs = NULL;
}
int iso_local_filesystem_new(IsoFilesystem **fs)
{
if (fs == NULL) {
return ISO_NULL_POINTER;
}
if (lfs != NULL) {
/* just take a new ref */
iso_filesystem_ref(lfs);
} else {
lfs = malloc(sizeof(IsoFilesystem));
if (lfs == NULL) {
return ISO_OUT_OF_MEM;
}
/* fill struct */
strncpy(lfs->type, "file", 4);
lfs->refcount = 1;
lfs->version = 0;
lfs->data = NULL; /* we don't need private data */
lfs->get_root = lfs_get_root;
lfs->get_by_path = lfs_get_by_path;
lfs->get_id = lfs_get_id;
lfs->open = lfs_fs_open;
lfs->close = lfs_fs_close;
lfs->free = lfs_fs_free;
}
*fs = lfs;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "fsource.h"
#include <stdlib.h>
/**
* Values belong 1000 are reserved for libisofs usage
*/
unsigned int iso_fs_global_id = 1000;
void iso_file_source_ref(IsoFileSource *src)
{
++src->refcount;
}
void iso_file_source_unref(IsoFileSource *src)
{
if (--src->refcount == 0) {
src->class->free(src);
free(src);
}
}
void iso_filesystem_ref(IsoFilesystem *fs)
{
++fs->refcount;
}
void iso_filesystem_unref(IsoFilesystem *fs)
{
if (--fs->refcount == 0) {
fs->free(fs);
free(fs);
}
}
/*
* this are just helpers to invoque methods in class
*/
inline
char* iso_file_source_get_path(IsoFileSource *src)
{
return src->class->get_path(src);
}
inline
char* iso_file_source_get_name(IsoFileSource *src)
{
return src->class->get_name(src);
}
inline
int iso_file_source_lstat(IsoFileSource *src, struct stat *info)
{
return src->class->lstat(src, info);
}
inline
int iso_file_source_access(IsoFileSource *src)
{
return src->class->access(src);
}
inline
int iso_file_source_stat(IsoFileSource *src, struct stat *info)
{
return src->class->stat(src, info);
}
inline
int iso_file_source_open(IsoFileSource *src)
{
return src->class->open(src);
}
inline
int iso_file_source_close(IsoFileSource *src)
{
return src->class->close(src);
}
inline
int iso_file_source_read(IsoFileSource *src, void *buf, size_t count)
{
return src->class->read(src, buf, count);
}
inline
off_t iso_file_source_lseek(IsoFileSource *src, off_t offset, int flag)
{
return src->class->lseek(src, offset, flag);
}
inline
int iso_file_source_readdir(IsoFileSource *src, IsoFileSource **child)
{
return src->class->readdir(src, child);
}
inline
int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz)
{
return src->class->readlink(src, buf, bufsiz);
}
inline
IsoFilesystem* iso_file_source_get_filesystem(IsoFileSource *src)
{
return src->class->get_filesystem(src);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_FSOURCE_H_
#define LIBISO_FSOURCE_H_
/*
* Definitions for the file sources. Most functions/structures related with
* this were moved to libisofs.h.
*/
#include "libisofs.h"
#define ISO_LOCAL_FS_ID 1
#define ISO_IMAGE_FS_ID 2
#define ISO_ELTORITO_FS_ID 3
#define ISO_MEM_FS_ID 4
#define ISO_FILTER_FS_ID 5
/**
* Create a new IsoFilesystem to deal with local filesystem.
*
* @return
* 1 sucess, < 0 error
*/
int iso_local_filesystem_new(IsoFilesystem **fs);
#endif /*LIBISO_FSOURCE_H_*/

View File

@ -0,0 +1,277 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "image.h"
#include "node.h"
#include "messages.h"
#include "eltorito.h"
#include <stdlib.h>
#include <string.h>
/**
* Create a new image, empty.
*
* The image will be owned by you and should be unref() when no more needed.
*
* @param name
* Name of the image. This will be used as volset_id and volume_id.
* @param image
* Location where the image pointer will be stored.
* @return
* 1 sucess, < 0 error
*/
int iso_image_new(const char *name, IsoImage **image)
{
int res;
IsoImage *img;
if (image == NULL) {
return ISO_NULL_POINTER;
}
img = calloc(1, sizeof(IsoImage));
if (img == NULL) {
return ISO_OUT_OF_MEM;
}
/* local filesystem will be used by default */
res = iso_local_filesystem_new(&(img->fs));
if (res < 0) {
free(img);
return ISO_OUT_OF_MEM;
}
/* use basic builder as default */
res = iso_node_basic_builder_new(&(img->builder));
if (res < 0) {
iso_filesystem_unref(img->fs);
free(img);
return ISO_OUT_OF_MEM;
}
/* fill image fields */
res = iso_node_new_root(&img->root);
if (res < 0) {
iso_node_builder_unref(img->builder);
iso_filesystem_unref(img->fs);
free(img);
return res;
}
img->refcount = 1;
img->id = iso_message_id++;
if (name != NULL) {
img->volset_id = strdup(name);
img->volume_id = strdup(name);
}
*image = img;
return ISO_SUCCESS;
}
/**
* Increments the reference counting of the given image.
*/
void iso_image_ref(IsoImage *image)
{
++image->refcount;
}
/**
* Decrements the reference couting of the given image.
* If it reaches 0, the image is free, together with its tree nodes (whether
* their refcount reach 0 too, of course).
*/
void iso_image_unref(IsoImage *image)
{
if (--image->refcount == 0) {
int nexcl;
/* we need to free the image */
if (image->user_data_free != NULL) {
/* free attached data */
image->user_data_free(image->user_data);
}
for (nexcl = 0; nexcl < image->nexcludes; ++nexcl) {
free(image->excludes[nexcl]);
}
free(image->excludes);
iso_node_unref((IsoNode*)image->root);
iso_node_builder_unref(image->builder);
iso_filesystem_unref(image->fs);
el_torito_boot_catalog_free(image->bootcat);
free(image->volset_id);
free(image->volume_id);
free(image->publisher_id);
free(image->data_preparer_id);
free(image->system_id);
free(image->application_id);
free(image->copyright_file_id);
free(image->abstract_file_id);
free(image->biblio_file_id);
free(image);
}
}
/**
* Attach user defined data to the image. Use this if your application needs
* to store addition info together with the IsoImage. If the image already
* has data attached, the old data will be freed.
*
* @param data
* Pointer to application defined data that will be attached to the
* image. You can pass NULL to remove any already attached data.
* @param give_up
* Function that will be called when the image does not need the data
* any more. It receives the data pointer as an argumente, and eventually
* causes data to be free. It can be NULL if you don't need it.
*/
int iso_image_attach_data(IsoImage *image, void *data, void (*give_up)(void*))
{
if (image == NULL || (data != NULL && free == NULL)) {
return ISO_NULL_POINTER;
}
if (image->user_data != NULL) {
/* free previously attached data */
if (image->user_data_free) {
image->user_data_free(image->user_data);
}
image->user_data = NULL;
image->user_data_free = NULL;
}
if (data != NULL) {
image->user_data = data;
image->user_data_free = give_up;
}
return ISO_SUCCESS;
}
/**
* The the data previously attached with iso_image_attach_data()
*/
void *iso_image_get_attached_data(IsoImage *image)
{
return image->user_data;
}
IsoDir *iso_image_get_root(const IsoImage *image)
{
return image->root;
}
void iso_image_set_volset_id(IsoImage *image, const char *volset_id)
{
free(image->volset_id);
image->volset_id = strdup(volset_id);
}
const char *iso_image_get_volset_id(const IsoImage *image)
{
return image->volset_id;
}
void iso_image_set_volume_id(IsoImage *image, const char *volume_id)
{
free(image->volume_id);
image->volume_id = strdup(volume_id);
}
const char *iso_image_get_volume_id(const IsoImage *image)
{
return image->volume_id;
}
void iso_image_set_publisher_id(IsoImage *image, const char *publisher_id)
{
free(image->publisher_id);
image->publisher_id = strdup(publisher_id);
}
const char *iso_image_get_publisher_id(const IsoImage *image)
{
return image->publisher_id;
}
void iso_image_set_data_preparer_id(IsoImage *image,
const char *data_preparer_id)
{
free(image->data_preparer_id);
image->data_preparer_id = strdup(data_preparer_id);
}
const char *iso_image_get_data_preparer_id(const IsoImage *image)
{
return image->data_preparer_id;
}
void iso_image_set_system_id(IsoImage *image, const char *system_id)
{
free(image->system_id);
image->system_id = strdup(system_id);
}
const char *iso_image_get_system_id(const IsoImage *image)
{
return image->system_id;
}
void iso_image_set_application_id(IsoImage *image, const char *application_id)
{
free(image->application_id);
image->application_id = strdup(application_id);
}
const char *iso_image_get_application_id(const IsoImage *image)
{
return image->application_id;
}
void iso_image_set_copyright_file_id(IsoImage *image,
const char *copyright_file_id)
{
free(image->copyright_file_id);
image->copyright_file_id = strdup(copyright_file_id);
}
const char *iso_image_get_copyright_file_id(const IsoImage *image)
{
return image->copyright_file_id;
}
void iso_image_set_abstract_file_id(IsoImage *image,
const char *abstract_file_id)
{
free(image->abstract_file_id);
image->abstract_file_id = strdup(abstract_file_id);
}
const char *iso_image_get_abstract_file_id(const IsoImage *image)
{
return image->abstract_file_id;
}
void iso_image_set_biblio_file_id(IsoImage *image, const char *biblio_file_id)
{
free(image->biblio_file_id);
image->biblio_file_id = strdup(biblio_file_id);
}
const char *iso_image_get_biblio_file_id(const IsoImage *image)
{
return image->biblio_file_id;
}
int iso_image_get_msg_id(IsoImage *image)
{
return image->id;
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_IMAGE_H_
#define LIBISO_IMAGE_H_
#include "libisofs.h"
#include "node.h"
#include "fsource.h"
#include "builder.h"
/*
* Image is a context for image manipulation.
* Global objects such as the message_queues must belogn to that
* context. Thus we will have, for example, a msg queue per image,
* so images are completelly independent and can be managed together.
* (Usefull, for example, in Multiple-Document-Interface GUI apps.
* [The stuff we have in init belongs really to image!]
*/
struct Iso_Image
{
int refcount;
IsoDir *root;
char *volset_id;
char *volume_id; /**< Volume identifier. */
char *publisher_id; /**< Volume publisher. */
char *data_preparer_id; /**< Volume data preparer. */
char *system_id; /**< Volume system identifier. */
char *application_id; /**< Volume application id */
char *copyright_file_id;
char *abstract_file_id;
char *biblio_file_id;
/* el-torito boot catalog */
struct el_torito_boot_catalog *bootcat;
/* image identifier, for message origin identifier */
int id;
/**
* Default filesystem to use when adding files to the image tree.
*/
IsoFilesystem *fs;
/*
* Default builder to use when adding files to the image tree.
*/
IsoNodeBuilder *builder;
/**
* Whether to follow symlinks or just add them as symlinks
*/
unsigned int follow_symlinks : 1;
/**
* Whether to skip hidden files
*/
unsigned int ignore_hidden : 1;
/**
* Flags that determine what special files should be ignore. It is a
* bitmask:
* bit0: ignore FIFOs
* bit1: ignore Sockets
* bit2: ignore char devices
* bit3: ignore block devices
*/
int ignore_special;
/**
* Files to exclude. Wildcard support is included.
*/
char** excludes;
int nexcludes;
/**
* if the dir already contains a node with the same name, whether to
* replace or not the old node with the new.
*/
enum iso_replace_mode replace;
/* TODO
enum iso_replace_mode (*confirm_replace)(IsoFileSource *src, IsoNode *node);
*/
/**
* When this is not NULL, it is a pointer to a function that will
* be called just before a file will be added. You can control where
* the file will be in fact added or ignored.
*
* @return
* 1 add, 0 ignore, < 0 cancel
*/
int (*report)(IsoImage *image, IsoFileSource *src);
/**
* User supplied data
*/
void *user_data;
void (*user_data_free)(void *ptr);
};
#endif /*LIBISO_IMAGE_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/**
* Structures related to ISO/IEC 9660:1999, that is version 2 of ISO-9660
* "See doc/devel/cookbook/ISO 9660-1999" and
* ISO/IEC DIS 9660:1999(E) "Information processing. Volume and file structure
* of CD­-ROM for Information Interchange"
* for further details.
*/
#ifndef LIBISO_ISO1999_H
#define LIBISO_ISO1999_H
#include "libisofs.h"
#include "ecma119.h"
enum iso1999_node_type {
ISO1999_FILE,
ISO1999_DIR
};
struct iso1999_dir_info {
Iso1999Node **children;
size_t nchildren;
size_t len;
size_t block;
};
struct iso1999_node
{
char *name; /**< Name chosen output charset. */
Iso1999Node *parent;
IsoNode *node; /*< reference to the iso node */
enum iso1999_node_type type;
union {
IsoFileSrc *file;
struct iso1999_dir_info *dir;
} info;
};
/**
* Create a IsoWriter to deal with ISO 9660:1999 estructures, and add it to
* the given target.
*
* @return
* 1 on success, < 0 on error
*/
int iso1999_writer_create(Ecma119Image *target);
#endif /* LIBISO_ISO1999_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/**
* Declare Joliet related structures.
*/
#ifndef LIBISO_JOLIET_H
#define LIBISO_JOLIET_H
#include "libisofs.h"
#include "ecma119.h"
enum joliet_node_type {
JOLIET_FILE,
JOLIET_DIR
};
struct joliet_dir_info {
JolietNode **children;
size_t nchildren;
size_t len;
size_t block;
};
struct joliet_node
{
uint16_t *name; /**< Name in UCS-2BE. */
JolietNode *parent;
IsoNode *node; /*< reference to the iso node */
enum joliet_node_type type;
union {
IsoFileSrc *file;
struct joliet_dir_info *dir;
} info;
};
/**
* Create a IsoWriter to deal with Joliet estructures, and add it to the given
* target.
*
* @return
* 1 on success, < 0 on error
*/
int joliet_writer_create(Ecma119Image *target);
#endif /* LIBISO_JOLIET_H */

View File

@ -0,0 +1,439 @@
/* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008)
Message handling facility of libisofs.
Copyright (C) 2006 - 2008 Thomas Schmitt <scdbackup@gmx.net>,
provided under GPL version 2
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
/* Only this single source module is entitled to do this */
#define LIBISO_MSGS_H_INTERNAL 1
/* All participants in the messaging system must do this */
#include "libiso_msgs.h"
/* ----------------------------- libiso_msgs_item ------------------------- */
static int libiso_msgs_item_new(struct libiso_msgs_item **item,
struct libiso_msgs_item *link, int flag)
{
int ret;
struct libiso_msgs_item *o;
struct timeval tv;
struct timezone tz;
(*item)= o=
(struct libiso_msgs_item *) malloc(sizeof(struct libiso_msgs_item));
if(o==NULL)
return(-1);
o->timestamp= 0.0;
ret= gettimeofday(&tv,&tz);
if(ret==0)
o->timestamp= tv.tv_sec+0.000001*tv.tv_usec;
o->process_id= getpid();
o->origin= -1;
o->severity= LIBISO_MSGS_SEV_ALL;
o->priority= LIBISO_MSGS_PRIO_ZERO;
o->error_code= 0;
o->msg_text= NULL;
o->os_errno= 0;
o->prev= link;
o->next= NULL;
if(link!=NULL) {
if(link->next!=NULL) {
link->next->prev= o;
o->next= link->next;
}
link->next= o;
}
return(1);
}
/** Detaches item from its queue and eventually readjusts start, end pointers
of the queue */
int libiso_msgs_item_unlink(struct libiso_msgs_item *o,
struct libiso_msgs_item **chain_start,
struct libiso_msgs_item **chain_end, int flag)
{
if(o->prev!=NULL)
o->prev->next= o->next;
if(o->next!=NULL)
o->next->prev= o->prev;
if(chain_start!=NULL)
if(*chain_start == o)
*chain_start= o->next;
if(chain_end!=NULL)
if(*chain_end == o)
*chain_end= o->prev;
o->next= o->prev= NULL;
return(1);
}
int libiso_msgs_item_destroy(struct libiso_msgs_item **item,
int flag)
{
struct libiso_msgs_item *o;
o= *item;
if(o==NULL)
return(0);
libiso_msgs_item_unlink(o,NULL,NULL,0);
if(o->msg_text!=NULL)
free((char *) o->msg_text);
free((char *) o);
*item= NULL;
return(1);
}
int libiso_msgs_item_get_msg(struct libiso_msgs_item *item,
int *error_code, char **msg_text, int *os_errno,
int flag)
{
*error_code= item->error_code;
*msg_text= item->msg_text;
*os_errno= item->os_errno;
return(1);
}
int libiso_msgs_item_get_origin(struct libiso_msgs_item *item,
double *timestamp, pid_t *process_id, int *origin,
int flag)
{
*timestamp= item->timestamp;
*process_id= item->process_id;
*origin= item->origin;
return(1);
}
int libiso_msgs_item_get_rank(struct libiso_msgs_item *item,
int *severity, int *priority, int flag)
{
*severity= item->severity;
*priority= item->priority;
return(1);
}
/* ------------------------------- libiso_msgs ---------------------------- */
int libiso_msgs_new(struct libiso_msgs **m, int flag)
{
struct libiso_msgs *o;
(*m)= o= (struct libiso_msgs *) malloc(sizeof(struct libiso_msgs));
if(o==NULL)
return(-1);
o->refcount= 1;
o->oldest= NULL;
o->youngest= NULL;
o->count= 0;
o->queue_severity= LIBISO_MSGS_SEV_ALL;
o->print_severity= LIBISO_MSGS_SEV_NEVER;
strcpy(o->print_id,"libiso: ");
#ifndef LIBISO_MSGS_SINGLE_THREADED
pthread_mutex_init(&(o->lock_mutex),NULL);
#endif
return(1);
}
static int libiso_msgs_lock(struct libiso_msgs *m, int flag)
{
#ifndef LIBISO_MSGS_SINGLE_THREADED
int ret;
ret= pthread_mutex_lock(&(m->lock_mutex));
if(ret!=0)
return(0);
#endif
return(1);
}
static int libiso_msgs_unlock(struct libiso_msgs *m, int flag)
{
#ifndef LIBISO_MSGS_SINGLE_THREADED
int ret;
ret= pthread_mutex_unlock(&(m->lock_mutex));
if(ret!=0)
return(0);
#endif
return(1);
}
int libiso_msgs_destroy(struct libiso_msgs **m, int flag)
{
struct libiso_msgs *o;
struct libiso_msgs_item *item, *next_item;
o= *m;
if(o==NULL)
return(0);
if(o->refcount > 1) {
if(libiso_msgs_lock(*m,0)<=0)
return(-1);
o->refcount--;
libiso_msgs_unlock(*m,0);
*m= NULL;
return(1);
}
#ifndef LIBISO_MSGS_SINGLE_THREADED
if(pthread_mutex_destroy(&(o->lock_mutex))!=0) {
pthread_mutex_unlock(&(o->lock_mutex));
pthread_mutex_destroy(&(o->lock_mutex));
}
#endif
for(item= o->oldest; item!=NULL; item= next_item) {
next_item= item->next;
libiso_msgs_item_destroy(&item,0);
}
free((char *) o);
*m= NULL;
return(1);
}
int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *m, int flag)
{
if(libiso_msgs_lock(m,0)<=0)
return(0);
m->refcount++;
*pt= m;
libiso_msgs_unlock(m,0);
return(1);
}
int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity,
int print_severity, char *print_id, int flag)
{
if(libiso_msgs_lock(m,0)<=0)
return(0);
m->queue_severity= queue_severity;
m->print_severity= print_severity;
strncpy(m->print_id,print_id,80);
m->print_id[80]= 0;
libiso_msgs_unlock(m,0);
return(1);
}
int libiso_msgs__text_to_sev(char *severity_name, int *severity,
int flag)
{
if(strncmp(severity_name,"NEVER",5)==0)
*severity= LIBISO_MSGS_SEV_NEVER;
else if(strncmp(severity_name,"ABORT",5)==0)
*severity= LIBISO_MSGS_SEV_ABORT;
else if(strncmp(severity_name,"FATAL",5)==0)
*severity= LIBISO_MSGS_SEV_FATAL;
else if(strncmp(severity_name,"FAILURE",7)==0)
*severity= LIBISO_MSGS_SEV_FAILURE;
else if(strncmp(severity_name,"MISHAP",6)==0)
*severity= LIBISO_MSGS_SEV_MISHAP;
else if(strncmp(severity_name,"SORRY",5)==0)
*severity= LIBISO_MSGS_SEV_SORRY;
else if(strncmp(severity_name,"WARNING",7)==0)
*severity= LIBISO_MSGS_SEV_WARNING;
else if(strncmp(severity_name,"HINT",4)==0)
*severity= LIBISO_MSGS_SEV_HINT;
else if(strncmp(severity_name,"NOTE",4)==0)
*severity= LIBISO_MSGS_SEV_NOTE;
else if(strncmp(severity_name,"UPDATE",6)==0)
*severity= LIBISO_MSGS_SEV_UPDATE;
else if(strncmp(severity_name,"DEBUG",5)==0)
*severity= LIBISO_MSGS_SEV_DEBUG;
else if(strncmp(severity_name,"ERRFILE",7)==0)
*severity= LIBISO_MSGS_SEV_ERRFILE;
else if(strncmp(severity_name,"ALL",3)==0)
*severity= LIBISO_MSGS_SEV_ALL;
else {
*severity= LIBISO_MSGS_SEV_ALL;
return(0);
}
return(1);
}
int libiso_msgs__sev_to_text(int severity, char **severity_name,
int flag)
{
if(flag&1) {
*severity_name= "NEVER\nABORT\nFATAL\nFAILURE\nMISHAP\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nERRFILE\nALL";
return(1);
}
*severity_name= "";
if(severity>=LIBISO_MSGS_SEV_NEVER)
*severity_name= "NEVER";
else if(severity>=LIBISO_MSGS_SEV_ABORT)
*severity_name= "ABORT";
else if(severity>=LIBISO_MSGS_SEV_FATAL)
*severity_name= "FATAL";
else if(severity>=LIBISO_MSGS_SEV_FAILURE)
*severity_name= "FAILURE";
else if(severity>=LIBISO_MSGS_SEV_MISHAP)
*severity_name= "MISHAP";
else if(severity>=LIBISO_MSGS_SEV_SORRY)
*severity_name= "SORRY";
else if(severity>=LIBISO_MSGS_SEV_WARNING)
*severity_name= "WARNING";
else if(severity>=LIBISO_MSGS_SEV_HINT)
*severity_name= "HINT";
else if(severity>=LIBISO_MSGS_SEV_NOTE)
*severity_name= "NOTE";
else if(severity>=LIBISO_MSGS_SEV_UPDATE)
*severity_name= "UPDATE";
else if(severity>=LIBISO_MSGS_SEV_DEBUG)
*severity_name= "DEBUG";
else if(severity>=LIBISO_MSGS_SEV_ERRFILE)
*severity_name= "ERRFILE";
else if(severity>=LIBISO_MSGS_SEV_ALL)
*severity_name= "ALL";
else {
*severity_name= "";
return(0);
}
return(1);
}
int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code,
int severity, int priority, char *msg_text,
int os_errno, int flag)
{
int ret;
char *textpt,*sev_name,sev_text[81];
struct libiso_msgs_item *item= NULL;
if(severity >= m->print_severity) {
if(msg_text==NULL)
textpt= "";
else
textpt= msg_text;
sev_text[0]= 0;
ret= libiso_msgs__sev_to_text(severity,&sev_name,0);
if(ret>0)
sprintf(sev_text,"%s : ",sev_name);
fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt);
if(os_errno!=0) {
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
fprintf(stderr,"%s( Most recent system error: %d '%s' )\n",
m->print_id,os_errno,strerror(os_errno));
libiso_msgs_unlock(m,0);
}
}
if(severity < m->queue_severity)
return(0);
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
ret= libiso_msgs_item_new(&item,m->youngest,0);
if(ret<=0)
goto failed;
item->origin= origin;
item->error_code= error_code;
item->severity= severity;
item->priority= priority;
if(msg_text!=NULL) {
item->msg_text= malloc(strlen(msg_text)+1);
if(item->msg_text==NULL)
goto failed;
strcpy(item->msg_text,msg_text);
}
item->os_errno= os_errno;
if(m->oldest==NULL)
m->oldest= item;
m->youngest= item;
m->count++;
libiso_msgs_unlock(m,0);
/*
fprintf(stderr,"libiso_experimental: message submitted to queue (now %d)\n",
m->count);
*/
return(1);
failed:;
libiso_msgs_item_destroy(&item,0);
libiso_msgs_unlock(m,0);
return(-1);
}
int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item,
int severity, int priority, int flag)
{
int ret;
struct libiso_msgs_item *im, *next_im= NULL;
*item= NULL;
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
for(im= m->oldest; im!=NULL; im= next_im) {
for(; im!=NULL; im= next_im) {
next_im= im->next;
if(im->severity>=severity)
break;
libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
libiso_msgs_item_destroy(&im,0); /* severity too low: delete */
}
if(im==NULL)
break;
if(im->priority>=priority)
break;
}
if(im==NULL)
{ret= 0; goto ex;}
libiso_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
*item= im;
ret= 1;
ex:;
libiso_msgs_unlock(m,0);
return(ret);
}
int libiso_msgs_destroy_item(struct libiso_msgs *m,
struct libiso_msgs_item **item, int flag)
{
int ret;
ret= libiso_msgs_lock(m,0);
if(ret<=0)
return(-1);
ret= libiso_msgs_item_destroy(item,0);
libiso_msgs_unlock(m,0);
return(ret);
}

View File

@ -0,0 +1,682 @@
/* libiso_msgs (generated from libdax_msgs : Fri Feb 22 19:42:52 CET 2008)
Message handling facility of libisofs.
Copyright (C) 2006-2008 Thomas Schmitt <scdbackup@gmx.net>,
provided under GPL version 2
*/
/*
*Never* set this macro outside libiso_msgs.c !
The entrails of the message handling facility are not to be seen by
the other library components or the applications.
*/
#ifdef LIBISO_MSGS_H_INTERNAL
#ifndef LIBISO_MSGS_SINGLE_THREADED
#include <pthread.h>
#endif
struct libiso_msgs_item {
double timestamp;
pid_t process_id;
int origin;
int severity;
int priority;
/* Apply for your developer's error code range at
libburn-hackers@pykix.org
Report introduced codes in the list below. */
int error_code;
char *msg_text;
int os_errno;
struct libiso_msgs_item *prev,*next;
};
struct libiso_msgs {
int refcount;
struct libiso_msgs_item *oldest;
struct libiso_msgs_item *youngest;
int count;
int queue_severity;
int print_severity;
char print_id[81];
#ifndef LIBISO_MSGS_SINGLE_THREADED
pthread_mutex_t lock_mutex;
#endif
};
#endif /* LIBISO_MSGS_H_INTERNAL */
#ifndef LIBISO_MSGS_H_INCLUDED
#define LIBISO_MSGS_H_INCLUDED 1
#ifndef LIBISO_MSGS_H_INTERNAL
/* Architectural aspects */
/*
libdax_msgs is designed to serve in libraries which want to offer their
applications a way to control the output of library messages. It shall be
incorporated by an owner, i.e. a software entity which encloses the code
of the .c file.
Owner of libdax_msgs is libburn. A fully compatible variant named libiso_msgs
is owned by libisofs and can get generated by a script of the libburn
project: libburn/libiso_msgs_to_xyz_msgs.sh .
Reason: One cannot link two owners of the same variant together because
both would offer the same functions to the linker. For that situation one
has to create a compatible variant as it is done for libisofs.
Compatible variants may get plugged together by call combinations like
burn_set_messenger(iso_get_messenger());
A new variant would demand a _set_messenger() function if it has to work
with libisofs. If only libburn is planned as link partner then a simple
_get_messenger() does suffice.
Take care to shutdown libburn before its provider of the *_msgs object
gets shut down.
*/
/* Public Opaque Handles */
/** A pointer to this is a opaque handle to a message handling facility */
struct libiso_msgs;
/** A pointer to this is a opaque handle to a single message item */
struct libiso_msgs_item;
#endif /* ! LIBISO_MSGS_H_INTERNAL */
/* Public Macros */
/* Registered Severities */
/* It is well advisable to let applications select severities via strings and
forwarded functions libiso_msgs__text_to_sev(), libiso_msgs__sev_to_text().
These macros are for use by the owner of libiso_msgs.
*/
/** Use this to get messages of any severity. Do not use for submitting.
*/
#define LIBISO_MSGS_SEV_ALL 0x00000000
/** Messages of this severity shall transport plain disk file paths
whenever an event of severity SORRY or above is related with an
individual disk file.
No message text shall be added to the file path. The ERRFILE message
shall be issued before the human readable message which carries the
true event severity. That message should contain the file path so it
can be found by strstr(message, path)!=NULL.
The error code shall be the same as with the human readable message.
*/
#define LIBISO_MSGS_SEV_ERRFILE 0x08000000
/** Debugging messages not to be visible to normal users by default
*/
#define LIBISO_MSGS_SEV_DEBUG 0x10000000
/** Update of a progress report about long running actions
*/
#define LIBISO_MSGS_SEV_UPDATE 0x20000000
/** Not so usual events which were gracefully handled
*/
#define LIBISO_MSGS_SEV_NOTE 0x30000000
/** Possibilities to achieve a better result
*/
#define LIBISO_MSGS_SEV_HINT 0x40000000
/** Warnings about problems which could not be handled optimally
*/
#define LIBISO_MSGS_SEV_WARNING 0x50000000
/** Non-fatal error messages indicating that parts of an action failed but
processing may go on if one accepts deviations from the desired result.
SORRY may also be the severity for incidents which are severe enough
for FAILURE but happen within already started irrevocable actions,
like ISO image generation. A precondition for such a severity ease is
that the action can be continued after the incident.
See below MISHAP for what xorriso would need instead of this kind of SORRY
and generates for itself in case of libisofs image generation.
E.g.: A pattern yields no result.
A speed setting cannot be made.
A libisofs input file is inaccessible during image generation.
After SORRY a function should try to go on if that makes any sense
and if no threshold prescribes abort on SORRY. The function should
nevertheless indicate some failure in its return value.
It should - but it does not have to.
*/
#define LIBISO_MSGS_SEV_SORRY 0x60000000
/** A FAILURE (see below) which can be tolerated during long lasting
operations just because they cannot simply be stopped or revoked.
xorriso converts libisofs SORRY messages issued during image generation
into MISHAP messages in order to allow its evaluators to distinguish
image generation problems from minor image composition problems.
E.g.:
A libisofs input file is inaccessible during image generation.
After a MISHAP a function should behave like after SORRY.
*/
#define LIBISO_MSGS_SEV_MISHAP 0x64000000
/** Non-fatal error indicating that an important part of an action failed and
that only a new setup of preconditions will give hope for sufficient
success.
E.g.: No media is inserted in the output drive.
No write mode can be found for inserted media.
A libisofs input file is inaccessible during grafting.
After FAILURE a function should end with a return value indicating failure.
It is at the discretion of the function whether it ends immediately in any
case or whether it tries to go on if the eventual threshold allows.
*/
#define LIBISO_MSGS_SEV_FAILURE 0x68000000
/** An error message which puts the whole operation of the program in question
E.g.: Not enough memory for essential temporary objects.
Irregular errors from resources.
Programming errors (soft assert).
After FATAL a function should end very soon with a return value
indicating severe failure.
*/
#define LIBISO_MSGS_SEV_FATAL 0x70000000
/** A message from an abort handler which will finally finish libburn
*/
#define LIBISO_MSGS_SEV_ABORT 0x71000000
/** A severity to exclude resp. discard any possible message.
Do not use this severity for submitting.
*/
#define LIBISO_MSGS_SEV_NEVER 0x7fffffff
/* Registered Priorities */
/* Priorities are to be selected by the programmers and not by the user. */
#define LIBISO_MSGS_PRIO_ZERO 0x00000000
#define LIBISO_MSGS_PRIO_LOW 0x10000000
#define LIBISO_MSGS_PRIO_MEDIUM 0x20000000
#define LIBISO_MSGS_PRIO_HIGH 0x30000000
#define LIBISO_MSGS_PRIO_TOP 0x7ffffffe
/* Do not use this priority for submitting */
#define LIBISO_MSGS_PRIO_NEVER 0x7fffffff
/* Origin numbers of libburn drives may range from 0 to 1048575 */
#define LIBISO_MSGS_ORIGIN_DRIVE_BASE 0
#define LIBISO_MSGS_ORIGIN_DRIVE_TOP 0xfffff
/* Origin numbers of libisofs images may range from 1048575 to 2097152 */
#define LIBISO_MSGS_ORIGIN_IMAGE_BASE 0x100000
#define LIBISO_MSGS_ORIGIN_IMAGE_TOP 0x1fffff
/* Public Functions */
/* Calls initiated from inside the direct owner (e.g. from libburn) */
/** Create new empty message handling facility with queue and issue a first
official reference to it.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
int libiso_msgs_new(struct libiso_msgs **m, int flag);
/** Destroy a message handling facility and all its eventual messages.
The submitted pointer gets set to NULL.
Actually only the last destroy call of all offical references to the object
will really dispose it. All others just decrement the reference counter.
Call this function only with official reference pointers obtained by
libiso_msgs_new() or libiso_msgs_refer(), and only once per such pointer.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL, -1 for fatal error
*/
int libiso_msgs_destroy(struct libiso_msgs **m, int flag);
/** Create an official reference to an existing libiso_msgs object. The
references keep the object alive at least until it is released by
a matching number of destroy calls. So each reference MUST be revoked
by exactly one call to libiso_msgs_destroy().
@param pt The pointer to be set and registered
@param m A pointer to the existing object
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for failure
*/
int libiso_msgs_refer(struct libiso_msgs **pt, struct libiso_msgs *o, int flag);
/** Submit a message to a message handling facility.
@param origin program specific identification number of the originator of
a message. E.g. drive number. Programs should have an own
range of origin numbers. See above LIBISO_MSGS_ORIGIN_*_BASE
Use -1 if no number is known.
@param error_code Unique error code. Use only registered codes. See below.
The same unique error_code may be issued at different
occasions but those should be equivalent out of the view
of a libiso_msgs application. (E.g. "cannot open ATA drive"
versus "cannot open SCSI drive" would be equivalent.)
@param severity The LIBISO_MSGS_SEV_* of the event.
@param priority The LIBISO_MSGS_PRIO_* number of the event.
@param msg_text Printable and human readable message text.
@param os_errno Eventual error code from operating system (0 if none)
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on rejection, <0 for severe errors
*/
int libiso_msgs_submit(struct libiso_msgs *m, int origin, int error_code,
int severity, int priority, char *msg_text,
int os_errno, int flag);
/* Calls from applications (to be forwarded by direct owner) */
/** Convert a registered severity number into a severity name
@param flag Bitfield for control purposes:
bit0= list all severity names in a newline separated string
@return >0 success, <=0 failure
*/
int libiso_msgs__sev_to_text(int severity, char **severity_name,
int flag);
/** Convert a severity name into a severity number,
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
int libiso_msgs__text_to_sev(char *severity_name, int *severity,
int flag);
/** Set minimum severity for messages to be queued (default
LIBISO_MSGS_SEV_ALL) and for messages to be printed directly to stderr
(default LIBISO_MSGS_SEV_NEVER).
@param print_id A text of at most 80 characters to be printed before
any eventually printed message (default is "libiso: ").
@param flag Bitfield for control purposes (unused yet, submit 0)
@return always 1 for now
*/
int libiso_msgs_set_severities(struct libiso_msgs *m, int queue_severity,
int print_severity, char *print_id, int flag);
/** Obtain a message item that has at least the given severity and priority.
Usually all older messages of lower severity are discarded then. If no
item of sufficient severity was found, all others are discarded from the
queue.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 if a matching item was found, 0 if not, <0 for severe errors
*/
int libiso_msgs_obtain(struct libiso_msgs *m, struct libiso_msgs_item **item,
int severity, int priority, int flag);
/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted
pointer gets set to NULL.
Caution: Copy eventually obtained msg_text before destroying the item,
if you want to use it further.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL, <0 for severe errors
*/
int libiso_msgs_destroy_item(struct libiso_msgs *m,
struct libiso_msgs_item **item, int flag);
/** Obtain from a message item the three application oriented components as
submitted with the originating call of libiso_msgs_submit().
Caution: msg_text becomes a pointer into item, not a copy.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_msg(struct libiso_msgs_item *item,
int *error_code, char **msg_text, int *os_errno,
int flag);
/** Obtain from a message item the submitter identification submitted
with the originating call of libiso_msgs_submit().
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_origin(struct libiso_msgs_item *item,
double *timestamp, pid_t *process_id, int *origin,
int flag);
/** Obtain from a message item severity and priority as submitted
with the originating call of libiso_msgs_submit().
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 on success, 0 on invalid item, <0 for servere errors
*/
int libiso_msgs_item_get_rank(struct libiso_msgs_item *item,
int *severity, int *priority, int flag);
#ifdef LIDBAX_MSGS_________________
/* Registered Error Codes */
Format: error_code (LIBISO_MSGS_SEV_*,LIBISO_MSGS_PRIO_*) = explanation
If no severity or priority are fixely associated, use "(,)".
------------------------------------------------------------------------------
Range "libiso_msgs" : 0x00000000 to 0x0000ffff
0x00000000 (ALL,ZERO) = Initial setting in new libiso_msgs_item
0x00000001 (DEBUG,ZERO) = Test error message
0x00000002 (DEBUG,ZERO) = Debugging message
0x00000003 (FATAL,HIGH) = Out of virtual memory
------------------------------------------------------------------------------
Range "elmom" : 0x00010000 to 0x0001ffff
------------------------------------------------------------------------------
Range "scdbackup" : 0x00020000 to 0x0002ffff
Acessing and defending drives:
0x00020001 (SORRY,LOW) = Cannot open busy device
0x00020002 (SORRY,HIGH) = Encountered error when closing drive
0x00020003 (SORRY,HIGH) = Could not grab drive
0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling
0x00020005 (SORRY,HIGH) = Failed to open device
0x00020006 (FATAL,HIGH) = Too many scsi siblings
0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings
0x00020008 (SORRY,HIGH) = Device busy. Failed to fcntl-lock
0x00020009 (SORRY,HIGH) = Neither stdio-path nor its directory exist
General library operations:
0x00020101 (WARNING,HIGH) = Cannot find given worker item
0x00020102 (SORRY,HIGH) = A drive operation is still going on
0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on
0x00020104 (SORRY,HIGH) = NULL pointer caught
0x00020105 (SORRY,HIGH) = Drive is already released
0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close
0x00020107 (WARNING,HIGH) = A drive is still busy on shutdown of library
0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry
0x00020108 (FATAL,HIGH) = Could not allocate new drive object
0x00020109 (FATAL,HIGH) = Library not running
0x0002010a (FATAL,HIGH) = Unsuitable track mode
0x0002010b (FATAL,HIGH) = Burn run failed
0x0002010c (FATAL,HIGH) = Failed to transfer command to drive
0x0002010d (DEBUG,HIGH) = Could not inquire TOC
0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive
0x0002010f (DEBUG,HIGH) = SCSI error condition on command
0x00020110 (FATAL,HIGH) = Persistent drive address too long
0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object
0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type
0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet
0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data
0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value
0x00020116 (FATAL,HIGH) = Track mode has unusable value
0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use
0x00020118 (DEBUG,HIGH) = Closing track
0x00020119 (DEBUG,HIGH) = Closing session
0x0002011a (NOTE,HIGH) = Padding up track to minimum size
0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive
0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive
0x0002011d (FATAL,HIGH) = SCSI error on write
0x0002011e (SORRY,HIGH) = Unsuitable media detected
0x0002011f (SORRY,HIGH) = Burning is restricted to a single track
0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored
0x00020121 (FATAL,HIGH) = Write preparation setup failed
0x00020122 (FATAL,HIGH) = SCSI error on format_unit
0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type
0x00020124 (SORRY,HIGH) = SCSI error on set_streaming
0x00020125 (SORRY,HIGH) = Write start address not supported
0x00020126 (SORRY,HIGH) = Write start address not properly aligned
0x00020127 (NOTE,HIGH) = Write start address is ...
0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance
0x00020129 (SORRY,HIGH) = Will not format media type
0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities
0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job
0x0002012c (SORRY,HIGH) = Too many logical tracks recorded
0x0002012d (FATAL,HIGH) = Exceeding range of permissible write addresses
0x0002012e (NOTE,HIGH) = Activated track default size
0x0002012f (SORRY,HIGH) = SAO is restricted to single fixed size session
0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking
0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive
0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn
0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode
0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO
0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO
0x00020136 (SORRY,HIGH) = DAO burning restricted to single fixed size track
0x00020137 (HINT,HIGH) = TAO would be possible
0x00020138 (FATAL,HIGH) = Cannot reserve track
0x00020139 (SORRY,HIGH) = Write job parameters are unsuitable
0x0002013a (FATAL,HIGH) = No suitable media detected
0x0002013b (DEBUG,HIGH) = SCSI command indicates host or driver error
0x0002013c (SORRY,HIGH) = Malformed capabilities page 2Ah received
0x0002013d (DEBUG,LOW) = Waiting for free buffer space takes long time
0x0002013e (SORRY,HIGH) = Timeout with waiting for free buffer. Now disabled
0x0002013f (DEBUG,LOW) = Reporting total time spent with waiting for buffer
0x00020140 (FATAL,HIGH) = Drive is busy on attempt to write random access
0x00020141 (SORRY,HIGH) = Write data count not properly aligned
0x00020142 (FATAL,HIGH) = Drive is not grabbed on random access write
0x00020143 (SORRY,HIGH) = Read start address not properly aligned
0x00020144 (SORRY,HIGH) = SCSI error on read
0x00020145 (FATAL,HIGH) = Drive is busy on attempt to read data
0x00020146 (FATAL,HIGH) = Drive is a virtual placeholder
0x00020147 (SORRY,HIGH) = Cannot address start byte
0x00020148 (SORRY,HIGH) = Cannot write desired amount of data
0x00020149 (SORRY,HIGH) = Unsuitable filetype for pseudo-drive
0x0002014a (SORRY,HIGH) = Cannot read desired amount of data
0x0002014b (SORRY,HIGH) = Drive is already registered resp. scanned
0x0002014c (FATAL,HIGH) = Emulated drive caught in SCSI function
0x0002014d (SORRY,HIGH) = Asynchromous SCSI error
0x0002014f (SORRY,HIGH) = Timeout with asynchromous SCSI command
0x00020150 (DEBUG,LOW) = Reporting asynchronous waiting time
0x00020151 (FATAL,HIGH) = Read attempt on write-only drive
0x00020152 (FATAL,HIGH) = Cannot start fifo thread
0x00020153 (SORRY,HIGH) = Read error on fifo input
0x00020154 (NOTE,HIGH) = Forwarded input error ends output
0x00020155 (SORRY,HIGH) = Desired fifo buffer too large
0x00020156 (SORRY,HIGH) = Desired fifo buffer too small
0x00020157 (FATAL,HIGH) = burn_source is not a fifo object
0x00020158 (DEBUG,LOW) = Reporting thread disposal precautions
0x00020159 (DEBUG,HIGH) = TOC Format 0 returns inconsistent data
libiso_audioxtr:
0x00020200 (SORRY,HIGH) = Cannot open audio source file
0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format
0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data
------------------------------------------------------------------------------
Range "vreixo" : 0x00030000 to 0x0003ffff
0x0003ffff (FAILURE,HIGH) = Operation canceled
0x0003fffe (FATAL,HIGH) = Unknown or unexpected fatal error
0x0003fffd (FAILURE,HIGH) = Unknown or unexpected error
0x0003fffc (FATAL,HIGH) = Internal programming error
0x0003fffb (FAILURE,HIGH) = NULL pointer where NULL not allowed
0x0003fffa (FATAL,HIGH) = Memory allocation error
0x0003fff9 (FATAL,HIGH) = Interrupted by a signal
0x0003fff8 (FAILURE,HIGH) = Invalid parameter value
0x0003fff7 (FATAL,HIGH) = Cannot create a needed thread
0x0003fff6 (FAILURE,HIGH) = Write error
0x0003fff5 (FAILURE,HIGH) = Buffer read error
0x0003ffc0 (FAILURE,HIGH) = Trying to add a node already added to another dir
0x0003ffbf (FAILURE,HIGH) = Node with same name already exist
0x0003ffbe (FAILURE,HIGH) = Trying to remove a node that was not added to dir
0x0003ffbd (FAILURE,HIGH) = A requested node does not exist
0x0003ffbc (FAILURE,HIGH) = Image already bootable
0x0003ffbb (FAILURE,HIGH) = Trying to use an invalid file as boot image
0x0003ff80 (FAILURE,HIGH) = Error on file operation
0x0003ff7f (FAILURE,HIGH) = Trying to open an already openned file
0x0003ff7e (FAILURE,HIGH) = Access to file is not allowed
0x0003ff7d (FAILURE,HIGH) = Incorrect path to file
0x0003ff7c (FAILURE,HIGH) = The file does not exist in the filesystem
0x0003ff7b (FAILURE,HIGH) = Trying to read or close a file not openned
0x0003ff7a (FAILURE,HIGH) = Directory used where no dir is expected
0x0003ff79 (FAILURE,HIGH) = File read error
0x0003ff78 (FAILURE,HIGH) = Not dir used where a dir is expected
0x0003ff77 (FAILURE,HIGH) = Not symlink used where a symlink is expected
0x0003ff76 (FAILURE,HIGH) = Cannot seek to specified location
0x0003ff75 (HINT,MEDIUM) = File not supported in ECMA-119 tree and ignored
0x0003ff74 (HINT,MEDIUM) = File bigger than supported by used standard
0x0003ff73 (MISHAP,HIGH) = File read error during image creation
0x0003ff72 (HINT,MEDIUM) = Cannot convert filename to requested charset
0x0003ff71 (SORRY,HIGH) = File cannot be added to the tree
0x0003ff70 (HINT,MEDIUM) = File path breaks specification constraints
0x0003ff00 (FAILURE,HIGH) = Charset conversion error
0x0003feff (FAILURE,HIGH) = Too much files to mangle
0x0003fec0 (FAILURE,HIGH) = Wrong or damaged Primary Volume Descriptor
0x0003febf (SORRY,HIGH) = Wrong or damaged RR entry
0x0003febe (SORRY,HIGH) = Unsupported RR feature
0x0003febd (FAILURE,HIGH) = Wrong or damaged ECMA-119
0x0003febc (FAILURE,HIGH) = Unsupported ECMA-119 feature
0x0003febb (SORRY,HIGH) = Wrong or damaged El-Torito catalog
0x0003feba (SORRY,HIGH) = Unsupported El-Torito feature
0x0003feb9 (SORRY,HIGH) = Cannot patch isolinux boot image
0x0003feb8 (SORRY,HIGH) = Unsupported SUSP feature
0x0003feb7 (WARNING,HIGH) = Error on a RR entry that can be ignored
0x0003feb6 (HINT,MEDIUM) = Error on a RR entry that can be ignored
0x0003feb5 (WARNING,HIGH) = Multiple ER SUSP entries found
0x0003feb4 (HINT,MEDIUM) = Unsupported volume descriptor found
0x0003feb3 (WARNING,HIGH) = El-Torito related warning
0x0003feb2 (MISHAP,HIGH) = Image write cancelled
0x0003feb1 (WARNING,HIGH) = El-Torito image is hidden
Outdated codes which may not be re-used for other purposes than
re-instating them, if ever:
X 0x00031001 (SORRY,HIGH) = Cannot read file (ignored)
X 0x00031002 (FATAL,HIGH) = Cannot read file (operation canceled)
X 0x00031000 (FATAL,HIGH) = Unsupported ISO-9660 image
X 0x00031001 (HINT,MEDIUM) = Unsupported Vol Desc that will be ignored
X 0x00031002 (FATAL,HIGH) = Damaged ISO-9660 image
X 0x00031003 (SORRY,HIGH) = Cannot read previous image file
X 0x00030101 (HINT,MEDIUM) = Unsupported SUSP entry that will be ignored
X 0x00030102 (SORRY,HIGH) = Wrong/damaged SUSP entry
X 0x00030103 (WARNING,MEDIUM)= Multiple SUSP ER entries where found
X 0x00030111 (SORRY,HIGH) = Unsupported RR feature
X 0x00030112 (SORRY,HIGH) = Error in a Rock Ridge entry
X 0x00030201 (HINT,MEDIUM) = Unsupported Boot Vol Desc that will be ignored
X 0x00030202 (SORRY,HIGH) = Wrong El-Torito catalog
X 0x00030203 (HINT,MEDIUM) = Unsupported El-Torito feature
X 0x00030204 (SORRY,HIGH) = Invalid file to be an El-Torito image
X 0x00030205 (WARNING,MEDIUM)= Cannot properly patch isolinux image
X 0x00030206 (WARNING,MEDIUM)= Copying El-Torito from a previous image without
X enought info about it
X 0x00030301 (NOTE,MEDIUM) = Unsupported file type for Joliet tree
------------------------------------------------------------------------------
Range "application" : 0x00040000 to 0x0004ffff
0x00040000 (ABORT,HIGH) : Application supplied message
0x00040001 (FATAL,HIGH) : Application supplied message
0x00040002 (SORRY,HIGH) : Application supplied message
0x00040003 (WARNING,HIGH) : Application supplied message
0x00040004 (HINT,HIGH) : Application supplied message
0x00040005 (NOTE,HIGH) : Application supplied message
0x00040006 (UPDATE,HIGH) : Application supplied message
0x00040007 (DEBUG,HIGH) : Application supplied message
0x00040008 (*,HIGH) : Application supplied message
------------------------------------------------------------------------------
Range "libisofs-xorriso" : 0x00050000 to 0x0005ffff
This is an alternative representation of libisofs.so.6 error codes in xorriso.
If values returned by iso_error_get_code() do not fit into 0x30000 to 0x3ffff
then they get truncated to 16 bit and mapped into this range.
(This should never need to happen, of course.)
------------------------------------------------------------------------------
Range "libisoburn" : 0x00060000 to 0x00006ffff
0x00060000 (*,*) : Message which shall be attributed to libisoburn
>>> the messages of libisoburn need to be registered individually
------------------------------------------------------------------------------
#endif /* LIDBAX_MSGS_________________ */
#ifdef LIBISO_MSGS_H_INTERNAL
/* Internal Functions */
/** Lock before doing side effect operations on m */
static int libiso_msgs_lock(struct libiso_msgs *m, int flag);
/** Unlock after effect operations on m are done */
static int libiso_msgs_unlock(struct libiso_msgs *m, int flag);
/** Create new empty message item.
@param link Previous item in queue
@param flag Bitfield for control purposes (unused yet, submit 0)
@return >0 success, <=0 failure
*/
static int libiso_msgs_item_new(struct libiso_msgs_item **item,
struct libiso_msgs_item *link, int flag);
/** Destroy a message item obtained by libiso_msgs_obtain(). The submitted
pointer gets set to NULL.
@param flag Bitfield for control purposes (unused yet, submit 0)
@return 1 for success, 0 for pointer to NULL
*/
static int libiso_msgs_item_destroy(struct libiso_msgs_item **item, int flag);
#endif /* LIBISO_MSGS_H_INTERNAL */
#endif /* ! LIBISO_MSGS_H_INCLUDED */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,428 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "libiso_msgs.h"
#include "libisofs.h"
#include "messages.h"
/*
* error codes are 32 bit numbers, that follow the following conventions:
*
* bit 31 (MSB) -> 1 (to make the value always negative)
* bits 30-24 -> Encoded severity (Use ISO_ERR_SEV to translate an error code
* to a LIBISO_MSGS_SEV_* constant)
* = 0x10 -> DEBUG
* = 0x20 -> UPDATE
* = 0x30 -> NOTE
* = 0x40 -> HINT
* = 0x50 -> WARNING
* = 0x60 -> SORRY
* = 0x64 -> MISHAP
* = 0x68 -> FAILURE
* = 0x70 -> FATAL
* = 0x71 -> ABORT
* bits 23-20 -> Encoded priority (Use ISO_ERR_PRIO to translate an error code
* to a LIBISO_MSGS_PRIO_* constant)
* = 0x0 -> ZERO
* = 0x1 -> LOW
* = 0x2 -> MEDIUM
* = 0x3 -> HIGH
* bits 19-16 -> Reserved for future usage (maybe message ranges)
* bits 15-0 -> Error code
*/
#define ISO_ERR_SEV(e) (e & 0x7F000000)
#define ISO_ERR_PRIO(e) ((e & 0x00F00000) << 8)
#define ISO_ERR_CODE(e) ((e & 0x0000FFFF) | 0x00030000)
int iso_message_id = LIBISO_MSGS_ORIGIN_IMAGE_BASE;
/**
* Threshold for aborting.
*/
int abort_threshold = LIBISO_MSGS_SEV_FAILURE;
#define MAX_MSG_LEN 4096
struct libiso_msgs *libiso_msgr = NULL;
int iso_init()
{
if (libiso_msgr == NULL) {
if (libiso_msgs_new(&libiso_msgr, 0) <= 0)
return ISO_FATAL_ERROR;
}
libiso_msgs_set_severities(libiso_msgr, LIBISO_MSGS_SEV_NEVER,
LIBISO_MSGS_SEV_FATAL, "libisofs: ", 0);
return 1;
}
void iso_finish()
{
libiso_msgs_destroy(&libiso_msgr, 0);
}
int iso_set_abort_severity(char *severity)
{
int ret, sevno;
ret = libiso_msgs__text_to_sev(severity, &sevno, 0);
if (ret <= 0)
return ISO_WRONG_ARG_VALUE;
if (sevno > LIBISO_MSGS_SEV_FAILURE || sevno < LIBISO_MSGS_SEV_NOTE)
return ISO_WRONG_ARG_VALUE;
ret = abort_threshold;
abort_threshold = sevno;
return ret;
}
void iso_msg_debug(int imgid, const char *fmt, ...)
{
char msg[MAX_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, MAX_MSG_LEN, fmt, ap);
va_end(ap);
libiso_msgs_submit(libiso_msgr, imgid, 0x00000002, LIBISO_MSGS_SEV_DEBUG,
LIBISO_MSGS_PRIO_ZERO, msg, 0, 0);
}
const char *iso_error_to_msg(int errcode)
{
switch(errcode) {
case ISO_CANCELED:
return "Operation canceled";
case ISO_FATAL_ERROR:
return "Unknown or unexpected fatal error";
case ISO_ERROR:
return "Unknown or unexpected error";
case ISO_ASSERT_FAILURE:
return "Internal programming error. Please report this bug";
case ISO_NULL_POINTER:
return "NULL pointer as value for an arg. that doesn't allow NULL";
case ISO_OUT_OF_MEM:
return "Memory allocation error";
case ISO_INTERRUPTED:
return "Interrupted by a signal";
case ISO_WRONG_ARG_VALUE:
return "Invalid parameter value";
case ISO_THREAD_ERROR:
return "Can't create a needed thread";
case ISO_WRITE_ERROR:
return "Write error";
case ISO_BUF_READ_ERROR:
return "Buffer read error";
case ISO_NODE_ALREADY_ADDED:
return "Trying to add to a dir a node already added to a dir";
case ISO_NODE_NAME_NOT_UNIQUE:
return "Node with same name already exists";
case ISO_NODE_NOT_ADDED_TO_DIR:
return "Trying to remove a node that was not added to dir";
case ISO_NODE_DOESNT_EXIST:
return "A requested node does not exist";
case ISO_IMAGE_ALREADY_BOOTABLE:
return "Try to set the boot image of an already bootable image";
case ISO_BOOT_IMAGE_NOT_VALID:
return "Trying to use an invalid file as boot image";
case ISO_FILE_ERROR:
return "Error on file operation";
case ISO_FILE_ALREADY_OPENED:
return "Trying to open an already opened file";
case ISO_FILE_ACCESS_DENIED:
return "Access to file is not allowed";
case ISO_FILE_BAD_PATH:
return "Incorrect path to file";
case ISO_FILE_DOESNT_EXIST:
return "The file does not exist in the filesystem";
case ISO_FILE_NOT_OPENED:
return "Trying to read or close a file not opened";
case ISO_FILE_IS_DIR:
return "Directory used where no dir is expected";
case ISO_FILE_READ_ERROR:
return "Read error";
case ISO_FILE_IS_NOT_DIR:
return "Not dir used where a dir is expected";
case ISO_FILE_IS_NOT_SYMLINK:
return "Not symlink used where a symlink is expected";
case ISO_FILE_SEEK_ERROR:
return "Can't seek to specified location";
case ISO_FILE_IGNORED:
return "File not supported in ECMA-119 tree and thus ignored";
case ISO_FILE_TOO_BIG:
return "A file is bigger than supported by used standard";
case ISO_FILE_CANT_WRITE:
return "File read error during image creation";
case ISO_FILENAME_WRONG_CHARSET:
return "Can't convert filename to requested charset";
case ISO_FILE_CANT_ADD:
return "File can't be added to the tree";
case ISO_FILE_IMGPATH_WRONG:
return "File path break specification constraints and will be ignored";
case ISO_CHARSET_CONV_ERROR:
return "Charset conversion error";
case ISO_MANGLE_TOO_MUCH_FILES:
return "Too much files to mangle, can't guarantee unique file names";
case ISO_WRONG_PVD:
return "Wrong or damaged Primary Volume Descriptor";
case ISO_WRONG_RR:
return "Wrong or damaged RR entry";
case ISO_UNSUPPORTED_RR:
return "Unsupported RR feature";
case ISO_WRONG_ECMA119:
return "Wrong or damaged ECMA-119";
case ISO_UNSUPPORTED_ECMA119:
return "Unsupported ECMA-119 feature";
case ISO_WRONG_EL_TORITO:
return "Wrong or damaged El-Torito catalog";
case ISO_UNSUPPORTED_EL_TORITO:
return "Unsupported El-Torito feature";
case ISO_ISOLINUX_CANT_PATCH:
return "Can't patch isolinux boot image";
case ISO_UNSUPPORTED_SUSP:
return "Unsupported SUSP feature";
case ISO_WRONG_RR_WARN:
return "Error on a RR entry that can be ignored";
case ISO_SUSP_UNHANDLED:
return "Error on a RR entry that can be ignored";
case ISO_SUSP_MULTIPLE_ER:
return "Multiple ER SUSP entries found";
case ISO_UNSUPPORTED_VD:
return "Unsupported volume descriptor found";
case ISO_EL_TORITO_WARN:
return "El-Torito related warning";
case ISO_IMAGE_WRITE_CANCELED:
return "Image write cancelled";
case ISO_EL_TORITO_HIDDEN:
return "El-Torito image is hidden";
default:
return "Unknown error";
}
}
int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...)
{
char msg[MAX_MSG_LEN];
va_list ap;
/* when called with ISO_CANCELED, we don't need to submit any message */
if (errcode == ISO_CANCELED && fmt == NULL) {
return ISO_CANCELED;
}
if (fmt) {
va_start(ap, fmt);
vsnprintf(msg, MAX_MSG_LEN, fmt, ap);
va_end(ap);
} else {
strncpy(msg, iso_error_to_msg(errcode), MAX_MSG_LEN);
}
libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(errcode),
ISO_ERR_SEV(errcode), ISO_ERR_PRIO(errcode), msg, 0, 0);
if (causedby != 0) {
snprintf(msg, MAX_MSG_LEN, " > Caused by: %s",
iso_error_to_msg(causedby));
libiso_msgs_submit(libiso_msgr, imgid, ISO_ERR_CODE(causedby),
LIBISO_MSGS_SEV_NOTE, LIBISO_MSGS_PRIO_LOW, msg, 0, 0);
if (ISO_ERR_SEV(causedby) == LIBISO_MSGS_SEV_FATAL) {
return ISO_CANCELED;
}
}
if (ISO_ERR_SEV(errcode) >= abort_threshold) {
return ISO_CANCELED;
} else {
return 0;
}
}
/**
* Control queueing and stderr printing of messages from libisofs.
* Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
* "NOTE", "UPDATE", "DEBUG", "ALL".
*
* @param queue_severity Gives the minimum limit for messages to be queued.
* Default: "NEVER". If you queue messages then you
* must consume them by iso_msgs_obtain().
* @param print_severity Does the same for messages to be printed directly
* to stderr.
* @param print_id A text prefix to be printed before the message.
* @return >0 for success, <=0 for error
*/
int iso_set_msgs_severities(char *queue_severity, char *print_severity,
char *print_id)
{
int ret, queue_sevno, print_sevno;
ret = libiso_msgs__text_to_sev(queue_severity, &queue_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs__text_to_sev(print_severity, &print_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs_set_severities(libiso_msgr, queue_sevno, print_sevno,
print_id, 0);
if (ret <= 0)
return 0;
return 1;
}
/**
* Obtain the oldest pending libisofs message from the queue which has at
* least the given minimum_severity. This message and any older message of
* lower severity will get discarded from the queue and is then lost forever.
*
* Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
* "NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
* will discard the whole queue.
*
* @param error_code Will become a unique error code as listed in messages.h
* @param imgid Id of the image that was issued the message.
* @param msg_text Must provide at least ISO_MSGS_MESSAGE_LEN bytes.
* @param severity Will become the severity related to the message and
* should provide at least 80 bytes.
* @return 1 if a matching item was found, 0 if not, <0 for severe errors
*/
int iso_obtain_msgs(char *minimum_severity, int *error_code, int *imgid,
char msg_text[], char severity[])
{
int ret, minimum_sevno, sevno, priority, os_errno;
double timestamp;
pid_t pid;
char *textpt, *sev_name;
struct libiso_msgs_item *item= NULL;
ret = libiso_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0);
if (ret <= 0)
return 0;
ret = libiso_msgs_obtain(libiso_msgr, &item, minimum_sevno,
LIBISO_MSGS_PRIO_ZERO, 0);
if (ret <= 0)
goto ex;
ret = libiso_msgs_item_get_msg(item, error_code, &textpt, &os_errno, 0);
if (ret <= 0)
goto ex;
strncpy(msg_text, textpt, ISO_MSGS_MESSAGE_LEN-1);
if (strlen(textpt) >= ISO_MSGS_MESSAGE_LEN)
msg_text[ISO_MSGS_MESSAGE_LEN-1] = 0;
ret = libiso_msgs_item_get_origin(item, &timestamp, &pid, imgid, 0);
if (ret <= 0)
goto ex;
severity[0]= 0;
ret = libiso_msgs_item_get_rank(item, &sevno, &priority, 0);
if (ret <= 0)
goto ex;
ret = libiso_msgs__sev_to_text(sevno, &sev_name, 0);
if (ret <= 0)
goto ex;
strcpy(severity, sev_name);
ret = 1;
ex: ;
libiso_msgs_destroy_item(libiso_msgr, &item, 0);
return ret;
}
/* ts A80222 : derived from libburn/init.c:burn_msgs_submit()
*/
int iso_msgs_submit(int error_code, char msg_text[], int os_errno,
char severity[], int origin)
{
int ret, sevno;
ret = libiso_msgs__text_to_sev(severity, &sevno, 0);
if (ret <= 0)
sevno = LIBISO_MSGS_SEV_ALL;
if (error_code <= 0) {
switch(sevno) {
case LIBISO_MSGS_SEV_ABORT: error_code = 0x00040000;
break; case LIBISO_MSGS_SEV_FATAL: error_code = 0x00040001;
break; case LIBISO_MSGS_SEV_SORRY: error_code = 0x00040002;
break; case LIBISO_MSGS_SEV_WARNING: error_code = 0x00040003;
break; case LIBISO_MSGS_SEV_HINT: error_code = 0x00040004;
break; case LIBISO_MSGS_SEV_NOTE: error_code = 0x00040005;
break; case LIBISO_MSGS_SEV_UPDATE: error_code = 0x00040006;
break; case LIBISO_MSGS_SEV_DEBUG: error_code = 0x00040007;
break; default: error_code = 0x00040008;
}
}
ret = libiso_msgs_submit(libiso_msgr, origin, error_code,
sevno, LIBISO_MSGS_PRIO_HIGH, msg_text, os_errno, 0);
return ret;
}
/* ts A80222 : derived from libburn/init.c:burn_text_to_sev()
*/
int iso_text_to_sev(char *severity_name, int *sevno)
{
int ret;
ret = libiso_msgs__text_to_sev(severity_name, sevno, 0);
if (ret <= 0)
*sevno = LIBISO_MSGS_SEV_FATAL;
return ret;
}
/* ts A80222 : derived from libburn/init.c:burn_sev_to_text()
*/
int iso_sev_to_text(int severity_number, char **severity_name)
{
int ret;
ret = libiso_msgs__sev_to_text(severity_number, severity_name, 0);
return ret;
}
/**
* Return the messenger object handle used by libisofs. This handle
* may be used by related libraries to their own compatible
* messenger objects and thus to direct their messages to the libisofs
* message queue. See also: libburn, API function burn_set_messenger().
*
* @return the handle. Do only use with compatible
*/
void *iso_get_messenger()
{
return libiso_msgr;
}
int iso_error_get_severity(int e)
{
return ISO_ERR_SEV(e);
}
int iso_error_get_priority(int e)
{
return ISO_ERR_PRIO(e);
}
int iso_error_get_code(int e)
{
return ISO_ERR_CODE(e);
}
/* ts A80222 */
int iso_report_errfile(char *path, int error_code, int os_errno, int flag)
{
libiso_msgs_submit(libiso_msgr, 0, error_code,
LIBISO_MSGS_SEV_ERRFILE, LIBISO_MSGS_PRIO_HIGH,
path, os_errno, 0);
return(1);
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* Message handling for libisofs
*/
#ifndef MESSAGES_H_
#define MESSAGES_H_
/**
* Take and increment this variable to get a valid identifier for message
* origin.
*/
extern int iso_message_id;
/**
* Submit a debug message.
*/
void iso_msg_debug(int imgid, const char *fmt, ...);
/**
*
* @param errcode
* The error code.
* @param causedby
* Error that was caused the errcode. If this error is a FATAL error,
* < 0 will be returned in any case. Use 0 if there is no previous
* cause for the error.
* @return
* 1 on success, < 0 if function must abort asap.
*/
int iso_msg_submit(int imgid, int errcode, int causedby, const char *fmt, ...);
/* ts A80222 */
/* To be called with events which report incidents with individual input
files from the local filesystem. Not with image nodes, files containing an
image or similar file-like objects.
*/
int iso_report_errfile(char *path, int error_code, int os_errno, int flag);
#endif /*MESSAGES_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,344 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_NODE_H_
#define LIBISO_NODE_H_
/*
* Definitions for the public iso tree
*/
#include "libisofs.h"
#include "stream.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
/* #define LIBISO_EXTENDED_INFORMATION */
#ifdef LIBISO_EXTENDED_INFORMATION
/**
* The extended information is a way to attach additional information to each
* IsoNode. External applications may want to use this extension system to
* store application speficic information related to each node. On the other
* side, libisofs may make use of this struct to attach information to nodes in
* some particular, uncommon, cases, without incrementing the size of the
* IsoNode struct.
*
* It is implemented like a chained list.
*/
typedef struct iso_extended_info IsoExtendedInfo;
struct iso_extended_info {
/**
* Next struct in the chain. NULL if it is the last item
*/
IsoExtendedInfo *next;
/**
* Function to handle this particular extended information. The function
* pointer acts as an identifier for the type of the information. Structs
* with same information type must use the same function.
*
* @param data
* Attached data
* @param flag
* What to do with the data. At this time the following values are
* defined:
* -> 1 the data must be freed
* @return
* 1
*/
int (*process)(void *data, int flag);
/**
* Pointer to information specific data.
*/
void *data;
};
#endif
/**
*
*/
struct Iso_Node
{
/*
* Initilized to 1, originally owned by user, until added to another node.
* Then it is owned by the parent node, so the user must take his own ref
* if needed. With the exception of the creation functions, none of the
* other libisofs functions that return an IsoNode increment its
* refcount. This is responsablity of the client, if (s)he needs it.
*/
int refcount;
/** Type of the IsoNode, do not confuse with mode */
enum IsoNodeType type;
char *name; /**< Real name, in default charset */
mode_t mode; /**< protection */
uid_t uid; /**< user ID of owner */
gid_t gid; /**< group ID of owner */
/* TODO #00001 : consider adding new timestamps */
time_t atime; /**< time of last access */
time_t mtime; /**< time of last modification */
time_t ctime; /**< time of last status change */
int hidden; /**< whether the node will be hidden, see IsoHideNodeFlag */
IsoDir *parent; /**< parent node, NULL for root */
/*
* Pointer to the linked list of children in a dir.
*/
IsoNode *next;
#ifdef LIBISO_EXTENDED_INFORMATION
/**
* Extended information for the node.
*/
IsoExtendedInfo *xinfo;
#endif
};
struct Iso_Dir
{
IsoNode node;
size_t nchildren; /**< The number of children of this directory. */
IsoNode *children; /**< list of children. ptr to first child */
};
struct Iso_File
{
IsoNode node;
/**
* Location of a file extent in a ms disc, 0 for newly added file
*/
uint32_t msblock;
/**
* It sorts the order in which the file data is written to the CD image.
* Higher weighting files are written at the beginning of image
*/
int sort_weight;
IsoStream *stream;
};
struct Iso_Symlink
{
IsoNode node;
char *dest;
};
struct Iso_Special
{
IsoNode node;
dev_t dev;
};
struct iso_dir_iter_iface
{
int (*next)(IsoDirIter *iter, IsoNode **node);
int (*has_next)(IsoDirIter *iter);
void (*free)(IsoDirIter *iter);
int (*take)(IsoDirIter *iter);
int (*remove)(IsoDirIter *iter);
/**
* This is called just before remove a node from a directory. The iterator
* may want to update its internal state according to this.
*/
void (*notify_child_taken)(IsoDirIter *iter, IsoNode *node);
};
/**
* An iterator for directory children.
*/
struct Iso_Dir_Iter
{
struct iso_dir_iter_iface *class;
/* the directory this iterator iterates over */
IsoDir *dir;
void *data;
};
int iso_node_new_root(IsoDir **root);
/**
* Create a new IsoDir. Attributes, uid/gid, timestamps, etc are set to
* default (0) values. You must set them.
*
* @param name
* Name for the node. It is not strdup() so you shouldn't use this
* reference when this function returns successfully. NULL is not
* allowed.
* @param dir
*
* @return
* 1 on success, < 0 on error.
*/
int iso_node_new_dir(char *name, IsoDir **dir);
/**
* Create a new file node. Attributes, uid/gid, timestamps, etc are set to
* default (0) values. You must set them.
*
* @param name
* Name for the node. It is not strdup() so you shouldn't use this
* reference when this function returns successfully. NULL is not
* allowed.
* @param stream
* Source for file contents. The reference is taken by the node,
* you must call iso_stream_ref() if you need your own ref.
* @return
* 1 on success, < 0 on error.
*/
int iso_node_new_file(char *name, IsoStream *stream, IsoFile **file);
/**
* Creates a new IsoSymlink node. Attributes, uid/gid, timestamps, etc are set
* to default (0) values. You must set them.
*
* @param name
* name for the new symlink. It is not strdup() so you shouldn't use this
* reference when this function returns successfully. NULL is not
* allowed.
* @param dest
* destination of the link. It is not strdup() so you shouldn't use this
* reference when this function returns successfully. NULL is not
* allowed.
* @param link
* place where to store a pointer to the newly created link.
* @return
* 1 on success, < 0 otherwise
*/
int iso_node_new_symlink(char *name, char *dest, IsoSymlink **link);
/**
* Create a new special file node. As far as libisofs concerns,
* an special file is a block device, a character device, a FIFO (named pipe)
* or a socket. You can choose the specific kind of file you want to add
* by setting mode propertly (see man 2 stat).
*
* Note that special files are only written to image when Rock Ridge
* extensions are enabled. Moreover, a special file is just a directory entry
* in the image tree, no data is written beyond that.
*
* Owner and hidden atts are taken from parent. You can modify any of them
* later.
*
* @param name
* name for the new special file. It is not strdup() so you shouldn't use
* this reference when this function returns successfully. NULL is not
* allowed.
* @param mode
* file type and permissions for the new node. Note that you can't
* specify any kind of file here, only special types are allowed. i.e,
* S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK,
* S_IFREG and S_IFDIR aren't.
* @param dev
* device ID, equivalent to the st_rdev field in man 2 stat.
* @param special
* place where to store a pointer to the newly created special file.
* @return
* 1 on success, < 0 otherwise
*/
int iso_node_new_special(char *name, mode_t mode, dev_t dev,
IsoSpecial **special);
/**
* Check if a given name is valid for an iso node.
*
* @return
* 1 if yes, 0 if not
*/
int iso_node_is_valid_name(const char *name);
/**
* Check if a given path is valid for the destination of a link.
*
* @return
* 1 if yes, 0 if not
*/
int iso_node_is_valid_link_dest(const char *dest);
/**
* Find the position where to insert a node
*
* @param dir
* A valid dir. It can't be NULL
* @param name
* The node name to search for. It can't be NULL
* @param pos
* Will be filled with the position where to insert. It can't be NULL
*/
void iso_dir_find(IsoDir *dir, const char *name, IsoNode ***pos);
/**
* Check if a node with the given name exists in a dir.
*
* @param dir
* A valid dir. It can't be NULL
* @param name
* The node name to search for. It can't be NULL
* @param pos
* If not NULL, will be filled with the position where to insert. If the
* node exists, (**pos) will refer to the given node.
* @return
* 1 if node exists, 0 if not
*/
int iso_dir_exists(IsoDir *dir, const char *name, IsoNode ***pos);
/**
* Inserts a given node in a dir, at the specified position.
*
* @param dir
* Dir where to insert. It can't be NULL
* @param node
* The node to insert. It can't be NULL
* @param pos
* Position where the node will be inserted. It is a pointer previously
* obtained with a call to iso_dir_exists() or iso_dir_find().
* It can't be NULL.
* @param replace
* Whether to replace an old node with the same name with the new node.
* @return
* If success, number of children in dir. < 0 on error
*/
int iso_dir_insert(IsoDir *dir, IsoNode *node, IsoNode **pos,
enum iso_replace_mode replace);
/**
* Add a new iterator to the registry. The iterator register keeps track of
* all iterators being used, and are notified when directory structure
* changes.
*/
int iso_dir_iter_register(IsoDirIter *iter);
/**
* Unregister a directory iterator.
*/
void iso_dir_iter_unregister(IsoDirIter *iter);
void iso_notify_dir_iters(IsoNode *node, int flag);
#endif /*LIBISO_NODE_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,267 @@
/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/**
* This header defines the functions and structures needed to add RockRidge
* extensions to an ISO image.
*
* References:
*
* - SUSP (IEEE 1281).
* System Use Sharing Protocol, draft standard version 1.12.
*
* - RRIP (IEEE 1282)
* Rock Ridge Interchange Protocol, Draft Standard version 1.12.
*
* - ECMA-119 (ISO-9660)
* Volume and File Structure of CDROM for Information Interchange.
*/
#ifndef LIBISO_ROCKRIDGE_H
#define LIBISO_ROCKRIDGE_H
#include "ecma119.h"
#define SUSP_SIG(entry, a, b) ((entry->sig[0] == a) && (entry->sig[1] == b))
/**
* This contains the information about the System Use Fields (SUSP, 4.1),
* that will be written in the System Use Areas, both in the ISO directory
* record System Use field (ECMA-119, 9.1.13) or in a Continuation Area as
* defined by SUSP.
*/
struct susp_info
{
/** Number of SUSP fields in the System Use field */
size_t n_susp_fields;
uint8_t **susp_fields;
/** Length of the part of the SUSP area that fits in the dirent. */
int suf_len;
/** Length of the part of the SUSP area that will go in a CE area. */
uint32_t ce_block;
uint32_t ce_len;
size_t n_ce_susp_fields;
uint8_t **ce_susp_fields;
};
/* SUSP 5.1 */
struct susp_CE {
uint8_t block[8];
uint8_t offset[8];
uint8_t len[8];
};
/* SUSP 5.3 */
struct susp_SP {
uint8_t be[1];
uint8_t ef[1];
uint8_t len_skp[1];
};
/* SUSP 5.5 */
struct susp_ER {
uint8_t len_id[1];
uint8_t len_des[1];
uint8_t len_src[1];
uint8_t ext_ver[1];
uint8_t ext_id[1]; /*< up to len_id bytes */
/* ext_des, ext_src */
};
/** POSIX file attributes (RRIP, 4.1.1) */
struct rr_PX {
uint8_t mode[8];
uint8_t links[8];
uint8_t uid[8];
uint8_t gid[8];
uint8_t serial[8];
};
/** Time stamps for a file (RRIP, 4.1.6) */
struct rr_TF {
uint8_t flags[1];
uint8_t t_stamps[1];
};
/** Info for character and block device (RRIP, 4.1.2) */
struct rr_PN {
uint8_t high[8];
uint8_t low[8];
};
/** Alternate name (RRIP, 4.1.4) */
struct rr_NM {
uint8_t flags[1];
uint8_t name[1];
};
/** Link for a relocated directory (RRIP, 4.1.5.1) */
struct rr_CL {
uint8_t child_loc[8];
};
/** Sim link (RRIP, 4.1.3) */
struct rr_SL {
uint8_t flags[1];
uint8_t comps[1];
};
/**
* Struct for a SUSP System User Entry (SUSP, 4.1)
*/
struct susp_sys_user_entry
{
uint8_t sig[2];
uint8_t len_sue[1];
uint8_t version[1];
union {
struct susp_CE CE;
struct susp_SP SP;
struct susp_ER ER;
struct rr_PX PX;
struct rr_TF TF;
struct rr_PN PN;
struct rr_NM NM;
struct rr_CL CL;
struct rr_SL SL;
} data; /* 5 to 4+len_sue */
};
/**
* Compute the length needed for write all RR and SUSP entries for a given
* node.
*
* @param type
* 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".."
* for that node (i.e., it will refer to the parent)
* @param space
* Available space in the System Use Area for the directory record.
* @param ce
* Will be filled with the space needed in a CE
* @return
* The size needed for the RR entries in the System Use Area
*/
size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space,
size_t *ce);
/**
* Fill a struct susp_info with the RR/SUSP entries needed for a given
* node.
*
* @param type
* 0 normal entry, 1 "." entry for that node (it is a dir), 2 ".."
* for that node (i.e., it will refer to the parent)
* @param space
* Available space in the System Use Area for the directory record.
* @param info
* Pointer to the struct susp_info where the entries will be stored.
* If some entries need to go to a Continuation Area, they will be added
* to the existing ce_susp_fields, and ce_len will be incremented
* propertly. Please ensure ce_block is initialized propertly.
* @return
* 1 success, < 0 error
*/
int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
size_t space, struct susp_info *info);
/**
* Write the given SUSP fields into buf. Note that Continuation Area
* fields are not written.
* If info does not contain any SUSP entry this function just return.
* After written, the info susp_fields array will be freed, and the counters
* updated propertly.
*/
void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
uint8_t *buf);
/**
* Write the Continuation Area entries for the given struct susp_info, using
* the iso_write() function.
* After written, the ce_susp_fields array will be freed.
*/
int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info);
/**
* The SUSP iterator is used to iterate over the System User Entries
* of a ECMA-168 directory record.
* It takes care about Continuation Areas, handles the end of the different
* system user entries and skip padding areas. Thus, using an iteration
* we are accessing just to the meaning entries.
*/
typedef struct susp_iterator SuspIterator;
SuspIterator *
susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record,
uint8_t len_skp, int msgid);
/**
* Get the next SUSP System User Entry using given iterator.
*
* @param sue
* Pointer to the next susp entry. It refers to an internal buffer and
* it's not guaranteed to be allocated after calling susp_iter_next()
* again. Thus, if you need to keep some entry you have to do a copy.
* @return
* 1 on success, 0 if no more entries, < 0 error
*/
int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue);
/**
* Free a given susp iterator.
*/
void susp_iter_free(SuspIterator *iter);
/**
* Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1).
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st);
/**
* Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6)
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st);
/**
* Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to
* the given name. You can pass a pointer to NULL as name.
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont);
/**
* Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues.
*
* @param cont
* 0 not continue, 1 continue, 2 continue component
* @return
* 1 on success, < 0 on error
*/
int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont);
/**
* Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2).
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st);
#endif /* LIBISO_ROCKRIDGE_H */

View File

@ -0,0 +1,419 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#include "libisofs.h"
#include "ecma119.h"
#include "util.h"
#include "rockridge.h"
#include "messages.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
struct susp_iterator
{
uint8_t* base;
int pos;
int size;
IsoDataSource *src;
int msgid;
/* block and offset for next continuation area */
uint32_t ce_block;
uint32_t ce_off;
/** Length of the next continuation area, 0 if no more CA are specified */
uint32_t ce_len;
uint8_t *buffer; /*< If there are continuation areas */
};
SuspIterator*
susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record,
uint8_t len_skp, int msgid)
{
int pad = (record->len_fi[0] + 1) % 2;
struct susp_iterator *iter = malloc(sizeof(struct susp_iterator));
if (iter == NULL) {
return NULL;
}
iter->base = record->file_id + record->len_fi[0] + pad;
iter->pos = len_skp; /* 0 in most cases */
iter->size = record->len_dr[0] - record->len_fi[0] - 33 - pad;
iter->src = src;
iter->msgid = msgid;
iter->ce_len = 0;
iter->buffer = NULL;
return iter;
}
int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue)
{
struct susp_sys_user_entry *entry;
entry = (struct susp_sys_user_entry*)(iter->base + iter->pos);
if ( (iter->pos + 4 > iter->size) || (SUSP_SIG(entry, 'S', 'T'))) {
/*
* End of the System Use Area or Continuation Area.
* Note that ST is not needed when the space left is less than 4.
* (IEEE 1281, SUSP. section 4)
*/
if (iter->ce_len) {
uint32_t block;
int nblocks;
/* A CE has found, there is another continuation area */
nblocks = DIV_UP(iter->ce_off + iter->ce_len, BLOCK_SIZE);
iter->buffer = realloc(iter->buffer, nblocks * BLOCK_SIZE);
/* read all blocks needed to cache the full CE */
for (block = 0; block < nblocks; ++block) {
int ret;
ret = iter->src->read_block(iter->src, iter->ce_block + block,
iter->buffer + block * BLOCK_SIZE);
if (ret < 0) {
return ret;
}
}
iter->base = iter->buffer + iter->ce_off;
iter->pos = 0;
iter->size = iter->ce_len;
iter->ce_len = 0;
entry = (struct susp_sys_user_entry*)iter->base;
} else {
return 0;
}
}
if (entry->len_sue[0] == 0) {
/* a wrong image with this lead us to a infinity loop */
iso_msg_submit(iter->msgid, ISO_WRONG_RR, 0,
"Damaged RR/SUSP information.");
return ISO_WRONG_RR;
}
iter->pos += entry->len_sue[0];
if (SUSP_SIG(entry, 'C', 'E')) {
/* Continuation entry */
if (iter->ce_len) {
int ret;
ret = iso_msg_submit(iter->msgid, ISO_UNSUPPORTED_SUSP, 0,
"More than one CE System user entry has found in a single "
"System Use field or continuation area. This breaks SUSP "
"standard and it's not supported. Ignoring last CE. Maybe "
"the image is damaged.");
if (ret < 0) {
return ret;
}
} else {
iter->ce_block = iso_read_bb(entry->data.CE.block, 4, NULL);
iter->ce_off = iso_read_bb(entry->data.CE.offset, 4, NULL);
iter->ce_len = iso_read_bb(entry->data.CE.len, 4, NULL);
}
/* we don't want to return CE entry to the user */
return susp_iter_next(iter, sue);
} else if (SUSP_SIG(entry, 'P', 'D')) {
/* skip padding */
return susp_iter_next(iter, sue);
}
*sue = entry;
return ISO_SUCCESS;
}
void susp_iter_free(SuspIterator *iter)
{
free(iter->buffer);
free(iter);
}
/**
* Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1).
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st)
{
if (px == NULL || st == NULL) {
return ISO_NULL_POINTER;
}
if (px->sig[0] != 'P' || px->sig[1] != 'X') {
return ISO_WRONG_ARG_VALUE;
}
if (px->len_sue[0] != 44 && px->len_sue[0] != 36) {
return ISO_WRONG_RR;
}
st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL);
st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL);
st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL);
st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL);
if (px->len_sue[0] == 44) {
/* this corresponds to RRIP 1.12, so we have inode serial number */
st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL);
}
return ISO_SUCCESS;
}
/**
* Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6)
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st)
{
time_t time;
int s;
int nts = 0;
if (tf == NULL || st == NULL) {
return ISO_NULL_POINTER;
}
if (tf->sig[0] != 'T' || tf->sig[1] != 'F') {
return ISO_WRONG_ARG_VALUE;
}
if (tf->data.TF.flags[0] & (1 << 7)) {
/* long form */
s = 17;
} else {
s = 7;
}
/* 1. Creation time */
if (tf->data.TF.flags[0] & (1 << 0)) {
/* the creation is the recording time. we ignore this */
/* TODO maybe it would be good to manage it in ms discs, where
* the recording time could be different than now!! */
++nts;
}
/* 2. modify time */
if (tf->data.TF.flags[0] & (1 << 1)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
/* RR TF entry too short. */
return ISO_WRONG_RR;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_mtime = time;
++nts;
}
/* 3. access time */
if (tf->data.TF.flags[0] & (1 << 2)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
/* RR TF entry too short. */
return ISO_WRONG_RR;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_atime = time;
++nts;
}
/* 4. attributes time */
if (tf->data.TF.flags[0] & (1 << 3)) {
if (tf->len_sue[0] < 5 + (nts+1) * s) {
/* RR TF entry too short. */
return ISO_WRONG_RR;
}
if (s == 7) {
time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]);
} else {
time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]);
}
st->st_ctime = time;
++nts;
}
/* we ignore backup, expire and effect times */
return ISO_SUCCESS;
}
/**
* Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to
* the given name. You can pass a pointer to NULL as name.
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont)
{
if (nm == NULL || name == NULL) {
return ISO_NULL_POINTER;
}
if (nm->sig[0] != 'N' || nm->sig[1] != 'M') {
return ISO_WRONG_ARG_VALUE;
}
if (nm->len_sue[0] == 5) {
if (nm->data.NM.flags[0] & 0x2) {
/* it is a "." entry */
if (*name == NULL) {
return ISO_SUCCESS;
} else {
/* we can't have a previous not-NULL name */
return ISO_WRONG_RR;
}
}
}
if (nm->len_sue[0] <= 5) {
/* ".." entry is an error, as we will never call it */
return ISO_WRONG_RR;
}
/* concatenate the results */
if (*cont) {
*name = realloc(*name, strlen(*name) + nm->len_sue[0] - 5 + 1);
strncat(*name, (char*)nm->data.NM.name, nm->len_sue[0] - 5);
} else {
*name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5);
}
if (*name == NULL) {
return ISO_OUT_OF_MEM;
}
/* and set cond according to the value of CONTINUE flag */
*cont = nm->data.NM.flags[0] & 0x01;
return ISO_SUCCESS;
}
/**
* Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues.
*
* @param cont
* 0 not continue, 1 continue, 2 continue component
* @return
* 1 on success, < 0 on error
*/
int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont)
{
int pos;
if (sl == NULL || dest == NULL) {
return ISO_NULL_POINTER;
}
if (sl->sig[0] != 'S' || sl->sig[1] != 'L') {
return ISO_WRONG_ARG_VALUE;
}
for (pos = 0; pos + 5 < sl->len_sue[0];
pos += 2 + sl->data.SL.comps[pos + 1]) {
char *comp;
uint8_t len;
uint8_t flags = sl->data.SL.comps[pos];
if (flags & 0x2) {
/* current directory */
len = 1;
comp = ".";
} else if (flags & 0x4) {
/* parent directory */
len = 2;
comp = "..";
} else if (flags & 0x8) {
/* root directory */
len = 1;
comp = "/";
} else if (flags & ~0x01) {
/* unsupported flag component */
return ISO_UNSUPPORTED_RR;
} else {
len = sl->data.SL.comps[pos + 1];
comp = (char*)&sl->data.SL.comps[pos + 2];
}
if (*cont == 1) {
/* new component */
size_t size = strlen(*dest);
*dest = realloc(*dest, strlen(*dest) + len + 2);
if (*dest == NULL) {
return ISO_OUT_OF_MEM;
}
/* it is a new compoenent, add the '/' */
if ((*dest)[size-1] != '/') {
(*dest)[size] = '/';
(*dest)[size+1] = '\0';
}
strncat(*dest, comp, len);
} else if (*cont == 2) {
/* the component continues */
*dest = realloc(*dest, strlen(*dest) + len + 1);
if (*dest == NULL) {
return ISO_OUT_OF_MEM;
}
/* we don't have to add the '/' */
strncat(*dest, comp, len);
} else {
*dest = strcopy(comp, len);
}
if (*dest == NULL) {
return ISO_OUT_OF_MEM;
}
/* do the component continue or not? */
*cont = (flags & 0x01) ? 2 : 1;
}
if (*cont == 2) {
/* TODO check that SL flag is set to continute too ?*/
} else {
*cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0;
}
return ISO_SUCCESS;
}
/**
* Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2).
*
* @return
* 1 on success, < 0 on error
*/
int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st)
{
if (pn == NULL || pn == NULL) {
return ISO_NULL_POINTER;
}
if (pn->sig[0] != 'P' || pn->sig[1] != 'N') {
return ISO_WRONG_ARG_VALUE;
}
if (pn->len_sue[0] != 20) {
return ISO_WRONG_RR;
}
st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32)
|| (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL);
return ISO_SUCCESS;
}

View File

@ -0,0 +1,622 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "stream.h"
#include "fsource.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
ino_t serial_id = (ino_t)1;
ino_t mem_serial_id = (ino_t)1;
ino_t cut_out_serial_id = (ino_t)1;
typedef struct
{
IsoFileSource *src;
/* key for file identification inside filesystem */
dev_t dev_id;
ino_t ino_id;
off_t size; /**< size of this file */
} FSrcStreamData;
static
int fsrc_open(IsoStream *stream)
{
int ret;
struct stat info;
off_t esize;
IsoFileSource *src;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
src = ((FSrcStreamData*)stream->data)->src;
ret = iso_file_source_stat(src, &info);
if (ret < 0) {
return ret;
}
ret = iso_file_source_open(src);
if (ret < 0) {
return ret;
}
esize = ((FSrcStreamData*)stream->data)->size;
if (info.st_size == esize) {
return ISO_SUCCESS;
} else {
return (esize > info.st_size) ? 3 : 2;
}
}
static
int fsrc_close(IsoStream *stream)
{
IsoFileSource *src;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
src = ((FSrcStreamData*)stream->data)->src;
return iso_file_source_close(src);
}
static
off_t fsrc_get_size(IsoStream *stream)
{
FSrcStreamData *data;
data = (FSrcStreamData*)stream->data;
return data->size;
}
static
int fsrc_read(IsoStream *stream, void *buf, size_t count)
{
IsoFileSource *src;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
src = ((FSrcStreamData*)stream->data)->src;
return iso_file_source_read(src, buf, count);
}
static
int fsrc_is_repeatable(IsoStream *stream)
{
int ret;
struct stat info;
FSrcStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = (FSrcStreamData*)stream->data;
/* mode is not cached, this function is only useful for filters */
ret = iso_file_source_stat(data->src, &info);
if (ret < 0) {
return ret;
}
if (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode)) {
return 1;
} else {
return 0;
}
}
static
void fsrc_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
ino_t *ino_id)
{
FSrcStreamData *data;
IsoFilesystem *fs;
data = (FSrcStreamData*)stream->data;
fs = iso_file_source_get_filesystem(data->src);
*fs_id = fs->get_id(fs);
*dev_id = data->dev_id;
*ino_id = data->ino_id;
}
static
void fsrc_free(IsoStream *stream)
{
FSrcStreamData *data;
data = (FSrcStreamData*)stream->data;
iso_file_source_unref(data->src);
free(data);
}
IsoStreamIface fsrc_stream_class = {
0,
"fsrc",
fsrc_open,
fsrc_close,
fsrc_get_size,
fsrc_read,
fsrc_is_repeatable,
fsrc_get_id,
fsrc_free
};
int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream)
{
int r;
struct stat info;
IsoStream *str;
FSrcStreamData *data;
if (src == NULL || stream == NULL) {
return ISO_NULL_POINTER;
}
r = iso_file_source_stat(src, &info);
if (r < 0) {
return r;
}
if (S_ISDIR(info.st_mode)) {
return ISO_FILE_IS_DIR;
}
/* check for read access to contents */
r = iso_file_source_access(src);
if (r < 0) {
return r;
}
str = malloc(sizeof(IsoStream));
if (str == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(FSrcStreamData));
if (data == NULL) {
free(str);
return ISO_OUT_OF_MEM;
}
/* take the ref to IsoFileSource */
data->src = src;
data->size = info.st_size;
/* get the id numbers */
{
IsoFilesystem *fs;
unsigned int fs_id;
fs = iso_file_source_get_filesystem(data->src);
fs_id = fs->get_id(fs);
if (fs_id == 0) {
/*
* the filesystem implementation is unable to provide valid
* st_dev and st_ino fields. Use serial_id.
*/
data->dev_id = (dev_t) 0;
data->ino_id = serial_id++;
} else {
data->dev_id = info.st_dev;
data->ino_id = info.st_ino;
}
}
str->refcount = 1;
str->data = data;
str->class = &fsrc_stream_class;
*stream = str;
return ISO_SUCCESS;
}
struct cut_out_stream
{
IsoFileSource *src;
/* key for file identification inside filesystem */
dev_t dev_id;
ino_t ino_id;
off_t offset; /**< offset where read begins */
off_t size; /**< size of this file */
off_t pos; /* position on the file for read */
};
static
int cut_out_open(IsoStream *stream)
{
int ret;
struct stat info;
IsoFileSource *src;
struct cut_out_stream *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = stream->data;
src = data->src;
ret = iso_file_source_stat(data->src, &info);
if (ret < 0) {
return ret;
}
ret = iso_file_source_open(src);
if (ret < 0) {
return ret;
}
{
off_t ret;
if (data->offset > info.st_size) {
/* file is smaller than expected */
ret = iso_file_source_lseek(src, info.st_size, 0);
} else {
ret = iso_file_source_lseek(src, data->offset, 0);
}
if (ret < 0) {
return (int) ret;
}
}
data->pos = 0;
if (data->offset + data->size > info.st_size) {
return 3; /* file smaller than expected */
} else {
return ISO_SUCCESS;
}
}
static
int cut_out_close(IsoStream *stream)
{
IsoFileSource *src;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
src = ((struct cut_out_stream*)stream->data)->src;
return iso_file_source_close(src);
}
static
off_t cut_out_get_size(IsoStream *stream)
{
struct cut_out_stream *data = stream->data;
return data->size;
}
static
int cut_out_read(IsoStream *stream, void *buf, size_t count)
{
struct cut_out_stream *data = stream->data;
count = (size_t)MIN(data->size - data->pos, count);
if (count == 0) {
return 0;
}
return iso_file_source_read(data->src, buf, count);
}
static
int cut_out_is_repeatable(IsoStream *stream)
{
/* reg files are always repeatable */
return 1;
}
static
void cut_out_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
ino_t *ino_id)
{
FSrcStreamData *data;
IsoFilesystem *fs;
data = (FSrcStreamData*)stream->data;
fs = iso_file_source_get_filesystem(data->src);
*fs_id = fs->get_id(fs);
*dev_id = data->dev_id;
*ino_id = data->ino_id;
}
static
void cut_out_free(IsoStream *stream)
{
struct cut_out_stream *data = stream->data;
iso_file_source_unref(data->src);
free(data);
}
IsoStreamIface cut_out_stream_class = {
0,
"cout",
cut_out_open,
cut_out_close,
cut_out_get_size,
cut_out_read,
cut_out_is_repeatable,
cut_out_get_id,
cut_out_free
};
int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size,
IsoStream **stream)
{
int r;
struct stat info;
IsoStream *str;
struct cut_out_stream *data;
if (src == NULL || stream == NULL) {
return ISO_NULL_POINTER;
}
if (size == 0) {
return ISO_WRONG_ARG_VALUE;
}
r = iso_file_source_stat(src, &info);
if (r < 0) {
return r;
}
if (!S_ISREG(info.st_mode)) {
return ISO_WRONG_ARG_VALUE;
}
if (offset > info.st_size) {
return ISO_FILE_OFFSET_TOO_BIG;
}
/* check for read access to contents */
r = iso_file_source_access(src);
if (r < 0) {
return r;
}
str = malloc(sizeof(IsoStream));
if (str == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(struct cut_out_stream));
if (data == NULL) {
free(str);
return ISO_OUT_OF_MEM;
}
/* take a new ref to IsoFileSource */
data->src = src;
iso_file_source_ref(src);
data->offset = offset;
data->size = MIN(info.st_size - offset, size);
/* get the id numbers */
data->dev_id = (dev_t) 0;
data->ino_id = cut_out_serial_id++;
str->refcount = 1;
str->data = data;
str->class = &cut_out_stream_class;
*stream = str;
return ISO_SUCCESS;
}
typedef struct
{
uint8_t *buf;
ssize_t offset; /* -1 if stream closed */
ino_t ino_id;
size_t size;
} MemStreamData;
static
int mem_open(IsoStream *stream)
{
MemStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = (MemStreamData*)stream->data;
if (data->offset != -1) {
return ISO_FILE_ALREADY_OPENED;
}
data->offset = 0;
return ISO_SUCCESS;
}
static
int mem_close(IsoStream *stream)
{
MemStreamData *data;
if (stream == NULL) {
return ISO_NULL_POINTER;
}
data = (MemStreamData*)stream->data;
if (data->offset == -1) {
return ISO_FILE_NOT_OPENED;
}
data->offset = -1;
return ISO_SUCCESS;
}
static
off_t mem_get_size(IsoStream *stream)
{
MemStreamData *data;
data = (MemStreamData*)stream->data;
return (off_t)data->size;
}
static
int mem_read(IsoStream *stream, void *buf, size_t count)
{
size_t len;
MemStreamData *data;
if (stream == NULL || buf == NULL) {
return ISO_NULL_POINTER;
}
if (count == 0) {
return ISO_WRONG_ARG_VALUE;
}
data = stream->data;
if (data->offset == -1) {
return ISO_FILE_NOT_OPENED;
}
if (data->offset >= data->size) {
return 0; /* EOF */
}
len = MIN(count, data->size - data->offset);
memcpy(buf, data->buf + data->offset, len);
data->offset += len;
return len;
}
static
int mem_is_repeatable(IsoStream *stream)
{
return 1;
}
static
void mem_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
ino_t *ino_id)
{
MemStreamData *data;
data = (MemStreamData*)stream->data;
*fs_id = ISO_MEM_FS_ID;
*dev_id = 0;
*ino_id = data->ino_id;
}
static
void mem_free(IsoStream *stream)
{
MemStreamData *data;
data = (MemStreamData*)stream->data;
free(data->buf);
free(data);
}
IsoStreamIface mem_stream_class = {
0,
"mem ",
mem_open,
mem_close,
mem_get_size,
mem_read,
mem_is_repeatable,
mem_get_id,
mem_free
};
/**
* Create a stream for reading from a arbitrary memory buffer.
* When the Stream refcount reach 0, the buffer is free(3).
*
* @return
* 1 sucess, < 0 error
*/
int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream)
{
IsoStream *str;
MemStreamData *data;
if (buf == NULL || stream == NULL) {
return ISO_NULL_POINTER;
}
str = malloc(sizeof(IsoStream));
if (str == NULL) {
return ISO_OUT_OF_MEM;
}
data = malloc(sizeof(MemStreamData));
if (str == NULL) {
free(str);
return ISO_OUT_OF_MEM;
}
/* fill data */
data->buf = buf;
data->size = size;
data->offset = -1;
data->ino_id = mem_serial_id++;
str->refcount = 1;
str->data = data;
str->class = &mem_stream_class;
*stream = str;
return ISO_SUCCESS;
}
void iso_stream_ref(IsoStream *stream)
{
++stream->refcount;
}
void iso_stream_unref(IsoStream *stream)
{
if (--stream->refcount == 0) {
stream->class->free(stream);
free(stream);
}
}
inline
int iso_stream_open(IsoStream *stream)
{
return stream->class->open(stream);
}
inline
int iso_stream_close(IsoStream *stream)
{
return stream->class->close(stream);
}
inline
off_t iso_stream_get_size(IsoStream *stream)
{
return stream->class->get_size(stream);
}
inline
int iso_stream_read(IsoStream *stream, void *buf, size_t count)
{
return stream->class->read(stream, buf, count);
}
inline
int iso_stream_is_repeatable(IsoStream *stream)
{
return stream->class->is_repeatable(stream);
}
inline
void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
ino_t *ino_id)
{
stream->class->get_id(stream, fs_id, dev_id, ino_id);
}
void iso_stream_get_file_name(IsoStream *stream, char *name)
{
char *type = stream->class->type;
if (!strncmp(type, "fsrc", 4)) {
FSrcStreamData *data = stream->data;
char *path = iso_file_source_get_path(data->src);
strncpy(name, path, PATH_MAX);
} else if (!strncmp(type, "boot", 4)) {
strcpy(name, "BOOT CATALOG");
} else if (!strncmp(type, "mem ", 4)) {
strcpy(name, "MEM SOURCE");
} else {
strcpy(name, "UNKNOWN SOURCE");
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_STREAM_H_
#define LIBISO_STREAM_H_
/*
* Definitions of streams.
*/
#include "fsource.h"
/**
* Get an identifier for the file of the source, for debug purposes
* @param name
* Should provide at least PATH_MAX bytes
*/
void iso_stream_get_file_name(IsoStream *stream, char *name);
/**
* Create a stream to read from a IsoFileSource.
* The stream will take the ref. to the IsoFileSource, so after a successfully
* exectution of this function, you musn't unref() the source, unless you
* take an extra ref.
*
* @return
* 1 sucess, < 0 error
* Possible errors:
*
*/
int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream);
/**
* Create a new stream to read a chunk of an IsoFileSource..
* The stream will add a ref. to the IsoFileSource.
*
* @return
* 1 sucess, < 0 error
*/
int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size,
IsoStream **stream);
/**
* Create a stream for reading from a arbitrary memory buffer.
* When the Stream refcount reach 0, the buffer is free(3).
*
* @return
* 1 sucess, < 0 error
*/
int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream);
#endif /*STREAM_H_*/

View File

@ -0,0 +1,959 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
/*
* Functions that act on the iso tree.
*/
#include "libisofs.h"
#include "node.h"
#include "image.h"
#include "fsource.h"
#include "builder.h"
#include "messages.h"
#include "tree.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <stdio.h>
#include <fnmatch.h>
/**
* Add a new directory to the iso tree.
*
* @param parent
* the dir where the new directory will be created
* @param name
* name for the new dir. If a node with same name already exists on
* parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
* @param dir
* place where to store a pointer to the newly created dir. No extra
* ref is addded, so you will need to call iso_node_ref() if you really
* need it. You can pass NULL in this parameter if you don't need the
* pointer.
* @return
* number of nodes in dir if succes, < 0 otherwise
* Possible errors:
* ISO_NULL_POINTER, if parent or name are NULL
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
*/
int iso_tree_add_new_dir(IsoDir *parent, const char *name, IsoDir **dir)
{
int ret;
char *n;
IsoDir *node;
IsoNode **pos;
time_t now;
if (parent == NULL || name == NULL) {
return ISO_NULL_POINTER;
}
if (dir) {
*dir = NULL;
}
/* find place where to insert and check if it exists */
if (iso_dir_exists(parent, name, &pos)) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
n = strdup(name);
ret = iso_node_new_dir(n, &node);
if (ret < 0) {
free(n);
return ret;
}
/* permissions from parent */
iso_node_set_permissions((IsoNode*)node, parent->node.mode);
iso_node_set_uid((IsoNode*)node, parent->node.uid);
iso_node_set_gid((IsoNode*)node, parent->node.gid);
iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
/* current time */
now = time(NULL);
iso_node_set_atime((IsoNode*)node, now);
iso_node_set_ctime((IsoNode*)node, now);
iso_node_set_mtime((IsoNode*)node, now);
if (dir) {
*dir = node;
}
/* add to dir */
return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
}
/**
* Add a new symlink to the directory tree. Permissions are set to 0777,
* owner and hidden atts are taken from parent. You can modify any of them
* later.
*
* @param parent
* the dir where the new symlink will be created
* @param name
* name for the new dir. If a node with same name already exists on
* parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
* @param dest
* destination of the link
* @param link
* place where to store a pointer to the newly created link. No extra
* ref is addded, so you will need to call iso_node_ref() if you really
* need it. You can pass NULL in this parameter if you don't need the
* pointer
* @return
* number of nodes in parent if success, < 0 otherwise
* Possible errors:
* ISO_NULL_POINTER, if parent, name or dest are NULL
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
* ISO_OUT_OF_MEM
*/
int iso_tree_add_new_symlink(IsoDir *parent, const char *name,
const char *dest, IsoSymlink **link)
{
int ret;
char *n, *d;
IsoSymlink *node;
IsoNode **pos;
time_t now;
if (parent == NULL || name == NULL || dest == NULL) {
return ISO_NULL_POINTER;
}
if (link) {
*link = NULL;
}
/* find place where to insert */
if (iso_dir_exists(parent, name, &pos)) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
n = strdup(name);
d = strdup(dest);
ret = iso_node_new_symlink(n, d, &node);
if (ret < 0) {
free(n);
free(d);
return ret;
}
/* permissions from parent */
iso_node_set_permissions((IsoNode*)node, 0777);
iso_node_set_uid((IsoNode*)node, parent->node.uid);
iso_node_set_gid((IsoNode*)node, parent->node.gid);
iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
/* current time */
now = time(NULL);
iso_node_set_atime((IsoNode*)node, now);
iso_node_set_ctime((IsoNode*)node, now);
iso_node_set_mtime((IsoNode*)node, now);
if (link) {
*link = node;
}
/* add to dir */
return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
}
/**
* Add a new special file to the directory tree. As far as libisofs concerns,
* an special file is a block device, a character device, a FIFO (named pipe)
* or a socket. You can choose the specific kind of file you want to add
* by setting mode propertly (see man 2 stat).
*
* Note that special files are only written to image when Rock Ridge
* extensions are enabled. Moreover, a special file is just a directory entry
* in the image tree, no data is written beyond that.
*
* Owner and hidden atts are taken from parent. You can modify any of them
* later.
*
* @param parent
* the dir where the new special file will be created
* @param name
* name for the new special file. If a node with same name already exists
* on parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
* @param mode
* file type and permissions for the new node. Note that you can't
* specify any kind of file here, only special types are allowed. i.e,
* S_IFSOCK, S_IFBLK, S_IFCHR and S_IFIFO are valid types; S_IFLNK,
* S_IFREG and S_IFDIR aren't.
* @param dev
* device ID, equivalent to the st_rdev field in man 2 stat.
* @param special
* place where to store a pointer to the newly created special file. No
* extra ref is addded, so you will need to call iso_node_ref() if you
* really need it. You can pass NULL in this parameter if you don't need
* the pointer.
* @return
* number of nodes in parent if success, < 0 otherwise
* Possible errors:
* ISO_NULL_POINTER, if parent, name or dest are NULL
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
* ISO_OUT_OF_MEM
*
*/
int iso_tree_add_new_special(IsoDir *parent, const char *name, mode_t mode,
dev_t dev, IsoSpecial **special)
{
int ret;
char *n;
IsoSpecial *node;
IsoNode **pos;
time_t now;
if (parent == NULL || name == NULL) {
return ISO_NULL_POINTER;
}
if (S_ISLNK(mode) || S_ISREG(mode) || S_ISDIR(mode)) {
return ISO_WRONG_ARG_VALUE;
}
if (special) {
*special = NULL;
}
/* find place where to insert */
if (iso_dir_exists(parent, name, &pos)) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
n = strdup(name);
ret = iso_node_new_special(n, mode, dev, &node);
if (ret < 0) {
free(n);
return ret;
}
/* atts from parent */
iso_node_set_uid((IsoNode*)node, parent->node.uid);
iso_node_set_gid((IsoNode*)node, parent->node.gid);
iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
/* current time */
now = time(NULL);
iso_node_set_atime((IsoNode*)node, now);
iso_node_set_ctime((IsoNode*)node, now);
iso_node_set_mtime((IsoNode*)node, now);
if (special) {
*special = node;
}
/* add to dir */
return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
}
/**
* Add a new regular file to the iso tree. Permissions are set to 0444,
* owner and hidden atts are taken from parent. You can modify any of them
* later.
*
* @param parent
* the dir where the new file will be created
* @param name
* name for the new file. If a node with same name already exists on
* parent, this functions fails with ISO_NODE_NAME_NOT_UNIQUE.
* @param stream
* IsoStream for the contents of the file
* @param file
* place where to store a pointer to the newly created file. No extra
* ref is addded, so you will need to call iso_node_ref() if you really
* need it. You can pass NULL in this parameter if you don't need the
* pointer
* @return
* number of nodes in parent if success, < 0 otherwise
* Possible errors:
* ISO_NULL_POINTER, if parent, name or dest are NULL
* ISO_NODE_NAME_NOT_UNIQUE, a node with same name already exists
* ISO_OUT_OF_MEM
*
* @since 0.6.4
*/
int iso_tree_add_new_file(IsoDir *parent, const char *name, IsoStream *stream,
IsoFile **file)
{
int ret;
char *n;
IsoFile *node;
IsoNode **pos;
time_t now;
if (parent == NULL || name == NULL || stream == NULL) {
return ISO_NULL_POINTER;
}
if (file) {
*file = NULL;
}
/* find place where to insert */
if (iso_dir_exists(parent, name, &pos)) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
n = strdup(name);
ret = iso_node_new_file(n, stream, &node);
if (ret < 0) {
free(n);
return ret;
}
/* permissions from parent */
iso_node_set_permissions((IsoNode*)node, 0444);
iso_node_set_uid((IsoNode*)node, parent->node.uid);
iso_node_set_gid((IsoNode*)node, parent->node.gid);
iso_node_set_hidden((IsoNode*)node, parent->node.hidden);
/* current time */
now = time(NULL);
iso_node_set_atime((IsoNode*)node, now);
iso_node_set_ctime((IsoNode*)node, now);
iso_node_set_mtime((IsoNode*)node, now);
if (file) {
*file = node;
}
/* add to dir */
return iso_dir_insert(parent, (IsoNode*)node, pos, ISO_REPLACE_NEVER);
}
/**
* Set whether to follow or not symbolic links when added a file from a source
* to IsoImage.
*/
void iso_tree_set_follow_symlinks(IsoImage *image, int follow)
{
image->follow_symlinks = follow ? 1 : 0;
}
/**
* Get current setting for follow_symlinks.
*
* @see iso_tree_set_follow_symlinks
*/
int iso_tree_get_follow_symlinks(IsoImage *image)
{
return image->follow_symlinks;
}
/**
* Set whether to skip or not hidden files when adding a directory recursibely.
* Default behavior is to not ignore them, i.e., to add hidden files to image.
*/
void iso_tree_set_ignore_hidden(IsoImage *image, int skip)
{
image->ignore_hidden = skip ? 1 : 0;
}
/**
* Get current setting for ignore_hidden.
*
* @see iso_tree_set_ignore_hidden
*/
int iso_tree_get_ignore_hidden(IsoImage *image)
{
return image->ignore_hidden;
}
void iso_tree_set_replace_mode(IsoImage *image, enum iso_replace_mode mode)
{
image->replace = mode;
}
enum iso_replace_mode iso_tree_get_replace_mode(IsoImage *image)
{
return image->replace;
}
/**
* Set whether to skip or not special files. Default behavior is to not skip
* them. Note that, despite of this setting, special files won't never be added
* to an image unless RR extensions were enabled.
*
* @param skip
* Bitmask to determine what kind of special files will be skipped:
* bit0: ignore FIFOs
* bit1: ignore Sockets
* bit2: ignore char devices
* bit3: ignore block devices
*/
void iso_tree_set_ignore_special(IsoImage *image, int skip)
{
image->ignore_special = skip & 0x0F;
}
/**
* Get current setting for ignore_special.
*
* @see iso_tree_set_ignore_special
*/
int iso_tree_get_ignore_special(IsoImage *image)
{
return image->ignore_special;
}
/**
* Set a callback function that libisofs will call for each file that is
* added to the given image by a recursive addition function. This includes
* image import.
*
* @param report
* pointer to a function that will be called just before a file will be
* added to the image. You can control whether the file will be in fact
* added or ignored.
* This function should return 1 to add the file, 0 to ignore it and
* continue, < 0 to abort the process
* NULL is allowed if you don't want any callback.
*/
void iso_tree_set_report_callback(IsoImage *image,
int (*report)(IsoImage*, IsoFileSource*))
{
image->report = report;
}
/**
* Add a excluded path. These are paths that won't never added to image,
* and will be excluded even when adding recursively its parent directory.
*
* For example, in
*
* iso_tree_add_exclude(image, "/home/user/data/private");
* iso_tree_add_dir_rec(image, root, "/home/user/data");
*
* the directory /home/user/data/private won't be added to image.
*
* @return
* 1 on success, < 0 on error
*/
int iso_tree_add_exclude(IsoImage *image, const char *path)
{
if (image == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
image->excludes = realloc(image->excludes, ++image->nexcludes *
sizeof(void*));
if (image->excludes == NULL) {
return ISO_OUT_OF_MEM;
}
image->excludes[image->nexcludes - 1] = strdup(path);
if (image->excludes[image->nexcludes - 1] == NULL) {
return ISO_OUT_OF_MEM;
}
return ISO_SUCCESS;
}
/**
* Remove a previously added exclude.
*
* @see iso_tree_add_exclude
* @return
* 1 on success, 0 exclude do not exists, < 0 on error
*/
int iso_tree_remove_exclude(IsoImage *image, const char *path)
{
size_t i, j;
if (image == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
for (i = 0; i < image->nexcludes; ++i) {
if (strcmp(image->excludes[i], path) == 0) {
/* exclude found */
free(image->excludes[i]);
--image->nexcludes;
for (j = i; j < image->nexcludes; ++j) {
image->excludes[j] = image->excludes[j+1];
}
image->excludes = realloc(image->excludes, image->nexcludes *
sizeof(void*));
return ISO_SUCCESS;
}
}
return 0;
}
static
int iso_tree_add_node_builder(IsoImage *image, IsoDir *parent,
IsoFileSource *src, IsoNodeBuilder *builder,
IsoNode **node)
{
int result;
IsoNode *new;
IsoNode **pos;
char *name;
if (parent == NULL || src == NULL || builder == NULL) {
return ISO_NULL_POINTER;
}
if (node) {
*node = NULL;
}
name = iso_file_source_get_name(src);
/* find place where to insert */
result = iso_dir_exists(parent, name, &pos);
free(name);
if (result) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
result = builder->create_node(builder, image, src, &new);
if (result < 0) {
return result;
}
if (node) {
*node = new;
}
/* finally, add node to parent */
return iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER);
}
int iso_tree_add_node(IsoImage *image, IsoDir *parent, const char *path,
IsoNode **node)
{
int result;
IsoFilesystem *fs;
IsoFileSource *file;
if (image == NULL || parent == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
fs = image->fs;
result = fs->get_by_path(fs, path, &file);
if (result < 0) {
return result;
}
result = iso_tree_add_node_builder(image, parent, file, image->builder,
node);
/* free the file */
iso_file_source_unref(file);
return result;
}
int iso_tree_add_new_node(IsoImage *image, IsoDir *parent, const char *name,
const char *path, IsoNode **node)
{
int result;
IsoFilesystem *fs;
IsoFileSource *file;
IsoNode *new;
IsoNode **pos;
if (image == NULL || parent == NULL || name == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
if (node) {
*node = NULL;
}
fs = image->fs;
result = fs->get_by_path(fs, path, &file);
if (result < 0) {
return result;
}
/* find place where to insert */
result = iso_dir_exists(parent, name, &pos);
if (result) {
/* a node with same name already exists */
iso_file_source_unref(file);
return ISO_NODE_NAME_NOT_UNIQUE;
}
result = image->builder->create_node(image->builder, image, file, &new);
/* free the file */
iso_file_source_unref(file);
if (result < 0) {
return result;
}
result = iso_node_set_name(new, name);
if (result < 0) {
iso_node_unref(new);
return result;
}
if (node) {
*node = new;
}
/* finally, add node to parent */
return iso_dir_insert(parent, new, pos, ISO_REPLACE_NEVER);
}
int iso_tree_add_new_cut_out_node(IsoImage *image, IsoDir *parent,
const char *name, const char *path,
off_t offset, off_t size,
IsoNode **node)
{
int result;
struct stat info;
IsoFilesystem *fs;
IsoFileSource *src;
IsoFile *new;
IsoNode **pos;
IsoStream *stream;
if (image == NULL || parent == NULL || name == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
if (node) {
*node = NULL;
}
/* find place where to insert */
result = iso_dir_exists(parent, name, &pos);
if (result) {
/* a node with same name already exists */
return ISO_NODE_NAME_NOT_UNIQUE;
}
fs = image->fs;
result = fs->get_by_path(fs, path, &src);
if (result < 0) {
return result;
}
result = iso_file_source_stat(src, &info);
if (result < 0) {
iso_file_source_unref(src);
return result;
}
if (!S_ISREG(info.st_mode)) {
return ISO_WRONG_ARG_VALUE;
}
if (offset >= info.st_size) {
return ISO_WRONG_ARG_VALUE;
}
/* force regular file */
result = image->builder->create_file(image->builder, image, src, &new);
/* free the file */
iso_file_source_unref(src);
if (result < 0) {
return result;
}
/* replace file iso stream with a cut-out-stream */
result = iso_cut_out_stream_new(src, offset, size, &stream);
if (result < 0) {
iso_node_unref((IsoNode*)new);
return result;
}
iso_stream_unref(new->stream);
new->stream = stream;
result = iso_node_set_name((IsoNode*)new, name);
if (result < 0) {
iso_node_unref((IsoNode*)new);
return result;
}
if (node) {
*node = (IsoNode*)new;
}
/* finally, add node to parent */
return iso_dir_insert(parent, (IsoNode*)new, pos, ISO_REPLACE_NEVER);
}
static
int check_excludes(IsoImage *image, const char *path)
{
int i;
for (i = 0; i < image->nexcludes; ++i) {
char *exclude = image->excludes[i];
if (exclude[0] == '/') {
/* absolute exclude, must completely match path */
if (!fnmatch(exclude, path, FNM_PERIOD|FNM_PATHNAME)) {
return 1;
}
} else {
/* relative exclude, it is enought if a part of the path matches */
char *pos = (char*)path;
while (pos != NULL) {
pos++;
if (!fnmatch(exclude, pos, FNM_PERIOD|FNM_PATHNAME)) {
return 1;
}
pos = strchr(pos, '/');
}
}
}
return 0;
}
static
int check_hidden(IsoImage *image, const char *name)
{
return (image->ignore_hidden && name[0] == '.');
}
static
int check_special(IsoImage *image, mode_t mode)
{
if (image->ignore_special != 0) {
switch(mode & S_IFMT) {
case S_IFBLK:
return image->ignore_special & 0x08 ? 1 : 0;
case S_IFCHR:
return image->ignore_special & 0x04 ? 1 : 0;
case S_IFSOCK:
return image->ignore_special & 0x02 ? 1 : 0;
case S_IFIFO:
return image->ignore_special & 0x01 ? 1 : 0;
default:
return 0;
}
}
return 0;
}
/**
* Recursively add a given directory to the image tree.
*
* @return
* 1 continue, < 0 error (ISO_CANCELED stop)
*/
int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir)
{
int ret;
IsoNodeBuilder *builder;
IsoFileSource *file;
IsoNode **pos;
struct stat info;
char *name, *path;
IsoNode *new;
enum iso_replace_mode replace;
ret = iso_file_source_open(dir);
if (ret < 0) {
char *path = iso_file_source_get_path(dir);
/* instead of the probable error, we throw a sorry event */
ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret,
"Can't open dir %s", path);
free(path);
return ret;
}
builder = image->builder;
/* iterate over all directory children */
while (1) {
int skip = 0;
ret = iso_file_source_readdir(dir, &file);
if (ret <= 0) {
if (ret < 0) {
/* error reading dir */
ret = iso_msg_submit(image->id, ret, ret, "Error reading dir");
}
break;
}
path = iso_file_source_get_path(file);
name = strrchr(path, '/') + 1;
if (image->follow_symlinks) {
ret = iso_file_source_stat(file, &info);
} else {
ret = iso_file_source_lstat(file, &info);
}
if (ret < 0) {
goto dir_rec_continue;
}
if (check_excludes(image, path)) {
iso_msg_debug(image->id, "Skipping excluded file %s", path);
skip = 1;
} else if (check_hidden(image, name)) {
iso_msg_debug(image->id, "Skipping hidden file %s", path);
skip = 1;
} else if (check_special(image, info.st_mode)) {
iso_msg_debug(image->id, "Skipping special file %s", path);
skip = 1;
}
if (skip) {
goto dir_rec_continue;
}
replace = image->replace;
/* find place where to insert */
ret = iso_dir_exists(parent, name, &pos);
/* TODO
* if (ret && replace == ISO_REPLACE_ASK) {
* replace = /....
* }
*/
/* chek if we must insert or not */
/* TODO check for other replace behavior */
if (ret && (replace == ISO_REPLACE_NEVER)) {
/* skip file */
goto dir_rec_continue;
}
/* if we are here we must insert. Give user a chance for cancel */
if (image->report) {
int r = image->report(image, file);
if (r <= 0) {
ret = (r < 0 ? ISO_CANCELED : ISO_SUCCESS);
goto dir_rec_continue;
}
}
ret = builder->create_node(builder, image, file, &new);
if (ret < 0) {
ret = iso_msg_submit(image->id, ISO_FILE_CANT_ADD, ret,
"Error when adding file %s", path);
goto dir_rec_continue;
}
/* ok, node has correctly created, we need to add it */
ret = iso_dir_insert(parent, new, pos, replace);
if (ret < 0) {
iso_node_unref(new);
if (ret != ISO_NODE_NAME_NOT_UNIQUE) {
/* error */
goto dir_rec_continue;
} else {
/* file ignored because a file with same node already exists */
iso_msg_debug(image->id, "Skipping file %s. A node with same "
"file already exists", path);
ret = 0;
}
} else {
iso_msg_debug(image->id, "Added file %s", path);
}
/* finally, if the node is a directory we need to recurse */
if (new->type == LIBISO_DIR && S_ISDIR(info.st_mode)) {
ret = iso_add_dir_src_rec(image, (IsoDir*)new, file);
}
dir_rec_continue:;
free(path);
iso_file_source_unref(file);
/* check for error severity to decide what to do */
if (ret < 0) {
ret = iso_msg_submit(image->id, ret, 0, NULL);
if (ret < 0) {
break;
}
}
} /* while */
iso_file_source_close(dir);
return ret < 0 ? ret : ISO_SUCCESS;
}
int iso_tree_add_dir_rec(IsoImage *image, IsoDir *parent, const char *dir)
{
int result;
struct stat info;
IsoFilesystem *fs;
IsoFileSource *file;
if (image == NULL || parent == NULL || dir == NULL) {
return ISO_NULL_POINTER;
}
fs = image->fs;
result = fs->get_by_path(fs, dir, &file);
if (result < 0) {
return result;
}
/* we also allow dir path to be a symlink to a dir */
result = iso_file_source_stat(file, &info);
if (result < 0) {
iso_file_source_unref(file);
return result;
}
if (!S_ISDIR(info.st_mode)) {
iso_file_source_unref(file);
return ISO_FILE_IS_NOT_DIR;
}
result = iso_add_dir_src_rec(image, parent, file);
iso_file_source_unref(file);
return result;
}
int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node)
{
int result;
IsoNode *n;
IsoDir *dir;
char *ptr, *brk_info, *component;
if (image == NULL || path == NULL) {
return ISO_NULL_POINTER;
}
/* get the first child at the root of the image that is "/" */
dir = image->root;
n = (IsoNode *)dir;
if (!strcmp(path, "/")) {
if (node) {
*node = n;
}
return ISO_SUCCESS;
}
ptr = strdup(path);
result = 0;
/* get the first component of the path */
component = strtok_r(ptr, "/", &brk_info);
while (component) {
if (n->type != LIBISO_DIR) {
n = NULL;
break;
}
dir = (IsoDir *)n;
result = iso_dir_get_node(dir, component, &n);
if (result != 1) {
n = NULL;
break;
}
component = strtok_r(NULL, "/", &brk_info);
}
free(ptr);
if (node) {
*node = n;
}
return result;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_IMAGE_TREE_H_
#define LIBISO_IMAGE_TREE_H_
#include "image.h"
/**
* Recursively add a given directory to the image tree.
*
* @return
* 1 continue, 0 stop, < 0 error
*/
int iso_add_dir_src_rec(IsoImage *image, IsoDir *parent, IsoFileSource *dir);
#endif /*LIBISO_IMAGE_TREE_H_*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,438 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_UTIL_H_
#define LIBISO_UTIL_H_
#include <stdint.h>
#include <time.h>
#ifndef MAX
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define DIV_UP(n,div) ((n + div - 1) / div)
#define ROUND_UP(n,mul) (DIV_UP(n, mul) * mul)
int int_pow(int base, int power);
/**
* Convert the charset encoding of a given string.
*
* @param input
* Input string
* @param icharset
* Input charset. Must be supported by iconv
* @param ocharset
* Output charset. Must be supported by iconv
* @param output
* Location where the pointer to the ouput string will be stored
* @return
* 1 on success, < 0 on error
*/
int strconv(const char *input, const char *icharset, const char *ocharset,
char **output);
int strnconv(const char *str, const char *icharset, const char *ocharset,
size_t len, char **output);
/**
* Convert a given string from any input charset to ASCII
*
* @param icharset
* Input charset. Must be supported by iconv
* @param input
* Input string
* @param output
* Location where the pointer to the ouput string will be stored
* @return
* 1 on success, < 0 on error
*/
int str2ascii(const char *icharset, const char *input, char **output);
/**
* Convert a given string from any input charset to UCS-2BE charset,
* used for Joliet file identifiers.
*
* @param icharset
* Input charset. Must be supported by iconv
* @param input
* Input string
* @param output
* Location where the pointer to the ouput string will be stored
* @return
* 1 on success, < 0 on error
*/
int str2ucs(const char *icharset, const char *input, uint16_t **output);
/**
* Create a level 1 directory identifier.
*
* @param src
* The identifier, in ASCII encoding.
*/
char *iso_1_dirid(const char *src);
/**
* Create a level 2 directory identifier.
*
* @param src
* The identifier, in ASCII encoding.
*/
char *iso_2_dirid(const char *src);
/**
* Create a dir name suitable for an ISO image with relaxed constraints.
*
* @param src
* The identifier, in ASCII encoding.
* @param size
* Max len for the name
* @param relaxed
* 0 only allow d-characters, 1 allow also lowe case chars,
* 2 allow all characters
*/
char *iso_r_dirid(const char *src, int size, int relaxed);
/**
* Create a level 1 file identifier that consists of a name, in 8.3
* format.
* Note that version number is not added to the file name
*
* @param src
* The identifier, in ASCII encoding.
*/
char *iso_1_fileid(const char *src);
/**
* Create a level 2 file identifier.
* Note that version number is not added to the file name
*
* @param src
* The identifier, in ASCII encoding.
*/
char *iso_2_fileid(const char *src);
/**
* Create a file name suitable for an ISO image with relaxed constraints.
*
* @param src
* The identifier, in ASCII encoding.
* @param len
* Max len for the name, without taken the "." into account.
* @param relaxed
* 0 only allow d-characters, 1 allow also lowe case chars,
* 2 allow all characters
* @param forcedot
* Whether to ensure that "." is added
*/
char *iso_r_fileid(const char *src, size_t len, int relaxed, int forcedot);
/**
* Create a Joliet file identifier that consists of name and extension. The
* combined name and extension length will not exceed 128 bytes, and the
* name and extension will be separated (.). All characters consist of
* 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL.
*
* Note that version number and (;1) is not appended.
*
* @return
* NULL if the original name and extension both are of length 0.
*/
uint16_t *iso_j_file_id(const uint16_t *src);
/**
* Create a Joliet directory identifier that consists of name and optionally
* extension. The combined name and extension length will not exceed 128 bytes,
* and the name and extension will be separated (.). All characters consist of
* 2 bytes and the resulting string is NULL-terminated by a 2-byte NULL.
*
* @return
* NULL if the original name and extension both are of length 0.
*/
uint16_t *iso_j_dir_id(const uint16_t *src);
/**
* Like strlen, but for Joliet strings.
*/
size_t ucslen(const uint16_t *str);
/**
* Like strrchr, but for Joliet strings.
*/
uint16_t *ucsrchr(const uint16_t *str, char c);
/**
* Like strdup, but for Joliet strings.
*/
uint16_t *ucsdup(const uint16_t *str);
/**
* Like strcmp, but for Joliet strings.
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2);
/**
* Like strcpy, but for Joliet strings.
*/
uint16_t *ucscpy(uint16_t *dest, const uint16_t *src);
/**
* Like strncpy, but for Joliet strings.
* @param n
* Maximum number of characters to copy (2 bytes per char).
*/
uint16_t *ucsncpy(uint16_t *dest, const uint16_t *src, size_t n);
/**
* Convert a given input string to d-chars.
* @return
* 1 on succes, < 0 error, 0 if input was null (output is set to null)
*/
int str2d_char(const char *icharset, const char *input, char **output);
int str2a_char(const char *icharset, const char *input, char **output);
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);
/**
* if error != NULL it will be set to 1 if LSB and MSB integers don't match.
*/
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error);
/**
* Records the date/time into a 7 byte buffer (ECMA-119, 9.1.5)
*
* @param buf
* Buffer where the date will be written
* @param t
* The time to be written
* @param always_gmt
* Always write the date in GMT and not in local time.
*/
void iso_datetime_7(uint8_t *buf, time_t t, int always_gmt);
/** Records the date/time into a 17 byte buffer (ECMA-119, 8.4.26.1) */
void iso_datetime_17(uint8_t *buf, time_t t, int always_gmt);
time_t iso_datetime_read_7(const uint8_t *buf);
time_t iso_datetime_read_17(const uint8_t *buf);
/**
* Check whether the caller process has read access to the given local file.
*
* @return
* 1 on success (i.e, the process has read access), < 0 on error
* (including ISO_FILE_ACCESS_DENIED on access denied to the specified file
* or any directory on the path).
*/
int iso_eaccess(const char *path);
/**
* Copy up to \p len chars from \p buf and return this newly allocated
* string. The new string is null-terminated.
*/
char *strcopy(const char *buf, size_t len);
/**
* Copy up to \p max characters from \p src to \p dest. If \p src has less than
* \p max characters, we pad dest with " " characters.
*/
void strncpy_pad(char *dest, const char *src, size_t max);
/**
* Convert a Joliet string with a length of \p len bytes to a new string
* in local charset.
*/
char *ucs2str(const char *buf, size_t len);
typedef struct iso_rbtree IsoRBTree;
typedef struct iso_htable IsoHTable;
typedef unsigned int (*hash_funtion_t)(const void *key);
typedef int (*compare_function_t)(const void *a, const void *b);
typedef void (*hfree_data_t)(void *key, void *data);
/**
* Create a new binary tree. libisofs binary trees allow you to add any data
* passing it as a pointer. You must provide a function suitable for compare
* two elements.
*
* @param compare
* A function to compare two keys. It takes a pointer to both keys
* and return 0, -1 or 1 if the first key is equal, less or greater
* than the second one.
* @param tree
* Location where the tree structure will be stored.
*/
int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree);
/**
* Destroy a given tree.
*
* Note that only the structure itself is deleted. To delete the elements, you
* should provide a valid free_data function. It will be called for each
* element of the tree, so you can use it to free any related data.
*/
void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *));
/**
* Inserts a given element in a Red-Black tree.
*
* @param tree
* the tree where to insert
* @param data
* element to be inserted on the tree. It can't be NULL
* @param item
* if not NULL, it will point to a location where the tree element ptr
* will be stored. If data was inserted, *item == data. If data was
* already on the tree, *item points to the previously inserted object
* that is equal to data.
* @return
* 1 success, 0 element already inserted, < 0 error
*/
int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item);
/**
* Get the number of elements in a given tree.
*/
size_t iso_rbtree_get_size(IsoRBTree *tree);
/**
* Get an array view of the elements of the tree.
*
* @param include_item
* Function to select which elements to include in the array. It that takes
* a pointer to an element and returns 1 if the element should be included,
* 0 if not. If you want to add all elements to the array, you can pass a
* NULL pointer.
* @param size
* If not null, will be filled with the number of elements in the array,
* without counting the final NULL item.
* @return
* A sorted array with the contents of the tree, or NULL if there is not
* enought memory to allocate the array. You should free(3) the array when
* no more needed. Note that the array is NULL-terminated, and thus it
* has size + 1 length.
*/
void **iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
size_t *size);
/**
* Create a new hash table.
*
* @param size
* Number of slots in table.
* @param hash
* Function used to generate
*/
int iso_htable_create(size_t size, hash_funtion_t hash,
compare_function_t compare, IsoHTable **table);
/**
* Put an element in a Hash Table. The element will be identified by
* the given key, that you should use to retrieve the element again.
*
* This function allow duplicates, i.e., two items with the same key. In those
* cases, the value returned by iso_htable_get() is undefined. If you don't
* want to allow duplicates, use iso_htable_put() instead;
*
* Both the key and data pointers will be stored internally, so you should
* free the objects they point to. Use iso_htable_remove() to delete an
* element from the table.
*/
int iso_htable_add(IsoHTable *table, void *key, void *data);
/**
* Like iso_htable_add(), but this doesn't allow dulpicates.
*
* @return
* 1 success, 0 if an item with the same key already exists, < 0 error
*/
int iso_htable_put(IsoHTable *table, void *key, void *data);
/**
* Retrieve an element from the given table.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param data
* Will be filled with the element found. Remains untouched if no
* element with the given key is found.
* @return
* 1 if found, 0 if not, < 0 on error
*/
int iso_htable_get(IsoHTable *table, void *key, void **data);
/**
* Remove an item with the given key from the table. In tables that allow
* duplicates, it is undefined the element that will be deleted.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param free_data
* Function that will be called passing as parameters both the key and
* the element that will be deleted. The user can use it to free the
* element. You can pass NULL if you don't want to delete the item itself.
* @return
* 1 success, 0 no element exists with the given key, < 0 error
*/
int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data);
/**
* Like remove, but instead of checking for key equality using the compare
* function, it just compare the key pointers. If the table allows duplicates,
* and you provide different keys (i.e. different pointers) to elements
* with same key (i.e. same content), this function ensure the exact element
* is removed.
*
* It has the problem that you must provide the same key pointer, and not just
* a key whose contents are equal. Moreover, if you use the same key (same
* pointer) to identify several objects, what of those are removed is
* undefined.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param free_data
* Function that will be called passing as parameters both the key and
* the element that will be deleted. The user can use it to free the
* element. You can pass NULL if you don't want to delete the item itself.
* @return
* 1 success, 0 no element exists with the given key, < 0 error
*/
int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data);
/**
* Destroy the given hash table.
*
* Note that you're responsible to actually destroy the elements by providing
* a valid free_data function. You can pass NULL if you only want to delete
* the hash structure.
*/
void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data);
/**
* Hash function suitable for keys that are char strings.
*/
unsigned int iso_str_hash(const void *key);
#endif /*LIBISO_UTIL_H_*/

View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "util.h"
#include "libisofs.h"
#include <stdlib.h>
#include <string.h>
/*
* Hash table implementation
*/
struct iso_hnode
{
void *key;
void *data;
/** next node for chaining */
struct iso_hnode *next;
};
struct iso_htable
{
struct iso_hnode **table;
size_t size; /**< number of items in table */
size_t cap; /**< number of slots in table */
hash_funtion_t hash;
compare_function_t compare;
};
static
struct iso_hnode *iso_hnode_new(void *key, void *data)
{
struct iso_hnode *node = malloc(sizeof(struct iso_hnode));
if (node == NULL)
return NULL;
node->data = data;
node->key = key;
node->next = NULL;
return node;
}
/**
* Put an element in a Hash Table. The element will be identified by
* the given key, that you should use to retrieve the element again.
*
* This function allow duplicates, i.e., two items with the same key. In those
* cases, the value returned by iso_htable_get() is undefined. If you don't
* want to allow duplicates, use iso_htable_put() instead;
*
* Both the key and data pointers will be stored internally, so you should
* free the objects they point to. Use iso_htable_remove() to delete an
* element from the table.
*/
int iso_htable_add(IsoHTable *table, void *key, void *data)
{
struct iso_hnode *node;
struct iso_hnode *new;
unsigned int hash;
if (table == NULL || key == NULL) {
return ISO_NULL_POINTER;
}
new = iso_hnode_new(key, data);
if (new == NULL) {
return ISO_OUT_OF_MEM;
}
hash = table->hash(key) % table->cap;
node = table->table[hash];
table->size++;
new->next = node;
table->table[hash] = new;
return ISO_SUCCESS;
}
/**
* Like iso_htable_add(), but this doesn't allow dulpicates.
*
* @return
* 1 success, 0 if an item with the same key already exists, < 0 error
*/
int iso_htable_put(IsoHTable *table, void *key, void *data)
{
struct iso_hnode *node;
struct iso_hnode *new;
unsigned int hash;
if (table == NULL || key == NULL) {
return ISO_NULL_POINTER;
}
hash = table->hash(key) % table->cap;
node = table->table[hash];
while (node) {
if (!table->compare(key, node->key)) {
return 0;
}
node = node->next;
}
new = iso_hnode_new(key, data);
if (new == NULL) {
return ISO_OUT_OF_MEM;
}
table->size++;
new->next = table->table[hash];
table->table[hash] = new;
return ISO_SUCCESS;
}
/**
* Retrieve an element from the given table.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param data
* Will be filled with the element found. Remains untouched if no
* element with the given key is found.
* @return
* 1 if found, 0 if not, < 0 on error
*/
int iso_htable_get(IsoHTable *table, void *key, void **data)
{
struct iso_hnode *node;
unsigned int hash;
if (table == NULL || key == NULL) {
return ISO_NULL_POINTER;
}
hash = table->hash(key) % table->cap;
node = table->table[hash];
while (node) {
if (!table->compare(key, node->key)) {
if (data) {
*data = node->data;
}
return 1;
}
node = node->next;
}
return 0;
}
/**
* Remove an item with the given key from the table. In tables that allow
* duplicates, it is undefined the element that will be deleted.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param free_data
* Function that will be called passing as parameters both the key and
* the element that will be deleted. The user can use it to free the
* element. You can pass NULL if you don't want to delete the item itself.
* @return
* 1 success, 0 no element exists with the given key, < 0 error
*/
int iso_htable_remove(IsoHTable *table, void *key, hfree_data_t free_data)
{
struct iso_hnode *node, *prev;
unsigned int hash;
if (table == NULL || key == NULL) {
return ISO_NULL_POINTER;
}
hash = table->hash(key) % table->cap;
node = table->table[hash];
prev = NULL;
while (node) {
if (!table->compare(key, node->key)) {
if (free_data)
free_data(node->key, node->data);
if (prev) {
prev->next = node->next;
} else {
table->table[hash] = node->next;
}
free(node);
table->size--;
return 1;
}
prev = node;
node = node->next;
}
return 0;
}
/**
* Like remove, but instead of checking for key equality using the compare
* function, it just compare the key pointers. If the table allows duplicates,
* and you provide different keys (i.e. different pointers) to elements
* with same key (i.e. same content), this function ensure the exact element
* is removed.
*
* It has the problem that you must provide the same key pointer, and not just
* a key whose contents are equal. Moreover, if you use the same key (same
* pointer) to identify several objects, what of those are removed is
* undefined.
*
* @param table
* Hash table
* @param key
* Key of the element that will be removed
* @param free_data
* Function that will be called passing as parameters both the key and
* the element that will be deleted. The user can use it to free the
* element. You can pass NULL if you don't want to delete the item itself.
* @return
* 1 success, 0 no element exists with the given key, < 0 error
*/
int iso_htable_remove_ptr(IsoHTable *table, void *key, hfree_data_t free_data)
{
struct iso_hnode *node, *prev;
unsigned int hash;
if (table == NULL || key == NULL) {
return ISO_NULL_POINTER;
}
hash = table->hash(key) % table->cap;
node = table->table[hash];
prev = NULL;
while (node) {
if (key == node->key) {
if (free_data)
free_data(node->key, node->data);
if (prev) {
prev->next = node->next;
} else {
table->table[hash] = node->next;
}
free(node);
table->size--;
return 1;
}
prev = node;
node = node->next;
}
return 0;
}
/**
* Hash function suitable for keys that are char strings.
*/
unsigned int iso_str_hash(const void *key)
{
int i, len;
const char *p = key;
unsigned int h = 2166136261u;
len = strlen(p);
for (i = 0; i < len; i++)
h = (h * 16777619 ) ^ p[i];
return h;
}
/**
* Destroy the given hash table.
*
* Note that you're responsible to actually destroy the elements by providing
* a valid free_data function. You can pass NULL if you only want to delete
* the hash structure.
*/
void iso_htable_destroy(IsoHTable *table, hfree_data_t free_data)
{
size_t i;
struct iso_hnode *node, *tmp;
if (table == NULL) {
return;
}
for (i = 0; i < table->cap; ++i) {
node = table->table[i];
while (node) {
tmp = node->next;
if (free_data)
free_data(node->key, node->data);
free(node);
node = tmp;
}
}
free(table->table);
free(table);
}
/**
* Create a new hash table.
*
* @param size
* Number of slots in table.
* @param hash
* Function used to generate
*/
int iso_htable_create(size_t size, hash_funtion_t hash,
compare_function_t compare, IsoHTable **table)
{
IsoHTable *t;
if (table == NULL) {
return ISO_OUT_OF_MEM;
}
t = malloc(sizeof(IsoHTable));
if (t == NULL) {
return ISO_OUT_OF_MEM;
}
t->table = calloc(size, sizeof(void*));
if (t->table == NULL) {
free(t);
return ISO_OUT_OF_MEM;
}
t->cap = size;
t->size = 0;
t->hash = hash;
t->compare = compare;
*table = t;
return ISO_SUCCESS;
}

View File

@ -0,0 +1,296 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "util.h"
#include "libisofs.h"
#include <stdlib.h>
/*
* This implementation of Red-Black tree is based on the public domain
* implementation of Julienne Walker.
*/
struct iso_rbnode
{
void *data;
struct iso_rbnode *ch[2];
unsigned int red :1;
};
struct iso_rbtree
{
struct iso_rbnode *root;
size_t size;
int (*compare)(const void *a, const void *b);
};
/**
* Create a new binary tree. libisofs binary trees allow you to add any data
* passing it as a pointer. You must provide a function suitable for compare
* two elements.
*
* @param compare
* A function to compare two elements. It takes a pointer to both elements
* and return 0, -1 or 1 if the first element is equal, less or greater
* than the second one.
* @param tree
* Location where the tree structure will be stored.
*/
int iso_rbtree_new(int (*compare)(const void*, const void*), IsoRBTree **tree)
{
if (compare == NULL || tree == NULL) {
return ISO_NULL_POINTER;
}
*tree = calloc(1, sizeof(IsoRBTree));
if (*tree == NULL) {
return ISO_OUT_OF_MEM;
}
(*tree)->compare = compare;
return ISO_SUCCESS;
}
static
void rbtree_destroy_aux(struct iso_rbnode *root, void (*free_data)(void *))
{
if (root == NULL) {
return;
}
if (free_data != NULL) {
free_data(root->data);
}
rbtree_destroy_aux(root->ch[0], free_data);
rbtree_destroy_aux(root->ch[1], free_data);
free(root);
}
/**
* Destroy a given tree.
*
* Note that only the structure itself is deleted. To delete the elements, you
* should provide a valid free_data function. It will be called for each
* element of the tree, so you can use it to free any related data.
*/
void iso_rbtree_destroy(IsoRBTree *tree, void (*free_data)(void *))
{
if (tree == NULL) {
return;
}
rbtree_destroy_aux(tree->root, free_data);
free(tree);
}
static inline
int is_red(struct iso_rbnode *root)
{
return root != NULL && root->red;
}
static
struct iso_rbnode *iso_rbtree_single(struct iso_rbnode *root, int dir)
{
struct iso_rbnode *save = root->ch[!dir];
root->ch[!dir] = save->ch[dir];
save->ch[dir] = root;
root->red = 1;
save->red = 0;
return save;
}
static
struct iso_rbnode *iso_rbtree_double(struct iso_rbnode *root, int dir)
{
root->ch[!dir] = iso_rbtree_single(root->ch[!dir], !dir);
return iso_rbtree_single(root, dir);
}
static
struct iso_rbnode *iso_rbnode_new(void *data)
{
struct iso_rbnode *rn = malloc(sizeof(struct iso_rbnode));
if (rn != NULL) {
rn->data = data;
rn->red = 1;
rn->ch[0] = NULL;
rn->ch[1] = NULL;
}
return rn;
}
/**
* Inserts a given element in a Red-Black tree.
*
* @param tree
* the tree where to insert
* @param data
* element to be inserted on the tree. It can't be NULL
* @param item
* if not NULL, it will point to a location where the tree element ptr
* will be stored. If data was inserted, *item == data. If data was
* already on the tree, *item points to the previously inserted object
* that is equal to data.
* @return
* 1 success, 0 element already inserted, < 0 error
*/
int iso_rbtree_insert(IsoRBTree *tree, void *data, void **item)
{
struct iso_rbnode *new;
int added = 0; /* has a new node been added? */
if (tree == NULL || data == NULL) {
return ISO_NULL_POINTER;
}
if (tree->root == NULL) {
/* Empty tree case */
tree->root = iso_rbnode_new(data);
if (tree->root == NULL) {
return ISO_OUT_OF_MEM;
}
new = data;
added = 1;
} else {
struct iso_rbnode head = { 0 }; /* False tree root */
struct iso_rbnode *g, *t; /* Grandparent & parent */
struct iso_rbnode *p, *q; /* Iterator & parent */
int dir = 0, last = 0;
int comp;
/* Set up helpers */
t = &head;
g = p = NULL;
q = t->ch[1] = tree->root;
/* Search down the tree */
while (1) {
if (q == NULL) {
/* Insert new node at the bottom */
p->ch[dir] = q = iso_rbnode_new(data);
if (q == NULL) {
return ISO_OUT_OF_MEM;
}
added = 1;
} else if (is_red(q->ch[0]) && is_red(q->ch[1])) {
/* Color flip */
q->red = 1;
q->ch[0]->red = 0;
q->ch[1]->red = 0;
}
/* Fix red violation */
if (is_red(q) && is_red(p)) {
int dir2 = (t->ch[1] == g);
if (q == p->ch[last]) {
t->ch[dir2] = iso_rbtree_single(g, !last);
} else {
t->ch[dir2] = iso_rbtree_double(g, !last);
}
}
comp = tree->compare(q->data, data);
/* Stop if found */
if (comp == 0) {
new = q->data;
break;
}
last = dir;
dir = (comp < 0);
/* Update helpers */
if (g != NULL)
t = g;
g = p, p = q;
q = q->ch[dir];
}
/* Update root */
tree->root = head.ch[1];
}
/* Make root black */
tree->root->red = 0;
if (item != NULL) {
*item = new;
}
if (added) {
/* a new element has been added */
tree->size++;
return 1;
} else {
return 0;
}
}
/**
* Get the number of elements in a given tree.
*/
size_t iso_rbtree_get_size(IsoRBTree *tree)
{
return tree->size;
}
static
size_t rbtree_to_array_aux(struct iso_rbnode *root, void **array, size_t pos,
int (*include_item)(void *))
{
if (root == NULL) {
return pos;
}
pos = rbtree_to_array_aux(root->ch[0], array, pos, include_item);
if (include_item == NULL || include_item(root->data)) {
array[pos++] = root->data;
}
pos = rbtree_to_array_aux(root->ch[1], array, pos, include_item);
return pos;
}
/**
* Get an array view of the elements of the tree.
*
* @param include_item
* Function to select which elements to include in the array. It that takes
* a pointer to an element and returns 1 if the element should be included,
* 0 if not. If you want to add all elements to the array, you can pass a
* NULL pointer.
* @return
* A sorted array with the contents of the tree, or NULL if there is not
* enought memory to allocate the array. You should free(3) the array when
* no more needed. Note that the array is NULL-terminated, and thus it
* has size + 1 length.
*/
void ** iso_rbtree_to_array(IsoRBTree *tree, int (*include_item)(void *),
size_t *size)
{
size_t pos;
void **array;
array = malloc((tree->size + 1) * sizeof(void*));
if (array == NULL) {
return NULL;
}
/* fill array */
pos = rbtree_to_array_aux(tree->root, array, 0, include_item);
array[pos] = NULL;
array = realloc(array, (pos + 1) * sizeof(void*));
if (size) {
*size = pos;
}
return array;
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#ifndef LIBISO_IMAGE_WRITER_H_
#define LIBISO_IMAGE_WRITER_H_
#include "ecma119.h"
struct Iso_Image_Writer
{
/**
*
*/
int (*compute_data_blocks)(IsoImageWriter *writer);
int (*write_vol_desc)(IsoImageWriter *writer);
int (*write_data)(IsoImageWriter *writer);
int (*free_data)(IsoImageWriter *writer);
void *data;
Ecma119Image *target;
};
/**
* This is the function all Writers shoudl call to write data to image.
* Currently, it is just a wrapper for write(2) Unix system call.
*
* It is implemented in ecma119.c
*
* @return
* 1 on sucess, < 0 error
*/
int iso_write(Ecma119Image *target, void *buf, size_t count);
int ecma119_writer_create(Ecma119Image *target);
#endif /*LIBISO_IMAGE_WRITER_H_*/