You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
382 lines
10 KiB
382 lines
10 KiB
/* |
|
* 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> |
|
|
|
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 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); |
|
if (res < 0) { |
|
/* |
|
* UPS, very ugly error, the best we can do is just to write |
|
* 0's to image |
|
*/ |
|
char *name = iso_stream_get_name(file->stream); |
|
res = iso_msg_submit(t->image->id, ISO_FILE_CANT_WRITE, res, |
|
"File \"%s\" can't be opened. Filling with 0s.", name); |
|
free(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) { |
|
char *name = iso_stream_get_name(file->stream); |
|
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")); |
|
free(name); |
|
if (res < 0) { |
|
filesrc_close(file); |
|
return res; /* aborted due to error severity */ |
|
} |
|
} |
|
|
|
/* 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 */ |
|
char *name = iso_stream_get_name(file->stream); |
|
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); |
|
} |
|
free(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; |
|
}
|
|
|