libisofs/libisofs/filesrc.c

388 lines
10 KiB
C

/*
* 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;
}