Esempio n. 1
0
/* Write to quotafile */
static ssize_t ext2_quota_write(struct super_block *sb, int type,
				const char *data, size_t len, loff_t off)
{
	struct inode *inode = sb_dqopt(sb)->files[type];
	sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb);
	int err = 0;
	int offset = off & (sb->s_blocksize - 1);
	int tocopy;
	size_t towrite = len;
	struct buffer_head tmp_bh;
	struct buffer_head *bh;

	mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
	while (towrite > 0) {
		tocopy = sb->s_blocksize - offset < towrite ?
				sb->s_blocksize - offset : towrite;

		tmp_bh.b_state = 0;
		err = ext2_get_block(inode, blk, &tmp_bh, 1);
		if (err < 0)
			goto out;
		if (offset || tocopy != EXT2_BLOCK_SIZE(sb))
			bh = sb_bread(sb, tmp_bh.b_blocknr);
		else
			bh = sb_getblk(sb, tmp_bh.b_blocknr);
		if (!bh) {
			err = -EIO;
			goto out;
		}
		lock_buffer(bh);
		memcpy(bh->b_data+offset, data, tocopy);
		flush_dcache_page(bh->b_page);
		set_buffer_uptodate(bh);
		mark_buffer_dirty(bh);
		unlock_buffer(bh);
		brelse(bh);
		offset = 0;
		towrite -= tocopy;
		data += tocopy;
		blk++;
	}
out:
	if (len == towrite) {
		mutex_unlock(&inode->i_mutex);
		return err;
	}
	if (inode->i_size < off+len-towrite)
		i_size_write(inode, off+len-towrite);
	inode->i_version++;
	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	mark_inode_dirty(inode);
	mutex_unlock(&inode->i_mutex);
	return len - towrite;
}
/*
 * This clever recursive function handles i_blocks[] as well as
 * indirect, double indirect, and triple indirect blocks.  It iterates
 * over the entries in the i_blocks array or indirect blocks, and for
 * each one, will recursively handle any indirect blocks and then
 * frees and deallocates the blocks.
 */
static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
			   char *block_buf, blk_t *p, int level,
			   blk_t start, blk_t count, int max)
{
	errcode_t	retval;
	blk_t		b;
	int		i;
	blk64_t		offset, incr;
	int		freed = 0;

#ifdef PUNCH_DEBUG
	printf("Entering ind_punch, level %d, start %u, count %u, "
	       "max %d\n", level, start, count, max);
#endif
	incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
	for (i=0, offset=0; i < max; i++, p++, offset += incr) {
		if (offset >= start + count)
			break;
		if (*p == 0 || (offset+incr) <= start)
			continue;
		b = *p;
		if (level > 0) {
			blk_t start2;
#ifdef PUNCH_DEBUG
			printf("Reading indirect block %u\n", b);
#endif
			retval = ext2fs_read_ind_block(fs, b, block_buf);
			if (retval)
				return retval;
			start2 = (start > offset) ? start - offset : 0;
			retval = ind_punch(fs, inode, block_buf + fs->blocksize,
					   (blk_t *) block_buf, level - 1,
					   start2, count - offset,
					   fs->blocksize >> 2);
			if (retval)
				return retval;
			retval = ext2fs_write_ind_block(fs, b, block_buf);
			if (retval)
				return retval;
			if (!check_zero_block(block_buf, fs->blocksize))
				continue;
		}
#ifdef PUNCH_DEBUG
		printf("Freeing block %u (offset %llu)\n", b, offset);
#endif
		ext2fs_block_alloc_stats(fs, b, -1);
		*p = 0;
		freed++;
	}
Esempio n. 3
0
/* Read data from quotafile - avoid pagecache and such because we cannot afford
 * acquiring the locks... As quota files are never truncated and quota code
 * itself serializes the operations (and noone else should touch the files)
 * we don't have to be afraid of races */
static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data,
			       size_t len, loff_t off)
{
	struct inode *inode = sb_dqopt(sb)->files[type];
	sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb);
	int err = 0;
	int offset = off & (sb->s_blocksize - 1);
	int tocopy;
	size_t toread;
	struct buffer_head tmp_bh;
	struct buffer_head *bh;
	loff_t i_size = i_size_read(inode);

	if (off > i_size)
		return 0;
	if (off+len > i_size)
		len = i_size-off;
	toread = len;
	while (toread > 0) {
		tocopy = sb->s_blocksize - offset < toread ?
				sb->s_blocksize - offset : toread;

		tmp_bh.b_state = 0;
		tmp_bh.b_size = sb->s_blocksize;
		err = ext2_get_block(inode, blk, &tmp_bh, 0);
		if (err < 0)
			return err;
		if (!buffer_mapped(&tmp_bh))	/* A hole? */
			memset(data, 0, tocopy);
		else {
			bh = sb_bread(sb, tmp_bh.b_blocknr);
			if (!bh)
				return -EIO;
			memcpy(data, bh->b_data+offset, tocopy);
			brelse(bh);
		}
		offset = 0;
		toread -= tocopy;
		data += tocopy;
		blk++;
	}
	return len;
}
Esempio n. 4
0
static int bext2_file_write (struct inode * inode, struct file * filp,
			    const char * buf, int count)
{
	const loff_t two_gb = 2147483647;
	loff_t pos;
	off_t pos2;
	long block;
	int offset;
	int written, c;
	struct buffer_head * bh, *bufferlist[NBUF];
	struct super_block * sb;
	int err;
	int i,buffercount,write_error;

	write_error = buffercount = 0;
	if (!inode) {
		printk("bext2_file_write: inode = NULL\n");
		return -EINVAL;
	}
	sb = inode->i_sb;
	if (sb->s_flags & MS_RDONLY)
		/*
		 * This fs has been automatically remounted ro because of errors
		 */
		return -ENOSPC;

	if (!S_ISREG(inode->i_mode)) {
		bext2_warning (sb, "bext2_file_write", "mode = %07o",
			      inode->i_mode);
		return -EINVAL;
	}
	if (filp->f_flags & O_APPEND)
		pos = inode->i_size;
	else
		pos = filp->f_pos;
	pos2 = (off_t) pos;
	/*
	 * If a file has been opened in synchronous mode, we have to ensure
	 * that meta-data will also be written synchronously.  Thus, we
	 * set the i_osync field.  This field is tested by the allocation
	 * routines.
	 */
	if (filp->f_flags & O_SYNC)
		inode->u.ext2_i.i_osync++;
	block = pos2 >> EXT2_BLOCK_SIZE_BITS(sb);
	offset = pos2 & (sb->s_blocksize - 1);
	c = sb->s_blocksize - offset;
	written = 0;
	while (count > 0) {
		if (pos > two_gb) {
			if (!written)
				written = -EFBIG;
			break;
		}
		bh = bext2_getblk (inode, block, 1, &err);
		if (!bh) {
			if (!written)
				written = err;
			break;
		}
		count -= c;
		if (count < 0)
			c += count;
		if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
			ll_rw_block (READ, 1, &bh);
			wait_on_buffer (bh);
			if (!buffer_uptodate(bh)) {
				brelse (bh);
				if (!written)
					written = -EIO;
				break;
			}
		}
		memcpy_fromfs (bh->b_data + offset, buf, c);
		update_vm_cache(inode, pos, bh->b_data + offset, c);
		pos2 += c;
		pos += c;
		written += c;
		buf += c;
		mark_buffer_uptodate(bh, 1);
		mark_buffer_dirty(bh, 0);
		if (filp->f_flags & O_SYNC)
			bufferlist[buffercount++] = bh;
		else
			brelse(bh);
		if (buffercount == NBUF){
			ll_rw_block(WRITE, buffercount, bufferlist);
			for(i=0; i<buffercount; i++){
				wait_on_buffer(bufferlist[i]);
				if (!buffer_uptodate(bufferlist[i]))
					write_error=1;
				brelse(bufferlist[i]);
			}
			buffercount=0;
		}
		if(write_error)
			break;
		block++;
		offset = 0;
		c = sb->s_blocksize;
	}
	if ( buffercount ){
		ll_rw_block(WRITE, buffercount, bufferlist);
		for(i=0; i<buffercount; i++){
			wait_on_buffer(bufferlist[i]);
			if (!buffer_uptodate(bufferlist[i]))
				write_error=1;
			brelse(bufferlist[i]);
		}
	}		
	if (pos > inode->i_size)
		inode->i_size = pos;
	if (filp->f_flags & O_SYNC)
		inode->u.ext2_i.i_osync--;
	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
	filp->f_pos = pos;
	inode->i_dirt = 1;
	return written;
}
Esempio n. 5
0
static int ext2_readdir (struct inode * inode, struct file * filp,
			 struct dirent * dirent, int count)
{
	unsigned long offset, blk;
	int i, num;
	struct buffer_head * bh, * tmp, * bha[16];
	struct ext2_dir_entry * de;
	struct super_block * sb;
	int err;
	
	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	sb = inode->i_sb;
	while (filp->f_pos < inode->i_size) {
		offset = filp->f_pos & (sb->s_blocksize - 1);
		blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
		bh = ext2_bread (inode, blk, 0, &err);
		if (!bh) {
			filp->f_pos += sb->s_blocksize - offset;
			continue;
		}

		/*
		 * Do the readahead
		 */
		if (!offset) {
			for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
			     i > 0; i--) {
				tmp = ext2_getblk (inode, ++blk, 0, &err);
				if (tmp && !tmp->b_uptodate && !tmp->b_lock)
					bha[num++] = tmp;
				else
					brelse (tmp);
			}
			if (num) {
				ll_rw_block (READA, num, bha);
				for (i = 0; i < num; i++)
					brelse (bha[i]);
			}
		}
		
		de = (struct ext2_dir_entry *) (offset + bh->b_data);
		while (offset < sb->s_blocksize && filp->f_pos < inode->i_size) {
			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
						   bh, offset)) {
				brelse (bh);
				return 0;
			}
			offset += de->rec_len;
			filp->f_pos += de->rec_len;
			if (de->inode) {
				memcpy_tofs (dirent->d_name, de->name,
					     de->name_len);
				put_fs_long (de->inode, &dirent->d_ino);
				put_fs_byte (0, de->name_len + dirent->d_name);
				put_fs_word (de->name_len, &dirent->d_reclen);
				dcache_add(inode, de->name, de->name_len,
						 de->inode);
				i = de->name_len;
				brelse (bh);
				if (!IS_RDONLY(inode)) {
					inode->i_atime = CURRENT_TIME;
					inode->i_dirt = 1;
				}
				return i;
			}
			de = (struct ext2_dir_entry *) ((char *) de +
							de->rec_len);
		}
		brelse (bh);
	}
	if (!IS_RDONLY(inode)) {
		inode->i_atime = CURRENT_TIME;
		inode->i_dirt = 1;
	}
	return 0;
}
Esempio n. 6
0
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
				  struct ext2_inode * inode, int bufsize)
{
	blk64_t block_nr;
	unsigned long group, block, offset;
	errcode_t retval = 0;
	struct ext2_inode_large *w_inode;
	char *ptr;
	unsigned i;
	int clen;
	int length = EXT2_INODE_SIZE(fs->super);

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user provided an override function */
	if (fs->write_inode) {
		retval = (fs->write_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}

	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;

	/* Prepare our shadow buffer for read/modify/byteswap/write */
	retval = ext2fs_get_mem(length, &w_inode);
	if (retval)
		return retval;

	if (bufsize < length) {
		int old_flags = fs->flags;
		fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
		retval = ext2fs_read_inode_full(fs, ino,
						(struct ext2_inode *)w_inode,
						length);
		fs->flags = (old_flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
			    (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
		if (retval)
			goto errout;
	}

	/* Check to see if the inode cache needs to be updated */
	if (fs->icache) {
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				memcpy(fs->icache->cache[i].inode, inode,
				       (bufsize > length) ? length : bufsize);
				break;
			}
		}
	} else {
		retval = ext2fs_create_inode_cache(fs, 4);
		if (retval)
			goto errout;
	}
	memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);

	if (!(fs->flags & EXT2_FLAG_RW)) {
		retval = EXT2_ET_RO_FILSYS;
		goto errout;
	}

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
#endif

	retval = ext2fs_inode_csum_set(fs, ino, w_inode);
	if (retval)
		goto errout;

	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
		EXT2_INODE_SIZE(fs->super);
	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
	if (!ext2fs_inode_table_loc(fs, (unsigned) group)) {
		retval = EXT2_ET_MISSING_INODE_TABLE;
		goto errout;
	}
	block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block;

	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	ptr = (char *) w_inode;

	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (fs->icache->buffer_blk != block_nr) {
			retval = io_channel_read_blk64(fs->io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				goto errout;
			fs->icache->buffer_blk = block_nr;
		}


		memcpy((char *) fs->icache->buffer + (unsigned) offset,
		       ptr, clen);

		retval = io_channel_write_blk64(fs->io, block_nr, 1,
					      fs->icache->buffer);
		if (retval)
			goto errout;

		offset = 0;
		ptr += clen;
		length -= clen;
		block_nr++;
	}

	fs->flags |= EXT2_FLAG_CHANGED;
errout:
	ext2fs_free_mem(&w_inode);
	return retval;
}
Esempio n. 7
0
/*
 * Functions to read and write a single inode.
 */
errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode * inode, int bufsize)
{
	blk64_t		block_nr;
	unsigned long 	group, block, offset;
	char 		*ptr;
	errcode_t	retval;
	unsigned	i;
	int		clen, inodes_per_block;
	io_channel	io;
	int		length = EXT2_INODE_SIZE(fs->super);
	struct ext2_inode_large	*iptr;
	int		cache_slot, fail_csum;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user has an override function */
	if (fs->read_inode &&
	    ((bufsize == sizeof(struct ext2_inode)) ||
	     (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) {
		retval = (fs->read_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}
	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;
	/* Create inode cache if not present */
	if (!fs->icache) {
		retval = ext2fs_create_inode_cache(fs, 4);
		if (retval)
			return retval;
	}
	/* Check to see if it's in the inode cache */
	for (i = 0; i < fs->icache->cache_size; i++) {
		if (fs->icache->cache[i].ino == ino) {
			memcpy(inode, fs->icache->cache[i].inode,
			       (bufsize > length) ? length : bufsize);
			return 0;
		}
	}
	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
		block_nr = fs->image_header->offset_inode / fs->blocksize;
		block_nr += (ino - 1) / inodes_per_block;
		offset = ((ino - 1) % inodes_per_block) *
			EXT2_INODE_SIZE(fs->super);
		io = fs->image_io;
	} else {
		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
		if (group > fs->group_desc_count)
			return EXT2_ET_BAD_INODE_NUM;
		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
			EXT2_INODE_SIZE(fs->super);
		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
		if (!ext2fs_inode_table_loc(fs, (unsigned) group))
			return EXT2_ET_MISSING_INODE_TABLE;
		block_nr = ext2fs_inode_table_loc(fs, group) +
			block;
		io = fs->io;
	}
	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
	iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;

	ptr = (char *) iptr;
	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (block_nr != fs->icache->buffer_blk) {
			retval = io_channel_read_blk64(io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				return retval;
			fs->icache->buffer_blk = block_nr;
		}

		memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
		       clen);

		offset = 0;
		length -= clen;
		ptr += clen;
		block_nr++;
	}
	length = EXT2_INODE_SIZE(fs->super);

	/* Verify the inode checksum. */
	fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr);

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
			       (struct ext2_inode_large *) iptr,
			       0, length);
#endif

	/* Update the inode cache bookkeeping */
	if (!fail_csum) {
		fs->icache->cache_last = cache_slot;
		fs->icache->cache[cache_slot].ino = ino;
	}
	memcpy(inode, iptr, (bufsize > length) ? length : bufsize);

	if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && fail_csum)
		return EXT2_ET_INODE_CSUM_INVALID;

	return 0;
}
Esempio n. 8
0
static int ext2_readdir(struct file * filp,
			 void * dirent, filldir_t filldir)
{
	int error = 0;
	unsigned long offset, blk;
	int i, num, stored;
	struct buffer_head * bh, * tmp, * bha[16];
	struct ext2_dir_entry_2 * de;
	struct super_block * sb;
	int err;
	struct inode *inode = filp->f_dentry->d_inode;

	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	sb = inode->i_sb;

	stored = 0;
	bh = NULL;
	offset = filp->f_pos & (sb->s_blocksize - 1);

	while (!error && !stored && filp->f_pos < inode->i_size) {
		blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
		bh = ext2_bread (inode, blk, 0, &err);
		if (!bh) {
			ext2_error (sb, "ext2_readdir",
				    "directory #%lu contains a hole at offset %lu",
				    inode->i_ino, (unsigned long)filp->f_pos);
			filp->f_pos += sb->s_blocksize - offset;
			continue;
		}

		/*
		 * Do the readahead
		 */
		if (!offset) {
			for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
			     i > 0; i--) {
				tmp = ext2_getblk (inode, ++blk, 0, &err);
				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
					bha[num++] = tmp;
				else
					brelse (tmp);
			}
			if (num) {
				ll_rw_block (READA, num, bha);
				for (i = 0; i < num; i++)
					brelse (bha[i]);
			}
		}
		
revalidate:
		/* If the dir block has changed since the last call to
		 * readdir(2), then we might be pointing to an invalid
		 * dirent right now.  Scan from the start of the block
		 * to make sure. */
		if (filp->f_version != inode->i_version) {
			for (i = 0; i < sb->s_blocksize && i < offset; ) {
				de = (struct ext2_dir_entry_2 *) 
					(bh->b_data + i);
				/* It's too expensive to do a full
				 * dirent test each time round this
				 * loop, but we do have to test at
				 * least that it is non-zero.  A
				 * failure will be detected in the
				 * dirent test below. */
				if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1))
					break;
				i += le16_to_cpu(de->rec_len);
			}
			offset = i;
			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
				| offset;
			filp->f_version = inode->i_version;
		}
		
		while (!error && filp->f_pos < inode->i_size 
		       && offset < sb->s_blocksize) {
			de = (struct ext2_dir_entry_2 *) (bh->b_data + offset);
			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
						   bh, offset)) {
				/* On error, skip the f_pos to the
                                   next block. */
				filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
					      + sb->s_blocksize;
				brelse (bh);
				return stored;
			}
			offset += le16_to_cpu(de->rec_len);
			if (le32_to_cpu(de->inode)) {
				/* We might block in the next section
				 * if the data destination is
				 * currently swapped out.  So, use a
				 * version stamp to detect whether or
				 * not the directory has been modified
				 * during the copy operation.
				 */
				unsigned long version = inode->i_version;

				error = filldir(dirent, de->name,
						de->name_len,
						filp->f_pos, le32_to_cpu(de->inode));
				if (error)
					break;
				if (version != inode->i_version)
					goto revalidate;
				stored ++;
			}
			filp->f_pos += le16_to_cpu(de->rec_len);
		}
		offset = 0;
		brelse (bh);
	}
	UPDATE_ATIME(inode);
	return 0;
}
Esempio n. 9
0
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
				  struct ext2_inode * inode, int bufsize)
{
	blk64_t block_nr;
	unsigned long group, block, offset;
	errcode_t retval = 0;
	struct ext2_inode_large temp_inode, *w_inode;
	char *ptr;
	int clen, i, length;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user provided an override function */
	if (fs->write_inode) {
		retval = (fs->write_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}

	/* Check to see if the inode cache needs to be updated */
	if (fs->icache) {
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				fs->icache->cache[i].inode = *inode;
				break;
			}
		}
	} else {
		retval = create_icache(fs);
		if (retval)
			return retval;
	}

	if (!(fs->flags & EXT2_FLAG_RW))
		return EXT2_ET_RO_FILSYS;

	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;

	length = bufsize;
	if (length < EXT2_INODE_SIZE(fs->super))
		length = EXT2_INODE_SIZE(fs->super);

	if (length > (int) sizeof(struct ext2_inode_large)) {
		w_inode = malloc(length);
		if (!w_inode) {
			retval = ENOMEM;
			goto errout;
		}
	} else
		w_inode = &temp_inode;
	memset(w_inode, 0, length);

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, w_inode,
			       (struct ext2_inode_large *) inode,
			       1, bufsize);
#else
	memcpy(w_inode, inode, bufsize);
#endif

	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
		EXT2_INODE_SIZE(fs->super);
	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
	if (!ext2fs_inode_table_loc(fs, (unsigned) group)) {
		retval = EXT2_ET_MISSING_INODE_TABLE;
		goto errout;
	}
	block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block;

	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	length = EXT2_INODE_SIZE(fs->super);
	if (length > bufsize)
		length = bufsize;

	ptr = (char *) w_inode;

	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (fs->icache->buffer_blk != block_nr) {
			retval = io_channel_read_blk64(fs->io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				goto errout;
			fs->icache->buffer_blk = block_nr;
		}


		memcpy((char *) fs->icache->buffer + (unsigned) offset,
		       ptr, clen);

		retval = io_channel_write_blk64(fs->io, block_nr, 1,
					      fs->icache->buffer);
		if (retval)
			goto errout;

		offset = 0;
		ptr += clen;
		length -= clen;
		block_nr++;
	}

	fs->flags |= EXT2_FLAG_CHANGED;
errout:
	if (w_inode && w_inode != &temp_inode)
		free(w_inode);
	return retval;
}
Esempio n. 10
0
/*
 * Functions to read and write a single inode.
 */
errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
				 struct ext2_inode * inode, int bufsize)
{
	blk64_t		block_nr;
	unsigned long 	group, block, offset;
	char 		*ptr;
	errcode_t	retval;
	int 		clen, i, inodes_per_block, length;
	io_channel	io;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	/* Check to see if user has an override function */
	if (fs->read_inode &&
	    ((bufsize == sizeof(struct ext2_inode)) ||
	     (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) {
		retval = (fs->read_inode)(fs, ino, inode);
		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
			return retval;
	}
	if ((ino == 0) || (ino > fs->super->s_inodes_count))
		return EXT2_ET_BAD_INODE_NUM;
	/* Create inode cache if not present */
	if (!fs->icache) {
		retval = create_icache(fs);
		if (retval)
			return retval;
	}
	/* Check to see if it's in the inode cache */
	if (bufsize == sizeof(struct ext2_inode)) {
		/* only old good inode can be retrieved from the cache */
		for (i=0; i < fs->icache->cache_size; i++) {
			if (fs->icache->cache[i].ino == ino) {
				*inode = fs->icache->cache[i].inode;
				return 0;
			}
		}
	}
	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
		block_nr = fs->image_header->offset_inode / fs->blocksize;
		block_nr += (ino - 1) / inodes_per_block;
		offset = ((ino - 1) % inodes_per_block) *
			EXT2_INODE_SIZE(fs->super);
		io = fs->image_io;
	} else {
		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
		if (group > fs->group_desc_count)
			return EXT2_ET_BAD_INODE_NUM;
		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
			EXT2_INODE_SIZE(fs->super);
		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
		if (!ext2fs_inode_table_loc(fs, (unsigned) group))
			return EXT2_ET_MISSING_INODE_TABLE;
		block_nr = ext2fs_inode_table_loc(fs, group) +
			block;
		io = fs->io;
	}
	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);

	length = EXT2_INODE_SIZE(fs->super);
	if (bufsize < length)
		length = bufsize;

	ptr = (char *) inode;
	while (length) {
		clen = length;
		if ((offset + length) > fs->blocksize)
			clen = fs->blocksize - offset;

		if (block_nr != fs->icache->buffer_blk) {
			retval = io_channel_read_blk64(io, block_nr, 1,
						     fs->icache->buffer);
			if (retval)
				return retval;
			fs->icache->buffer_blk = block_nr;
		}

		memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
		       clen);

		offset = 0;
		length -= clen;
		ptr += clen;
		block_nr++;
	}

#ifdef WORDS_BIGENDIAN
	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
			       (struct ext2_inode_large *) inode,
			       0, bufsize);
#endif

	/* Update the inode cache */
	fs->icache->cache_last = (fs->icache->cache_last + 1) %
		fs->icache->cache_size;
	fs->icache->cache[fs->icache->cache_last].ino = ino;
	fs->icache->cache[fs->icache->cache_last].inode = *inode;

	return 0;
}
Esempio n. 11
0
extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino,
				    struct ext2_inode *inode,
				    ext2_extent_handle_t *ret_handle)
{
	struct ext2_extent_handle	*handle;
	errcode_t			retval;
	int				i;
	struct ext3_extent_header	*eh;

	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

	if (!inode)
		if ((ino == 0) || (ino > fs->super->s_inodes_count))
			return EXT2_ET_BAD_INODE_NUM;

	retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle);
	if (retval)
		return retval;
	memset(handle, 0, sizeof(struct ext2_extent_handle));

	handle->ino = ino;
	handle->fs = fs;

	if (inode) {
		handle->inode = inode;
	} else {
		handle->inode = &handle->inodebuf;
		retval = ext2fs_read_inode(fs, ino, handle->inode);
		if (retval)
			goto errout;
	}

	eh = (struct ext3_extent_header *) &handle->inode->i_block[0];

	for (i=0; i < EXT2_N_BLOCKS; i++)
		if (handle->inode->i_block[i])
			break;
	if (i >= EXT2_N_BLOCKS) {
		eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
		eh->eh_depth = 0;
		eh->eh_entries = 0;
		i = (sizeof(handle->inode->i_block) - sizeof(*eh)) /
			sizeof(struct ext3_extent);
		eh->eh_max = ext2fs_cpu_to_le16(i);
		handle->inode->i_flags |= EXT4_EXTENTS_FL;
	}

	if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) {
		retval = EXT2_ET_INODE_NOT_EXTENT;
		goto errout;
	}

	retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block));
	if (retval)
		goto errout;

	handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth);
	handle->type = ext2fs_le16_to_cpu(eh->eh_magic);

	retval = ext2fs_get_mem(((handle->max_depth+1) *
				 sizeof(struct extent_path)),
				&handle->path);
	memset(handle->path, 0,
	       (handle->max_depth+1) * sizeof(struct extent_path));
	handle->path[0].buf = (char *) handle->inode->i_block;

	handle->path[0].left = handle->path[0].entries =
		ext2fs_le16_to_cpu(eh->eh_entries);
	handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max);
	handle->path[0].curr = 0;
	handle->path[0].end_blk =
		(EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >>
		 EXT2_BLOCK_SIZE_BITS(fs->super);
	handle->path[0].visit_num = 1;
	handle->level = 0;
	handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE;

	*ret_handle = handle;
	return 0;

errout:
	ext2fs_extent_free(handle);
	return retval;
}
Esempio n. 12
0
static int ext2_readdir (struct inode * inode, struct file * filp,
			 struct dirent * dirent, int count)
{
	unsigned long offset, blk;
	int i, num, stored, dlen;
	struct buffer_head * bh, * tmp, * bha[16];
	struct ext2_dir_entry * de;
	struct super_block * sb;
	int err, version;

	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	sb = inode->i_sb;

	stored = 0;
	bh = NULL;
	offset = filp->f_pos & (sb->s_blocksize - 1);

	while (count > 0 && !stored && filp->f_pos < inode->i_size) {
		blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
		bh = ext2_bread (inode, blk, 0, &err);
		if (!bh) {
			filp->f_pos += sb->s_blocksize - offset;
			continue;
		}

		/*
		 * Do the readahead
		 */
		if (!offset) {
			for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
			     i > 0; i--) {
				tmp = ext2_getblk (inode, ++blk, 0, &err);
				if (tmp && !tmp->b_uptodate && !tmp->b_lock)
					bha[num++] = tmp;
				else
					brelse (tmp);
			}
			if (num) {
				ll_rw_block (READA, num, bha);
				for (i = 0; i < num; i++)
					brelse (bha[i]);
			}
		}
		
revalidate:
		/* If the dir block has changed since the last call to
		 * readdir(2), then we might be pointing to an invalid
		 * dirent right now.  Scan from the start of the block
		 * to make sure. */
		if (filp->f_version != inode->i_version) {
			for (i = 0; i < sb->s_blocksize && i < offset; ) {
				de = (struct ext2_dir_entry *) 
					(bh->b_data + i);
				/* It's too expensive to do a full
				 * dirent test each time round this
				 * loop, but we do have to test at
				 * least that it is non-zero.  A
				 * failure will be detected in the
				 * dirent test below. */
				if (de->rec_len < EXT2_DIR_REC_LEN(1))
					break;
				i += de->rec_len;
			}
			offset = i;
			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
				| offset;
			filp->f_version = inode->i_version;
		}
		
		while (count > 0 && filp->f_pos < inode->i_size 
		       && offset < sb->s_blocksize) {
			de = (struct ext2_dir_entry *) (bh->b_data + offset);
			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
						   bh, offset)) {
				/* On error, skip the f_pos to the
                                   next block. */
				filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
					      + sb->s_blocksize;
				brelse (bh);
				return stored;
			}
			if (de->inode) {
				dlen = ROUND_UP(NAME_OFFSET(dirent) 
						+ de->name_len + 1);
				/* Old libc libraries always use a
                                   count of 1. */
				if (count == 1 && !stored)
					count = dlen;
				if (count < dlen) {
					count = 0;
					break;
				}

				/* We might block in the next section
				 * if the data destination is
				 * currently swapped out.  So, use a
				 * version stamp to detect whether or
				 * not the directory has been modified
				 * during the copy operation. */
				version = inode->i_version;
				i = de->name_len;
				memcpy_tofs (dirent->d_name, de->name, i);
				put_fs_long (de->inode, &dirent->d_ino);
				put_fs_byte (0, dirent->d_name + i);
				put_fs_word (i, &dirent->d_reclen);
				put_fs_long (dlen, &dirent->d_off);
				if (version != inode->i_version)
					goto revalidate;
				dcache_add(inode, de->name, de->name_len,
						 de->inode);

				stored += dlen;
				count -= dlen;
				((char *) dirent) += dlen;
			}
			offset += de->rec_len;
			filp->f_pos += de->rec_len;
		}
		offset = 0;
		brelse (bh);
	}
	if (!IS_RDONLY(inode)) {
		inode->i_atime = CURRENT_TIME;
		inode->i_dirt = 1;
	}
	return stored;
}
Esempio n. 13
0
struct super_block * ext2_read_super (struct super_block * sb, void * data,
				      int silent)
{
	struct buffer_head * bh;
	struct ext2_super_block * es;
	unsigned long sb_block = 1;
	unsigned long logic_sb_block = 1;
	int dev = sb->s_dev;
	int bh_count;
	int i, j;
#ifdef EXT2FS_PRE_02B_COMPAT
	int fs_converted = 0;
#endif

	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
	if (!parse_options ((char *) data, &sb_block,
	    &sb->u.ext2_sb.s_mount_opt)) {
		sb->s_dev = 0;
		return NULL;
	}

	lock_super (sb);
	set_blocksize (dev, BLOCK_SIZE);
	if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
		sb->s_dev = 0;
		unlock_super (sb);
		printk ("EXT2-fs: unable to read superblock\n");
		return NULL;
	}
	/*
	 * Note: s_es must be initialized s_es as soon as possible because
	 * some ext2 macro-instructions depend on its value
	 */
	es = (struct ext2_super_block *) bh->b_data;
	sb->u.ext2_sb.s_es = es;
	sb->s_magic = es->s_magic;
	if (sb->s_magic != EXT2_SUPER_MAGIC
#ifdef EXT2FS_PRE_02B_COMPAT
	   && sb->s_magic != EXT2_PRE_02B_MAGIC
#endif
	   ) {
		sb->s_dev = 0;
		unlock_super (sb);
		brelse (bh);
		if (!silent)
			printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
				MAJOR(dev), MINOR(dev));
		return NULL;
	}
	sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
	sb->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(sb);
	if (sb->s_blocksize != BLOCK_SIZE && 
	    (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||  
	     sb->s_blocksize == 4096)) {
		unsigned long offset;

		brelse (bh);
		set_blocksize (dev, sb->s_blocksize);
		logic_sb_block = sb_block / sb->s_blocksize;
		offset = sb_block % sb->s_blocksize;
		bh = bread (dev, logic_sb_block, sb->s_blocksize);
		if(!bh)
			return NULL;
		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
		sb->u.ext2_sb.s_es = es;
		if (es->s_magic != EXT2_SUPER_MAGIC) {
			sb->s_dev = 0;
			unlock_super (sb);
			brelse (bh);
			printk ("EXT2-fs: Magic mismatch, very weird !\n");
			return NULL;
		}
	}
	sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
				   es->s_log_frag_size;
	if (sb->u.ext2_sb.s_frag_size)
		sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
						  sb->u.ext2_sb.s_frag_size;
	else
		sb->s_magic = 0;
	sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
	sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
	sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
	sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
					   sizeof (struct ext2_inode);
	sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
				        sb->u.ext2_sb.s_inodes_per_block;
	sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
					 sizeof (struct ext2_group_desc);
	sb->u.ext2_sb.s_sbh = bh;
	sb->u.ext2_sb.s_es = es;
	sb->u.ext2_sb.s_mount_state = es->s_state;
	sb->u.ext2_sb.s_rename_lock = 0;
	sb->u.ext2_sb.s_rename_wait = NULL;
#ifdef EXT2FS_PRE_02B_COMPAT
	if (sb->s_magic == EXT2_PRE_02B_MAGIC) {
		if (es->s_blocks_count > 262144) {
			/*
			 * fs > 256 MB can't be converted
			 */ 
			sb->s_dev = 0;
			unlock_super (sb);
			brelse (bh);
			printk ("EXT2-fs: trying to mount a pre-0.2b file"
				"system which cannot be converted\n");
			return NULL;
		}
		printk ("EXT2-fs: mounting a pre 0.2b file system, "
			"will try to convert the structure\n");
		if (!(sb->s_flags & MS_RDONLY)) {
			sb->s_dev = 0;
			unlock_super (sb);
			brelse (bh);
			printk ("EXT2-fs: cannot convert a read-only fs\n");
			return NULL;
		}
		if (!convert_pre_02b_fs (sb, bh)) {
			sb->s_dev = 0;
			unlock_super (sb);
			brelse (bh);
			printk ("EXT2-fs: conversion failed !!!\n");
			return NULL;
		}
		printk ("EXT2-fs: conversion succeeded !!!\n");
		fs_converted = 1;
	}
#endif
	if (sb->s_magic != EXT2_SUPER_MAGIC) {
		sb->s_dev = 0;
		unlock_super (sb);
		brelse (bh);
		if (!silent)
			printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
				MAJOR(dev), MINOR(dev));
		return NULL;
	}
	if (sb->s_blocksize != bh->b_size) {
		sb->s_dev = 0;
		unlock_super (sb);
		brelse (bh);
		if (!silent)
			printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
				dev);
		return NULL;
	}

	if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
		sb->s_dev = 0;
		unlock_super (sb);
		brelse (bh);
		printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
			sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
		return NULL;
	}

	sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
				        es->s_first_data_block +
				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
				       EXT2_BLOCKS_PER_GROUP(sb);
	for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
		sb->u.ext2_sb.s_group_desc[i] = NULL;
	bh_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
		   EXT2_DESC_PER_BLOCK(sb);
	if (bh_count > EXT2_MAX_GROUP_DESC) {
		sb->s_dev = 0;
		unlock_super (sb);
		brelse (bh);
		printk ("EXT2-fs: file system is too big\n");
		return NULL;
	}
	for (i = 0; i < bh_count; i++) {
		sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
						       sb->s_blocksize);
		if (!sb->u.ext2_sb.s_group_desc[i]) {
			sb->s_dev = 0;
			unlock_super (sb);
			for (j = 0; j < i; j++)
				brelse (sb->u.ext2_sb.s_group_desc[j]);
			brelse (bh);
			printk ("EXT2-fs: unable to read group descriptors\n");
			return NULL;
		}
	}
	if (!ext2_check_descriptors (sb)) {
		sb->s_dev = 0;
		unlock_super (sb);
		for (j = 0; j < i; j++)
			brelse (sb->u.ext2_sb.s_group_desc[j]);
		brelse (bh);
		printk ("EXT2-fs: group descriptors corrupted !\n");
		return NULL;
	}
	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
		sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
		sb->u.ext2_sb.s_inode_bitmap[i] = NULL;
		sb->u.ext2_sb.s_block_bitmap_number[i] = 0;
		sb->u.ext2_sb.s_block_bitmap[i] = NULL;
	}
	sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
	sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
	unlock_super (sb);
	/*
	 * set up enough so that it can read an inode
	 */
	sb->s_dev = dev;
	sb->s_op = &ext2_sops;
	if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) {
		sb->s_dev = 0;
		for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
			if (sb->u.ext2_sb.s_group_desc[i])
				brelse (sb->u.ext2_sb.s_group_desc[i]);
		brelse (bh);
		printk ("EXT2-fs: get root inode failed\n");
		return NULL;
	}
#ifdef EXT2FS_PRE_02B_COMPAT
	if (fs_converted) {
		for (i = 0; i < bh_count; i++)
			sb->u.ext2_sb.s_group_desc[i]->b_dirt = 1;
		sb->s_dirt = 1;
	}
#endif
	ext2_setup_super (sb, es);
	return sb;
}
Esempio n. 14
0
struct super_block * ext2_read_super (struct super_block * sb, void * data,
                      int silent)
{
    struct buffer_head * bh;
    struct ext2_super_block * es;
    unsigned long sb_block = 1;
    unsigned short resuid = EXT2_DEF_RESUID;
    unsigned short resgid = EXT2_DEF_RESGID;
    unsigned long logic_sb_block = 1;
    int dev = sb->s_dev;
    int db_count;
    int i, j;
#ifdef EXT2FS_PRE_02B_COMPAT
    int fs_converted = 0;
#endif

#ifndef OS2
    set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
#else
    set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_STRICT);
#endif
    if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
        &sb->u.ext2_sb.s_mount_opt)) {
        sb->s_dev = 0;
        return NULL;
    }

    lock_super (sb);
    set_blocksize (dev, BLOCK_SIZE);
    if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
        sb->s_dev = 0;
        unlock_super (sb);
        printk ("EXT2-fs: unable to read superblock\n");
        return NULL;
    }
    /*
     * Note: s_es must be initialized s_es as soon as possible because
     * some ext2 macro-instructions depend on its value
     */
    es = (struct ext2_super_block *) bh->b_data;
    sb->u.ext2_sb.s_es = es;
    sb->s_magic = es->s_magic;
    if (sb->s_magic != EXT2_SUPER_MAGIC
#ifdef EXT2FS_PRE_02B_COMPAT
       && sb->s_magic != EXT2_PRE_02B_MAGIC
#endif
       ) {
        if (!silent)
            printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
                MAJOR(dev), MINOR(dev));
        failed_mount:
        sb->s_dev = 0;
        unlock_super (sb);
        if (bh)
                brelse (bh);
        return NULL;
    }
#ifdef EXT2_DYNAMIC_REV
    if (es->s_rev_level > EXT2_GOOD_OLD_REV) {
        if (es->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) {
#ifndef OS2
            printk("EXT2-fs: %s: couldn't mount because of "
                   "unsupported optional features.\n",
                   kdevname(dev));
#else
            printk("EXT2-fs (drive %c:) couldn't mount because of "
                   "unsupported optional features.\n",
                   sb->s_drive + 'A');
#endif
            goto failed_mount;
        }
        if (!(sb->s_flags & MS_RDONLY) &&
            (es->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
#ifndef OS2
            printk("EXT2-fs: %s: couldn't mount RDWR because of "
                   "unsupported optional features.\n",
                   kdevname(dev));
#else
            printk("EXT2-fs (drive %c:) couldn't mount RDWR because of "
                   "unsupported optional features.\n",
                   sb->s_drive + 'A');
#endif
            goto failed_mount;
        }
    }
#endif
    sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
    sb->s_blocksize_bits = EXT2_BLOCK_SIZE_BITS(sb);
    if (sb->s_blocksize != BLOCK_SIZE &&
        (sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
         sb->s_blocksize == 4096)) {
        unsigned long offset;

        brelse (bh);
        set_blocksize (dev, sb->s_blocksize);
        logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize;
        offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize;
        bh = bread (dev, logic_sb_block, sb->s_blocksize);
        if(!bh)
            return NULL;
        es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
        sb->u.ext2_sb.s_es = es;
        if (es->s_magic != EXT2_SUPER_MAGIC) {
            sb->s_dev = 0;
            unlock_super (sb);
            brelse (bh);
            printk ("EXT2-fs: Magic mismatch, very weird !\n");
            return NULL;
        }
    }
#ifdef EXT2_DYNAMIC_REV
    if (es->s_rev_level == EXT2_GOOD_OLD_REV) {
        sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
        sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
    } else {
        sb->u.ext2_sb.s_inode_size = es->s_inode_size;
        sb->u.ext2_sb.s_first_ino = es->s_first_ino;
        if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
            printk ("EXT2-fs: unsupported inode size: %d\n",
                sb->u.ext2_sb.s_inode_size);
            goto failed_mount;
        }
    }
#endif
    sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
                   es->s_log_frag_size;
    if (sb->u.ext2_sb.s_frag_size)
        sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
                          sb->u.ext2_sb.s_frag_size;
    else
        sb->s_magic = 0;
    sb->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
    sb->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
    sb->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
    sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
                       sizeof (struct ext2_inode);
    sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
                        sb->u.ext2_sb.s_inodes_per_block;
    sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
                     sizeof (struct ext2_group_desc);
    sb->u.ext2_sb.s_sbh = bh;
    sb->u.ext2_sb.s_es = es;
    if (resuid != EXT2_DEF_RESUID)
        sb->u.ext2_sb.s_resuid = resuid;
    else
        sb->u.ext2_sb.s_resuid = es->s_def_resuid;
    if (resgid != EXT2_DEF_RESGID)
        sb->u.ext2_sb.s_resgid = resgid;
    else
        sb->u.ext2_sb.s_resgid = es->s_def_resgid;
    sb->u.ext2_sb.s_mount_state = es->s_state;
    sb->u.ext2_sb.s_rename_lock = 0;
#ifndef OS2
    sb->u.ext2_sb.s_rename_wait = NULL;
#else
    sb->u.ext2_sb.s_rename_wait = 0;
#endif
#ifdef EXT2FS_PRE_02B_COMPAT
    if (sb->s_magic == EXT2_PRE_02B_MAGIC) {
        if (es->s_blocks_count > 262144) {
            /*
             * fs > 256 MB can't be converted
             */
            sb->s_dev = 0;
            unlock_super (sb);
            brelse (bh);
            printk ("EXT2-fs: trying to mount a pre-0.2b file"
                "system which cannot be converted\n");
            return NULL;
        }
        printk ("EXT2-fs: mounting a pre 0.2b file system, "
            "will try to convert the structure\n");
        if (!(sb->s_flags & MS_RDONLY)) {
            sb->s_dev = 0;
            unlock_super (sb);
            brelse (bh);
            printk ("EXT2-fs: cannot convert a read-only fs\n");
            return NULL;
        }
        if (!convert_pre_02b_fs (sb, bh)) {
            sb->s_dev = 0;
            unlock_super (sb);
            brelse (bh);
            printk ("EXT2-fs: conversion failed !!!\n");
            return NULL;
        }
        printk ("EXT2-fs: conversion succeeded !!!\n");
        fs_converted = 1;
    }
#endif
    if (sb->s_magic != EXT2_SUPER_MAGIC) {
        sb->s_dev = 0;
        unlock_super (sb);
        brelse (bh);
        if (!silent)
            printk ("VFS: Can't find an ext2 filesystem on dev %d/%d.\n",
                MAJOR(dev), MINOR(dev));
        return NULL;
    }
    if (sb->s_blocksize != bh->b_size) {
        sb->s_dev = 0;
        unlock_super (sb);
        brelse (bh);
        if (!silent)
            printk ("VFS: Unsupported blocksize on dev 0x%04x.\n",
                dev);
        return NULL;
    }

    if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
        sb->s_dev = 0;
        unlock_super (sb);
        brelse (bh);
        printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
            sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
        return NULL;
    }

    sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
                        es->s_first_data_block +
                       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
                       EXT2_BLOCKS_PER_GROUP(sb);
    db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
           EXT2_DESC_PER_BLOCK(sb);
#ifndef OS2
    sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
    if (sb->u.ext2_sb.s_group_desc == NULL) {
#else
    if (DevHlp32_VMAlloc(db_count * sizeof (struct buffer_head *), VMDHA_NOPHYSADDR, VMDHA_SWAP, (void **)(&(sb->u.ext2_sb.s_group_desc))) != NO_ERROR) {
#endif
        sb->s_dev = 0;
        unlock_super (sb);
        brelse (bh);
        printk ("EXT2-fs: not enough memory\n");
        return NULL;
    }
    for (i = 0; i < db_count; i++) {
        sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
                               sb->s_blocksize);
        if (!sb->u.ext2_sb.s_group_desc[i]) {
            sb->s_dev = 0;
            unlock_super (sb);
            for (j = 0; j < i; j++)
                brelse (sb->u.ext2_sb.s_group_desc[j]);
            kfree_s (sb->u.ext2_sb.s_group_desc,
                 db_count * sizeof (struct buffer_head *));
            brelse (bh);
            printk ("EXT2-fs: unable to read group descriptors\n");
            return NULL;
        }
    }
    if (!ext2_check_descriptors (sb)) {
        sb->s_dev = 0;
        unlock_super (sb);
        for (j = 0; j < db_count; j++)
            brelse (sb->u.ext2_sb.s_group_desc[j]);
        kfree_s (sb->u.ext2_sb.s_group_desc,
             db_count * sizeof (struct buffer_head *));
        brelse (bh);
        printk ("EXT2-fs: group descriptors corrupted !\n");
        return NULL;
    }
    for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
        sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
        sb->u.ext2_sb.s_inode_bitmap[i] = NULL;
        sb->u.ext2_sb.s_block_bitmap_number[i] = 0;
        sb->u.ext2_sb.s_block_bitmap[i] = NULL;
    }
    sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
    sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
    sb->u.ext2_sb.s_db_per_group = db_count;
    unlock_super (sb);
    /*
     * set up enough so that it can read an inode
     */
    sb->s_dev = dev;
    sb->s_op = &ext2_sops;
    if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) {
        sb->s_dev = 0;
        for (i = 0; i < db_count; i++)
            if (sb->u.ext2_sb.s_group_desc[i])
                brelse (sb->u.ext2_sb.s_group_desc[i]);
        kfree_s (sb->u.ext2_sb.s_group_desc,
             db_count * sizeof (struct buffer_head *));
        brelse (bh);
        printk ("EXT2-fs: get root inode failed\n");
        return NULL;
    }
#ifdef EXT2FS_PRE_02B_COMPAT
    if (fs_converted) {
        for (i = 0; i < db_count; i++)
            mark_buffer_dirty(sb->u.ext2_sb.s_group_desc[i], 1);
        sb->s_dirt = 1;
    }
#endif
    ext2_setup_super (sb, es);
    return sb;
}

static void ext2_commit_super (struct super_block * sb,
                   struct ext2_super_block * es)
{
    es->s_wtime = CURRENT_TIME;
    mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
    sb->s_dirt = 0;
}