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