minix/minix/fs/isofs/inode.c
David van Moolenbroek 0314acfb2d libminixfs: miscellaneous API cleanup
Mostly removal of unused parameters from calls.

Change-Id: I0eb7b568265d1669492d958e78b9e69d7cf6fc05
2015-08-14 18:39:00 +00:00

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