0314acfb2d
Mostly removal of unused parameters from calls. Change-Id: I0eb7b568265d1669492d958e78b9e69d7cf6fc05
405 lines
9.7 KiB
C
405 lines
9.7 KiB
C
|
|
/*
|
|
* This file contains all the function that handle the dir records
|
|
* (inodes) for the ISO9660 filesystem.
|
|
*/
|
|
|
|
#include "inc.h"
|
|
|
|
static struct inode inodes[NR_INODE_RECORDS];
|
|
static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset);
|
|
|
|
int fs_putnode(ino_t ino_nr, unsigned int count)
|
|
{
|
|
/*
|
|
* Find the inode specified by the request message and decrease its
|
|
* counter.
|
|
*/
|
|
struct inode *i_node;
|
|
|
|
if ((i_node = find_inode(ino_nr)) == NULL) {
|
|
printf("ISOFS: trying to free unused inode\n");
|
|
return EINVAL;
|
|
}
|
|
if (count > i_node->i_count) {
|
|
printf("ISOFS: put_node count too high\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
i_node->i_count -= count - 1;
|
|
put_inode(i_node);
|
|
return OK;
|
|
}
|
|
|
|
struct inode* alloc_inode(void)
|
|
{
|
|
/*
|
|
* Return a free inode from the pool.
|
|
*/
|
|
static int i;
|
|
int end = i;
|
|
struct inode *i_node;
|
|
|
|
i = (i + 1) % NR_INODE_RECORDS;
|
|
do {
|
|
i_node = &inodes[i];
|
|
|
|
if (i_node->i_count == 0) {
|
|
free_extent(i_node->extent);
|
|
|
|
memset(i_node, 0, sizeof(*i_node));
|
|
i_node->i_count = 1;
|
|
|
|
return i_node;
|
|
}
|
|
|
|
i = (i + 1) % NR_INODE_RECORDS;
|
|
}
|
|
while(i != end);
|
|
|
|
panic("No free inodes in cache");
|
|
}
|
|
|
|
struct inode* find_inode(ino_t i)
|
|
{
|
|
/* Get inode from cache. */
|
|
int cpt;
|
|
struct inode *i_node;
|
|
|
|
if (i == 0)
|
|
return NULL;
|
|
|
|
for (cpt = 0; cpt < NR_INODE_RECORDS; cpt++) {
|
|
i_node = &inodes[cpt];
|
|
|
|
if ((i_node->i_stat.st_ino == i) && (i_node->i_count > 0))
|
|
return i_node;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct inode* get_inode(ino_t i)
|
|
{
|
|
struct inode *i_node;
|
|
struct dir_extent *extent;
|
|
|
|
if (i == 0)
|
|
return NULL;
|
|
|
|
/* Try to get inode from cache. */
|
|
i_node = find_inode(i);
|
|
if (i_node != NULL) {
|
|
dup_inode(i_node);
|
|
return i_node;
|
|
}
|
|
|
|
/*
|
|
* Inode wasn't in cache, try to load it.
|
|
* FIXME: a fake extent of one logical block is created for
|
|
* read_inode(). Reading a inode this way could be problematic if
|
|
* additional extents are stored behind the block boundary.
|
|
*/
|
|
i_node = alloc_inode();
|
|
extent = alloc_extent();
|
|
extent->location = i / v_pri.logical_block_size_l;
|
|
extent->length = 1;
|
|
|
|
if (read_inode(i_node, extent, i % v_pri.logical_block_size_l,
|
|
NULL) != OK) {
|
|
free_extent(extent);
|
|
put_inode(i_node);
|
|
return NULL;
|
|
}
|
|
|
|
free_extent(extent);
|
|
return i_node;
|
|
}
|
|
|
|
void put_inode(struct inode *i_node)
|
|
{
|
|
if (i_node == NULL)
|
|
return;
|
|
|
|
assert(i_node->i_count > 0);
|
|
|
|
i_node->i_count--;
|
|
}
|
|
|
|
void dup_inode(struct inode *i_node)
|
|
{
|
|
assert(i_node != NULL);
|
|
|
|
i_node->i_count++;
|
|
}
|
|
|
|
static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset)
|
|
{
|
|
struct iso9660_dir_record *dir_rec;
|
|
struct buf *bp;
|
|
|
|
/*
|
|
* Directory entries aren't allowed to cross a logical block boundary in
|
|
* ISO 9660, so we keep searching until we find something or reach the
|
|
* end of the extent.
|
|
*/
|
|
bp = read_extent_block(extent, *offset / v_pri.logical_block_size_l);
|
|
while (bp != NULL) {
|
|
dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset %
|
|
v_pri.logical_block_size_l);
|
|
if (dir_rec->length == 0) {
|
|
*offset -= *offset % v_pri.logical_block_size_l;
|
|
*offset += v_pri.logical_block_size_l;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
lmfs_put_block(bp);
|
|
bp = read_extent_block(extent, *offset /
|
|
v_pri.logical_block_size_l);
|
|
}
|
|
|
|
return bp;
|
|
}
|
|
|
|
int read_inode(struct inode *i_node, struct dir_extent *extent, size_t offset,
|
|
size_t *new_offset)
|
|
{
|
|
struct iso9660_dir_record *dir_rec;
|
|
struct buf *bp;
|
|
|
|
/* Find inode. */
|
|
bp = fetch_inode(extent, &offset);
|
|
if (bp == NULL)
|
|
return EOF;
|
|
|
|
dir_rec = (struct iso9660_dir_record*)(b_data(bp) + offset %
|
|
v_pri.logical_block_size_l);
|
|
|
|
/* Parse basic ISO 9660 specs. */
|
|
if (check_dir_record(dir_rec,
|
|
offset % v_pri.logical_block_size_l) != OK) {
|
|
lmfs_put_block(bp);
|
|
return EINVAL;
|
|
}
|
|
|
|
memset(&i_node->i_stat, 0, sizeof(struct stat));
|
|
|
|
i_node->i_stat.st_ino = get_extent_absolute_block_id(extent,
|
|
offset / v_pri.logical_block_size_l) * v_pri.logical_block_size_l +
|
|
offset % v_pri.logical_block_size_l;
|
|
|
|
read_inode_iso9660(i_node, dir_rec);
|
|
|
|
/* Parse extensions. */
|
|
read_inode_susp(i_node, dir_rec, bp,
|
|
offset % v_pri.logical_block_size_l);
|
|
|
|
offset += dir_rec->length;
|
|
read_inode_extents(i_node, dir_rec, extent, &offset);
|
|
|
|
lmfs_put_block(bp);
|
|
if (new_offset != NULL)
|
|
*new_offset = offset;
|
|
return OK;
|
|
}
|
|
|
|
void read_inode_iso9660(struct inode *i,
|
|
const struct iso9660_dir_record *dir_rec)
|
|
{
|
|
char *cp;
|
|
|
|
/* Parse first extent. */
|
|
if (dir_rec->data_length_l > 0) {
|
|
assert(i->extent == NULL);
|
|
i->extent = alloc_extent();
|
|
i->extent->location = dir_rec->loc_extent_l +
|
|
dir_rec->ext_attr_rec_length;
|
|
i->extent->length = dir_rec->data_length_l /
|
|
v_pri.logical_block_size_l;
|
|
if (dir_rec->data_length_l % v_pri.logical_block_size_l)
|
|
i->extent->length++;
|
|
|
|
i->i_stat.st_size = dir_rec->data_length_l;
|
|
}
|
|
|
|
/* Parse timestamps (record date). */
|
|
i->i_stat.st_atime = i->i_stat.st_mtime = i->i_stat.st_ctime =
|
|
i->i_stat.st_birthtime = date7_to_time_t(dir_rec->rec_date);
|
|
|
|
if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) {
|
|
i->i_stat.st_mode = S_IFDIR;
|
|
i->i_stat.st_ino =
|
|
i->extent->location * v_pri.logical_block_size_l;
|
|
}
|
|
else
|
|
i->i_stat.st_mode = S_IFREG;
|
|
i->i_stat.st_mode |= 0555;
|
|
|
|
/* Parse file name. */
|
|
if (dir_rec->file_id[0] == 0)
|
|
strcpy(i->i_name, ".");
|
|
else if (dir_rec->file_id[0] == 1)
|
|
strcpy(i->i_name, "..");
|
|
else {
|
|
memcpy(i->i_name, dir_rec->file_id, dir_rec->length_file_id);
|
|
|
|
/* Truncate/ignore file version suffix. */
|
|
cp = strchr(i->i_name, ';');
|
|
if (cp != NULL)
|
|
*cp = '\0';
|
|
/* Truncate dot if file has no extension. */
|
|
if (strchr(i->i_name, '.') + 1 == cp)
|
|
*(cp-1) = '\0';
|
|
}
|
|
|
|
/* Initialize stat. */
|
|
i->i_stat.st_dev = fs_dev;
|
|
i->i_stat.st_blksize = v_pri.logical_block_size_l;
|
|
i->i_stat.st_blocks =
|
|
dir_rec->data_length_l / v_pri.logical_block_size_l;
|
|
i->i_stat.st_nlink = 1;
|
|
}
|
|
|
|
void read_inode_extents(struct inode *i,
|
|
const struct iso9660_dir_record *dir_rec,
|
|
struct dir_extent *extent, size_t *offset)
|
|
{
|
|
struct buf *bp;
|
|
struct iso9660_dir_record *extent_rec;
|
|
struct dir_extent *cur_extent = i->extent;
|
|
int done = FALSE;
|
|
|
|
/*
|
|
* No need to search extents if file is empty or has final directory
|
|
* record flag set.
|
|
*/
|
|
if (cur_extent == NULL ||
|
|
((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0))
|
|
return;
|
|
|
|
while (!done) {
|
|
bp = fetch_inode(extent, offset);
|
|
if (bp == NULL)
|
|
return;
|
|
|
|
bp = read_extent_block(extent,
|
|
*offset / v_pri.logical_block_size_l);
|
|
extent_rec = (struct iso9660_dir_record*)(b_data(bp) +
|
|
*offset % v_pri.logical_block_size_l);
|
|
|
|
if (check_dir_record(dir_rec,
|
|
*offset % v_pri.logical_block_size_l) != OK) {
|
|
lmfs_put_block(bp);
|
|
return;
|
|
}
|
|
|
|
/* Extent entries should share the same name. */
|
|
if ((dir_rec->length_file_id == extent_rec->length_file_id) &&
|
|
(memcmp(dir_rec->file_id, extent_rec->file_id,
|
|
dir_rec->length_file_id) == 0)) {
|
|
/* Add the extent at the end of the linked list. */
|
|
assert(cur_extent->next == NULL);
|
|
cur_extent->next = alloc_extent();
|
|
cur_extent->next->location = dir_rec->loc_extent_l +
|
|
dir_rec->ext_attr_rec_length;
|
|
cur_extent->next->length = dir_rec->data_length_l /
|
|
v_pri.logical_block_size_l;
|
|
if (dir_rec->data_length_l % v_pri.logical_block_size_l)
|
|
cur_extent->next->length++;
|
|
|
|
i->i_stat.st_size += dir_rec->data_length_l;
|
|
i->i_stat.st_blocks += cur_extent->next->length;
|
|
|
|
cur_extent = cur_extent->next;
|
|
*offset += extent_rec->length;
|
|
}
|
|
else
|
|
done = TRUE;
|
|
|
|
/* Check if not last extent bit is not set. */
|
|
if ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0)
|
|
done = TRUE;
|
|
|
|
lmfs_put_block(bp);
|
|
}
|
|
}
|
|
|
|
void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec,
|
|
struct buf *bp, size_t offset)
|
|
{
|
|
int susp_offset, susp_size;
|
|
struct rrii_dir_record rrii_data;
|
|
|
|
susp_offset = 33 + dir_rec->length_file_id;
|
|
/* Get rid of padding byte. */
|
|
if(dir_rec->length_file_id % 2 == 0) {
|
|
susp_offset++;
|
|
}
|
|
|
|
if(dir_rec->length - susp_offset >= 4) {
|
|
susp_size = dir_rec->length - susp_offset;
|
|
|
|
/* Initialize record with known, sane data. */
|
|
memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
|
|
memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
|
|
memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
|
|
memcpy(rrii_data.birthtime, dir_rec->rec_date,
|
|
ISO9660_SIZE_DATE7);
|
|
|
|
rrii_data.d_mode = i->i_stat.st_mode;
|
|
rrii_data.uid = 0;
|
|
rrii_data.gid = 0;
|
|
rrii_data.rdev = NO_DEV;
|
|
rrii_data.file_id_rrip[0] = '\0';
|
|
rrii_data.slink_rrip[0] = '\0';
|
|
|
|
parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset,
|
|
susp_size);
|
|
|
|
/* Copy back data from rrii_dir_record structure. */
|
|
i->i_stat.st_atime = date7_to_time_t(rrii_data.atime);
|
|
i->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime);
|
|
i->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime);
|
|
i->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime);
|
|
|
|
i->i_stat.st_mode = rrii_data.d_mode;
|
|
i->i_stat.st_uid = rrii_data.uid;
|
|
i->i_stat.st_gid = rrii_data.gid;
|
|
i->i_stat.st_rdev = rrii_data.rdev;
|
|
|
|
if (rrii_data.file_id_rrip[0] != '\0')
|
|
strlcpy(i->i_name, rrii_data.file_id_rrip,
|
|
sizeof(i->i_name));
|
|
if (rrii_data.slink_rrip[0] != '\0')
|
|
strlcpy(i->s_link, rrii_data.slink_rrip,
|
|
sizeof(i->s_link));
|
|
}
|
|
}
|
|
|
|
int check_dir_record(const struct iso9660_dir_record *d, size_t offset)
|
|
{
|
|
/* Run some consistency check on a directory entry. */
|
|
if ((d->length < 33) || (d->length_file_id < 1))
|
|
return EINVAL;
|
|
if (d->length_file_id + 32 > d->length)
|
|
return EINVAL;
|
|
if (offset + d->length > v_pri.logical_block_size_l)
|
|
return EINVAL;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int check_inodes(void)
|
|
{
|
|
/* Check whether there are no more inodes in use. Called on unmount. */
|
|
int i;
|
|
|
|
for (i = 0; i < NR_INODE_RECORDS; i++)
|
|
if (inodes[i].i_count > 0)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|