<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kernel sources</title>
	<atom:link href="http://lynyrd.ru/feed" rel="self" type="application/rss+xml" />
	<link>http://lynyrd.ru</link>
	<description></description>
	<lastBuildDate>Sun, 31 Jan 2010 05:51:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>file.c</title>
		<link>http://lynyrd.ru/file-c-15</link>
		<comments>http://lynyrd.ru/file-c-15#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:51:15 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/file-c-15</guid>
		<description><![CDATA[/*
 *  linux/fs/ext3/file.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/file.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  ext3 fs regular ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext3/file.c<span id="more-1174"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/file.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  ext3 fs regular file handling primitives<br />
 *<br />
 *  64-bit file support on 64-bit platforms by Jakub Jelinek<br />
 *	(jj@sunsite.ms.mff.cuni.cz)<br />
 */</p>
<p>#include
<linux/time.h>
#include
<linux/fs.h>
#include
<linux/jbd.h>
#include
<linux/ext3_fs.h>
#include
<linux/ext3_jbd.h>
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;</p>
<p>/*<br />
 * Called when an inode is released. Note that this is different<br />
 * from ext3_file_open: open gets called at every open, but release<br />
 * gets called only when /all/ the files are closed.<br />
 */<br />
static int ext3_release_file (struct inode * inode, struct file * filp)<br />
{<br />
	if (EXT3_I(inode)->i_state &#038; EXT3_STATE_FLUSH_ON_CLOSE) {<br />
		filemap_flush(inode->i_mapping);<br />
		EXT3_I(inode)->i_state &#038;= ~EXT3_STATE_FLUSH_ON_CLOSE;<br />
	}<br />
	/* if we are the last writer on the inode, drop the block reservation */<br />
	if ((filp->f_mode &#038; FMODE_WRITE) &#038;&#038;<br />
			(atomic_read(&#038;inode->i_writecount) == 1))<br />
	{<br />
		mutex_lock(&#038;EXT3_I(inode)->truncate_mutex);<br />
		ext3_discard_reservation(inode);<br />
		mutex_unlock(&#038;EXT3_I(inode)->truncate_mutex);<br />
	}<br />
	if (is_dx(inode) &#038;&#038; filp->private_data)<br />
		ext3_htree_free_dir_info(filp->private_data);</p>
<p>	return 0;<br />
}</p>
<p>const struct file_operations ext3_file_operations = {<br />
	.llseek		= generic_file_llseek,<br />
	.read		= do_sync_read,<br />
	.write		= do_sync_write,<br />
	.aio_read	= generic_file_aio_read,<br />
	.aio_write	= generic_file_aio_write,<br />
	.unlocked_ioctl	= ext3_ioctl,<br />
#ifdef CONFIG_COMPAT<br />
	.compat_ioctl	= ext3_compat_ioctl,<br />
#endif<br />
	.mmap		= generic_file_mmap,<br />
	.open		= generic_file_open,<br />
	.release	= ext3_release_file,<br />
	.fsync		= ext3_sync_file,<br />
	.splice_read	= generic_file_splice_read,<br />
	.splice_write	= generic_file_splice_write,<br />
};</p>
<p>const struct inode_operations ext3_file_inode_operations = {<br />
	.truncate	= ext3_truncate,<br />
	.setattr	= ext3_setattr,<br />
#ifdef CONFIG_EXT3_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext3_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
	.check_acl	= ext3_check_acl,<br />
	.fiemap		= ext3_fiemap,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/file-c-15/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ext3_jbd.c</title>
		<link>http://lynyrd.ru/ext3_jbd-c</link>
		<comments>http://lynyrd.ru/ext3_jbd-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:50:57 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/ext3_jbd-c</guid>
		<description><![CDATA[/*
 * Interface between ext3 and JBD
 */
#include

int __ext3_journal_get_undo_access(const char *where, handle_t *handle,
				struct buffer_head *bh)
{
	int err = journal_get_undo_access(handle, bh);
	if (err)
		ext3_journal_abort_handle(where, __func__, bh, handle,err);
	return err;
}
int __ext3_journal_get_write_access(const char *where, handle_t *handle,
				struct buffer_head *bh)
{
	int err = journal_get_write_access(handle, bh);
	if (err)
		ext3_journal_abort_handle(where, __func__, bh, handle,err);
	return err;
}
int __ext3_journal_forget(const char *where, handle_t *handle,
				struct buffer_head *bh)
{
	int err = journal_forget(handle, bh);
	if (err)
		ext3_journal_abort_handle(where, __func__, bh, handle,err);
	return ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * Interface between ext3 and JBD<span id="more-1173"></span><br />
 */</p>
<p>#include
<linux/ext3_jbd.h>
<p>int __ext3_journal_get_undo_access(const char *where, handle_t *handle,<br />
				struct buffer_head *bh)<br />
{<br />
	int err = journal_get_undo_access(handle, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
<p>int __ext3_journal_get_write_access(const char *where, handle_t *handle,<br />
				struct buffer_head *bh)<br />
{<br />
	int err = journal_get_write_access(handle, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
<p>int __ext3_journal_forget(const char *where, handle_t *handle,<br />
				struct buffer_head *bh)<br />
{<br />
	int err = journal_forget(handle, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
<p>int __ext3_journal_revoke(const char *where, handle_t *handle,<br />
				unsigned long blocknr, struct buffer_head *bh)<br />
{<br />
	int err = journal_revoke(handle, blocknr, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
<p>int __ext3_journal_get_create_access(const char *where,<br />
				handle_t *handle, struct buffer_head *bh)<br />
{<br />
	int err = journal_get_create_access(handle, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
<p>int __ext3_journal_dirty_metadata(const char *where,<br />
				handle_t *handle, struct buffer_head *bh)<br />
{<br />
	int err = journal_dirty_metadata(handle, bh);<br />
	if (err)<br />
		ext3_journal_abort_handle(where, __func__, bh, handle,err);<br />
	return err;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/ext3_jbd-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>dir.c</title>
		<link>http://lynyrd.ru/dir-c-13</link>
		<comments>http://lynyrd.ru/dir-c-13#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:50:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/dir-c-13</guid>
		<description><![CDATA[/*
 *  linux/fs/ext3/dir.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/dir.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  ext3 directory handling ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext3/dir.c<span id="more-1172"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/dir.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  ext3 directory handling functions<br />
 *<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 *<br />
 * Hash Tree Directory indexing (c) 2001  Daniel Phillips<br />
 *<br />
 */</p>
<p>#include
<linux/fs.h>
#include
<linux/jbd.h>
#include
<linux/ext3_fs.h>
#include
<linux/buffer_head.h>
#include
<linux/slab.h>
#include
<linux/rbtree.h>
<p>static unsigned char ext3_filetype_table[] = {<br />
	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK<br />
};</p>
<p>static int ext3_readdir(struct file *, void *, filldir_t);<br />
static int ext3_dx_readdir(struct file * filp,<br />
			   void * dirent, filldir_t filldir);<br />
static int ext3_release_dir (struct inode * inode,<br />
				struct file * filp);</p>
<p>const struct file_operations ext3_dir_operations = {<br />
	.llseek		= generic_file_llseek,<br />
	.read		= generic_read_dir,<br />
	.readdir	= ext3_readdir,		/* we take BKL. needed?*/<br />
	.unlocked_ioctl	= ext3_ioctl,<br />
#ifdef CONFIG_COMPAT<br />
	.compat_ioctl	= ext3_compat_ioctl,<br />
#endif<br />
	.fsync		= ext3_sync_file,	/* BKL held */<br />
	.release	= ext3_release_dir,<br />
};</p>
<p>static unsigned char get_dtype(struct super_block *sb, int filetype)<br />
{<br />
	if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_FILETYPE) ||<br />
	    (filetype >= EXT3_FT_MAX))<br />
		return DT_UNKNOWN;</p>
<p>	return (ext3_filetype_table[filetype]);<br />
}</p>
<p>int ext3_check_dir_entry (const char * function, struct inode * dir,<br />
			  struct ext3_dir_entry_2 * de,<br />
			  struct buffer_head * bh,<br />
			  unsigned long offset)<br />
{<br />
	const char * error_msg = NULL;<br />
	const int rlen = ext3_rec_len_from_disk(de->rec_len);</p>
<p>	if (rlen < EXT3_DIR_REC_LEN(1))<br />
		error_msg = "rec_len is smaller than minimal";<br />
	else if (rlen % 4 != 0)<br />
		error_msg = "rec_len % 4 != 0";<br />
	else if (rlen < EXT3_DIR_REC_LEN(de->name_len))<br />
		error_msg = &laquo;rec_len is too small for name_len&raquo;;<br />
	else if (((char *) de &#8211; bh->b_data) + rlen > dir->i_sb->s_blocksize)<br />
		error_msg = &laquo;directory entry across blocks&raquo;;<br />
	else if (le32_to_cpu(de->inode) ><br />
			le32_to_cpu(EXT3_SB(dir->i_sb)->s_es->s_inodes_count))<br />
		error_msg = &laquo;inode out of bounds&raquo;;</p>
<p>	if (error_msg != NULL)<br />
		ext3_error (dir->i_sb, function,<br />
			&laquo;bad entry in directory #%lu: %s &#8211; &raquo;<br />
			&laquo;offset=%lu, inode=%lu, rec_len=%d, name_len=%d&raquo;,<br />
			dir->i_ino, error_msg, offset,<br />
			(unsigned long) le32_to_cpu(de->inode),<br />
			rlen, de->name_len);<br />
	return error_msg == NULL ? 1 : 0;<br />
}</p>
<p>static int ext3_readdir(struct file * filp,<br />
			 void * dirent, filldir_t filldir)<br />
{<br />
	int error = 0;<br />
	unsigned long offset;<br />
	int i, stored;<br />
	struct ext3_dir_entry_2 *de;<br />
	struct super_block *sb;<br />
	int err;<br />
	struct inode *inode = filp->f_path.dentry->d_inode;<br />
	int ret = 0;<br />
	int dir_has_error = 0;</p>
<p>	sb = inode->i_sb;</p>
<p>	if (EXT3_HAS_COMPAT_FEATURE(inode->i_sb,<br />
				    EXT3_FEATURE_COMPAT_DIR_INDEX) &#038;&#038;<br />
	    ((EXT3_I(inode)->i_flags &#038; EXT3_INDEX_FL) ||<br />
	     ((inode->i_size >> sb->s_blocksize_bits) == 1))) {<br />
		err = ext3_dx_readdir(filp, dirent, filldir);<br />
		if (err != ERR_BAD_DX_DIR) {<br />
			ret = err;<br />
			goto out;<br />
		}<br />
		/*<br />
		 * We don&#8217;t set the inode dirty flag since it&#8217;s not<br />
		 * critical that it get flushed back to the disk.<br />
		 */<br />
		EXT3_I(filp->f_path.dentry->d_inode)->i_flags &#038;= ~EXT3_INDEX_FL;<br />
	}<br />
	stored = 0;<br />
	offset = filp->f_pos &#038; (sb->s_blocksize &#8211; 1);</p>
<p>	while (!error &#038;&#038; !stored &#038;&#038; filp->f_pos < inode->i_size) {<br />
		unsigned long blk = filp->f_pos >> EXT3_BLOCK_SIZE_BITS(sb);<br />
		struct buffer_head map_bh;<br />
		struct buffer_head *bh = NULL;</p>
<p>		map_bh.b_state = 0;<br />
		err = ext3_get_blocks_handle(NULL, inode, blk, 1, &#038;map_bh, 0);<br />
		if (err > 0) {<br />
			pgoff_t index = map_bh.b_blocknr >><br />
					(PAGE_CACHE_SHIFT &#8211; inode->i_blkbits);<br />
			if (!ra_has_index(&#038;filp->f_ra, index))<br />
				page_cache_sync_readahead(<br />
					sb->s_bdev->bd_inode->i_mapping,<br />
					&#038;filp->f_ra, filp,<br />
					index, 1);<br />
			filp->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;<br />
			bh = ext3_bread(NULL, inode, blk, 0, &#038;err);<br />
		}</p>
<p>		/*<br />
		 * We ignore I/O errors on directories so users have a chance<br />
		 * of recovering data when there's a bad sector<br />
		 */<br />
		if (!bh) {<br />
			if (!dir_has_error) {<br />
				ext3_error(sb, __func__, "directory #%lu "<br />
					"contains a hole at offset %lld",<br />
					inode->i_ino, filp->f_pos);<br />
				dir_has_error = 1;<br />
			}<br />
			/* corrupt size?  Maybe no more blocks to read */<br />
			if (filp->f_pos > inode->i_blocks << 9)<br />
				break;<br />
			filp->f_pos += sb->s_blocksize &#8211; offset;<br />
			continue;<br />
		}</p>
<p>revalidate:<br />
		/* If the dir block has changed since the last call to<br />
		 * readdir(2), then we might be pointing to an invalid<br />
		 * dirent right now.  Scan from the start of the block<br />
		 * to make sure. */<br />
		if (filp->f_version != inode->i_version) {<br />
			for (i = 0; i < sb->s_blocksize &#038;&#038; i < offset; ) {<br />
				de = (struct ext3_dir_entry_2 *)<br />
					(bh->b_data + i);<br />
				/* It&#8217;s too expensive to do a full<br />
				 * dirent test each time round this<br />
				 * loop, but we do have to test at<br />
				 * least that it is non-zero.  A<br />
				 * failure will be detected in the<br />
				 * dirent test below. */<br />
				if (ext3_rec_len_from_disk(de->rec_len) <<br />
						EXT3_DIR_REC_LEN(1))<br />
					break;<br />
				i += ext3_rec_len_from_disk(de->rec_len);<br />
			}<br />
			offset = i;<br />
			filp->f_pos = (filp->f_pos &#038; ~(sb->s_blocksize &#8211; 1))<br />
				| offset;<br />
			filp->f_version = inode->i_version;<br />
		}</p>
<p>		while (!error &#038;&#038; filp->f_pos < inode->i_size<br />
		       &#038;&#038; offset < sb->s_blocksize) {<br />
			de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);<br />
			if (!ext3_check_dir_entry (&raquo;ext3_readdir&raquo;, inode, de,<br />
						   bh, offset)) {<br />
				/* On error, skip the f_pos to the<br />
                                   next block. */<br />
				filp->f_pos = (filp->f_pos |<br />
						(sb->s_blocksize &#8211; 1)) + 1;<br />
				brelse (bh);<br />
				ret = stored;<br />
				goto out;<br />
			}<br />
			offset += ext3_rec_len_from_disk(de->rec_len);<br />
			if (le32_to_cpu(de->inode)) {<br />
				/* We might block in the next section<br />
				 * if the data destination is<br />
				 * currently swapped out.  So, use a<br />
				 * version stamp to detect whether or<br />
				 * not the directory has been modified<br />
				 * during the copy operation.<br />
				 */<br />
				u64 version = filp->f_version;</p>
<p>				error = filldir(dirent, de->name,<br />
						de->name_len,<br />
						filp->f_pos,<br />
						le32_to_cpu(de->inode),<br />
						get_dtype(sb, de->file_type));<br />
				if (error)<br />
					break;<br />
				if (version != filp->f_version)<br />
					goto revalidate;<br />
				stored ++;<br />
			}<br />
			filp->f_pos += ext3_rec_len_from_disk(de->rec_len);<br />
		}<br />
		offset = 0;<br />
		brelse (bh);<br />
	}<br />
out:<br />
	return ret;<br />
}</p>
<p>/*<br />
 * These functions convert from the major/minor hash to an f_pos<br />
 * value.<br />
 *<br />
 * Currently we only use major hash numer.  This is unfortunate, but<br />
 * on 32-bit machines, the same VFS interface is used for lseek and<br />
 * llseek, so if we use the 64 bit offset, then the 32-bit versions of<br />
 * lseek/telldir/seekdir will blow out spectacularly, and from within<br />
 * the ext2 low-level routine, we don&#8217;t know if we&#8217;re being called by<br />
 * a 64-bit version of the system call or the 32-bit version of the<br />
 * system call.  Worse yet, NFSv2 only allows for a 32-bit readdir<br />
 * cookie.  Sigh.<br />
 */<br />
#define hash2pos(major, minor)	(major >> 1)<br />
#define pos2maj_hash(pos)	((pos << 1) &#038; 0xffffffff)<br />
#define pos2min_hash(pos)	(0)</p>
<p>/*<br />
 * This structure holds the nodes of the red-black tree used to store<br />
 * the directory entry in hash order.<br />
 */<br />
struct fname {<br />
	__u32		hash;<br />
	__u32		minor_hash;<br />
	struct rb_node	rb_hash;<br />
	struct fname	*next;<br />
	__u32		inode;<br />
	__u8		name_len;<br />
	__u8		file_type;<br />
	char		name[0];<br />
};</p>
<p>/*<br />
 * This functoin implements a non-recursive way of freeing all of the<br />
 * nodes in the red-black tree.<br />
 */<br />
static void free_rb_tree_fname(struct rb_root *root)<br />
{<br />
	struct rb_node	*n = root->rb_node;<br />
	struct rb_node	*parent;<br />
	struct fname	*fname;</p>
<p>	while (n) {<br />
		/* Do the node&#8217;s children first */<br />
		if (n->rb_left) {<br />
			n = n->rb_left;<br />
			continue;<br />
		}<br />
		if (n->rb_right) {<br />
			n = n->rb_right;<br />
			continue;<br />
		}<br />
		/*<br />
		 * The node has no children; free it, and then zero<br />
		 * out parent&#8217;s link to it.  Finally go to the<br />
		 * beginning of the loop and try to free the parent<br />
		 * node.<br />
		 */<br />
		parent = rb_parent(n);<br />
		fname = rb_entry(n, struct fname, rb_hash);<br />
		while (fname) {<br />
			struct fname * old = fname;<br />
			fname = fname->next;<br />
			kfree (old);<br />
		}<br />
		if (!parent)<br />
			root->rb_node = NULL;<br />
		else if (parent->rb_left == n)<br />
			parent->rb_left = NULL;<br />
		else if (parent->rb_right == n)<br />
			parent->rb_right = NULL;<br />
		n = parent;<br />
	}<br />
}</p>
<p>static struct dir_private_info *ext3_htree_create_dir_info(loff_t pos)<br />
{<br />
	struct dir_private_info *p;</p>
<p>	p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL);<br />
	if (!p)<br />
		return NULL;<br />
	p->curr_hash = pos2maj_hash(pos);<br />
	p->curr_minor_hash = pos2min_hash(pos);<br />
	return p;<br />
}</p>
<p>void ext3_htree_free_dir_info(struct dir_private_info *p)<br />
{<br />
	free_rb_tree_fname(&#038;p->root);<br />
	kfree(p);<br />
}</p>
<p>/*<br />
 * Given a directory entry, enter it into the fname rb tree.<br />
 */<br />
int ext3_htree_store_dirent(struct file *dir_file, __u32 hash,<br />
			     __u32 minor_hash,<br />
			     struct ext3_dir_entry_2 *dirent)<br />
{<br />
	struct rb_node **p, *parent = NULL;<br />
	struct fname * fname, *new_fn;<br />
	struct dir_private_info *info;<br />
	int len;</p>
<p>	info = (struct dir_private_info *) dir_file->private_data;<br />
	p = &#038;info->root.rb_node;</p>
<p>	/* Create and allocate the fname structure */<br />
	len = sizeof(struct fname) + dirent->name_len + 1;<br />
	new_fn = kzalloc(len, GFP_KERNEL);<br />
	if (!new_fn)<br />
		return -ENOMEM;<br />
	new_fn->hash = hash;<br />
	new_fn->minor_hash = minor_hash;<br />
	new_fn->inode = le32_to_cpu(dirent->inode);<br />
	new_fn->name_len = dirent->name_len;<br />
	new_fn->file_type = dirent->file_type;<br />
	memcpy(new_fn->name, dirent->name, dirent->name_len);<br />
	new_fn->name[dirent->name_len] = 0;</p>
<p>	while (*p) {<br />
		parent = *p;<br />
		fname = rb_entry(parent, struct fname, rb_hash);</p>
<p>		/*<br />
		 * If the hash and minor hash match up, then we put<br />
		 * them on a linked list.  This rarely happens&#8230;<br />
		 */<br />
		if ((new_fn->hash == fname->hash) &#038;&#038;<br />
		    (new_fn->minor_hash == fname->minor_hash)) {<br />
			new_fn->next = fname->next;<br />
			fname->next = new_fn;<br />
			return 0;<br />
		}</p>
<p>		if (new_fn->hash < fname->hash)<br />
			p = &#038;(*p)->rb_left;<br />
		else if (new_fn->hash > fname->hash)<br />
			p = &#038;(*p)->rb_right;<br />
		else if (new_fn->minor_hash < fname->minor_hash)<br />
			p = &#038;(*p)->rb_left;<br />
		else /* if (new_fn->minor_hash > fname->minor_hash) */<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;new_fn->rb_hash, parent, p);<br />
	rb_insert_color(&#038;new_fn->rb_hash, &#038;info->root);<br />
	return 0;<br />
}</p>
<p>/*<br />
 * This is a helper function for ext3_dx_readdir.  It calls filldir<br />
 * for all entres on the fname linked list.  (Normally there is only<br />
 * one entry on the linked list, unless there are 62 bit hash collisions.)<br />
 */<br />
static int call_filldir(struct file * filp, void * dirent,<br />
			filldir_t filldir, struct fname *fname)<br />
{<br />
	struct dir_private_info *info = filp->private_data;<br />
	loff_t	curr_pos;<br />
	struct inode *inode = filp->f_path.dentry->d_inode;<br />
	struct super_block * sb;<br />
	int error;</p>
<p>	sb = inode->i_sb;</p>
<p>	if (!fname) {<br />
		printk(&raquo;call_filldir: called with null fname?!?\n&raquo;);<br />
		return 0;<br />
	}<br />
	curr_pos = hash2pos(fname->hash, fname->minor_hash);<br />
	while (fname) {<br />
		error = filldir(dirent, fname->name,<br />
				fname->name_len, curr_pos,<br />
				fname->inode,<br />
				get_dtype(sb, fname->file_type));<br />
		if (error) {<br />
			filp->f_pos = curr_pos;<br />
			info->extra_fname = fname;<br />
			return error;<br />
		}<br />
		fname = fname->next;<br />
	}<br />
	return 0;<br />
}</p>
<p>static int ext3_dx_readdir(struct file * filp,<br />
			 void * dirent, filldir_t filldir)<br />
{<br />
	struct dir_private_info *info = filp->private_data;<br />
	struct inode *inode = filp->f_path.dentry->d_inode;<br />
	struct fname *fname;<br />
	int	ret;</p>
<p>	if (!info) {<br />
		info = ext3_htree_create_dir_info(filp->f_pos);<br />
		if (!info)<br />
			return -ENOMEM;<br />
		filp->private_data = info;<br />
	}</p>
<p>	if (filp->f_pos == EXT3_HTREE_EOF)<br />
		return 0;	/* EOF */</p>
<p>	/* Some one has messed with f_pos; reset the world */<br />
	if (info->last_pos != filp->f_pos) {<br />
		free_rb_tree_fname(&#038;info->root);<br />
		info->curr_node = NULL;<br />
		info->extra_fname = NULL;<br />
		info->curr_hash = pos2maj_hash(filp->f_pos);<br />
		info->curr_minor_hash = pos2min_hash(filp->f_pos);<br />
	}</p>
<p>	/*<br />
	 * If there are any leftover names on the hash collision<br />
	 * chain, return them first.<br />
	 */<br />
	if (info->extra_fname) {<br />
		if (call_filldir(filp, dirent, filldir, info->extra_fname))<br />
			goto finished;<br />
		info->extra_fname = NULL;<br />
		goto next_node;<br />
	} else if (!info->curr_node)<br />
		info->curr_node = rb_first(&#038;info->root);</p>
<p>	while (1) {<br />
		/*<br />
		 * Fill the rbtree if we have no more entries,<br />
		 * or the inode has changed since we last read in the<br />
		 * cached entries.<br />
		 */<br />
		if ((!info->curr_node) ||<br />
		    (filp->f_version != inode->i_version)) {<br />
			info->curr_node = NULL;<br />
			free_rb_tree_fname(&#038;info->root);<br />
			filp->f_version = inode->i_version;<br />
			ret = ext3_htree_fill_tree(filp, info->curr_hash,<br />
						   info->curr_minor_hash,<br />
						   &#038;info->next_hash);<br />
			if (ret < 0)<br />
				return ret;<br />
			if (ret == 0) {<br />
				filp->f_pos = EXT3_HTREE_EOF;<br />
				break;<br />
			}<br />
			info->curr_node = rb_first(&#038;info->root);<br />
		}</p>
<p>		fname = rb_entry(info->curr_node, struct fname, rb_hash);<br />
		info->curr_hash = fname->hash;<br />
		info->curr_minor_hash = fname->minor_hash;<br />
		if (call_filldir(filp, dirent, filldir, fname))<br />
			break;<br />
	next_node:<br />
		info->curr_node = rb_next(info->curr_node);<br />
		if (info->curr_node) {<br />
			fname = rb_entry(info->curr_node, struct fname,<br />
					 rb_hash);<br />
			info->curr_hash = fname->hash;<br />
			info->curr_minor_hash = fname->minor_hash;<br />
		} else {<br />
			if (info->next_hash == ~0) {<br />
				filp->f_pos = EXT3_HTREE_EOF;<br />
				break;<br />
			}<br />
			info->curr_hash = info->next_hash;<br />
			info->curr_minor_hash = 0;<br />
		}<br />
	}<br />
finished:<br />
	info->last_pos = filp->f_pos;<br />
	return 0;<br />
}</p>
<p>static int ext3_release_dir (struct inode * inode, struct file * filp)<br />
{<br />
       if (filp->private_data)<br />
		ext3_htree_free_dir_info(filp->private_data);</p>
<p>	return 0;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/dir-c-13/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>bitmap.c</title>
		<link>http://lynyrd.ru/bitmap-c-2</link>
		<comments>http://lynyrd.ru/bitmap-c-2#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:50:00 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1170</guid>
		<description><![CDATA[/*
 *  linux/fs/ext3/bitmap.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 */
#include

#include

#include

#ifdef EXT3FS_DEBUG
static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
unsigned long ext3_count_free ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext3/bitmap.c<span id="more-1170"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 */</p>
<p>#include
<linux/buffer_head.h>
#include
<linux/jbd.h>
#include
<linux/ext3_fs.h>
<p>#ifdef EXT3FS_DEBUG</p>
<p>static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};</p>
<p>unsigned long ext3_count_free (struct buffer_head * map, unsigned int numchars)<br />
{<br />
	unsigned int i;<br />
	unsigned long sum = 0;</p>
<p>	if (!map)<br />
		return (0);<br />
	for (i = 0; i < numchars; i++)<br />
		sum += nibblemap[map->b_data[i] &#038; 0xf] +<br />
			nibblemap[(map->b_data[i] >> 4) &#038; 0xf];<br />
	return (sum);<br />
}</p>
<p>#endif  /*  EXT3FS_DEBUG  */</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/bitmap-c-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>balloc.c</title>
		<link>http://lynyrd.ru/balloc-c-2</link>
		<comments>http://lynyrd.ru/balloc-c-2#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:49:43 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/balloc-c-2</guid>
		<description><![CDATA[/*
 *  linux/fs/ext3/balloc.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  Enhanced block allocation by Stephen Tweedie (sct@redhat.com), 1993
 *  Big-endian to little-endian byte-swapping/bitmaps by
 *      ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext3/balloc.c<span id="more-1169"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  Enhanced block allocation by Stephen Tweedie (sct@redhat.com), 1993<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 */</p>
<p>#include
<linux/time.h>
#include
<linux/capability.h>
#include
<linux/fs.h>
#include
<linux/jbd.h>
#include
<linux/ext3_fs.h>
#include
<linux/ext3_jbd.h>
#include
<linux/quotaops.h>
#include
<linux/buffer_head.h>
<p>/*<br />
 * balloc.c contains the blocks allocation and deallocation routines<br />
 */</p>
<p>/*<br />
 * The free blocks are managed by bitmaps.  A file system contains several<br />
 * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap<br />
 * block for inodes, N blocks for the inode table and data blocks.<br />
 *<br />
 * The file system contains group descriptors which are located after the<br />
 * super block.  Each descriptor contains the number of the bitmap block and<br />
 * the free blocks count in the block.  The descriptors are loaded in memory<br />
 * when a file system is mounted (see ext3_fill_super).<br />
 */</p>
<p>#define in_range(b, first, len)	((b) >= (first) &#038;&#038; (b) <= (first) + (len) - 1)</p>
<p>/**<br />
 * ext3_get_group_desc() -- load group descriptor from disk<br />
 * @sb:			super block<br />
 * @block_group:	given block group<br />
 * @bh:			pointer to the buffer head to store the block<br />
 *			group descriptor<br />
 */<br />
struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,<br />
					     unsigned int block_group,<br />
					     struct buffer_head ** bh)<br />
{<br />
	unsigned long group_desc;<br />
	unsigned long offset;<br />
	struct ext3_group_desc * desc;<br />
	struct ext3_sb_info *sbi = EXT3_SB(sb);</p>
<p>	if (block_group >= sbi->s_groups_count) {<br />
		ext3_error (sb, &laquo;ext3_get_group_desc&raquo;,<br />
			    &laquo;block_group >= groups_count &#8211; &raquo;<br />
			    &laquo;block_group = %d, groups_count = %lu&raquo;,<br />
			    block_group, sbi->s_groups_count);</p>
<p>		return NULL;<br />
	}<br />
	smp_rmb();</p>
<p>	group_desc = block_group >> EXT3_DESC_PER_BLOCK_BITS(sb);<br />
	offset = block_group &#038; (EXT3_DESC_PER_BLOCK(sb) &#8211; 1);<br />
	if (!sbi->s_group_desc[group_desc]) {<br />
		ext3_error (sb, &laquo;ext3_get_group_desc&raquo;,<br />
			    &laquo;Group descriptor not loaded &#8211; &raquo;<br />
			    &laquo;block_group = %d, group_desc = %lu, desc = %lu&raquo;,<br />
			     block_group, group_desc, offset);<br />
		return NULL;<br />
	}</p>
<p>	desc = (struct ext3_group_desc *) sbi->s_group_desc[group_desc]->b_data;<br />
	if (bh)<br />
		*bh = sbi->s_group_desc[group_desc];<br />
	return desc + offset;<br />
}</p>
<p>static int ext3_valid_block_bitmap(struct super_block *sb,<br />
					struct ext3_group_desc *desc,<br />
					unsigned int block_group,<br />
					struct buffer_head *bh)<br />
{<br />
	ext3_grpblk_t offset;<br />
	ext3_grpblk_t next_zero_bit;<br />
	ext3_fsblk_t bitmap_blk;<br />
	ext3_fsblk_t group_first_block;</p>
<p>	group_first_block = ext3_group_first_block_no(sb, block_group);</p>
<p>	/* check whether block bitmap block number is set */<br />
	bitmap_blk = le32_to_cpu(desc->bg_block_bitmap);<br />
	offset = bitmap_blk &#8211; group_first_block;<br />
	if (!ext3_test_bit(offset, bh->b_data))<br />
		/* bad block bitmap */<br />
		goto err_out;</p>
<p>	/* check whether the inode bitmap block number is set */<br />
	bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap);<br />
	offset = bitmap_blk &#8211; group_first_block;<br />
	if (!ext3_test_bit(offset, bh->b_data))<br />
		/* bad block bitmap */<br />
		goto err_out;</p>
<p>	/* check whether the inode table block number is set */<br />
	bitmap_blk = le32_to_cpu(desc->bg_inode_table);<br />
	offset = bitmap_blk &#8211; group_first_block;<br />
	next_zero_bit = ext3_find_next_zero_bit(bh->b_data,<br />
				offset + EXT3_SB(sb)->s_itb_per_group,<br />
				offset);<br />
	if (next_zero_bit >= offset + EXT3_SB(sb)->s_itb_per_group)<br />
		/* good bitmap for inode tables */<br />
		return 1;</p>
<p>err_out:<br />
	ext3_error(sb, __func__,<br />
			&laquo;Invalid block bitmap &#8211; &raquo;<br />
			&laquo;block_group = %d, block = %lu&raquo;,<br />
			block_group, bitmap_blk);<br />
	return 0;<br />
}</p>
<p>/**<br />
 * read_block_bitmap()<br />
 * @sb:			super block<br />
 * @block_group:	given block group<br />
 *<br />
 * Read the bitmap for a given block_group,and validate the<br />
 * bits for block/inode/inode tables are set in the bitmaps<br />
 *<br />
 * Return buffer_head on success or NULL in case of failure.<br />
 */<br />
static struct buffer_head *<br />
read_block_bitmap(struct super_block *sb, unsigned int block_group)<br />
{<br />
	struct ext3_group_desc * desc;<br />
	struct buffer_head * bh = NULL;<br />
	ext3_fsblk_t bitmap_blk;</p>
<p>	desc = ext3_get_group_desc(sb, block_group, NULL);<br />
	if (!desc)<br />
		return NULL;<br />
	bitmap_blk = le32_to_cpu(desc->bg_block_bitmap);<br />
	bh = sb_getblk(sb, bitmap_blk);<br />
	if (unlikely(!bh)) {<br />
		ext3_error(sb, __func__,<br />
			    &laquo;Cannot read block bitmap &#8211; &raquo;<br />
			    &laquo;block_group = %d, block_bitmap = %u&raquo;,<br />
			    block_group, le32_to_cpu(desc->bg_block_bitmap));<br />
		return NULL;<br />
	}<br />
	if (likely(bh_uptodate_or_lock(bh)))<br />
		return bh;</p>
<p>	if (bh_submit_read(bh) < 0) {<br />
		brelse(bh);<br />
		ext3_error(sb, __func__,<br />
			    "Cannot read block bitmap - "<br />
			    "block_group = %d, block_bitmap = %u",<br />
			    block_group, le32_to_cpu(desc->bg_block_bitmap));<br />
		return NULL;<br />
	}<br />
	ext3_valid_block_bitmap(sb, desc, block_group, bh);<br />
	/*<br />
	 * file system mounted not to panic on error, continue with corrupt<br />
	 * bitmap<br />
	 */<br />
	return bh;<br />
}<br />
/*<br />
 * The reservation window structure operations<br />
 * &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
 * Operations include:<br />
 * dump, find, add, remove, is_empty, find_next_reservable_window, etc.<br />
 *<br />
 * We use a red-black tree to represent per-filesystem reservation<br />
 * windows.<br />
 *<br />
 */</p>
<p>/**<br />
 * __rsv_window_dump() &#8212; Dump the filesystem block allocation reservation map<br />
 * @rb_root:		root of per-filesystem reservation rb tree<br />
 * @verbose:		verbose mode<br />
 * @fn:			function which wishes to dump the reservation map<br />
 *<br />
 * If verbose is turned on, it will print the whole block reservation<br />
 * windows(start, end).	Otherwise, it will only print out the &laquo;bad&raquo; windows,<br />
 * those windows that overlap with their immediate neighbors.<br />
 */<br />
#if 1<br />
static void __rsv_window_dump(struct rb_root *root, int verbose,<br />
			      const char *fn)<br />
{<br />
	struct rb_node *n;<br />
	struct ext3_reserve_window_node *rsv, *prev;<br />
	int bad;</p>
<p>restart:<br />
	n = rb_first(root);<br />
	bad = 0;<br />
	prev = NULL;</p>
<p>	printk(&raquo;Block Allocation Reservation Windows Map (%s):\n&raquo;, fn);<br />
	while (n) {<br />
		rsv = rb_entry(n, struct ext3_reserve_window_node, rsv_node);<br />
		if (verbose)<br />
			printk(&raquo;reservation window 0x%p &raquo;<br />
			       &laquo;start:  %lu, end:  %lu\n&raquo;,<br />
			       rsv, rsv->rsv_start, rsv->rsv_end);<br />
		if (rsv->rsv_start &#038;&#038; rsv->rsv_start >= rsv->rsv_end) {<br />
			printk(&raquo;Bad reservation %p (start >= end)\n&raquo;,<br />
			       rsv);<br />
			bad = 1;<br />
		}<br />
		if (prev &#038;&#038; prev->rsv_end >= rsv->rsv_start) {<br />
			printk(&raquo;Bad reservation %p (prev->end >= start)\n&raquo;,<br />
			       rsv);<br />
			bad = 1;<br />
		}<br />
		if (bad) {<br />
			if (!verbose) {<br />
				printk(&raquo;Restarting reservation walk in verbose mode\n&raquo;);<br />
				verbose = 1;<br />
				goto restart;<br />
			}<br />
		}<br />
		n = rb_next(n);<br />
		prev = rsv;<br />
	}<br />
	printk(&raquo;Window map complete.\n&raquo;);<br />
	BUG_ON(bad);<br />
}<br />
#define rsv_window_dump(root, verbose) \<br />
	__rsv_window_dump((root), (verbose), __func__)<br />
#else<br />
#define rsv_window_dump(root, verbose) do {} while (0)<br />
#endif</p>
<p>/**<br />
 * goal_in_my_reservation()<br />
 * @rsv:		inode&#8217;s reservation window<br />
 * @grp_goal:		given goal block relative to the allocation block group<br />
 * @group:		the current allocation block group<br />
 * @sb:			filesystem super block<br />
 *<br />
 * Test if the given goal block (group relative) is within the file&#8217;s<br />
 * own block reservation window range.<br />
 *<br />
 * If the reservation window is outside the goal allocation group, return 0;<br />
 * grp_goal (given goal block) could be -1, which means no specific<br />
 * goal block. In this case, always return 1.<br />
 * If the goal block is within the reservation window, return 1;<br />
 * otherwise, return 0;<br />
 */<br />
static int<br />
goal_in_my_reservation(struct ext3_reserve_window *rsv, ext3_grpblk_t grp_goal,<br />
			unsigned int group, struct super_block * sb)<br />
{<br />
	ext3_fsblk_t group_first_block, group_last_block;</p>
<p>	group_first_block = ext3_group_first_block_no(sb, group);<br />
	group_last_block = group_first_block + (EXT3_BLOCKS_PER_GROUP(sb) &#8211; 1);</p>
<p>	if ((rsv->_rsv_start > group_last_block) ||<br />
	    (rsv->_rsv_end < group_first_block))<br />
		return 0;<br />
	if ((grp_goal >= 0) &#038;&#038; ((grp_goal + group_first_block < rsv->_rsv_start)<br />
		|| (grp_goal + group_first_block > rsv->_rsv_end)))<br />
		return 0;<br />
	return 1;<br />
}</p>
<p>/**<br />
 * search_reserve_window()<br />
 * @rb_root:		root of reservation tree<br />
 * @goal:		target allocation block<br />
 *<br />
 * Find the reserved window which includes the goal, or the previous one<br />
 * if the goal is not in any window.<br />
 * Returns NULL if there are no windows or if all windows start after the goal.<br />
 */<br />
static struct ext3_reserve_window_node *<br />
search_reserve_window(struct rb_root *root, ext3_fsblk_t goal)<br />
{<br />
	struct rb_node *n = root->rb_node;<br />
	struct ext3_reserve_window_node *rsv;</p>
<p>	if (!n)<br />
		return NULL;</p>
<p>	do {<br />
		rsv = rb_entry(n, struct ext3_reserve_window_node, rsv_node);</p>
<p>		if (goal < rsv->rsv_start)<br />
			n = n->rb_left;<br />
		else if (goal > rsv->rsv_end)<br />
			n = n->rb_right;<br />
		else<br />
			return rsv;<br />
	} while (n);<br />
	/*<br />
	 * We&#8217;ve fallen off the end of the tree: the goal wasn&#8217;t inside<br />
	 * any particular node.  OK, the previous node must be to one<br />
	 * side of the interval containing the goal.  If it&#8217;s the RHS,<br />
	 * we need to back up one.<br />
	 */<br />
	if (rsv->rsv_start > goal) {<br />
		n = rb_prev(&#038;rsv->rsv_node);<br />
		rsv = rb_entry(n, struct ext3_reserve_window_node, rsv_node);<br />
	}<br />
	return rsv;<br />
}</p>
<p>/**<br />
 * ext3_rsv_window_add() &#8212; Insert a window to the block reservation rb tree.<br />
 * @sb:			super block<br />
 * @rsv:		reservation window to add<br />
 *<br />
 * Must be called with rsv_lock hold.<br />
 */<br />
void ext3_rsv_window_add(struct super_block *sb,<br />
		    struct ext3_reserve_window_node *rsv)<br />
{<br />
	struct rb_root *root = &#038;EXT3_SB(sb)->s_rsv_window_root;<br />
	struct rb_node *node = &#038;rsv->rsv_node;<br />
	ext3_fsblk_t start = rsv->rsv_start;</p>
<p>	struct rb_node ** p = &#038;root->rb_node;<br />
	struct rb_node * parent = NULL;<br />
	struct ext3_reserve_window_node *this;</p>
<p>	while (*p)<br />
	{<br />
		parent = *p;<br />
		this = rb_entry(parent, struct ext3_reserve_window_node, rsv_node);</p>
<p>		if (start < this->rsv_start)<br />
			p = &#038;(*p)->rb_left;<br />
		else if (start > this->rsv_end)<br />
			p = &#038;(*p)->rb_right;<br />
		else {<br />
			rsv_window_dump(root, 1);<br />
			BUG();<br />
		}<br />
	}</p>
<p>	rb_link_node(node, parent, p);<br />
	rb_insert_color(node, root);<br />
}</p>
<p>/**<br />
 * ext3_rsv_window_remove() &#8212; unlink a window from the reservation rb tree<br />
 * @sb:			super block<br />
 * @rsv:		reservation window to remove<br />
 *<br />
 * Mark the block reservation window as not allocated, and unlink it<br />
 * from the filesystem reservation window rb tree. Must be called with<br />
 * rsv_lock hold.<br />
 */<br />
static void rsv_window_remove(struct super_block *sb,<br />
			      struct ext3_reserve_window_node *rsv)<br />
{<br />
	rsv->rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;<br />
	rsv->rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;<br />
	rsv->rsv_alloc_hit = 0;<br />
	rb_erase(&#038;rsv->rsv_node, &#038;EXT3_SB(sb)->s_rsv_window_root);<br />
}</p>
<p>/*<br />
 * rsv_is_empty() &#8212; Check if the reservation window is allocated.<br />
 * @rsv:		given reservation window to check<br />
 *<br />
 * returns 1 if the end block is EXT3_RESERVE_WINDOW_NOT_ALLOCATED.<br />
 */<br />
static inline int rsv_is_empty(struct ext3_reserve_window *rsv)<br />
{<br />
	/* a valid reservation end block could not be 0 */<br />
	return rsv->_rsv_end == EXT3_RESERVE_WINDOW_NOT_ALLOCATED;<br />
}</p>
<p>/**<br />
 * ext3_init_block_alloc_info()<br />
 * @inode:		file inode structure<br />
 *<br />
 * Allocate and initialize the	reservation window structure, and<br />
 * link the window to the ext3 inode structure at last<br />
 *<br />
 * The reservation window structure is only dynamically allocated<br />
 * and linked to ext3 inode the first time the open file<br />
 * needs a new block. So, before every ext3_new_block(s) call, for<br />
 * regular files, we should check whether the reservation window<br />
 * structure exists or not. In the latter case, this function is called.<br />
 * Fail to do so will result in block reservation being turned off for that<br />
 * open file.<br />
 *<br />
 * This function is called from ext3_get_blocks_handle(), also called<br />
 * when setting the reservation window size through ioctl before the file<br />
 * is open for write (needs block allocation).<br />
 *<br />
 * Needs truncate_mutex protection prior to call this function.<br />
 */<br />
void ext3_init_block_alloc_info(struct inode *inode)<br />
{<br />
	struct ext3_inode_info *ei = EXT3_I(inode);<br />
	struct ext3_block_alloc_info *block_i = ei->i_block_alloc_info;<br />
	struct super_block *sb = inode->i_sb;</p>
<p>	block_i = kmalloc(sizeof(*block_i), GFP_NOFS);<br />
	if (block_i) {<br />
		struct ext3_reserve_window_node *rsv = &#038;block_i->rsv_window_node;</p>
<p>		rsv->rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;<br />
		rsv->rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;</p>
<p>		/*<br />
		 * if filesystem is mounted with NORESERVATION, the goal<br />
		 * reservation window size is set to zero to indicate<br />
		 * block reservation is off<br />
		 */<br />
		if (!test_opt(sb, RESERVATION))<br />
			rsv->rsv_goal_size = 0;<br />
		else<br />
			rsv->rsv_goal_size = EXT3_DEFAULT_RESERVE_BLOCKS;<br />
		rsv->rsv_alloc_hit = 0;<br />
		block_i->last_alloc_logical_block = 0;<br />
		block_i->last_alloc_physical_block = 0;<br />
	}<br />
	ei->i_block_alloc_info = block_i;<br />
}</p>
<p>/**<br />
 * ext3_discard_reservation()<br />
 * @inode:		inode<br />
 *<br />
 * Discard(free) block reservation window on last file close, or truncate<br />
 * or at last iput().<br />
 *<br />
 * It is being called in three cases:<br />
 *	ext3_release_file(): last writer close the file<br />
 *	ext3_clear_inode(): last iput(), when nobody link to this file.<br />
 *	ext3_truncate(): when the block indirect map is about to change.<br />
 *<br />
 */<br />
void ext3_discard_reservation(struct inode *inode)<br />
{<br />
	struct ext3_inode_info *ei = EXT3_I(inode);<br />
	struct ext3_block_alloc_info *block_i = ei->i_block_alloc_info;<br />
	struct ext3_reserve_window_node *rsv;<br />
	spinlock_t *rsv_lock = &#038;EXT3_SB(inode->i_sb)->s_rsv_window_lock;</p>
<p>	if (!block_i)<br />
		return;</p>
<p>	rsv = &#038;block_i->rsv_window_node;<br />
	if (!rsv_is_empty(&#038;rsv->rsv_window)) {<br />
		spin_lock(rsv_lock);<br />
		if (!rsv_is_empty(&#038;rsv->rsv_window))<br />
			rsv_window_remove(inode->i_sb, rsv);<br />
		spin_unlock(rsv_lock);<br />
	}<br />
}</p>
<p>/**<br />
 * ext3_free_blocks_sb() &#8212; Free given blocks and update quota<br />
 * @handle:			handle to this transaction<br />
 * @sb:				super block<br />
 * @block:			start physcial block to free<br />
 * @count:			number of blocks to free<br />
 * @pdquot_freed_blocks:	pointer to quota<br />
 */<br />
void ext3_free_blocks_sb(handle_t *handle, struct super_block *sb,<br />
			 ext3_fsblk_t block, unsigned long count,<br />
			 unsigned long *pdquot_freed_blocks)<br />
{<br />
	struct buffer_head *bitmap_bh = NULL;<br />
	struct buffer_head *gd_bh;<br />
	unsigned long block_group;<br />
	ext3_grpblk_t bit;<br />
	unsigned long i;<br />
	unsigned long overflow;<br />
	struct ext3_group_desc * desc;<br />
	struct ext3_super_block * es;<br />
	struct ext3_sb_info *sbi;<br />
	int err = 0, ret;<br />
	ext3_grpblk_t group_freed;</p>
<p>	*pdquot_freed_blocks = 0;<br />
	sbi = EXT3_SB(sb);<br />
	es = sbi->s_es;<br />
	if (block < le32_to_cpu(es->s_first_data_block) ||<br />
	    block + count < block ||<br />
	    block + count > le32_to_cpu(es->s_blocks_count)) {<br />
		ext3_error (sb, &laquo;ext3_free_blocks&raquo;,<br />
			    &laquo;Freeing blocks not in datazone &#8211; &raquo;<br />
			    &laquo;block = &laquo;E3FSBLK&raquo;, count = %lu&raquo;, block, count);<br />
		goto error_return;<br />
	}</p>
<p>	ext3_debug (&raquo;freeing block(s) %lu-%lu\n&raquo;, block, block + count &#8211; 1);</p>
<p>do_more:<br />
	overflow = 0;<br />
	block_group = (block &#8211; le32_to_cpu(es->s_first_data_block)) /<br />
		      EXT3_BLOCKS_PER_GROUP(sb);<br />
	bit = (block &#8211; le32_to_cpu(es->s_first_data_block)) %<br />
		      EXT3_BLOCKS_PER_GROUP(sb);<br />
	/*<br />
	 * Check to see if we are freeing blocks across a group<br />
	 * boundary.<br />
	 */<br />
	if (bit + count > EXT3_BLOCKS_PER_GROUP(sb)) {<br />
		overflow = bit + count &#8211; EXT3_BLOCKS_PER_GROUP(sb);<br />
		count -= overflow;<br />
	}<br />
	brelse(bitmap_bh);<br />
	bitmap_bh = read_block_bitmap(sb, block_group);<br />
	if (!bitmap_bh)<br />
		goto error_return;<br />
	desc = ext3_get_group_desc (sb, block_group, &#038;gd_bh);<br />
	if (!desc)<br />
		goto error_return;</p>
<p>	if (in_range (le32_to_cpu(desc->bg_block_bitmap), block, count) ||<br />
	    in_range (le32_to_cpu(desc->bg_inode_bitmap), block, count) ||<br />
	    in_range (block, le32_to_cpu(desc->bg_inode_table),<br />
		      sbi->s_itb_per_group) ||<br />
	    in_range (block + count &#8211; 1, le32_to_cpu(desc->bg_inode_table),<br />
		      sbi->s_itb_per_group)) {<br />
		ext3_error (sb, &laquo;ext3_free_blocks&raquo;,<br />
			    &laquo;Freeing blocks in system zones &#8211; &raquo;<br />
			    &laquo;Block = &laquo;E3FSBLK&raquo;, count = %lu&raquo;,<br />
			    block, count);<br />
		goto error_return;<br />
	}</p>
<p>	/*<br />
	 * We are about to start releasing blocks in the bitmap,<br />
	 * so we need undo access.<br />
	 */<br />
	/* @@@ check errors */<br />
	BUFFER_TRACE(bitmap_bh, &laquo;getting undo access&raquo;);<br />
	err = ext3_journal_get_undo_access(handle, bitmap_bh);<br />
	if (err)<br />
		goto error_return;</p>
<p>	/*<br />
	 * We are about to modify some metadata.  Call the journal APIs<br />
	 * to unshare ->b_data if a currently-committing transaction is<br />
	 * using it<br />
	 */<br />
	BUFFER_TRACE(gd_bh, &laquo;get_write_access&raquo;);<br />
	err = ext3_journal_get_write_access(handle, gd_bh);<br />
	if (err)<br />
		goto error_return;</p>
<p>	jbd_lock_bh_state(bitmap_bh);</p>
<p>	for (i = 0, group_freed = 0; i < count; i++) {<br />
		/*<br />
		 * An HJ special.  This is expensive...<br />
		 */<br />
#ifdef CONFIG_JBD_DEBUG<br />
		jbd_unlock_bh_state(bitmap_bh);<br />
		{<br />
			struct buffer_head *debug_bh;<br />
			debug_bh = sb_find_get_block(sb, block + i);<br />
			if (debug_bh) {<br />
				BUFFER_TRACE(debug_bh, "Deleted!");<br />
				if (!bh2jh(bitmap_bh)->b_committed_data)<br />
					BUFFER_TRACE(debug_bh,<br />
						&laquo;No commited data in bitmap&raquo;);<br />
				BUFFER_TRACE2(debug_bh, bitmap_bh, &laquo;bitmap&raquo;);<br />
				__brelse(debug_bh);<br />
			}<br />
		}<br />
		jbd_lock_bh_state(bitmap_bh);<br />
#endif<br />
		if (need_resched()) {<br />
			jbd_unlock_bh_state(bitmap_bh);<br />
			cond_resched();<br />
			jbd_lock_bh_state(bitmap_bh);<br />
		}<br />
		/* @@@ This prevents newly-allocated data from being<br />
		 * freed and then reallocated within the same<br />
		 * transaction.<br />
		 *<br />
		 * Ideally we would want to allow that to happen, but to<br />
		 * do so requires making journal_forget() capable of<br />
		 * revoking the queued write of a data block, which<br />
		 * implies blocking on the journal lock.  *forget()<br />
		 * cannot block due to truncate races.<br />
		 *<br />
		 * Eventually we can fix this by making journal_forget()<br />
		 * return a status indicating whether or not it was able<br />
		 * to revoke the buffer.  On successful revoke, it is<br />
		 * safe not to set the allocation bit in the committed<br />
		 * bitmap, because we know that there is no outstanding<br />
		 * activity on the buffer any more and so it is safe to<br />
		 * reallocate it.<br />
		 */<br />
		BUFFER_TRACE(bitmap_bh, &laquo;set in b_committed_data&raquo;);<br />
		J_ASSERT_BH(bitmap_bh,<br />
				bh2jh(bitmap_bh)->b_committed_data != NULL);<br />
		ext3_set_bit_atomic(sb_bgl_lock(sbi, block_group), bit + i,<br />
				bh2jh(bitmap_bh)->b_committed_data);</p>
<p>		/*<br />
		 * We clear the bit in the bitmap after setting the committed<br />
		 * data bit, because this is the reverse order to that which<br />
		 * the allocator uses.<br />
		 */<br />
		BUFFER_TRACE(bitmap_bh, &laquo;clear bit&raquo;);<br />
		if (!ext3_clear_bit_atomic(sb_bgl_lock(sbi, block_group),<br />
						bit + i, bitmap_bh->b_data)) {<br />
			jbd_unlock_bh_state(bitmap_bh);<br />
			ext3_error(sb, __func__,<br />
				&laquo;bit already cleared for block &laquo;E3FSBLK,<br />
				 block + i);<br />
			jbd_lock_bh_state(bitmap_bh);<br />
			BUFFER_TRACE(bitmap_bh, &laquo;bit already cleared&raquo;);<br />
		} else {<br />
			group_freed++;<br />
		}<br />
	}<br />
	jbd_unlock_bh_state(bitmap_bh);</p>
<p>	spin_lock(sb_bgl_lock(sbi, block_group));<br />
	le16_add_cpu(&#038;desc->bg_free_blocks_count, group_freed);<br />
	spin_unlock(sb_bgl_lock(sbi, block_group));<br />
	percpu_counter_add(&#038;sbi->s_freeblocks_counter, count);</p>
<p>	/* We dirtied the bitmap block */<br />
	BUFFER_TRACE(bitmap_bh, &laquo;dirtied bitmap block&raquo;);<br />
	err = ext3_journal_dirty_metadata(handle, bitmap_bh);</p>
<p>	/* And the group descriptor block */<br />
	BUFFER_TRACE(gd_bh, &laquo;dirtied group descriptor block&raquo;);<br />
	ret = ext3_journal_dirty_metadata(handle, gd_bh);<br />
	if (!err) err = ret;<br />
	*pdquot_freed_blocks += group_freed;</p>
<p>	if (overflow &#038;&#038; !err) {<br />
		block += count;<br />
		count = overflow;<br />
		goto do_more;<br />
	}</p>
<p>error_return:<br />
	brelse(bitmap_bh);<br />
	ext3_std_error(sb, err);<br />
	return;<br />
}</p>
<p>/**<br />
 * ext3_free_blocks() &#8212; Free given blocks and update quota<br />
 * @handle:		handle for this transaction<br />
 * @inode:		inode<br />
 * @block:		start physical block to free<br />
 * @count:		number of blocks to count<br />
 */<br />
void ext3_free_blocks(handle_t *handle, struct inode *inode,<br />
			ext3_fsblk_t block, unsigned long count)<br />
{<br />
	struct super_block * sb;<br />
	unsigned long dquot_freed_blocks;</p>
<p>	sb = inode->i_sb;<br />
	if (!sb) {<br />
		printk (&raquo;ext3_free_blocks: nonexistent device&raquo;);<br />
		return;<br />
	}<br />
	ext3_free_blocks_sb(handle, sb, block, count, &#038;dquot_freed_blocks);<br />
	if (dquot_freed_blocks)<br />
		vfs_dq_free_block(inode, dquot_freed_blocks);<br />
	return;<br />
}</p>
<p>/**<br />
 * ext3_test_allocatable()<br />
 * @nr:			given allocation block group<br />
 * @bh:			bufferhead contains the bitmap of the given block group<br />
 *<br />
 * For ext3 allocations, we must not reuse any blocks which are<br />
 * allocated in the bitmap buffer&#8217;s &laquo;last committed data&raquo; copy.  This<br />
 * prevents deletes from freeing up the page for reuse until we have<br />
 * committed the delete transaction.<br />
 *<br />
 * If we didn&#8217;t do this, then deleting something and reallocating it as<br />
 * data would allow the old block to be overwritten before the<br />
 * transaction committed (because we force data to disk before commit).<br />
 * This would lead to corruption if we crashed between overwriting the<br />
 * data and committing the delete.<br />
 *<br />
 * @@@ We may want to make this allocation behaviour conditional on<br />
 * data-writes at some point, and disable it for metadata allocations or<br />
 * sync-data inodes.<br />
 */<br />
static int ext3_test_allocatable(ext3_grpblk_t nr, struct buffer_head *bh)<br />
{<br />
	int ret;<br />
	struct journal_head *jh = bh2jh(bh);</p>
<p>	if (ext3_test_bit(nr, bh->b_data))<br />
		return 0;</p>
<p>	jbd_lock_bh_state(bh);<br />
	if (!jh->b_committed_data)<br />
		ret = 1;<br />
	else<br />
		ret = !ext3_test_bit(nr, jh->b_committed_data);<br />
	jbd_unlock_bh_state(bh);<br />
	return ret;<br />
}</p>
<p>/**<br />
 * bitmap_search_next_usable_block()<br />
 * @start:		the starting block (group relative) of the search<br />
 * @bh:			bufferhead contains the block group bitmap<br />
 * @maxblocks:		the ending block (group relative) of the reservation<br />
 *<br />
 * The bitmap search &#8212; search forward alternately through the actual<br />
 * bitmap on disk and the last-committed copy in journal, until we find a<br />
 * bit free in both bitmaps.<br />
 */<br />
static ext3_grpblk_t<br />
bitmap_search_next_usable_block(ext3_grpblk_t start, struct buffer_head *bh,<br />
					ext3_grpblk_t maxblocks)<br />
{<br />
	ext3_grpblk_t next;<br />
	struct journal_head *jh = bh2jh(bh);</p>
<p>	while (start < maxblocks) {<br />
		next = ext3_find_next_zero_bit(bh->b_data, maxblocks, start);<br />
		if (next >= maxblocks)<br />
			return -1;<br />
		if (ext3_test_allocatable(next, bh))<br />
			return next;<br />
		jbd_lock_bh_state(bh);<br />
		if (jh->b_committed_data)<br />
			start = ext3_find_next_zero_bit(jh->b_committed_data,<br />
							maxblocks, next);<br />
		jbd_unlock_bh_state(bh);<br />
	}<br />
	return -1;<br />
}</p>
<p>/**<br />
 * find_next_usable_block()<br />
 * @start:		the starting block (group relative) to find next<br />
 *			allocatable block in bitmap.<br />
 * @bh:			bufferhead contains the block group bitmap<br />
 * @maxblocks:		the ending block (group relative) for the search<br />
 *<br />
 * Find an allocatable block in a bitmap.  We honor both the bitmap and<br />
 * its last-committed copy (if that exists), and perform the &laquo;most<br />
 * appropriate allocation&raquo; algorithm of looking for a free block near<br />
 * the initial goal; then for a free byte somewhere in the bitmap; then<br />
 * for any free bit in the bitmap.<br />
 */<br />
static ext3_grpblk_t<br />
find_next_usable_block(ext3_grpblk_t start, struct buffer_head *bh,<br />
			ext3_grpblk_t maxblocks)<br />
{<br />
	ext3_grpblk_t here, next;<br />
	char *p, *r;</p>
<p>	if (start > 0) {<br />
		/*<br />
		 * The goal was occupied; search forward for a free<br />
		 * block within the next XX blocks.<br />
		 *<br />
		 * end_goal is more or less random, but it has to be<br />
		 * less than EXT3_BLOCKS_PER_GROUP. Aligning up to the<br />
		 * next 64-bit boundary is simple..<br />
		 */<br />
		ext3_grpblk_t end_goal = (start + 63) &#038; ~63;<br />
		if (end_goal > maxblocks)<br />
			end_goal = maxblocks;<br />
		here = ext3_find_next_zero_bit(bh->b_data, end_goal, start);<br />
		if (here < end_goal &#038;&#038; ext3_test_allocatable(here, bh))<br />
			return here;<br />
		ext3_debug("Bit not found near goal\n");<br />
	}</p>
<p>	here = start;<br />
	if (here < 0)<br />
		here = 0;</p>
<p>	p = ((char *)bh->b_data) + (here >> 3);<br />
	r = memscan(p, 0, ((maxblocks + 7) >> 3) &#8211; (here >> 3));<br />
	next = (r &#8211; ((char *)bh->b_data)) << 3;</p>
<p>	if (next < maxblocks &#038;&#038; next >= start &#038;&#038; ext3_test_allocatable(next, bh))<br />
		return next;</p>
<p>	/*<br />
	 * The bitmap search &#8212; search forward alternately through the actual<br />
	 * bitmap and the last-committed copy until we find a bit free in<br />
	 * both<br />
	 */<br />
	here = bitmap_search_next_usable_block(here, bh, maxblocks);<br />
	return here;<br />
}</p>
<p>/**<br />
 * claim_block()<br />
 * @block:		the free block (group relative) to allocate<br />
 * @bh:			the bufferhead containts the block group bitmap<br />
 *<br />
 * We think we can allocate this block in this bitmap.  Try to set the bit.<br />
 * If that succeeds then check that nobody has allocated and then freed the<br />
 * block since we saw that is was not marked in b_committed_data.  If it _was_<br />
 * allocated and freed then clear the bit in the bitmap again and return<br />
 * zero (failure).<br />
 */<br />
static inline int<br />
claim_block(spinlock_t *lock, ext3_grpblk_t block, struct buffer_head *bh)<br />
{<br />
	struct journal_head *jh = bh2jh(bh);<br />
	int ret;</p>
<p>	if (ext3_set_bit_atomic(lock, block, bh->b_data))<br />
		return 0;<br />
	jbd_lock_bh_state(bh);<br />
	if (jh->b_committed_data &#038;&#038; ext3_test_bit(block,jh->b_committed_data)) {<br />
		ext3_clear_bit_atomic(lock, block, bh->b_data);<br />
		ret = 0;<br />
	} else {<br />
		ret = 1;<br />
	}<br />
	jbd_unlock_bh_state(bh);<br />
	return ret;<br />
}</p>
<p>/**<br />
 * ext3_try_to_allocate()<br />
 * @sb:			superblock<br />
 * @handle:		handle to this transaction<br />
 * @group:		given allocation block group<br />
 * @bitmap_bh:		bufferhead holds the block bitmap<br />
 * @grp_goal:		given target block within the group<br />
 * @count:		target number of blocks to allocate<br />
 * @my_rsv:		reservation window<br />
 *<br />
 * Attempt to allocate blocks within a give range. Set the range of allocation<br />
 * first, then find the first free bit(s) from the bitmap (within the range),<br />
 * and at last, allocate the blocks by claiming the found free bit as allocated.<br />
 *<br />
 * To set the range of this allocation:<br />
 *	if there is a reservation window, only try to allocate block(s) from the<br />
 *	file&#8217;s own reservation window;<br />
 *	Otherwise, the allocation range starts from the give goal block, ends at<br />
 *	the block group&#8217;s last block.<br />
 *<br />
 * If we failed to allocate the desired block then we may end up crossing to a<br />
 * new bitmap.  In that case we must release write access to the old one via<br />
 * ext3_journal_release_buffer(), else we&#8217;ll run out of credits.<br />
 */<br />
static ext3_grpblk_t<br />
ext3_try_to_allocate(struct super_block *sb, handle_t *handle, int group,<br />
			struct buffer_head *bitmap_bh, ext3_grpblk_t grp_goal,<br />
			unsigned long *count, struct ext3_reserve_window *my_rsv)<br />
{<br />
	ext3_fsblk_t group_first_block;<br />
	ext3_grpblk_t start, end;<br />
	unsigned long num = 0;</p>
<p>	/* we do allocation within the reservation window if we have a window */<br />
	if (my_rsv) {<br />
		group_first_block = ext3_group_first_block_no(sb, group);<br />
		if (my_rsv->_rsv_start >= group_first_block)<br />
			start = my_rsv->_rsv_start &#8211; group_first_block;<br />
		else<br />
			/* reservation window cross group boundary */<br />
			start = 0;<br />
		end = my_rsv->_rsv_end &#8211; group_first_block + 1;<br />
		if (end > EXT3_BLOCKS_PER_GROUP(sb))<br />
			/* reservation window crosses group boundary */<br />
			end = EXT3_BLOCKS_PER_GROUP(sb);<br />
		if ((start <= grp_goal) &#038;&#038; (grp_goal < end))<br />
			start = grp_goal;<br />
		else<br />
			grp_goal = -1;<br />
	} else {<br />
		if (grp_goal > 0)<br />
			start = grp_goal;<br />
		else<br />
			start = 0;<br />
		end = EXT3_BLOCKS_PER_GROUP(sb);<br />
	}</p>
<p>	BUG_ON(start > EXT3_BLOCKS_PER_GROUP(sb));</p>
<p>repeat:<br />
	if (grp_goal < 0 || !ext3_test_allocatable(grp_goal, bitmap_bh)) {<br />
		grp_goal = find_next_usable_block(start, bitmap_bh, end);<br />
		if (grp_goal < 0)<br />
			goto fail_access;<br />
		if (!my_rsv) {<br />
			int i;</p>
<p>			for (i = 0; i < 7 &#038;&#038; grp_goal > start &#038;&#038;<br />
					ext3_test_allocatable(grp_goal &#8211; 1,<br />
								bitmap_bh);<br />
					i++, grp_goal&#8211;)<br />
				;<br />
		}<br />
	}<br />
	start = grp_goal;</p>
<p>	if (!claim_block(sb_bgl_lock(EXT3_SB(sb), group),<br />
		grp_goal, bitmap_bh)) {<br />
		/*<br />
		 * The block was allocated by another thread, or it was<br />
		 * allocated and then freed by another thread<br />
		 */<br />
		start++;<br />
		grp_goal++;<br />
		if (start >= end)<br />
			goto fail_access;<br />
		goto repeat;<br />
	}<br />
	num++;<br />
	grp_goal++;<br />
	while (num < *count &#038;&#038; grp_goal < end<br />
		&#038;&#038; ext3_test_allocatable(grp_goal, bitmap_bh)<br />
		&#038;&#038; claim_block(sb_bgl_lock(EXT3_SB(sb), group),<br />
				grp_goal, bitmap_bh)) {<br />
		num++;<br />
		grp_goal++;<br />
	}<br />
	*count = num;<br />
	return grp_goal - num;<br />
fail_access:<br />
	*count = num;<br />
	return -1;<br />
}</p>
<p>/**<br />
 *	find_next_reservable_window():<br />
 *		find a reservable space within the given range.<br />
 *		It does not allocate the reservation window for now:<br />
 *		alloc_new_reservation() will do the work later.<br />
 *<br />
 *	@search_head: the head of the searching list;<br />
 *		This is not necessarily the list head of the whole filesystem<br />
 *<br />
 *		We have both head and start_block to assist the search<br />
 *		for the reservable space. The list starts from head,<br />
 *		but we will shift to the place where start_block is,<br />
 *		then start from there, when looking for a reservable space.<br />
 *<br />
 *	@size: the target new reservation window size<br />
 *<br />
 *	@group_first_block: the first block we consider to start<br />
 *			the real search from<br />
 *<br />
 *	@last_block:<br />
 *		the maximum block number that our goal reservable space<br />
 *		could start from. This is normally the last block in this<br />
 *		group. The search will end when we found the start of next<br />
 *		possible reservable space is out of this boundary.<br />
 *		This could handle the cross boundary reservation window<br />
 *		request.<br />
 *<br />
 *	basically we search from the given range, rather than the whole<br />
 *	reservation double linked list, (start_block, last_block)<br />
 *	to find a free region that is of my size and has not<br />
 *	been reserved.<br />
 *<br />
 */<br />
static int find_next_reservable_window(<br />
				struct ext3_reserve_window_node *search_head,<br />
				struct ext3_reserve_window_node *my_rsv,<br />
				struct super_block * sb,<br />
				ext3_fsblk_t start_block,<br />
				ext3_fsblk_t last_block)<br />
{<br />
	struct rb_node *next;<br />
	struct ext3_reserve_window_node *rsv, *prev;<br />
	ext3_fsblk_t cur;<br />
	int size = my_rsv->rsv_goal_size;</p>
<p>	/* TODO: make the start of the reservation window byte-aligned */<br />
	/* cur = *start_block &#038; ~7;*/<br />
	cur = start_block;<br />
	rsv = search_head;<br />
	if (!rsv)<br />
		return -1;</p>
<p>	while (1) {<br />
		if (cur <= rsv->rsv_end)<br />
			cur = rsv->rsv_end + 1;</p>
<p>		/* TODO?<br />
		 * in the case we could not find a reservable space<br />
		 * that is what is expected, during the re-search, we could<br />
		 * remember what&#8217;s the largest reservable space we could have<br />
		 * and return that one.<br />
		 *<br />
		 * For now it will fail if we could not find the reservable<br />
		 * space with expected-size (or more)&#8230;<br />
		 */<br />
		if (cur > last_block)<br />
			return -1;		/* fail */</p>
<p>		prev = rsv;<br />
		next = rb_next(&#038;rsv->rsv_node);<br />
		rsv = rb_entry(next,struct ext3_reserve_window_node,rsv_node);</p>
<p>		/*<br />
		 * Reached the last reservation, we can just append to the<br />
		 * previous one.<br />
		 */<br />
		if (!next)<br />
			break;</p>
<p>		if (cur + size <= rsv->rsv_start) {<br />
			/*<br />
			 * Found a reserveable space big enough.  We could<br />
			 * have a reservation across the group boundary here<br />
			 */<br />
			break;<br />
		}<br />
	}<br />
	/*<br />
	 * we come here either :<br />
	 * when we reach the end of the whole list,<br />
	 * and there is empty reservable space after last entry in the list.<br />
	 * append it to the end of the list.<br />
	 *<br />
	 * or we found one reservable space in the middle of the list,<br />
	 * return the reservation window that we could append to.<br />
	 * succeed.<br />
	 */</p>
<p>	if ((prev != my_rsv) &#038;&#038; (!rsv_is_empty(&#038;my_rsv->rsv_window)))<br />
		rsv_window_remove(sb, my_rsv);</p>
<p>	/*<br />
	 * Let&#8217;s book the whole avaliable window for now.  We will check the<br />
	 * disk bitmap later and then, if there are free blocks then we adjust<br />
	 * the window size if it&#8217;s larger than requested.<br />
	 * Otherwise, we will remove this node from the tree next time<br />
	 * call find_next_reservable_window.<br />
	 */<br />
	my_rsv->rsv_start = cur;<br />
	my_rsv->rsv_end = cur + size &#8211; 1;<br />
	my_rsv->rsv_alloc_hit = 0;</p>
<p>	if (prev != my_rsv)<br />
		ext3_rsv_window_add(sb, my_rsv);</p>
<p>	return 0;<br />
}</p>
<p>/**<br />
 *	alloc_new_reservation()&#8211;allocate a new reservation window<br />
 *<br />
 *		To make a new reservation, we search part of the filesystem<br />
 *		reservation list (the list that inside the group). We try to<br />
 *		allocate a new reservation window near the allocation goal,<br />
 *		or the beginning of the group, if there is no goal.<br />
 *<br />
 *		We first find a reservable space after the goal, then from<br />
 *		there, we check the bitmap for the first free block after<br />
 *		it. If there is no free block until the end of group, then the<br />
 *		whole group is full, we failed. Otherwise, check if the free<br />
 *		block is inside the expected reservable space, if so, we<br />
 *		succeed.<br />
 *		If the first free block is outside the reservable space, then<br />
 *		start from the first free block, we search for next available<br />
 *		space, and go on.<br />
 *<br />
 *	on succeed, a new reservation will be found and inserted into the list<br />
 *	It contains at least one free block, and it does not overlap with other<br />
 *	reservation windows.<br />
 *<br />
 *	failed: we failed to find a reservation window in this group<br />
 *<br />
 *	@rsv: the reservation<br />
 *<br />
 *	@grp_goal: The goal (group-relative).  It is where the search for a<br />
 *		free reservable space should start from.<br />
 *		if we have a grp_goal(grp_goal >0 ), then start from there,<br />
 *		no grp_goal(grp_goal = -1), we start from the first block<br />
 *		of the group.<br />
 *<br />
 *	@sb: the super block<br />
 *	@group: the group we are trying to allocate in<br />
 *	@bitmap_bh: the block group block bitmap<br />
 *<br />
 */<br />
static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,<br />
		ext3_grpblk_t grp_goal, struct super_block *sb,<br />
		unsigned int group, struct buffer_head *bitmap_bh)<br />
{<br />
	struct ext3_reserve_window_node *search_head;<br />
	ext3_fsblk_t group_first_block, group_end_block, start_block;<br />
	ext3_grpblk_t first_free_block;<br />
	struct rb_root *fs_rsv_root = &#038;EXT3_SB(sb)->s_rsv_window_root;<br />
	unsigned long size;<br />
	int ret;<br />
	spinlock_t *rsv_lock = &#038;EXT3_SB(sb)->s_rsv_window_lock;</p>
<p>	group_first_block = ext3_group_first_block_no(sb, group);<br />
	group_end_block = group_first_block + (EXT3_BLOCKS_PER_GROUP(sb) &#8211; 1);</p>
<p>	if (grp_goal < 0)<br />
		start_block = group_first_block;<br />
	else<br />
		start_block = grp_goal + group_first_block;</p>
<p>	size = my_rsv->rsv_goal_size;</p>
<p>	if (!rsv_is_empty(&#038;my_rsv->rsv_window)) {<br />
		/*<br />
		 * if the old reservation is cross group boundary<br />
		 * and if the goal is inside the old reservation window,<br />
		 * we will come here when we just failed to allocate from<br />
		 * the first part of the window. We still have another part<br />
		 * that belongs to the next group. In this case, there is no<br />
		 * point to discard our window and try to allocate a new one<br />
		 * in this group(which will fail). we should<br />
		 * keep the reservation window, just simply move on.<br />
		 *<br />
		 * Maybe we could shift the start block of the reservation<br />
		 * window to the first block of next group.<br />
		 */</p>
<p>		if ((my_rsv->rsv_start <= group_end_block) &#038;&#038;<br />
				(my_rsv->rsv_end > group_end_block) &#038;&#038;<br />
				(start_block >= my_rsv->rsv_start))<br />
			return -1;</p>
<p>		if ((my_rsv->rsv_alloc_hit ><br />
		     (my_rsv->rsv_end &#8211; my_rsv->rsv_start + 1) / 2)) {<br />
			/*<br />
			 * if the previously allocation hit ratio is<br />
			 * greater than 1/2, then we double the size of<br />
			 * the reservation window the next time,<br />
			 * otherwise we keep the same size window<br />
			 */<br />
			size = size * 2;<br />
			if (size > EXT3_MAX_RESERVE_BLOCKS)<br />
				size = EXT3_MAX_RESERVE_BLOCKS;<br />
			my_rsv->rsv_goal_size= size;<br />
		}<br />
	}</p>
<p>	spin_lock(rsv_lock);<br />
	/*<br />
	 * shift the search start to the window near the goal block<br />
	 */<br />
	search_head = search_reserve_window(fs_rsv_root, start_block);</p>
<p>	/*<br />
	 * find_next_reservable_window() simply finds a reservable window<br />
	 * inside the given range(start_block, group_end_block).<br />
	 *<br />
	 * To make sure the reservation window has a free bit inside it, we<br />
	 * need to check the bitmap after we found a reservable window.<br />
	 */<br />
retry:<br />
	ret = find_next_reservable_window(search_head, my_rsv, sb,<br />
						start_block, group_end_block);</p>
<p>	if (ret == -1) {<br />
		if (!rsv_is_empty(&#038;my_rsv->rsv_window))<br />
			rsv_window_remove(sb, my_rsv);<br />
		spin_unlock(rsv_lock);<br />
		return -1;<br />
	}</p>
<p>	/*<br />
	 * On success, find_next_reservable_window() returns the<br />
	 * reservation window where there is a reservable space after it.<br />
	 * Before we reserve this reservable space, we need<br />
	 * to make sure there is at least a free block inside this region.<br />
	 *<br />
	 * searching the first free bit on the block bitmap and copy of<br />
	 * last committed bitmap alternatively, until we found a allocatable<br />
	 * block. Search start from the start block of the reservable space<br />
	 * we just found.<br />
	 */<br />
	spin_unlock(rsv_lock);<br />
	first_free_block = bitmap_search_next_usable_block(<br />
			my_rsv->rsv_start &#8211; group_first_block,<br />
			bitmap_bh, group_end_block &#8211; group_first_block + 1);</p>
<p>	if (first_free_block < 0) {<br />
		/*<br />
		 * no free block left on the bitmap, no point<br />
		 * to reserve the space. return failed.<br />
		 */<br />
		spin_lock(rsv_lock);<br />
		if (!rsv_is_empty(&#038;my_rsv->rsv_window))<br />
			rsv_window_remove(sb, my_rsv);<br />
		spin_unlock(rsv_lock);<br />
		return -1;		/* failed */<br />
	}</p>
<p>	start_block = first_free_block + group_first_block;<br />
	/*<br />
	 * check if the first free block is within the<br />
	 * free space we just reserved<br />
	 */<br />
	if (start_block >= my_rsv->rsv_start &#038;&#038; start_block <= my_rsv->rsv_end)<br />
		return 0;		/* success */<br />
	/*<br />
	 * if the first free bit we found is out of the reservable space<br />
	 * continue search for next reservable space,<br />
	 * start from where the free block is,<br />
	 * we also shift the list head to where we stopped last time<br />
	 */<br />
	search_head = my_rsv;<br />
	spin_lock(rsv_lock);<br />
	goto retry;<br />
}</p>
<p>/**<br />
 * try_to_extend_reservation()<br />
 * @my_rsv:		given reservation window<br />
 * @sb:			super block<br />
 * @size:		the delta to extend<br />
 *<br />
 * Attempt to expand the reservation window large enough to have<br />
 * required number of free blocks<br />
 *<br />
 * Since ext3_try_to_allocate() will always allocate blocks within<br />
 * the reservation window range, if the window size is too small,<br />
 * multiple blocks allocation has to stop at the end of the reservation<br />
 * window. To make this more efficient, given the total number of<br />
 * blocks needed and the current size of the window, we try to<br />
 * expand the reservation window size if necessary on a best-effort<br />
 * basis before ext3_new_blocks() tries to allocate blocks,<br />
 */<br />
static void try_to_extend_reservation(struct ext3_reserve_window_node *my_rsv,<br />
			struct super_block *sb, int size)<br />
{<br />
	struct ext3_reserve_window_node *next_rsv;<br />
	struct rb_node *next;<br />
	spinlock_t *rsv_lock = &#038;EXT3_SB(sb)->s_rsv_window_lock;</p>
<p>	if (!spin_trylock(rsv_lock))<br />
		return;</p>
<p>	next = rb_next(&#038;my_rsv->rsv_node);</p>
<p>	if (!next)<br />
		my_rsv->rsv_end += size;<br />
	else {<br />
		next_rsv = rb_entry(next, struct ext3_reserve_window_node, rsv_node);</p>
<p>		if ((next_rsv->rsv_start &#8211; my_rsv->rsv_end &#8211; 1) >= size)<br />
			my_rsv->rsv_end += size;<br />
		else<br />
			my_rsv->rsv_end = next_rsv->rsv_start &#8211; 1;<br />
	}<br />
	spin_unlock(rsv_lock);<br />
}</p>
<p>/**<br />
 * ext3_try_to_allocate_with_rsv()<br />
 * @sb:			superblock<br />
 * @handle:		handle to this transaction<br />
 * @group:		given allocation block group<br />
 * @bitmap_bh:		bufferhead holds the block bitmap<br />
 * @grp_goal:		given target block within the group<br />
 * @count:		target number of blocks to allocate<br />
 * @my_rsv:		reservation window<br />
 * @errp:		pointer to store the error code<br />
 *<br />
 * This is the main function used to allocate a new block and its reservation<br />
 * window.<br />
 *<br />
 * Each time when a new block allocation is need, first try to allocate from<br />
 * its own reservation.  If it does not have a reservation window, instead of<br />
 * looking for a free bit on bitmap first, then look up the reservation list to<br />
 * see if it is inside somebody else&#8217;s reservation window, we try to allocate a<br />
 * reservation window for it starting from the goal first. Then do the block<br />
 * allocation within the reservation window.<br />
 *<br />
 * This will avoid keeping on searching the reservation list again and<br />
 * again when somebody is looking for a free block (without<br />
 * reservation), and there are lots of free blocks, but they are all<br />
 * being reserved.<br />
 *<br />
 * We use a red-black tree for the per-filesystem reservation list.<br />
 *<br />
 */<br />
static ext3_grpblk_t<br />
ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,<br />
			unsigned int group, struct buffer_head *bitmap_bh,<br />
			ext3_grpblk_t grp_goal,<br />
			struct ext3_reserve_window_node * my_rsv,<br />
			unsigned long *count, int *errp)<br />
{<br />
	ext3_fsblk_t group_first_block, group_last_block;<br />
	ext3_grpblk_t ret = 0;<br />
	int fatal;<br />
	unsigned long num = *count;</p>
<p>	*errp = 0;</p>
<p>	/*<br />
	 * Make sure we use undo access for the bitmap, because it is critical<br />
	 * that we do the frozen_data COW on bitmap buffers in all cases even<br />
	 * if the buffer is in BJ_Forget state in the committing transaction.<br />
	 */<br />
	BUFFER_TRACE(bitmap_bh, &laquo;get undo access for new block&raquo;);<br />
	fatal = ext3_journal_get_undo_access(handle, bitmap_bh);<br />
	if (fatal) {<br />
		*errp = fatal;<br />
		return -1;<br />
	}</p>
<p>	/*<br />
	 * we don&#8217;t deal with reservation when<br />
	 * filesystem is mounted without reservation<br />
	 * or the file is not a regular file<br />
	 * or last attempt to allocate a block with reservation turned on failed<br />
	 */<br />
	if (my_rsv == NULL ) {<br />
		ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh,<br />
						grp_goal, count, NULL);<br />
		goto out;<br />
	}<br />
	/*<br />
	 * grp_goal is a group relative block number (if there is a goal)<br />
	 * 0 <= grp_goal < EXT3_BLOCKS_PER_GROUP(sb)<br />
	 * first block is a filesystem wide block number<br />
	 * first block is the block number of the first block in this group<br />
	 */<br />
	group_first_block = ext3_group_first_block_no(sb, group);<br />
	group_last_block = group_first_block + (EXT3_BLOCKS_PER_GROUP(sb) - 1);</p>
<p>	/*<br />
	 * Basically we will allocate a new block from inode's reservation<br />
	 * window.<br />
	 *<br />
	 * We need to allocate a new reservation window, if:<br />
	 * a) inode does not have a reservation window; or<br />
	 * b) last attempt to allocate a block from existing reservation<br />
	 *    failed; or<br />
	 * c) we come here with a goal and with a reservation window<br />
	 *<br />
	 * We do not need to allocate a new reservation window if we come here<br />
	 * at the beginning with a goal and the goal is inside the window, or<br />
	 * we don't have a goal but already have a reservation window.<br />
	 * then we could go to allocate from the reservation window directly.<br />
	 */<br />
	while (1) {<br />
		if (rsv_is_empty(&#038;my_rsv->rsv_window) || (ret < 0) ||<br />
			!goal_in_my_reservation(&#038;my_rsv->rsv_window,<br />
						grp_goal, group, sb)) {<br />
			if (my_rsv->rsv_goal_size < *count)<br />
				my_rsv->rsv_goal_size = *count;<br />
			ret = alloc_new_reservation(my_rsv, grp_goal, sb,<br />
							group, bitmap_bh);<br />
			if (ret < 0)<br />
				break;			/* failed */</p>
<p>			if (!goal_in_my_reservation(&#038;my_rsv->rsv_window,<br />
							grp_goal, group, sb))<br />
				grp_goal = -1;<br />
		} else if (grp_goal >= 0) {<br />
			int curr = my_rsv->rsv_end -<br />
					(grp_goal + group_first_block) + 1;</p>
<p>			if (curr < *count)<br />
				try_to_extend_reservation(my_rsv, sb,<br />
							*count - curr);<br />
		}</p>
<p>		if ((my_rsv->rsv_start > group_last_block) ||<br />
				(my_rsv->rsv_end < group_first_block)) {<br />
			rsv_window_dump(&#038;EXT3_SB(sb)->s_rsv_window_root, 1);<br />
			BUG();<br />
		}<br />
		ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh,<br />
					   grp_goal, &#038;num, &#038;my_rsv->rsv_window);<br />
		if (ret >= 0) {<br />
			my_rsv->rsv_alloc_hit += num;<br />
			*count = num;<br />
			break;				/* succeed */<br />
		}<br />
		num = *count;<br />
	}<br />
out:<br />
	if (ret >= 0) {<br />
		BUFFER_TRACE(bitmap_bh, &laquo;journal_dirty_metadata for &raquo;<br />
					&laquo;bitmap block&raquo;);<br />
		fatal = ext3_journal_dirty_metadata(handle, bitmap_bh);<br />
		if (fatal) {<br />
			*errp = fatal;<br />
			return -1;<br />
		}<br />
		return ret;<br />
	}</p>
<p>	BUFFER_TRACE(bitmap_bh, &laquo;journal_release_buffer&raquo;);<br />
	ext3_journal_release_buffer(handle, bitmap_bh);<br />
	return ret;<br />
}</p>
<p>/**<br />
 * ext3_has_free_blocks()<br />
 * @sbi:		in-core super block structure.<br />
 *<br />
 * Check if filesystem has at least 1 free block available for allocation.<br />
 */<br />
static int ext3_has_free_blocks(struct ext3_sb_info *sbi)<br />
{<br />
	ext3_fsblk_t free_blocks, root_blocks;</p>
<p>	free_blocks = percpu_counter_read_positive(&#038;sbi->s_freeblocks_counter);<br />
	root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);<br />
	if (free_blocks < root_blocks + 1 &#038;&#038; !capable(CAP_SYS_RESOURCE) &#038;&#038;<br />
		sbi->s_resuid != current_fsuid() &#038;&#038;<br />
		(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {<br />
		return 0;<br />
	}<br />
	return 1;<br />
}</p>
<p>/**<br />
 * ext3_should_retry_alloc()<br />
 * @sb:			super block<br />
 * @retries		number of attemps has been made<br />
 *<br />
 * ext3_should_retry_alloc() is called when ENOSPC is returned, and if<br />
 * it is profitable to retry the operation, this function will wait<br />
 * for the current or commiting transaction to complete, and then<br />
 * return TRUE.<br />
 *<br />
 * if the total number of retries exceed three times, return FALSE.<br />
 */<br />
int ext3_should_retry_alloc(struct super_block *sb, int *retries)<br />
{<br />
	if (!ext3_has_free_blocks(EXT3_SB(sb)) || (*retries)++ > 3)<br />
		return 0;</p>
<p>	jbd_debug(1, &laquo;%s: retrying operation after ENOSPC\n&raquo;, sb->s_id);</p>
<p>	return journal_force_commit_nested(EXT3_SB(sb)->s_journal);<br />
}</p>
<p>/**<br />
 * ext3_new_blocks() &#8212; core block(s) allocation function<br />
 * @handle:		handle to this transaction<br />
 * @inode:		file inode<br />
 * @goal:		given target block(filesystem wide)<br />
 * @count:		target number of blocks to allocate<br />
 * @errp:		error code<br />
 *<br />
 * ext3_new_blocks uses a goal block to assist allocation.  It tries to<br />
 * allocate block(s) from the block group contains the goal block first. If that<br />
 * fails, it will try to allocate block(s) from other block groups without<br />
 * any specific goal block.<br />
 *<br />
 */<br />
ext3_fsblk_t ext3_new_blocks(handle_t *handle, struct inode *inode,<br />
			ext3_fsblk_t goal, unsigned long *count, int *errp)<br />
{<br />
	struct buffer_head *bitmap_bh = NULL;<br />
	struct buffer_head *gdp_bh;<br />
	int group_no;<br />
	int goal_group;<br />
	ext3_grpblk_t grp_target_blk;	/* blockgroup relative goal block */<br />
	ext3_grpblk_t grp_alloc_blk;	/* blockgroup-relative allocated block*/<br />
	ext3_fsblk_t ret_block;		/* filesyetem-wide allocated block */<br />
	int bgi;			/* blockgroup iteration index */<br />
	int fatal = 0, err;<br />
	int performed_allocation = 0;<br />
	ext3_grpblk_t free_blocks;	/* number of free blocks in a group */<br />
	struct super_block *sb;<br />
	struct ext3_group_desc *gdp;<br />
	struct ext3_super_block *es;<br />
	struct ext3_sb_info *sbi;<br />
	struct ext3_reserve_window_node *my_rsv = NULL;<br />
	struct ext3_block_alloc_info *block_i;<br />
	unsigned short windowsz = 0;<br />
#ifdef EXT3FS_DEBUG<br />
	static int goal_hits, goal_attempts;<br />
#endif<br />
	unsigned long ngroups;<br />
	unsigned long num = *count;</p>
<p>	*errp = -ENOSPC;<br />
	sb = inode->i_sb;<br />
	if (!sb) {<br />
		printk(&raquo;ext3_new_block: nonexistent device&raquo;);<br />
		return 0;<br />
	}</p>
<p>	/*<br />
	 * Check quota for allocation of this block.<br />
	 */<br />
	if (vfs_dq_alloc_block(inode, num)) {<br />
		*errp = -EDQUOT;<br />
		return 0;<br />
	}</p>
<p>	sbi = EXT3_SB(sb);<br />
	es = EXT3_SB(sb)->s_es;<br />
	ext3_debug(&raquo;goal=%lu.\n&raquo;, goal);<br />
	/*<br />
	 * Allocate a block from reservation only when<br />
	 * filesystem is mounted with reservation(default,-o reservation), and<br />
	 * it&#8217;s a regular file, and<br />
	 * the desired window size is greater than 0 (One could use ioctl<br />
	 * command EXT3_IOC_SETRSVSZ to set the window size to 0 to turn off<br />
	 * reservation on that particular file)<br />
	 */<br />
	block_i = EXT3_I(inode)->i_block_alloc_info;<br />
	if (block_i &#038;&#038; ((windowsz = block_i->rsv_window_node.rsv_goal_size) > 0))<br />
		my_rsv = &#038;block_i->rsv_window_node;</p>
<p>	if (!ext3_has_free_blocks(sbi)) {<br />
		*errp = -ENOSPC;<br />
		goto out;<br />
	}</p>
<p>	/*<br />
	 * First, test whether the goal block is free.<br />
	 */<br />
	if (goal < le32_to_cpu(es->s_first_data_block) ||<br />
	    goal >= le32_to_cpu(es->s_blocks_count))<br />
		goal = le32_to_cpu(es->s_first_data_block);<br />
	group_no = (goal &#8211; le32_to_cpu(es->s_first_data_block)) /<br />
			EXT3_BLOCKS_PER_GROUP(sb);<br />
	goal_group = group_no;<br />
retry_alloc:<br />
	gdp = ext3_get_group_desc(sb, group_no, &#038;gdp_bh);<br />
	if (!gdp)<br />
		goto io_error;</p>
<p>	free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);<br />
	/*<br />
	 * if there is not enough free blocks to make a new resevation<br />
	 * turn off reservation for this allocation<br />
	 */<br />
	if (my_rsv &#038;&#038; (free_blocks < windowsz)<br />
		&#038;&#038; (free_blocks > 0)<br />
		&#038;&#038; (rsv_is_empty(&#038;my_rsv->rsv_window)))<br />
		my_rsv = NULL;</p>
<p>	if (free_blocks > 0) {<br />
		grp_target_blk = ((goal &#8211; le32_to_cpu(es->s_first_data_block)) %<br />
				EXT3_BLOCKS_PER_GROUP(sb));<br />
		bitmap_bh = read_block_bitmap(sb, group_no);<br />
		if (!bitmap_bh)<br />
			goto io_error;<br />
		grp_alloc_blk = ext3_try_to_allocate_with_rsv(sb, handle,<br />
					group_no, bitmap_bh, grp_target_blk,<br />
					my_rsv,	&#038;num, &#038;fatal);<br />
		if (fatal)<br />
			goto out;<br />
		if (grp_alloc_blk >= 0)<br />
			goto allocated;<br />
	}</p>
<p>	ngroups = EXT3_SB(sb)->s_groups_count;<br />
	smp_rmb();</p>
<p>	/*<br />
	 * Now search the rest of the groups.  We assume that<br />
	 * group_no and gdp correctly point to the last group visited.<br />
	 */<br />
	for (bgi = 0; bgi < ngroups; bgi++) {<br />
		group_no++;<br />
		if (group_no >= ngroups)<br />
			group_no = 0;<br />
		gdp = ext3_get_group_desc(sb, group_no, &#038;gdp_bh);<br />
		if (!gdp)<br />
			goto io_error;<br />
		free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);<br />
		/*<br />
		 * skip this group if the number of<br />
		 * free blocks is less than half of the reservation<br />
		 * window size.<br />
		 */<br />
		if (my_rsv &#038;&#038; (free_blocks <= (windowsz/2)))<br />
			continue;</p>
<p>		brelse(bitmap_bh);<br />
		bitmap_bh = read_block_bitmap(sb, group_no);<br />
		if (!bitmap_bh)<br />
			goto io_error;<br />
		/*<br />
		 * try to allocate block(s) from this group, without a goal(-1).<br />
		 */<br />
		grp_alloc_blk = ext3_try_to_allocate_with_rsv(sb, handle,<br />
					group_no, bitmap_bh, -1, my_rsv,<br />
					&#038;num, &#038;fatal);<br />
		if (fatal)<br />
			goto out;<br />
		if (grp_alloc_blk >= 0)<br />
			goto allocated;<br />
	}<br />
	/*<br />
	 * We may end up a bogus ealier ENOSPC error due to<br />
	 * filesystem is &laquo;full&raquo; of reservations, but<br />
	 * there maybe indeed free blocks avaliable on disk<br />
	 * In this case, we just forget about the reservations<br />
	 * just do block allocation as without reservations.<br />
	 */<br />
	if (my_rsv) {<br />
		my_rsv = NULL;<br />
		windowsz = 0;<br />
		group_no = goal_group;<br />
		goto retry_alloc;<br />
	}<br />
	/* No space left on the device */<br />
	*errp = -ENOSPC;<br />
	goto out;</p>
<p>allocated:</p>
<p>	ext3_debug(&raquo;using block group %d(%d)\n&raquo;,<br />
			group_no, gdp->bg_free_blocks_count);</p>
<p>	BUFFER_TRACE(gdp_bh, &laquo;get_write_access&raquo;);<br />
	fatal = ext3_journal_get_write_access(handle, gdp_bh);<br />
	if (fatal)<br />
		goto out;</p>
<p>	ret_block = grp_alloc_blk + ext3_group_first_block_no(sb, group_no);</p>
<p>	if (in_range(le32_to_cpu(gdp->bg_block_bitmap), ret_block, num) ||<br />
	    in_range(le32_to_cpu(gdp->bg_inode_bitmap), ret_block, num) ||<br />
	    in_range(ret_block, le32_to_cpu(gdp->bg_inode_table),<br />
		      EXT3_SB(sb)->s_itb_per_group) ||<br />
	    in_range(ret_block + num &#8211; 1, le32_to_cpu(gdp->bg_inode_table),<br />
		      EXT3_SB(sb)->s_itb_per_group)) {<br />
		ext3_error(sb, &laquo;ext3_new_block&raquo;,<br />
			    &laquo;Allocating block in system zone &#8211; &raquo;<br />
			    &laquo;blocks from &laquo;E3FSBLK&raquo;, length %lu&raquo;,<br />
			     ret_block, num);<br />
		/*<br />
		 * claim_block() marked the blocks we allocated as in use. So we<br />
		 * may want to selectively mark some of the blocks as free.<br />
		 */<br />
		goto retry_alloc;<br />
	}</p>
<p>	performed_allocation = 1;</p>
<p>#ifdef CONFIG_JBD_DEBUG<br />
	{<br />
		struct buffer_head *debug_bh;</p>
<p>		/* Record bitmap buffer state in the newly allocated block */<br />
		debug_bh = sb_find_get_block(sb, ret_block);<br />
		if (debug_bh) {<br />
			BUFFER_TRACE(debug_bh, &laquo;state when allocated&raquo;);<br />
			BUFFER_TRACE2(debug_bh, bitmap_bh, &laquo;bitmap state&raquo;);<br />
			brelse(debug_bh);<br />
		}<br />
	}<br />
	jbd_lock_bh_state(bitmap_bh);<br />
	spin_lock(sb_bgl_lock(sbi, group_no));<br />
	if (buffer_jbd(bitmap_bh) &#038;&#038; bh2jh(bitmap_bh)->b_committed_data) {<br />
		int i;</p>
<p>		for (i = 0; i < num; i++) {<br />
			if (ext3_test_bit(grp_alloc_blk+i,<br />
					bh2jh(bitmap_bh)->b_committed_data)) {<br />
				printk(&raquo;%s: block was unexpectedly set in &raquo;<br />
					&laquo;b_committed_data\n&raquo;, __func__);<br />
			}<br />
		}<br />
	}<br />
	ext3_debug(&raquo;found bit %d\n&raquo;, grp_alloc_blk);<br />
	spin_unlock(sb_bgl_lock(sbi, group_no));<br />
	jbd_unlock_bh_state(bitmap_bh);<br />
#endif</p>
<p>	if (ret_block + num &#8211; 1 >= le32_to_cpu(es->s_blocks_count)) {<br />
		ext3_error(sb, &laquo;ext3_new_block&raquo;,<br />
			    &laquo;block(&raquo;E3FSBLK&raquo;) >= blocks count(%d) &#8211; &raquo;<br />
			    &laquo;block_group = %d, es == %p &laquo;, ret_block,<br />
			le32_to_cpu(es->s_blocks_count), group_no, es);<br />
		goto out;<br />
	}</p>
<p>	/*<br />
	 * It is up to the caller to add the new buffer to a journal<br />
	 * list of some description.  We don&#8217;t know in advance whether<br />
	 * the caller wants to use it as metadata or data.<br />
	 */<br />
	ext3_debug(&raquo;allocating block %lu. Goal hits %d of %d.\n&raquo;,<br />
			ret_block, goal_hits, goal_attempts);</p>
<p>	spin_lock(sb_bgl_lock(sbi, group_no));<br />
	le16_add_cpu(&#038;gdp->bg_free_blocks_count, -num);<br />
	spin_unlock(sb_bgl_lock(sbi, group_no));<br />
	percpu_counter_sub(&#038;sbi->s_freeblocks_counter, num);</p>
<p>	BUFFER_TRACE(gdp_bh, &laquo;journal_dirty_metadata for group descriptor&raquo;);<br />
	err = ext3_journal_dirty_metadata(handle, gdp_bh);<br />
	if (!fatal)<br />
		fatal = err;</p>
<p>	if (fatal)<br />
		goto out;</p>
<p>	*errp = 0;<br />
	brelse(bitmap_bh);<br />
	vfs_dq_free_block(inode, *count-num);<br />
	*count = num;<br />
	return ret_block;</p>
<p>io_error:<br />
	*errp = -EIO;<br />
out:<br />
	if (fatal) {<br />
		*errp = fatal;<br />
		ext3_std_error(sb, fatal);<br />
	}<br />
	/*<br />
	 * Undo the block allocation<br />
	 */<br />
	if (!performed_allocation)<br />
		vfs_dq_free_block(inode, *count);<br />
	brelse(bitmap_bh);<br />
	return 0;<br />
}</p>
<p>ext3_fsblk_t ext3_new_block(handle_t *handle, struct inode *inode,<br />
			ext3_fsblk_t goal, int *errp)<br />
{<br />
	unsigned long count = 1;</p>
<p>	return ext3_new_blocks(handle, inode, goal, &#038;count, errp);<br />
}</p>
<p>/**<br />
 * ext3_count_free_blocks() &#8212; count filesystem free blocks<br />
 * @sb:		superblock<br />
 *<br />
 * Adds up the number of free blocks from each block group.<br />
 */<br />
ext3_fsblk_t ext3_count_free_blocks(struct super_block *sb)<br />
{<br />
	ext3_fsblk_t desc_count;<br />
	struct ext3_group_desc *gdp;<br />
	int i;<br />
	unsigned long ngroups = EXT3_SB(sb)->s_groups_count;<br />
#ifdef EXT3FS_DEBUG<br />
	struct ext3_super_block *es;<br />
	ext3_fsblk_t bitmap_count;<br />
	unsigned long x;<br />
	struct buffer_head *bitmap_bh = NULL;</p>
<p>	es = EXT3_SB(sb)->s_es;<br />
	desc_count = 0;<br />
	bitmap_count = 0;<br />
	gdp = NULL;</p>
<p>	smp_rmb();<br />
	for (i = 0; i < ngroups; i++) {<br />
		gdp = ext3_get_group_desc(sb, i, NULL);<br />
		if (!gdp)<br />
			continue;<br />
		desc_count += le16_to_cpu(gdp->bg_free_blocks_count);<br />
		brelse(bitmap_bh);<br />
		bitmap_bh = read_block_bitmap(sb, i);<br />
		if (bitmap_bh == NULL)<br />
			continue;</p>
<p>		x = ext3_count_free(bitmap_bh, sb->s_blocksize);<br />
		printk(&raquo;group %d: stored = %d, counted = %lu\n&raquo;,<br />
			i, le16_to_cpu(gdp->bg_free_blocks_count), x);<br />
		bitmap_count += x;<br />
	}<br />
	brelse(bitmap_bh);<br />
	printk(&raquo;ext3_count_free_blocks: stored = &laquo;E3FSBLK<br />
		&laquo;, computed = &laquo;E3FSBLK&raquo;, &laquo;E3FSBLK&raquo;\n&raquo;,<br />
	       le32_to_cpu(es->s_free_blocks_count),<br />
		desc_count, bitmap_count);<br />
	return bitmap_count;<br />
#else<br />
	desc_count = 0;<br />
	smp_rmb();<br />
	for (i = 0; i < ngroups; i++) {<br />
		gdp = ext3_get_group_desc(sb, i, NULL);<br />
		if (!gdp)<br />
			continue;<br />
		desc_count += le16_to_cpu(gdp->bg_free_blocks_count);<br />
	}</p>
<p>	return desc_count;<br />
#endif<br />
}</p>
<p>static inline int test_root(int a, int b)<br />
{<br />
	int num = b;</p>
<p>	while (a > num)<br />
		num *= b;<br />
	return num == a;<br />
}</p>
<p>static int ext3_group_sparse(int group)<br />
{<br />
	if (group <= 1)<br />
		return 1;<br />
	if (!(group &#038; 1))<br />
		return 0;<br />
	return (test_root(group, 7) || test_root(group, 5) ||<br />
		test_root(group, 3));<br />
}</p>
<p>/**<br />
 *	ext3_bg_has_super - number of blocks used by the superblock in group<br />
 *	@sb: superblock for filesystem<br />
 *	@group: group number to check<br />
 *<br />
 *	Return the number of blocks used by the superblock (primary or backup)<br />
 *	in this group.  Currently this will be only 0 or 1.<br />
 */<br />
int ext3_bg_has_super(struct super_block *sb, int group)<br />
{<br />
	if (EXT3_HAS_RO_COMPAT_FEATURE(sb,<br />
				EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER) &#038;&#038;<br />
			!ext3_group_sparse(group))<br />
		return 0;<br />
	return 1;<br />
}</p>
<p>static unsigned long ext3_bg_num_gdb_meta(struct super_block *sb, int group)<br />
{<br />
	unsigned long metagroup = group / EXT3_DESC_PER_BLOCK(sb);<br />
	unsigned long first = metagroup * EXT3_DESC_PER_BLOCK(sb);<br />
	unsigned long last = first + EXT3_DESC_PER_BLOCK(sb) - 1;</p>
<p>	if (group == first || group == first + 1 || group == last)<br />
		return 1;<br />
	return 0;<br />
}</p>
<p>static unsigned long ext3_bg_num_gdb_nometa(struct super_block *sb, int group)<br />
{<br />
	return ext3_bg_has_super(sb, group) ? EXT3_SB(sb)->s_gdb_count : 0;<br />
}</p>
<p>/**<br />
 *	ext3_bg_num_gdb &#8211; number of blocks used by the group table in group<br />
 *	@sb: superblock for filesystem<br />
 *	@group: group number to check<br />
 *<br />
 *	Return the number of blocks used by the group descriptor table<br />
 *	(primary or backup) in this group.  In the future there may be a<br />
 *	different number of descriptor blocks in each group.<br />
 */<br />
unsigned long ext3_bg_num_gdb(struct super_block *sb, int group)<br />
{<br />
	unsigned long first_meta_bg =<br />
			le32_to_cpu(EXT3_SB(sb)->s_es->s_first_meta_bg);<br />
	unsigned long metagroup = group / EXT3_DESC_PER_BLOCK(sb);</p>
<p>	if (!EXT3_HAS_INCOMPAT_FEATURE(sb,EXT3_FEATURE_INCOMPAT_META_BG) ||<br />
			metagroup < first_meta_bg)<br />
		return ext3_bg_num_gdb_nometa(sb,group);</p>
<p>	return ext3_bg_num_gdb_meta(sb,group);</p>
<p>}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/balloc-c-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>acl.h</title>
		<link>http://lynyrd.ru/acl-h-2</link>
		<comments>http://lynyrd.ru/acl-h-2#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:49:18 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/acl-h-2</guid>
		<description><![CDATA[/*
  File: fs/ext3/acl.h
  (C) 2001 Andreas Gruenbacher, 
*/
#include

#define EXT3_ACL_VERSION	0&#215;0001
typedef struct {
	__le16		e_tag;
	__le16		e_perm;
	__le32		e_id;
} ext3_acl_entry;
typedef struct {
	__le16		e_tag;
	__le16		e_perm;
} ext3_acl_entry_short;
typedef struct {
	__le32		a_version;
} ext3_acl_header;
static inline size_t ext3_acl_size(int count)
{
	if (count ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
  File: fs/ext3/acl.h<span id="more-1168"></span></p>
<p>  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org><br />
*/</p>
<p>#include
<linux/posix_acl_xattr.h>
<p>#define EXT3_ACL_VERSION	0&#215;0001</p>
<p>typedef struct {<br />
	__le16		e_tag;<br />
	__le16		e_perm;<br />
	__le32		e_id;<br />
} ext3_acl_entry;</p>
<p>typedef struct {<br />
	__le16		e_tag;<br />
	__le16		e_perm;<br />
} ext3_acl_entry_short;</p>
<p>typedef struct {<br />
	__le32		a_version;<br />
} ext3_acl_header;</p>
<p>static inline size_t ext3_acl_size(int count)<br />
{<br />
	if (count <= 4) {<br />
		return sizeof(ext3_acl_header) +<br />
		       count * sizeof(ext3_acl_entry_short);<br />
	} else {<br />
		return sizeof(ext3_acl_header) +<br />
		       4 * sizeof(ext3_acl_entry_short) +<br />
		       (count - 4) * sizeof(ext3_acl_entry);<br />
	}<br />
}</p>
<p>static inline int ext3_acl_count(size_t size)<br />
{<br />
	ssize_t s;<br />
	size -= sizeof(ext3_acl_header);<br />
	s = size - 4 * sizeof(ext3_acl_entry_short);<br />
	if (s < 0) {<br />
		if (size % sizeof(ext3_acl_entry_short))<br />
			return -1;<br />
		return size / sizeof(ext3_acl_entry_short);<br />
	} else {<br />
		if (s % sizeof(ext3_acl_entry))<br />
			return -1;<br />
		return s / sizeof(ext3_acl_entry) + 4;<br />
	}<br />
}</p>
<p>#ifdef CONFIG_EXT3_FS_POSIX_ACL</p>
<p>/* acl.c */<br />
extern int ext3_check_acl (struct inode *, int);<br />
extern int ext3_acl_chmod (struct inode *);<br />
extern int ext3_init_acl (handle_t *, struct inode *, struct inode *);</p>
<p>#else  /* CONFIG_EXT3_FS_POSIX_ACL */<br />
#include
<linux/sched.h>
#define ext3_check_acl NULL</p>
<p>static inline int<br />
ext3_acl_chmod(struct inode *inode)<br />
{<br />
	return 0;<br />
}</p>
<p>static inline int<br />
ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)<br />
{<br />
	return 0;<br />
}<br />
#endif  /* CONFIG_EXT3_FS_POSIX_ACL */</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/acl-h-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>acl.c</title>
		<link>http://lynyrd.ru/acl-c-3</link>
		<comments>http://lynyrd.ru/acl-c-3#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:48:52 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/acl-c-3</guid>
		<description><![CDATA[/*
 * linux/fs/ext3/acl.c
 *
 * Copyright (C) 2001-2003 Andreas Gruenbacher, 
 */
#include

#include

#include

#include

#include

#include

#include

#include &#171;xattr.h&#187;
#include &#171;acl.h&#187;
/*
 * Convert from filesystem to in-memory representation.
 */
static struct posix_acl *
ext3_acl_from_disk(const void *value, size_t size)
{
	const char *end = (char *)value + size;
	int n, count;
	struct posix_acl *acl;
	if (!value)
		return NULL;
	if (size < sizeof(ext3_acl_header))
		 return ERR_PTR(-EINVAL);
	if (((ext3_acl_header *)value)->a_version !=
	    cpu_to_le32(EXT3_ACL_VERSION))
		return ERR_PTR(-EINVAL);
	value ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext3/acl.c<span id="more-1167"></span><br />
 *<br />
 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de><br />
 */</p>
<p>#include
<linux/init.h>
#include
<linux/sched.h>
#include
<linux/slab.h>
#include
<linux/capability.h>
#include
<linux/fs.h>
#include
<linux/ext3_jbd.h>
#include
<linux/ext3_fs.h>
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;</p>
<p>/*<br />
 * Convert from filesystem to in-memory representation.<br />
 */<br />
static struct posix_acl *<br />
ext3_acl_from_disk(const void *value, size_t size)<br />
{<br />
	const char *end = (char *)value + size;<br />
	int n, count;<br />
	struct posix_acl *acl;</p>
<p>	if (!value)<br />
		return NULL;<br />
	if (size < sizeof(ext3_acl_header))<br />
		 return ERR_PTR(-EINVAL);<br />
	if (((ext3_acl_header *)value)->a_version !=<br />
	    cpu_to_le32(EXT3_ACL_VERSION))<br />
		return ERR_PTR(-EINVAL);<br />
	value = (char *)value + sizeof(ext3_acl_header);<br />
	count = ext3_acl_count(size);<br />
	if (count < 0)<br />
		return ERR_PTR(-EINVAL);<br />
	if (count == 0)<br />
		return NULL;<br />
	acl = posix_acl_alloc(count, GFP_NOFS);<br />
	if (!acl)<br />
		return ERR_PTR(-ENOMEM);<br />
	for (n=0; n < count; n++) {<br />
		ext3_acl_entry *entry =<br />
			(ext3_acl_entry *)value;<br />
		if ((char *)value + sizeof(ext3_acl_entry_short) > end)<br />
			goto fail;<br />
		acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);<br />
		acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);<br />
		switch(acl->a_entries[n].e_tag) {<br />
			case ACL_USER_OBJ:<br />
			case ACL_GROUP_OBJ:<br />
			case ACL_MASK:<br />
			case ACL_OTHER:<br />
				value = (char *)value +<br />
					sizeof(ext3_acl_entry_short);<br />
				acl->a_entries[n].e_id = ACL_UNDEFINED_ID;<br />
				break;</p>
<p>			case ACL_USER:<br />
			case ACL_GROUP:<br />
				value = (char *)value + sizeof(ext3_acl_entry);<br />
				if ((char *)value > end)<br />
					goto fail;<br />
				acl->a_entries[n].e_id =<br />
					le32_to_cpu(entry->e_id);<br />
				break;</p>
<p>			default:<br />
				goto fail;<br />
		}<br />
	}<br />
	if (value != end)<br />
		goto fail;<br />
	return acl;</p>
<p>fail:<br />
	posix_acl_release(acl);<br />
	return ERR_PTR(-EINVAL);<br />
}</p>
<p>/*<br />
 * Convert from in-memory to filesystem representation.<br />
 */<br />
static void *<br />
ext3_acl_to_disk(const struct posix_acl *acl, size_t *size)<br />
{<br />
	ext3_acl_header *ext_acl;<br />
	char *e;<br />
	size_t n;</p>
<p>	*size = ext3_acl_size(acl->a_count);<br />
	ext_acl = kmalloc(sizeof(ext3_acl_header) + acl->a_count *<br />
			sizeof(ext3_acl_entry), GFP_NOFS);<br />
	if (!ext_acl)<br />
		return ERR_PTR(-ENOMEM);<br />
	ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION);<br />
	e = (char *)ext_acl + sizeof(ext3_acl_header);<br />
	for (n=0; n < acl->a_count; n++) {<br />
		ext3_acl_entry *entry = (ext3_acl_entry *)e;<br />
		entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);<br />
		entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);<br />
		switch(acl->a_entries[n].e_tag) {<br />
			case ACL_USER:<br />
			case ACL_GROUP:<br />
				entry->e_id =<br />
					cpu_to_le32(acl->a_entries[n].e_id);<br />
				e += sizeof(ext3_acl_entry);<br />
				break;</p>
<p>			case ACL_USER_OBJ:<br />
			case ACL_GROUP_OBJ:<br />
			case ACL_MASK:<br />
			case ACL_OTHER:<br />
				e += sizeof(ext3_acl_entry_short);<br />
				break;</p>
<p>			default:<br />
				goto fail;<br />
		}<br />
	}<br />
	return (char *)ext_acl;</p>
<p>fail:<br />
	kfree(ext_acl);<br />
	return ERR_PTR(-EINVAL);<br />
}</p>
<p>/*<br />
 * Inode operation get_posix_acl().<br />
 *<br />
 * inode->i_mutex: don&#8217;t care<br />
 */<br />
static struct posix_acl *<br />
ext3_get_acl(struct inode *inode, int type)<br />
{<br />
	int name_index;<br />
	char *value = NULL;<br />
	struct posix_acl *acl;<br />
	int retval;</p>
<p>	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return NULL;</p>
<p>	acl = get_cached_acl(inode, type);<br />
	if (acl != ACL_NOT_CACHED)<br />
		return acl;</p>
<p>	switch (type) {<br />
	case ACL_TYPE_ACCESS:<br />
		name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;<br />
		break;<br />
	case ACL_TYPE_DEFAULT:<br />
		name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;<br />
		break;<br />
	default:<br />
		BUG();<br />
	}</p>
<p>	retval = ext3_xattr_get(inode, name_index, &laquo;&raquo;, NULL, 0);<br />
	if (retval > 0) {<br />
		value = kmalloc(retval, GFP_NOFS);<br />
		if (!value)<br />
			return ERR_PTR(-ENOMEM);<br />
		retval = ext3_xattr_get(inode, name_index, &laquo;&raquo;, value, retval);<br />
	}<br />
	if (retval > 0)<br />
		acl = ext3_acl_from_disk(value, retval);<br />
	else if (retval == -ENODATA || retval == -ENOSYS)<br />
		acl = NULL;<br />
	else<br />
		acl = ERR_PTR(retval);<br />
	kfree(value);</p>
<p>	if (!IS_ERR(acl))<br />
		set_cached_acl(inode, type, acl);</p>
<p>	return acl;<br />
}</p>
<p>/*<br />
 * Set the access or default ACL of an inode.<br />
 *<br />
 * inode->i_mutex: down unless called from ext3_new_inode<br />
 */<br />
static int<br />
ext3_set_acl(handle_t *handle, struct inode *inode, int type,<br />
	     struct posix_acl *acl)<br />
{<br />
	int name_index;<br />
	void *value = NULL;<br />
	size_t size = 0;<br />
	int error;</p>
<p>	if (S_ISLNK(inode->i_mode))<br />
		return -EOPNOTSUPP;</p>
<p>	switch(type) {<br />
		case ACL_TYPE_ACCESS:<br />
			name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;<br />
			if (acl) {<br />
				mode_t mode = inode->i_mode;<br />
				error = posix_acl_equiv_mode(acl, &#038;mode);<br />
				if (error < 0)<br />
					return error;<br />
				else {<br />
					inode->i_mode = mode;<br />
					ext3_mark_inode_dirty(handle, inode);<br />
					if (error == 0)<br />
						acl = NULL;<br />
				}<br />
			}<br />
			break;</p>
<p>		case ACL_TYPE_DEFAULT:<br />
			name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;<br />
			if (!S_ISDIR(inode->i_mode))<br />
				return acl ? -EACCES : 0;<br />
			break;</p>
<p>		default:<br />
			return -EINVAL;<br />
	}<br />
	if (acl) {<br />
		value = ext3_acl_to_disk(acl, &#038;size);<br />
		if (IS_ERR(value))<br />
			return (int)PTR_ERR(value);<br />
	}</p>
<p>	error = ext3_xattr_set_handle(handle, inode, name_index, &laquo;&raquo;,<br />
				      value, size, 0);</p>
<p>	kfree(value);</p>
<p>	if (!error)<br />
		set_cached_acl(inode, type, acl);</p>
<p>	return error;<br />
}</p>
<p>int<br />
ext3_check_acl(struct inode *inode, int mask)<br />
{<br />
	struct posix_acl *acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);</p>
<p>	if (IS_ERR(acl))<br />
		return PTR_ERR(acl);<br />
	if (acl) {<br />
		int error = posix_acl_permission(inode, acl, mask);<br />
		posix_acl_release(acl);<br />
		return error;<br />
	}</p>
<p>	return -EAGAIN;<br />
}</p>
<p>/*<br />
 * Initialize the ACLs of a new inode. Called from ext3_new_inode.<br />
 *<br />
 * dir->i_mutex: down<br />
 * inode->i_mutex: up (access to inode is still exclusive)<br />
 */<br />
int<br />
ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)<br />
{<br />
	struct posix_acl *acl = NULL;<br />
	int error = 0;</p>
<p>	if (!S_ISLNK(inode->i_mode)) {<br />
		if (test_opt(dir->i_sb, POSIX_ACL)) {<br />
			acl = ext3_get_acl(dir, ACL_TYPE_DEFAULT);<br />
			if (IS_ERR(acl))<br />
				return PTR_ERR(acl);<br />
		}<br />
		if (!acl)<br />
			inode->i_mode &#038;= ~current_umask();<br />
	}<br />
	if (test_opt(inode->i_sb, POSIX_ACL) &#038;&#038; acl) {<br />
		struct posix_acl *clone;<br />
		mode_t mode;</p>
<p>		if (S_ISDIR(inode->i_mode)) {<br />
			error = ext3_set_acl(handle, inode,<br />
					     ACL_TYPE_DEFAULT, acl);<br />
			if (error)<br />
				goto cleanup;<br />
		}<br />
		clone = posix_acl_clone(acl, GFP_NOFS);<br />
		error = -ENOMEM;<br />
		if (!clone)<br />
			goto cleanup;</p>
<p>		mode = inode->i_mode;<br />
		error = posix_acl_create_masq(clone, &#038;mode);<br />
		if (error >= 0) {<br />
			inode->i_mode = mode;<br />
			if (error > 0) {<br />
				/* This is an extended ACL */<br />
				error = ext3_set_acl(handle, inode,<br />
						     ACL_TYPE_ACCESS, clone);<br />
			}<br />
		}<br />
		posix_acl_release(clone);<br />
	}<br />
cleanup:<br />
	posix_acl_release(acl);<br />
	return error;<br />
}</p>
<p>/*<br />
 * Does chmod for an inode that may have an Access Control List. The<br />
 * inode->i_mode field must be updated to the desired value by the caller<br />
 * before calling this function.<br />
 * Returns 0 on success, or a negative error number.<br />
 *<br />
 * We change the ACL rather than storing some ACL entries in the file<br />
 * mode permission bits (which would be more efficient), because that<br />
 * would break once additional permissions (like  ACL_APPEND, ACL_DELETE<br />
 * for directories) are added. There are no more bits available in the<br />
 * file mode.<br />
 *<br />
 * inode->i_mutex: down<br />
 */<br />
int<br />
ext3_acl_chmod(struct inode *inode)<br />
{<br />
	struct posix_acl *acl, *clone;<br />
        int error;</p>
<p>	if (S_ISLNK(inode->i_mode))<br />
		return -EOPNOTSUPP;<br />
	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return 0;<br />
	acl = ext3_get_acl(inode, ACL_TYPE_ACCESS);<br />
	if (IS_ERR(acl) || !acl)<br />
		return PTR_ERR(acl);<br />
	clone = posix_acl_clone(acl, GFP_KERNEL);<br />
	posix_acl_release(acl);<br />
	if (!clone)<br />
		return -ENOMEM;<br />
	error = posix_acl_chmod_masq(clone, inode->i_mode);<br />
	if (!error) {<br />
		handle_t *handle;<br />
		int retries = 0;</p>
<p>	retry:<br />
		handle = ext3_journal_start(inode,<br />
				EXT3_DATA_TRANS_BLOCKS(inode->i_sb));<br />
		if (IS_ERR(handle)) {<br />
			error = PTR_ERR(handle);<br />
			ext3_std_error(inode->i_sb, error);<br />
			goto out;<br />
		}<br />
		error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone);<br />
		ext3_journal_stop(handle);<br />
		if (error == -ENOSPC &#038;&#038;<br />
		    ext3_should_retry_alloc(inode->i_sb, &#038;retries))<br />
			goto retry;<br />
	}<br />
out:<br />
	posix_acl_release(clone);<br />
	return error;<br />
}</p>
<p>/*<br />
 * Extended attribute handlers<br />
 */<br />
static size_t<br />
ext3_xattr_list_acl_access(struct inode *inode, char *list, size_t list_len,<br />
			   const char *name, size_t name_len)<br />
{<br />
	const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);</p>
<p>	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return 0;<br />
	if (list &#038;&#038; size <= list_len)<br />
		memcpy(list, POSIX_ACL_XATTR_ACCESS, size);<br />
	return size;<br />
}</p>
<p>static size_t<br />
ext3_xattr_list_acl_default(struct inode *inode, char *list, size_t list_len,<br />
			    const char *name, size_t name_len)<br />
{<br />
	const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);</p>
<p>	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return 0;<br />
	if (list &#038;&#038; size <= list_len)<br />
		memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);<br />
	return size;<br />
}</p>
<p>static int<br />
ext3_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)<br />
{<br />
	struct posix_acl *acl;<br />
	int error;</p>
<p>	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return -EOPNOTSUPP;</p>
<p>	acl = ext3_get_acl(inode, type);<br />
	if (IS_ERR(acl))<br />
		return PTR_ERR(acl);<br />
	if (acl == NULL)<br />
		return -ENODATA;<br />
	error = posix_acl_to_xattr(acl, buffer, size);<br />
	posix_acl_release(acl);</p>
<p>	return error;<br />
}</p>
<p>static int<br />
ext3_xattr_get_acl_access(struct inode *inode, const char *name,<br />
			  void *buffer, size_t size)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) != 0)<br />
		return -EINVAL;<br />
	return ext3_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);<br />
}</p>
<p>static int<br />
ext3_xattr_get_acl_default(struct inode *inode, const char *name,<br />
			   void *buffer, size_t size)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) != 0)<br />
		return -EINVAL;<br />
	return ext3_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);<br />
}</p>
<p>static int<br />
ext3_xattr_set_acl(struct inode *inode, int type, const void *value,<br />
		   size_t size)<br />
{<br />
	handle_t *handle;<br />
	struct posix_acl *acl;<br />
	int error, retries = 0;</p>
<p>	if (!test_opt(inode->i_sb, POSIX_ACL))<br />
		return -EOPNOTSUPP;<br />
	if (!is_owner_or_cap(inode))<br />
		return -EPERM;</p>
<p>	if (value) {<br />
		acl = posix_acl_from_xattr(value, size);<br />
		if (IS_ERR(acl))<br />
			return PTR_ERR(acl);<br />
		else if (acl) {<br />
			error = posix_acl_valid(acl);<br />
			if (error)<br />
				goto release_and_out;<br />
		}<br />
	} else<br />
		acl = NULL;</p>
<p>retry:<br />
	handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb));<br />
	if (IS_ERR(handle))<br />
		return PTR_ERR(handle);<br />
	error = ext3_set_acl(handle, inode, type, acl);<br />
	ext3_journal_stop(handle);<br />
	if (error == -ENOSPC &#038;&#038; ext3_should_retry_alloc(inode->i_sb, &#038;retries))<br />
		goto retry;</p>
<p>release_and_out:<br />
	posix_acl_release(acl);<br />
	return error;<br />
}</p>
<p>static int<br />
ext3_xattr_set_acl_access(struct inode *inode, const char *name,<br />
			  const void *value, size_t size, int flags)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) != 0)<br />
		return -EINVAL;<br />
	return ext3_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);<br />
}</p>
<p>static int<br />
ext3_xattr_set_acl_default(struct inode *inode, const char *name,<br />
			   const void *value, size_t size, int flags)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) != 0)<br />
		return -EINVAL;<br />
	return ext3_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);<br />
}</p>
<p>struct xattr_handler ext3_xattr_acl_access_handler = {<br />
	.prefix	= POSIX_ACL_XATTR_ACCESS,<br />
	.list	= ext3_xattr_list_acl_access,<br />
	.get	= ext3_xattr_get_acl_access,<br />
	.set	= ext3_xattr_set_acl_access,<br />
};</p>
<p>struct xattr_handler ext3_xattr_acl_default_handler = {<br />
	.prefix	= POSIX_ACL_XATTR_DEFAULT,<br />
	.list	= ext3_xattr_list_acl_default,<br />
	.get	= ext3_xattr_get_acl_default,<br />
	.set	= ext3_xattr_set_acl_default,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/acl-c-3/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xip.h</title>
		<link>http://lynyrd.ru/xip-h</link>
		<comments>http://lynyrd.ru/xip-h#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:48:28 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xip-h</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/xip.h
 *
 * Copyright (C) 2005 IBM Corporation
 * Author: Carsten Otte (cotte@de.ibm.com)
 */
#ifdef CONFIG_EXT2_FS_XIP
extern void ext2_xip_verify_sb (struct super_block *);
extern int ext2_clear_xip_target (struct inode *, sector_t);
static inline int ext2_use_xip (struct super_block *sb)
{
	struct ext2_sb_info *sbi = EXT2_SB(sb);
	return (sbi->s_mount_opt &#038; EXT2_MOUNT_XIP);
}
int ext2_get_xip_mem(struct address_space *, pgoff_t, int,
				void **, unsigned long *);
#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_mem)
#else
#define mapping_is_xip(map)			0
#define ext2_xip_verify_sb(sb)			do ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/xip.h<span id="more-1166"></span><br />
 *<br />
 * Copyright (C) 2005 IBM Corporation<br />
 * Author: Carsten Otte (cotte@de.ibm.com)<br />
 */</p>
<p>#ifdef CONFIG_EXT2_FS_XIP<br />
extern void ext2_xip_verify_sb (struct super_block *);<br />
extern int ext2_clear_xip_target (struct inode *, sector_t);</p>
<p>static inline int ext2_use_xip (struct super_block *sb)<br />
{<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	return (sbi->s_mount_opt &#038; EXT2_MOUNT_XIP);<br />
}<br />
int ext2_get_xip_mem(struct address_space *, pgoff_t, int,<br />
				void **, unsigned long *);<br />
#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_mem)<br />
#else<br />
#define mapping_is_xip(map)			0<br />
#define ext2_xip_verify_sb(sb)			do { } while (0)<br />
#define ext2_use_xip(sb)			0<br />
#define ext2_clear_xip_target(inode, chain)	0<br />
#define ext2_get_xip_mem			NULL<br />
#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xip-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xip.c</title>
		<link>http://lynyrd.ru/xip-c</link>
		<comments>http://lynyrd.ru/xip-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:48:06 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xip-c</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/xip.c
 *
 * Copyright (C) 2005 IBM Corporation
 * Author: Carsten Otte (cotte@de.ibm.com)
 */
#include

#include

#include

#include

#include

#include

#include

#include &#171;ext2.h&#187;
#include &#171;xip.h&#187;
static inline int
__inode_direct_access(struct inode *inode, sector_t block,
		      void **kaddr, unsigned long *pfn)
{
	struct block_device *bdev = inode->i_sb->s_bdev;
	const struct block_device_operations *ops = bdev->bd_disk->fops;
	sector_t sector;
	sector = block * (PAGE_SIZE / 512); /* ext2 block to ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/xip.c<span id="more-1165"></span><br />
 *<br />
 * Copyright (C) 2005 IBM Corporation<br />
 * Author: Carsten Otte (cotte@de.ibm.com)<br />
 */</p>
<p>#include
<linux/mm.h>
#include
<linux/fs.h>
#include
<linux/genhd.h>
#include
<linux/buffer_head.h>
#include
<linux/ext2_fs_sb.h>
#include
<linux/ext2_fs.h>
#include
<linux/blkdev.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xip.h&raquo;</p>
<p>static inline int<br />
__inode_direct_access(struct inode *inode, sector_t block,<br />
		      void **kaddr, unsigned long *pfn)<br />
{<br />
	struct block_device *bdev = inode->i_sb->s_bdev;<br />
	const struct block_device_operations *ops = bdev->bd_disk->fops;<br />
	sector_t sector;</p>
<p>	sector = block * (PAGE_SIZE / 512); /* ext2 block to bdev sector */</p>
<p>	BUG_ON(!ops->direct_access);<br />
	return ops->direct_access(bdev, sector, kaddr, pfn);<br />
}</p>
<p>static inline int<br />
__ext2_get_block(struct inode *inode, pgoff_t pgoff, int create,<br />
		   sector_t *result)<br />
{<br />
	struct buffer_head tmp;<br />
	int rc;</p>
<p>	memset(&#038;tmp, 0, sizeof(struct buffer_head));<br />
	rc = ext2_get_block(inode, pgoff, &#038;tmp, create);<br />
	*result = tmp.b_blocknr;</p>
<p>	/* did we get a sparse block (hole in the file)? */<br />
	if (!tmp.b_blocknr &#038;&#038; !rc) {<br />
		BUG_ON(create);<br />
		rc = -ENODATA;<br />
	}</p>
<p>	return rc;<br />
}</p>
<p>int<br />
ext2_clear_xip_target(struct inode *inode, sector_t block)<br />
{<br />
	void *kaddr;<br />
	unsigned long pfn;<br />
	int rc;</p>
<p>	rc = __inode_direct_access(inode, block, &#038;kaddr, &#038;pfn);<br />
	if (!rc)<br />
		clear_page(kaddr);<br />
	return rc;<br />
}</p>
<p>void ext2_xip_verify_sb(struct super_block *sb)<br />
{<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);</p>
<p>	if ((sbi->s_mount_opt &#038; EXT2_MOUNT_XIP) &#038;&#038;<br />
	    !sb->s_bdev->bd_disk->fops->direct_access) {<br />
		sbi->s_mount_opt &#038;= (~EXT2_MOUNT_XIP);<br />
		ext2_warning(sb, __func__,<br />
			     &laquo;ignoring xip option &#8211; not supported by bdev&raquo;);<br />
	}<br />
}</p>
<p>int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create,<br />
				void **kmem, unsigned long *pfn)<br />
{<br />
	int rc;<br />
	sector_t block;</p>
<p>	/* first, retrieve the sector number */<br />
	rc = __ext2_get_block(mapping->host, pgoff, create, &#038;block);<br />
	if (rc)<br />
		return rc;</p>
<p>	/* retrieve address of the target data */<br />
	rc = __inode_direct_access(mapping->host, block, kmem, pfn);<br />
	return rc;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xip-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xattr_user.c</title>
		<link>http://lynyrd.ru/xattr_user-c</link>
		<comments>http://lynyrd.ru/xattr_user-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:47:50 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xattr_user-c</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/xattr_user.c
 * Handler for extended user attributes.
 *
 * Copyright (C) 2001 by Andreas Gruenbacher, 
 */
#include

#include

#include

#include &#171;ext2.h&#187;
#include &#171;xattr.h&#187;
static size_t
ext2_xattr_user_list(struct inode *inode, char *list, size_t list_size,
		     const char *name, size_t name_len)
{
	const size_t prefix_len = XATTR_USER_PREFIX_LEN;
	const size_t total_len = prefix_len + name_len + 1;
	if (!test_opt(inode->i_sb, XATTR_USER))
		return 0;
	if (list &#038;&#038; total_len ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/xattr_user.c<span id="more-1164"></span><br />
 * Handler for extended user attributes.<br />
 *<br />
 * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org><br />
 */</p>
<p>#include
<linux/init.h>
#include
<linux/module.h>
#include
<linux/string.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;</p>
<p>static size_t<br />
ext2_xattr_user_list(struct inode *inode, char *list, size_t list_size,<br />
		     const char *name, size_t name_len)<br />
{<br />
	const size_t prefix_len = XATTR_USER_PREFIX_LEN;<br />
	const size_t total_len = prefix_len + name_len + 1;</p>
<p>	if (!test_opt(inode->i_sb, XATTR_USER))<br />
		return 0;</p>
<p>	if (list &#038;&#038; total_len <= list_size) {<br />
		memcpy(list, XATTR_USER_PREFIX, prefix_len);<br />
		memcpy(list+prefix_len, name, name_len);<br />
		list[prefix_len + name_len] = '\0';<br />
	}<br />
	return total_len;<br />
}</p>
<p>static int<br />
ext2_xattr_user_get(struct inode *inode, const char *name,<br />
		    void *buffer, size_t size)<br />
{<br />
	if (strcmp(name, "") == 0)<br />
		return -EINVAL;<br />
	if (!test_opt(inode->i_sb, XATTR_USER))<br />
		return -EOPNOTSUPP;<br />
	return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, buffer, size);<br />
}</p>
<p>static int<br />
ext2_xattr_user_set(struct inode *inode, const char *name,<br />
		    const void *value, size_t size, int flags)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) == 0)<br />
		return -EINVAL;<br />
	if (!test_opt(inode->i_sb, XATTR_USER))<br />
		return -EOPNOTSUPP;</p>
<p>	return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,<br />
			      value, size, flags);<br />
}</p>
<p>struct xattr_handler ext2_xattr_user_handler = {<br />
	.prefix	= XATTR_USER_PREFIX,<br />
	.list	= ext2_xattr_user_list,<br />
	.get	= ext2_xattr_user_get,<br />
	.set	= ext2_xattr_user_set,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xattr_user-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xattr_trusted.c</title>
		<link>http://lynyrd.ru/xattr_trusted-c</link>
		<comments>http://lynyrd.ru/xattr_trusted-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:47:32 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xattr_trusted-c</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/xattr_trusted.c
 * Handler for trusted extended attributes.
 *
 * Copyright (C) 2003 by Andreas Gruenbacher, 
 */
#include

#include

#include

#include

#include

#include &#171;xattr.h&#187;
static size_t
ext2_xattr_trusted_list(struct inode *inode, char *list, size_t list_size,
			const char *name, size_t name_len)
{
	const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;
	const size_t total_len = prefix_len + name_len + 1;
	if (!capable(CAP_SYS_ADMIN))
		return 0;
	if (list &#038;&#038; total_len ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/xattr_trusted.c<span id="more-1163"></span><br />
 * Handler for trusted extended attributes.<br />
 *<br />
 * Copyright (C) 2003 by Andreas Gruenbacher, <a.gruenbacher@computer.org><br />
 */</p>
<p>#include
<linux/module.h>
#include
<linux/string.h>
#include
<linux/capability.h>
#include
<linux/fs.h>
#include
<linux/ext2_fs.h>
#include &laquo;xattr.h&raquo;</p>
<p>static size_t<br />
ext2_xattr_trusted_list(struct inode *inode, char *list, size_t list_size,<br />
			const char *name, size_t name_len)<br />
{<br />
	const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;<br />
	const size_t total_len = prefix_len + name_len + 1;</p>
<p>	if (!capable(CAP_SYS_ADMIN))<br />
		return 0;</p>
<p>	if (list &#038;&#038; total_len <= list_size) {<br />
		memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);<br />
		memcpy(list+prefix_len, name, name_len);<br />
		list[prefix_len + name_len] = &#8216;\0&#8242;;<br />
	}<br />
	return total_len;<br />
}</p>
<p>static int<br />
ext2_xattr_trusted_get(struct inode *inode, const char *name,<br />
		       void *buffer, size_t size)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) == 0)<br />
		return -EINVAL;<br />
	return ext2_xattr_get(inode, EXT2_XATTR_INDEX_TRUSTED, name,<br />
			      buffer, size);<br />
}</p>
<p>static int<br />
ext2_xattr_trusted_set(struct inode *inode, const char *name,<br />
		       const void *value, size_t size, int flags)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) == 0)<br />
		return -EINVAL;<br />
	return ext2_xattr_set(inode, EXT2_XATTR_INDEX_TRUSTED, name,<br />
			      value, size, flags);<br />
}</p>
<p>struct xattr_handler ext2_xattr_trusted_handler = {<br />
	.prefix	= XATTR_TRUSTED_PREFIX,<br />
	.list	= ext2_xattr_trusted_list,<br />
	.get	= ext2_xattr_trusted_get,<br />
	.set	= ext2_xattr_trusted_set,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xattr_trusted-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xattr_security.c</title>
		<link>http://lynyrd.ru/xattr_security-c</link>
		<comments>http://lynyrd.ru/xattr_security-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:47:09 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xattr_security-c</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/xattr_security.c
 * Handler for storing security labels as extended attributes.
 */
#include

#include

#include

#include

#include

#include &#171;xattr.h&#187;
static size_t
ext2_xattr_security_list(struct inode *inode, char *list, size_t list_size,
			 const char *name, size_t name_len)
{
	const int prefix_len = XATTR_SECURITY_PREFIX_LEN;
	const size_t total_len = prefix_len + name_len + 1;
	if (list &#038;&#038; total_len ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/xattr_security.c<span id="more-1162"></span><br />
 * Handler for storing security labels as extended attributes.<br />
 */</p>
<p>#include
<linux/module.h>
#include
<linux/string.h>
#include
<linux/fs.h>
#include
<linux/ext2_fs.h>
#include
<linux/security.h>
#include &laquo;xattr.h&raquo;</p>
<p>static size_t<br />
ext2_xattr_security_list(struct inode *inode, char *list, size_t list_size,<br />
			 const char *name, size_t name_len)<br />
{<br />
	const int prefix_len = XATTR_SECURITY_PREFIX_LEN;<br />
	const size_t total_len = prefix_len + name_len + 1;</p>
<p>	if (list &#038;&#038; total_len <= list_size) {<br />
		memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);<br />
		memcpy(list+prefix_len, name, name_len);<br />
		list[prefix_len + name_len] = &#8216;\0&#8242;;<br />
	}<br />
	return total_len;<br />
}</p>
<p>static int<br />
ext2_xattr_security_get(struct inode *inode, const char *name,<br />
		       void *buffer, size_t size)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) == 0)<br />
		return -EINVAL;<br />
	return ext2_xattr_get(inode, EXT2_XATTR_INDEX_SECURITY, name,<br />
			      buffer, size);<br />
}</p>
<p>static int<br />
ext2_xattr_security_set(struct inode *inode, const char *name,<br />
		       const void *value, size_t size, int flags)<br />
{<br />
	if (strcmp(name, &laquo;&raquo;) == 0)<br />
		return -EINVAL;<br />
	return ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, name,<br />
			      value, size, flags);<br />
}</p>
<p>int<br />
ext2_init_security(struct inode *inode, struct inode *dir)<br />
{<br />
	int err;<br />
	size_t len;<br />
	void *value;<br />
	char *name;</p>
<p>	err = security_inode_init_security(inode, dir, &#038;name, &#038;value, &#038;len);<br />
	if (err) {<br />
		if (err == -EOPNOTSUPP)<br />
			return 0;<br />
		return err;<br />
	}<br />
	err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,<br />
			     name, value, len, 0);<br />
	kfree(name);<br />
	kfree(value);<br />
	return err;<br />
}</p>
<p>struct xattr_handler ext2_xattr_security_handler = {<br />
	.prefix	= XATTR_SECURITY_PREFIX,<br />
	.list	= ext2_xattr_security_list,<br />
	.get	= ext2_xattr_security_get,<br />
	.set	= ext2_xattr_security_set,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xattr_security-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xattr.h</title>
		<link>http://lynyrd.ru/xattr-h-2</link>
		<comments>http://lynyrd.ru/xattr-h-2#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:46:35 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/xattr-h-2</guid>
		<description><![CDATA[/*
  File: linux/ext2_xattr.h
  On-disk format of extended attributes for the ext2 filesystem.
  (C) 2001 Andreas Gruenbacher, 
*/
#include

#include

/* Magic value in attribute blocks */
#define EXT2_XATTR_MAGIC		0xEA020000
/* Maximum number of references to one attribute block */
#define EXT2_XATTR_REFCOUNT_MAX		1024
/* Name indexes */
#define EXT2_XATTR_INDEX_USER			1
#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS	2
#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT	3
#define EXT2_XATTR_INDEX_TRUSTED		4
#define	EXT2_XATTR_INDEX_LUSTRE			5
#define EXT2_XATTR_INDEX_SECURITY	        6
struct ext2_xattr_header {
	__le32	h_magic;	/* ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
  File: linux/ext2_xattr.h<span id="more-1161"></span></p>
<p>  On-disk format of extended attributes for the ext2 filesystem.</p>
<p>  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org><br />
*/</p>
<p>#include
<linux/init.h>
#include
<linux/xattr.h>
<p>/* Magic value in attribute blocks */<br />
#define EXT2_XATTR_MAGIC		0xEA020000</p>
<p>/* Maximum number of references to one attribute block */<br />
#define EXT2_XATTR_REFCOUNT_MAX		1024</p>
<p>/* Name indexes */<br />
#define EXT2_XATTR_INDEX_USER			1<br />
#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS	2<br />
#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT	3<br />
#define EXT2_XATTR_INDEX_TRUSTED		4<br />
#define	EXT2_XATTR_INDEX_LUSTRE			5<br />
#define EXT2_XATTR_INDEX_SECURITY	        6</p>
<p>struct ext2_xattr_header {<br />
	__le32	h_magic;	/* magic number for identification */<br />
	__le32	h_refcount;	/* reference count */<br />
	__le32	h_blocks;	/* number of disk blocks used */<br />
	__le32	h_hash;		/* hash value of all attributes */<br />
	__u32	h_reserved[4];	/* zero right now */<br />
};</p>
<p>struct ext2_xattr_entry {<br />
	__u8	e_name_len;	/* length of name */<br />
	__u8	e_name_index;	/* attribute name index */<br />
	__le16	e_value_offs;	/* offset in disk block of value */<br />
	__le32	e_value_block;	/* disk block attribute is stored on (n/i) */<br />
	__le32	e_value_size;	/* size of attribute value */<br />
	__le32	e_hash;		/* hash value of name and value */<br />
	char	e_name[0];	/* attribute name */<br />
};</p>
<p>#define EXT2_XATTR_PAD_BITS		2<br />
#define EXT2_XATTR_PAD		(1<<EXT2_XATTR_PAD_BITS)<br />
#define EXT2_XATTR_ROUND		(EXT2_XATTR_PAD-1)<br />
#define EXT2_XATTR_LEN(name_len) \<br />
	(((name_len) + EXT2_XATTR_ROUND + \<br />
	sizeof(struct ext2_xattr_entry)) &#038; ~EXT2_XATTR_ROUND)<br />
#define EXT2_XATTR_NEXT(entry) \<br />
	( (struct ext2_xattr_entry *)( \<br />
	  (char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )<br />
#define EXT2_XATTR_SIZE(size) \<br />
	(((size) + EXT2_XATTR_ROUND) &#038; ~EXT2_XATTR_ROUND)</p>
<p># ifdef CONFIG_EXT2_FS_XATTR</p>
<p>extern struct xattr_handler ext2_xattr_user_handler;<br />
extern struct xattr_handler ext2_xattr_trusted_handler;<br />
extern struct xattr_handler ext2_xattr_acl_access_handler;<br />
extern struct xattr_handler ext2_xattr_acl_default_handler;<br />
extern struct xattr_handler ext2_xattr_security_handler;</p>
<p>extern ssize_t ext2_listxattr(struct dentry *, char *, size_t);</p>
<p>extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t);<br />
extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int);</p>
<p>extern void ext2_xattr_delete_inode(struct inode *);<br />
extern void ext2_xattr_put_super(struct super_block *);</p>
<p>extern int init_ext2_xattr(void);<br />
extern void exit_ext2_xattr(void);</p>
<p>extern struct xattr_handler *ext2_xattr_handlers[];</p>
<p># else  /* CONFIG_EXT2_FS_XATTR */</p>
<p>static inline int<br />
ext2_xattr_get(struct inode *inode, int name_index,<br />
	       const char *name, void *buffer, size_t size)<br />
{<br />
	return -EOPNOTSUPP;<br />
}</p>
<p>static inline int<br />
ext2_xattr_set(struct inode *inode, int name_index, const char *name,<br />
	       const void *value, size_t size, int flags)<br />
{<br />
	return -EOPNOTSUPP;<br />
}</p>
<p>static inline void<br />
ext2_xattr_delete_inode(struct inode *inode)<br />
{<br />
}</p>
<p>static inline void<br />
ext2_xattr_put_super(struct super_block *sb)<br />
{<br />
}</p>
<p>static inline int<br />
init_ext2_xattr(void)<br />
{<br />
	return 0;<br />
}</p>
<p>static inline void<br />
exit_ext2_xattr(void)<br />
{<br />
}</p>
<p>#define ext2_xattr_handlers NULL</p>
<p># endif  /* CONFIG_EXT2_FS_XATTR */</p>
<p>#ifdef CONFIG_EXT2_FS_SECURITY<br />
extern int ext2_init_security(struct inode *inode, struct inode *dir);<br />
#else<br />
static inline int ext2_init_security(struct inode *inode, struct inode *dir)<br />
{<br />
	return 0;<br />
}<br />
#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xattr-h-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>xattr.c</title>
		<link>http://lynyrd.ru/xattr-c-4</link>
		<comments>http://lynyrd.ru/xattr-c-4#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:46:13 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1159</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/xattr.c
 *
 * Copyright (C) 2001-2003 Andreas Gruenbacher 
 *
 * Fix by Harrison Xing .
 * Extended attributes for symlinks and special files added per
 *  suggestion of Luka Renko .
 * xattr consolidation Copyright (c) 2004 James Morris ,
 *  Red Hat Inc.
 *
 */
/*
 * Extended attributes are ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/xattr.c<span id="more-1159"></span><br />
 *<br />
 * Copyright (C) 2001-2003 Andreas Gruenbacher <agruen@suse.de><br />
 *<br />
 * Fix by Harrison Xing <harrison@mountainviewdata.com>.<br />
 * Extended attributes for symlinks and special files added per<br />
 *  suggestion of Luka Renko <luka.renko@hermes.si>.<br />
 * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>,<br />
 *  Red Hat Inc.<br />
 *<br />
 */</p>
<p>/*<br />
 * Extended attributes are stored on disk blocks allocated outside of<br />
 * any inode. The i_file_acl field is then made to point to this allocated<br />
 * block. If all extended attributes of an inode are identical, these<br />
 * inodes may share the same extended attribute block. Such situations<br />
 * are automatically detected by keeping a cache of recent attribute block<br />
 * numbers and hashes over the block&#8217;s contents in memory.<br />
 *<br />
 *<br />
 * Extended attribute block layout:<br />
 *<br />
 *   +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
 *   | header           |<br />
 *   | entry 1          | |<br />
 *   | entry 2          | | growing downwards<br />
 *   | entry 3          | v<br />
 *   | four null bytes  |<br />
 *   | . . .            |<br />
 *   | value 1          | ^<br />
 *   | value 3          | | growing upwards<br />
 *   | value 2          | |<br />
 *   +&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;+<br />
 *<br />
 * The block header is followed by multiple entry descriptors. These entry<br />
 * descriptors are variable in size, and alligned to EXT2_XATTR_PAD<br />
 * byte boundaries. The entry descriptors are sorted by attribute name,<br />
 * so that two extended attribute blocks can be compared efficiently.<br />
 *<br />
 * Attribute values are aligned to the end of the block, stored in<br />
 * no specific order. They are also padded to EXT2_XATTR_PAD byte<br />
 * boundaries. No additional gaps are left between them.<br />
 *<br />
 * Locking strategy<br />
 * &#8212;&#8212;&#8212;&#8212;&#8212;-<br />
 * EXT2_I(inode)->i_file_acl is protected by EXT2_I(inode)->xattr_sem.<br />
 * EA blocks are only changed if they are exclusive to an inode, so<br />
 * holding xattr_sem also means that nothing but the EA block&#8217;s reference<br />
 * count will change. Multiple writers to an EA block are synchronized<br />
 * by the bh lock. No more than a single bh lock is held at any time<br />
 * to avoid deadlocks.<br />
 */</p>
<p>#include
<linux/buffer_head.h>
#include
<linux/module.h>
#include
<linux/init.h>
#include
<linux/slab.h>
#include
<linux/mbcache.h>
#include
<linux/quotaops.h>
#include
<linux/rwsem.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;</p>
<p>#define HDR(bh) ((struct ext2_xattr_header *)((bh)->b_data))<br />
#define ENTRY(ptr) ((struct ext2_xattr_entry *)(ptr))<br />
#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)<br />
#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)</p>
<p>#ifdef EXT2_XATTR_DEBUG<br />
# define ea_idebug(inode, f&#8230;) do { \<br />
		printk(KERN_DEBUG &laquo;inode %s:%ld: &laquo;, \<br />
			inode->i_sb->s_id, inode->i_ino); \<br />
		printk(f); \<br />
		printk(&raquo;\n&raquo;); \<br />
	} while (0)<br />
# define ea_bdebug(bh, f&#8230;) do { \<br />
		char b[BDEVNAME_SIZE]; \<br />
		printk(KERN_DEBUG &laquo;block %s:%lu: &laquo;, \<br />
			bdevname(bh->b_bdev, b), \<br />
			(unsigned long) bh->b_blocknr); \<br />
		printk(f); \<br />
		printk(&raquo;\n&raquo;); \<br />
	} while (0)<br />
#else<br />
# define ea_idebug(f&#8230;)<br />
# define ea_bdebug(f&#8230;)<br />
#endif</p>
<p>static int ext2_xattr_set2(struct inode *, struct buffer_head *,<br />
			   struct ext2_xattr_header *);</p>
<p>static int ext2_xattr_cache_insert(struct buffer_head *);<br />
static struct buffer_head *ext2_xattr_cache_find(struct inode *,<br />
						 struct ext2_xattr_header *);<br />
static void ext2_xattr_rehash(struct ext2_xattr_header *,<br />
			      struct ext2_xattr_entry *);</p>
<p>static struct mb_cache *ext2_xattr_cache;</p>
<p>static struct xattr_handler *ext2_xattr_handler_map[] = {<br />
	[EXT2_XATTR_INDEX_USER]		     = &#038;ext2_xattr_user_handler,<br />
#ifdef CONFIG_EXT2_FS_POSIX_ACL<br />
	[EXT2_XATTR_INDEX_POSIX_ACL_ACCESS]  = &#038;ext2_xattr_acl_access_handler,<br />
	[EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT] = &#038;ext2_xattr_acl_default_handler,<br />
#endif<br />
	[EXT2_XATTR_INDEX_TRUSTED]	     = &#038;ext2_xattr_trusted_handler,<br />
#ifdef CONFIG_EXT2_FS_SECURITY<br />
	[EXT2_XATTR_INDEX_SECURITY]	     = &#038;ext2_xattr_security_handler,<br />
#endif<br />
};</p>
<p>struct xattr_handler *ext2_xattr_handlers[] = {<br />
	&#038;ext2_xattr_user_handler,<br />
	&#038;ext2_xattr_trusted_handler,<br />
#ifdef CONFIG_EXT2_FS_POSIX_ACL<br />
	&#038;ext2_xattr_acl_access_handler,<br />
	&#038;ext2_xattr_acl_default_handler,<br />
#endif<br />
#ifdef CONFIG_EXT2_FS_SECURITY<br />
	&#038;ext2_xattr_security_handler,<br />
#endif<br />
	NULL<br />
};</p>
<p>static inline struct xattr_handler *<br />
ext2_xattr_handler(int name_index)<br />
{<br />
	struct xattr_handler *handler = NULL;</p>
<p>	if (name_index > 0 &#038;&#038; name_index < ARRAY_SIZE(ext2_xattr_handler_map))<br />
		handler = ext2_xattr_handler_map[name_index];<br />
	return handler;<br />
}</p>
<p>/*<br />
 * ext2_xattr_get()<br />
 *<br />
 * Copy an extended attribute into the buffer<br />
 * provided, or compute the buffer size required.<br />
 * Buffer is NULL to compute the size of the buffer required.<br />
 *<br />
 * Returns a negative error number on failure, or the number of bytes<br />
 * used / required on success.<br />
 */<br />
int<br />
ext2_xattr_get(struct inode *inode, int name_index, const char *name,<br />
	       void *buffer, size_t buffer_size)<br />
{<br />
	struct buffer_head *bh = NULL;<br />
	struct ext2_xattr_entry *entry;<br />
	size_t name_len, size;<br />
	char *end;<br />
	int error;</p>
<p>	ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",<br />
		  name_index, name, buffer, (long)buffer_size);</p>
<p>	if (name == NULL)<br />
		return -EINVAL;<br />
	down_read(&#038;EXT2_I(inode)->xattr_sem);<br />
	error = -ENODATA;<br />
	if (!EXT2_I(inode)->i_file_acl)<br />
		goto cleanup;<br />
	ea_idebug(inode, &laquo;reading block %d&raquo;, EXT2_I(inode)->i_file_acl);<br />
	bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);<br />
	error = -EIO;<br />
	if (!bh)<br />
		goto cleanup;<br />
	ea_bdebug(bh, &laquo;b_count=%d, refcount=%d&raquo;,<br />
		atomic_read(&#038;(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));<br />
	end = bh->b_data + bh->b_size;<br />
	if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||<br />
	    HDR(bh)->h_blocks != cpu_to_le32(1)) {<br />
bad_block:	ext2_error(inode->i_sb, &laquo;ext2_xattr_get&raquo;,<br />
			&laquo;inode %ld: bad block %d&raquo;, inode->i_ino,<br />
			EXT2_I(inode)->i_file_acl);<br />
		error = -EIO;<br />
		goto cleanup;<br />
	}<br />
	/* find named attribute */<br />
	name_len = strlen(name);</p>
<p>	error = -ERANGE;<br />
	if (name_len > 255)<br />
		goto cleanup;<br />
	entry = FIRST_ENTRY(bh);<br />
	while (!IS_LAST_ENTRY(entry)) {<br />
		struct ext2_xattr_entry *next =<br />
			EXT2_XATTR_NEXT(entry);<br />
		if ((char *)next >= end)<br />
			goto bad_block;<br />
		if (name_index == entry->e_name_index &#038;&#038;<br />
		    name_len == entry->e_name_len &#038;&#038;<br />
		    memcmp(name, entry->e_name, name_len) == 0)<br />
			goto found;<br />
		entry = next;<br />
	}<br />
	/* Check the remaining name entries */<br />
	while (!IS_LAST_ENTRY(entry)) {<br />
		struct ext2_xattr_entry *next =<br />
			EXT2_XATTR_NEXT(entry);<br />
		if ((char *)next >= end)<br />
			goto bad_block;<br />
		entry = next;<br />
	}<br />
	if (ext2_xattr_cache_insert(bh))<br />
		ea_idebug(inode, &laquo;cache insert failed&raquo;);<br />
	error = -ENODATA;<br />
	goto cleanup;<br />
found:<br />
	/* check the buffer size */<br />
	if (entry->e_value_block != 0)<br />
		goto bad_block;<br />
	size = le32_to_cpu(entry->e_value_size);<br />
	if (size > inode->i_sb->s_blocksize ||<br />
	    le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)<br />
		goto bad_block;</p>
<p>	if (ext2_xattr_cache_insert(bh))<br />
		ea_idebug(inode, &laquo;cache insert failed&raquo;);<br />
	if (buffer) {<br />
		error = -ERANGE;<br />
		if (size > buffer_size)<br />
			goto cleanup;<br />
		/* return value of attribute */<br />
		memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),<br />
			size);<br />
	}<br />
	error = size;</p>
<p>cleanup:<br />
	brelse(bh);<br />
	up_read(&#038;EXT2_I(inode)->xattr_sem);</p>
<p>	return error;<br />
}</p>
<p>/*<br />
 * ext2_xattr_list()<br />
 *<br />
 * Copy a list of attribute names into the buffer<br />
 * provided, or compute the buffer size required.<br />
 * Buffer is NULL to compute the size of the buffer required.<br />
 *<br />
 * Returns a negative error number on failure, or the number of bytes<br />
 * used / required on success.<br />
 */<br />
static int<br />
ext2_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)<br />
{<br />
	struct buffer_head *bh = NULL;<br />
	struct ext2_xattr_entry *entry;<br />
	char *end;<br />
	size_t rest = buffer_size;<br />
	int error;</p>
<p>	ea_idebug(inode, &laquo;buffer=%p, buffer_size=%ld&raquo;,<br />
		  buffer, (long)buffer_size);</p>
<p>	down_read(&#038;EXT2_I(inode)->xattr_sem);<br />
	error = 0;<br />
	if (!EXT2_I(inode)->i_file_acl)<br />
		goto cleanup;<br />
	ea_idebug(inode, &laquo;reading block %d&raquo;, EXT2_I(inode)->i_file_acl);<br />
	bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);<br />
	error = -EIO;<br />
	if (!bh)<br />
		goto cleanup;<br />
	ea_bdebug(bh, &laquo;b_count=%d, refcount=%d&raquo;,<br />
		atomic_read(&#038;(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));<br />
	end = bh->b_data + bh->b_size;<br />
	if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||<br />
	    HDR(bh)->h_blocks != cpu_to_le32(1)) {<br />
bad_block:	ext2_error(inode->i_sb, &laquo;ext2_xattr_list&raquo;,<br />
			&laquo;inode %ld: bad block %d&raquo;, inode->i_ino,<br />
			EXT2_I(inode)->i_file_acl);<br />
		error = -EIO;<br />
		goto cleanup;<br />
	}</p>
<p>	/* check the on-disk data structure */<br />
	entry = FIRST_ENTRY(bh);<br />
	while (!IS_LAST_ENTRY(entry)) {<br />
		struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);</p>
<p>		if ((char *)next >= end)<br />
			goto bad_block;<br />
		entry = next;<br />
	}<br />
	if (ext2_xattr_cache_insert(bh))<br />
		ea_idebug(inode, &laquo;cache insert failed&raquo;);</p>
<p>	/* list the attribute names */<br />
	for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);<br />
	     entry = EXT2_XATTR_NEXT(entry)) {<br />
		struct xattr_handler *handler =<br />
			ext2_xattr_handler(entry->e_name_index);</p>
<p>		if (handler) {<br />
			size_t size = handler->list(inode, buffer, rest,<br />
						    entry->e_name,<br />
						    entry->e_name_len);<br />
			if (buffer) {<br />
				if (size > rest) {<br />
					error = -ERANGE;<br />
					goto cleanup;<br />
				}<br />
				buffer += size;<br />
			}<br />
			rest -= size;<br />
		}<br />
	}<br />
	error = buffer_size &#8211; rest;  /* total size */</p>
<p>cleanup:<br />
	brelse(bh);<br />
	up_read(&#038;EXT2_I(inode)->xattr_sem);</p>
<p>	return error;<br />
}</p>
<p>/*<br />
 * Inode operation listxattr()<br />
 *<br />
 * dentry->d_inode->i_mutex: don&#8217;t care<br />
 */<br />
ssize_t<br />
ext2_listxattr(struct dentry *dentry, char *buffer, size_t size)<br />
{<br />
	return ext2_xattr_list(dentry->d_inode, buffer, size);<br />
}</p>
<p>/*<br />
 * If the EXT2_FEATURE_COMPAT_EXT_ATTR feature of this file system is<br />
 * not set, set it.<br />
 */<br />
static void ext2_xattr_update_super_block(struct super_block *sb)<br />
{<br />
	if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR))<br />
		return;</p>
<p>	EXT2_SET_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR);<br />
	sb->s_dirt = 1;<br />
	mark_buffer_dirty(EXT2_SB(sb)->s_sbh);<br />
}</p>
<p>/*<br />
 * ext2_xattr_set()<br />
 *<br />
 * Create, replace or remove an extended attribute for this inode. Buffer<br />
 * is NULL to remove an existing extended attribute, and non-NULL to<br />
 * either replace an existing extended attribute, or create a new extended<br />
 * attribute. The flags XATTR_REPLACE and XATTR_CREATE<br />
 * specify that an extended attribute must exist and must not exist<br />
 * previous to the call, respectively.<br />
 *<br />
 * Returns 0, or a negative error number on failure.<br />
 */<br />
int<br />
ext2_xattr_set(struct inode *inode, int name_index, const char *name,<br />
	       const void *value, size_t value_len, int flags)<br />
{<br />
	struct super_block *sb = inode->i_sb;<br />
	struct buffer_head *bh = NULL;<br />
	struct ext2_xattr_header *header = NULL;<br />
	struct ext2_xattr_entry *here, *last;<br />
	size_t name_len, free, min_offs = sb->s_blocksize;<br />
	int not_found = 1, error;<br />
	char *end;</p>
<p>	/*<br />
	 * header &#8212; Points either into bh, or to a temporarily<br />
	 *           allocated buffer.<br />
	 * here &#8212; The named entry found, or the place for inserting, within<br />
	 *         the block pointed to by header.<br />
	 * last &#8212; Points right after the last named entry within the block<br />
	 *         pointed to by header.<br />
	 * min_offs &#8212; The offset of the first value (values are aligned<br />
	 *             towards the end of the block).<br />
	 * end &#8212; Points right after the block pointed to by header.<br />
	 */</p>
<p>	ea_idebug(inode, &laquo;name=%d.%s, value=%p, value_len=%ld&raquo;,<br />
		  name_index, name, value, (long)value_len);</p>
<p>	if (value == NULL)<br />
		value_len = 0;<br />
	if (name == NULL)<br />
		return -EINVAL;<br />
	name_len = strlen(name);<br />
	if (name_len > 255 || value_len > sb->s_blocksize)<br />
		return -ERANGE;<br />
	down_write(&#038;EXT2_I(inode)->xattr_sem);<br />
	if (EXT2_I(inode)->i_file_acl) {<br />
		/* The inode already has an extended attribute block. */<br />
		bh = sb_bread(sb, EXT2_I(inode)->i_file_acl);<br />
		error = -EIO;<br />
		if (!bh)<br />
			goto cleanup;<br />
		ea_bdebug(bh, &laquo;b_count=%d, refcount=%d&raquo;,<br />
			atomic_read(&#038;(bh->b_count)),<br />
			le32_to_cpu(HDR(bh)->h_refcount));<br />
		header = HDR(bh);<br />
		end = bh->b_data + bh->b_size;<br />
		if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||<br />
		    header->h_blocks != cpu_to_le32(1)) {<br />
bad_block:		ext2_error(sb, &laquo;ext2_xattr_set&raquo;,<br />
				&laquo;inode %ld: bad block %d&raquo;, inode->i_ino,<br />
				   EXT2_I(inode)->i_file_acl);<br />
			error = -EIO;<br />
			goto cleanup;<br />
		}<br />
		/* Find the named attribute. */<br />
		here = FIRST_ENTRY(bh);<br />
		while (!IS_LAST_ENTRY(here)) {<br />
			struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);<br />
			if ((char *)next >= end)<br />
				goto bad_block;<br />
			if (!here->e_value_block &#038;&#038; here->e_value_size) {<br />
				size_t offs = le16_to_cpu(here->e_value_offs);<br />
				if (offs < min_offs)<br />
					min_offs = offs;<br />
			}<br />
			not_found = name_index - here->e_name_index;<br />
			if (!not_found)<br />
				not_found = name_len &#8211; here->e_name_len;<br />
			if (!not_found)<br />
				not_found = memcmp(name, here->e_name,name_len);<br />
			if (not_found <= 0)<br />
				break;<br />
			here = next;<br />
		}<br />
		last = here;<br />
		/* We still need to compute min_offs and last. */<br />
		while (!IS_LAST_ENTRY(last)) {<br />
			struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);<br />
			if ((char *)next >= end)<br />
				goto bad_block;<br />
			if (!last->e_value_block &#038;&#038; last->e_value_size) {<br />
				size_t offs = le16_to_cpu(last->e_value_offs);<br />
				if (offs < min_offs)<br />
					min_offs = offs;<br />
			}<br />
			last = next;<br />
		}</p>
<p>		/* Check whether we have enough space left. */<br />
		free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);<br />
	} else {<br />
		/* We will use a new extended attribute block. */<br />
		free = sb->s_blocksize -<br />
			sizeof(struct ext2_xattr_header) &#8211; sizeof(__u32);<br />
		here = last = NULL;  /* avoid gcc uninitialized warning. */<br />
	}</p>
<p>	if (not_found) {<br />
		/* Request to remove a nonexistent attribute? */<br />
		error = -ENODATA;<br />
		if (flags &#038; XATTR_REPLACE)<br />
			goto cleanup;<br />
		error = 0;<br />
		if (value == NULL)<br />
			goto cleanup;<br />
	} else {<br />
		/* Request to create an existing attribute? */<br />
		error = -EEXIST;<br />
		if (flags &#038; XATTR_CREATE)<br />
			goto cleanup;<br />
		if (!here->e_value_block &#038;&#038; here->e_value_size) {<br />
			size_t size = le32_to_cpu(here->e_value_size);</p>
<p>			if (le16_to_cpu(here->e_value_offs) + size ><br />
			    sb->s_blocksize || size > sb->s_blocksize)<br />
				goto bad_block;<br />
			free += EXT2_XATTR_SIZE(size);<br />
		}<br />
		free += EXT2_XATTR_LEN(name_len);<br />
	}<br />
	error = -ENOSPC;<br />
	if (free < EXT2_XATTR_LEN(name_len) + EXT2_XATTR_SIZE(value_len))<br />
		goto cleanup;</p>
<p>	/* Here we know that we can set the new attribute. */</p>
<p>	if (header) {<br />
		struct mb_cache_entry *ce;</p>
<p>		/* assert(header == HDR(bh)); */<br />
		ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev,<br />
					bh->b_blocknr);<br />
		lock_buffer(bh);<br />
		if (header->h_refcount == cpu_to_le32(1)) {<br />
			ea_bdebug(bh, &laquo;modifying in-place&raquo;);<br />
			if (ce)<br />
				mb_cache_entry_free(ce);<br />
			/* keep the buffer locked while modifying it. */<br />
		} else {<br />
			int offset;</p>
<p>			if (ce)<br />
				mb_cache_entry_release(ce);<br />
			unlock_buffer(bh);<br />
			ea_bdebug(bh, &laquo;cloning&raquo;);<br />
			header = kmalloc(bh->b_size, GFP_KERNEL);<br />
			error = -ENOMEM;<br />
			if (header == NULL)<br />
				goto cleanup;<br />
			memcpy(header, HDR(bh), bh->b_size);<br />
			header->h_refcount = cpu_to_le32(1);</p>
<p>			offset = (char *)here &#8211; bh->b_data;<br />
			here = ENTRY((char *)header + offset);<br />
			offset = (char *)last &#8211; bh->b_data;<br />
			last = ENTRY((char *)header + offset);<br />
		}<br />
	} else {<br />
		/* Allocate a buffer where we construct the new block. */<br />
		header = kzalloc(sb->s_blocksize, GFP_KERNEL);<br />
		error = -ENOMEM;<br />
		if (header == NULL)<br />
			goto cleanup;<br />
		end = (char *)header + sb->s_blocksize;<br />
		header->h_magic = cpu_to_le32(EXT2_XATTR_MAGIC);<br />
		header->h_blocks = header->h_refcount = cpu_to_le32(1);<br />
		last = here = ENTRY(header+1);<br />
	}</p>
<p>	/* Iff we are modifying the block in-place, bh is locked here. */</p>
<p>	if (not_found) {<br />
		/* Insert the new name. */<br />
		size_t size = EXT2_XATTR_LEN(name_len);<br />
		size_t rest = (char *)last &#8211; (char *)here;<br />
		memmove((char *)here + size, here, rest);<br />
		memset(here, 0, size);<br />
		here->e_name_index = name_index;<br />
		here->e_name_len = name_len;<br />
		memcpy(here->e_name, name, name_len);<br />
	} else {<br />
		if (!here->e_value_block &#038;&#038; here->e_value_size) {<br />
			char *first_val = (char *)header + min_offs;<br />
			size_t offs = le16_to_cpu(here->e_value_offs);<br />
			char *val = (char *)header + offs;<br />
			size_t size = EXT2_XATTR_SIZE(<br />
				le32_to_cpu(here->e_value_size));</p>
<p>			if (size == EXT2_XATTR_SIZE(value_len)) {<br />
				/* The old and the new value have the same<br />
				   size. Just replace. */<br />
				here->e_value_size = cpu_to_le32(value_len);<br />
				memset(val + size &#8211; EXT2_XATTR_PAD, 0,<br />
				       EXT2_XATTR_PAD); /* Clear pad bytes. */<br />
				memcpy(val, value, value_len);<br />
				goto skip_replace;<br />
			}</p>
<p>			/* Remove the old value. */<br />
			memmove(first_val + size, first_val, val &#8211; first_val);<br />
			memset(first_val, 0, size);<br />
			here->e_value_offs = 0;<br />
			min_offs += size;</p>
<p>			/* Adjust all value offsets. */<br />
			last = ENTRY(header+1);<br />
			while (!IS_LAST_ENTRY(last)) {<br />
				size_t o = le16_to_cpu(last->e_value_offs);<br />
				if (!last->e_value_block &#038;&#038; o < offs)<br />
					last->e_value_offs =<br />
						cpu_to_le16(o + size);<br />
				last = EXT2_XATTR_NEXT(last);<br />
			}<br />
		}<br />
		if (value == NULL) {<br />
			/* Remove the old name. */<br />
			size_t size = EXT2_XATTR_LEN(name_len);<br />
			last = ENTRY((char *)last &#8211; size);<br />
			memmove(here, (char*)here + size,<br />
				(char*)last &#8211; (char*)here);<br />
			memset(last, 0, size);<br />
		}<br />
	}</p>
<p>	if (value != NULL) {<br />
		/* Insert the new value. */<br />
		here->e_value_size = cpu_to_le32(value_len);<br />
		if (value_len) {<br />
			size_t size = EXT2_XATTR_SIZE(value_len);<br />
			char *val = (char *)header + min_offs &#8211; size;<br />
			here->e_value_offs =<br />
				cpu_to_le16((char *)val &#8211; (char *)header);<br />
			memset(val + size &#8211; EXT2_XATTR_PAD, 0,<br />
			       EXT2_XATTR_PAD); /* Clear the pad bytes. */<br />
			memcpy(val, value, value_len);<br />
		}<br />
	}</p>
<p>skip_replace:<br />
	if (IS_LAST_ENTRY(ENTRY(header+1))) {<br />
		/* This block is now empty. */<br />
		if (bh &#038;&#038; header == HDR(bh))<br />
			unlock_buffer(bh);  /* we were modifying in-place. */<br />
		error = ext2_xattr_set2(inode, bh, NULL);<br />
	} else {<br />
		ext2_xattr_rehash(header, here);<br />
		if (bh &#038;&#038; header == HDR(bh))<br />
			unlock_buffer(bh);  /* we were modifying in-place. */<br />
		error = ext2_xattr_set2(inode, bh, header);<br />
	}</p>
<p>cleanup:<br />
	brelse(bh);<br />
	if (!(bh &#038;&#038; header == HDR(bh)))<br />
		kfree(header);<br />
	up_write(&#038;EXT2_I(inode)->xattr_sem);</p>
<p>	return error;<br />
}</p>
<p>/*<br />
 * Second half of ext2_xattr_set(): Update the file system.<br />
 */<br />
static int<br />
ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,<br />
		struct ext2_xattr_header *header)<br />
{<br />
	struct super_block *sb = inode->i_sb;<br />
	struct buffer_head *new_bh = NULL;<br />
	int error;</p>
<p>	if (header) {<br />
		new_bh = ext2_xattr_cache_find(inode, header);<br />
		if (new_bh) {<br />
			/* We found an identical block in the cache. */<br />
			if (new_bh == old_bh) {<br />
				ea_bdebug(new_bh, &laquo;keeping this block&raquo;);<br />
			} else {<br />
				/* The old block is released after updating<br />
				   the inode.  */<br />
				ea_bdebug(new_bh, &laquo;reusing block&raquo;);</p>
<p>				error = -EDQUOT;<br />
				if (vfs_dq_alloc_block(inode, 1)) {<br />
					unlock_buffer(new_bh);<br />
					goto cleanup;<br />
				}<br />
				le32_add_cpu(&#038;HDR(new_bh)->h_refcount, 1);<br />
				ea_bdebug(new_bh, &laquo;refcount now=%d&raquo;,<br />
					le32_to_cpu(HDR(new_bh)->h_refcount));<br />
			}<br />
			unlock_buffer(new_bh);<br />
		} else if (old_bh &#038;&#038; header == HDR(old_bh)) {<br />
			/* Keep this block. No need to lock the block as we<br />
			   don&#8217;t need to change the reference count. */<br />
			new_bh = old_bh;<br />
			get_bh(new_bh);<br />
			ext2_xattr_cache_insert(new_bh);<br />
		} else {<br />
			/* We need to allocate a new block */<br />
			ext2_fsblk_t goal = ext2_group_first_block_no(sb,<br />
						EXT2_I(inode)->i_block_group);<br />
			int block = ext2_new_block(inode, goal, &#038;error);<br />
			if (error)<br />
				goto cleanup;<br />
			ea_idebug(inode, &laquo;creating block %d&raquo;, block);</p>
<p>			new_bh = sb_getblk(sb, block);<br />
			if (!new_bh) {<br />
				ext2_free_blocks(inode, block, 1);<br />
				error = -EIO;<br />
				goto cleanup;<br />
			}<br />
			lock_buffer(new_bh);<br />
			memcpy(new_bh->b_data, header, new_bh->b_size);<br />
			set_buffer_uptodate(new_bh);<br />
			unlock_buffer(new_bh);<br />
			ext2_xattr_cache_insert(new_bh);</p>
<p>			ext2_xattr_update_super_block(sb);<br />
		}<br />
		mark_buffer_dirty(new_bh);<br />
		if (IS_SYNC(inode)) {<br />
			sync_dirty_buffer(new_bh);<br />
			error = -EIO;<br />
			if (buffer_req(new_bh) &#038;&#038; !buffer_uptodate(new_bh))<br />
				goto cleanup;<br />
		}<br />
	}</p>
<p>	/* Update the inode. */<br />
	EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;<br />
	inode->i_ctime = CURRENT_TIME_SEC;<br />
	if (IS_SYNC(inode)) {<br />
		error = ext2_sync_inode (inode);<br />
		/* In case sync failed due to ENOSPC the inode was actually<br />
		 * written (only some dirty data were not) so we just proceed<br />
		 * as if nothing happened and cleanup the unused block */<br />
		if (error &#038;&#038; error != -ENOSPC) {<br />
			if (new_bh &#038;&#038; new_bh != old_bh)<br />
				vfs_dq_free_block(inode, 1);<br />
			goto cleanup;<br />
		}<br />
	} else<br />
		mark_inode_dirty(inode);</p>
<p>	error = 0;<br />
	if (old_bh &#038;&#038; old_bh != new_bh) {<br />
		struct mb_cache_entry *ce;</p>
<p>		/*<br />
		 * If there was an old block and we are no longer using it,<br />
		 * release the old block.<br />
		 */<br />
		ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev,<br />
					old_bh->b_blocknr);<br />
		lock_buffer(old_bh);<br />
		if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) {<br />
			/* Free the old block. */<br />
			if (ce)<br />
				mb_cache_entry_free(ce);<br />
			ea_bdebug(old_bh, &laquo;freeing&raquo;);<br />
			ext2_free_blocks(inode, old_bh->b_blocknr, 1);<br />
			/* We let our caller release old_bh, so we<br />
			 * need to duplicate the buffer before. */<br />
			get_bh(old_bh);<br />
			bforget(old_bh);<br />
		} else {<br />
			/* Decrement the refcount only. */<br />
			le32_add_cpu(&#038;HDR(old_bh)->h_refcount, -1);<br />
			if (ce)<br />
				mb_cache_entry_release(ce);<br />
			vfs_dq_free_block(inode, 1);<br />
			mark_buffer_dirty(old_bh);<br />
			ea_bdebug(old_bh, &laquo;refcount now=%d&raquo;,<br />
				le32_to_cpu(HDR(old_bh)->h_refcount));<br />
		}<br />
		unlock_buffer(old_bh);<br />
	}</p>
<p>cleanup:<br />
	brelse(new_bh);</p>
<p>	return error;<br />
}</p>
<p>/*<br />
 * ext2_xattr_delete_inode()<br />
 *<br />
 * Free extended attribute resources associated with this inode. This<br />
 * is called immediately before an inode is freed.<br />
 */<br />
void<br />
ext2_xattr_delete_inode(struct inode *inode)<br />
{<br />
	struct buffer_head *bh = NULL;<br />
	struct mb_cache_entry *ce;</p>
<p>	down_write(&#038;EXT2_I(inode)->xattr_sem);<br />
	if (!EXT2_I(inode)->i_file_acl)<br />
		goto cleanup;<br />
	bh = sb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl);<br />
	if (!bh) {<br />
		ext2_error(inode->i_sb, &laquo;ext2_xattr_delete_inode&raquo;,<br />
			&laquo;inode %ld: block %d read error&raquo;, inode->i_ino,<br />
			EXT2_I(inode)->i_file_acl);<br />
		goto cleanup;<br />
	}<br />
	ea_bdebug(bh, &laquo;b_count=%d&raquo;, atomic_read(&#038;(bh->b_count)));<br />
	if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||<br />
	    HDR(bh)->h_blocks != cpu_to_le32(1)) {<br />
		ext2_error(inode->i_sb, &laquo;ext2_xattr_delete_inode&raquo;,<br />
			&laquo;inode %ld: bad block %d&raquo;, inode->i_ino,<br />
			EXT2_I(inode)->i_file_acl);<br />
		goto cleanup;<br />
	}<br />
	ce = mb_cache_entry_get(ext2_xattr_cache, bh->b_bdev, bh->b_blocknr);<br />
	lock_buffer(bh);<br />
	if (HDR(bh)->h_refcount == cpu_to_le32(1)) {<br />
		if (ce)<br />
			mb_cache_entry_free(ce);<br />
		ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1);<br />
		get_bh(bh);<br />
		bforget(bh);<br />
		unlock_buffer(bh);<br />
	} else {<br />
		le32_add_cpu(&#038;HDR(bh)->h_refcount, -1);<br />
		if (ce)<br />
			mb_cache_entry_release(ce);<br />
		ea_bdebug(bh, &laquo;refcount now=%d&raquo;,<br />
			le32_to_cpu(HDR(bh)->h_refcount));<br />
		unlock_buffer(bh);<br />
		mark_buffer_dirty(bh);<br />
		if (IS_SYNC(inode))<br />
			sync_dirty_buffer(bh);<br />
		vfs_dq_free_block(inode, 1);<br />
	}<br />
	EXT2_I(inode)->i_file_acl = 0;</p>
<p>cleanup:<br />
	brelse(bh);<br />
	up_write(&#038;EXT2_I(inode)->xattr_sem);<br />
}</p>
<p>/*<br />
 * ext2_xattr_put_super()<br />
 *<br />
 * This is called when a file system is unmounted.<br />
 */<br />
void<br />
ext2_xattr_put_super(struct super_block *sb)<br />
{<br />
	mb_cache_shrink(sb->s_bdev);<br />
}</p>
<p>/*<br />
 * ext2_xattr_cache_insert()<br />
 *<br />
 * Create a new entry in the extended attribute cache, and insert<br />
 * it unless such an entry is already in the cache.<br />
 *<br />
 * Returns 0, or a negative error number on failure.<br />
 */<br />
static int<br />
ext2_xattr_cache_insert(struct buffer_head *bh)<br />
{<br />
	__u32 hash = le32_to_cpu(HDR(bh)->h_hash);<br />
	struct mb_cache_entry *ce;<br />
	int error;</p>
<p>	ce = mb_cache_entry_alloc(ext2_xattr_cache, GFP_NOFS);<br />
	if (!ce)<br />
		return -ENOMEM;<br />
	error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &#038;hash);<br />
	if (error) {<br />
		mb_cache_entry_free(ce);<br />
		if (error == -EBUSY) {<br />
			ea_bdebug(bh, &laquo;already in cache (%d cache entries)&raquo;,<br />
				atomic_read(&#038;ext2_xattr_cache->c_entry_count));<br />
			error = 0;<br />
		}<br />
	} else {<br />
		ea_bdebug(bh, &laquo;inserting [%x] (%d cache entries)&raquo;, (int)hash,<br />
			  atomic_read(&#038;ext2_xattr_cache->c_entry_count));<br />
		mb_cache_entry_release(ce);<br />
	}<br />
	return error;<br />
}</p>
<p>/*<br />
 * ext2_xattr_cmp()<br />
 *<br />
 * Compare two extended attribute blocks for equality.<br />
 *<br />
 * Returns 0 if the blocks are equal, 1 if they differ, and<br />
 * a negative error number on errors.<br />
 */<br />
static int<br />
ext2_xattr_cmp(struct ext2_xattr_header *header1,<br />
	       struct ext2_xattr_header *header2)<br />
{<br />
	struct ext2_xattr_entry *entry1, *entry2;</p>
<p>	entry1 = ENTRY(header1+1);<br />
	entry2 = ENTRY(header2+1);<br />
	while (!IS_LAST_ENTRY(entry1)) {<br />
		if (IS_LAST_ENTRY(entry2))<br />
			return 1;<br />
		if (entry1->e_hash != entry2->e_hash ||<br />
		    entry1->e_name_index != entry2->e_name_index ||<br />
		    entry1->e_name_len != entry2->e_name_len ||<br />
		    entry1->e_value_size != entry2->e_value_size ||<br />
		    memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))<br />
			return 1;<br />
		if (entry1->e_value_block != 0 || entry2->e_value_block != 0)<br />
			return -EIO;<br />
		if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),<br />
			   (char *)header2 + le16_to_cpu(entry2->e_value_offs),<br />
			   le32_to_cpu(entry1->e_value_size)))<br />
			return 1;</p>
<p>		entry1 = EXT2_XATTR_NEXT(entry1);<br />
		entry2 = EXT2_XATTR_NEXT(entry2);<br />
	}<br />
	if (!IS_LAST_ENTRY(entry2))<br />
		return 1;<br />
	return 0;<br />
}</p>
<p>/*<br />
 * ext2_xattr_cache_find()<br />
 *<br />
 * Find an identical extended attribute block.<br />
 *<br />
 * Returns a locked buffer head to the block found, or NULL if such<br />
 * a block was not found or an error occurred.<br />
 */<br />
static struct buffer_head *<br />
ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header)<br />
{<br />
	__u32 hash = le32_to_cpu(header->h_hash);<br />
	struct mb_cache_entry *ce;</p>
<p>	if (!header->h_hash)<br />
		return NULL;  /* never share */<br />
	ea_idebug(inode, &laquo;looking for cached blocks [%x]&laquo;, (int)hash);<br />
again:<br />
	ce = mb_cache_entry_find_first(ext2_xattr_cache, 0,<br />
				       inode->i_sb->s_bdev, hash);<br />
	while (ce) {<br />
		struct buffer_head *bh;</p>
<p>		if (IS_ERR(ce)) {<br />
			if (PTR_ERR(ce) == -EAGAIN)<br />
				goto again;<br />
			break;<br />
		}</p>
<p>		bh = sb_bread(inode->i_sb, ce->e_block);<br />
		if (!bh) {<br />
			ext2_error(inode->i_sb, &laquo;ext2_xattr_cache_find&raquo;,<br />
				&laquo;inode %ld: block %ld read error&raquo;,<br />
				inode->i_ino, (unsigned long) ce->e_block);<br />
		} else {<br />
			lock_buffer(bh);<br />
			if (le32_to_cpu(HDR(bh)->h_refcount) ><br />
				   EXT2_XATTR_REFCOUNT_MAX) {<br />
				ea_idebug(inode, &laquo;block %ld refcount %d>%d&raquo;,<br />
					  (unsigned long) ce->e_block,<br />
					  le32_to_cpu(HDR(bh)->h_refcount),<br />
					  EXT2_XATTR_REFCOUNT_MAX);<br />
			} else if (!ext2_xattr_cmp(header, HDR(bh))) {<br />
				ea_bdebug(bh, &laquo;b_count=%d&raquo;,<br />
					  atomic_read(&#038;(bh->b_count)));<br />
				mb_cache_entry_release(ce);<br />
				return bh;<br />
			}<br />
			unlock_buffer(bh);<br />
			brelse(bh);<br />
		}<br />
		ce = mb_cache_entry_find_next(ce, 0, inode->i_sb->s_bdev, hash);<br />
	}<br />
	return NULL;<br />
}</p>
<p>#define NAME_HASH_SHIFT 5<br />
#define VALUE_HASH_SHIFT 16</p>
<p>/*<br />
 * ext2_xattr_hash_entry()<br />
 *<br />
 * Compute the hash of an extended attribute.<br />
 */<br />
static inline void ext2_xattr_hash_entry(struct ext2_xattr_header *header,<br />
					 struct ext2_xattr_entry *entry)<br />
{<br />
	__u32 hash = 0;<br />
	char *name = entry->e_name;<br />
	int n;</p>
<p>	for (n=0; n < entry->e_name_len; n++) {<br />
		hash = (hash << NAME_HASH_SHIFT) ^<br />
		       (hash >> (8*sizeof(hash) &#8211; NAME_HASH_SHIFT)) ^<br />
		       *name++;<br />
	}</p>
<p>	if (entry->e_value_block == 0 &#038;&#038; entry->e_value_size != 0) {<br />
		__le32 *value = (__le32 *)((char *)header +<br />
			le16_to_cpu(entry->e_value_offs));<br />
		for (n = (le32_to_cpu(entry->e_value_size) +<br />
		     EXT2_XATTR_ROUND) >> EXT2_XATTR_PAD_BITS; n; n&#8211;) {<br />
			hash = (hash << VALUE_HASH_SHIFT) ^<br />
			       (hash >> (8*sizeof(hash) &#8211; VALUE_HASH_SHIFT)) ^<br />
			       le32_to_cpu(*value++);<br />
		}<br />
	}<br />
	entry->e_hash = cpu_to_le32(hash);<br />
}</p>
<p>#undef NAME_HASH_SHIFT<br />
#undef VALUE_HASH_SHIFT</p>
<p>#define BLOCK_HASH_SHIFT 16</p>
<p>/*<br />
 * ext2_xattr_rehash()<br />
 *<br />
 * Re-compute the extended attribute hash value after an entry has changed.<br />
 */<br />
static void ext2_xattr_rehash(struct ext2_xattr_header *header,<br />
			      struct ext2_xattr_entry *entry)<br />
{<br />
	struct ext2_xattr_entry *here;<br />
	__u32 hash = 0;</p>
<p>	ext2_xattr_hash_entry(header, entry);<br />
	here = ENTRY(header+1);<br />
	while (!IS_LAST_ENTRY(here)) {<br />
		if (!here->e_hash) {<br />
			/* Block is not shared if an entry&#8217;s hash value == 0 */<br />
			hash = 0;<br />
			break;<br />
		}<br />
		hash = (hash << BLOCK_HASH_SHIFT) ^<br />
		       (hash >> (8*sizeof(hash) &#8211; BLOCK_HASH_SHIFT)) ^<br />
		       le32_to_cpu(here->e_hash);<br />
		here = EXT2_XATTR_NEXT(here);<br />
	}<br />
	header->h_hash = cpu_to_le32(hash);<br />
}</p>
<p>#undef BLOCK_HASH_SHIFT</p>
<p>int __init<br />
init_ext2_xattr(void)<br />
{<br />
	ext2_xattr_cache = mb_cache_create(&raquo;ext2_xattr&raquo;, NULL,<br />
		sizeof(struct mb_cache_entry) +<br />
		sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6);<br />
	if (!ext2_xattr_cache)<br />
		return -ENOMEM;<br />
	return 0;<br />
}</p>
<p>void<br />
exit_ext2_xattr(void)<br />
{<br />
	mb_cache_destroy(ext2_xattr_cache);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/xattr-c-4/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>symlink.c</title>
		<link>http://lynyrd.ru/symlink-c-5</link>
		<comments>http://lynyrd.ru/symlink-c-5#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:45:48 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/symlink-c-5</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/symlink.c
 *
 * Only fast symlinks left here &#8211; the rest is done by generic code. AV, 1999
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/symlink.c<span id="more-1158"></span><br />
 *<br />
 * Only fast symlinks left here &#8211; the rest is done by generic code. AV, 1999<br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/symlink.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  ext2 symlink handling code<br />
 */</p>
<p>#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include
<linux/namei.h>
<p>static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)<br />
{<br />
	struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);<br />
	nd_set_link(nd, (char *)ei->i_data);<br />
	return NULL;<br />
}</p>
<p>const struct inode_operations ext2_symlink_inode_operations = {<br />
	.readlink	= generic_readlink,<br />
	.follow_link	= page_follow_link_light,<br />
	.put_link	= page_put_link,<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext2_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
};</p>
<p>const struct inode_operations ext2_fast_symlink_inode_operations = {<br />
	.readlink	= generic_readlink,<br />
	.follow_link	= ext2_follow_link,<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext2_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/symlink-c-5/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>super.c</title>
		<link>http://lynyrd.ru/super-c-8</link>
		<comments>http://lynyrd.ru/super-c-8#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:45:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/super-c-8</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/super.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/inode.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  Big-endian to little-endian ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/super.c<span id="more-1157"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/inode.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 */</p>
<p>#include
<linux/module.h>
#include
<linux/string.h>
#include
<linux/fs.h>
#include
<linux/slab.h>
#include
<linux/init.h>
#include
<linux/blkdev.h>
#include
<linux/parser.h>
#include
<linux/random.h>
#include
<linux/buffer_head.h>
#include
<linux/exportfs.h>
#include
<linux/smp_lock.h>
#include
<linux/vfs.h>
#include
<linux/seq_file.h>
#include
<linux/mount.h>
#include
<linux/log2.h>
#include
<linux/quotaops.h>
#include <asm/uaccess.h><br />
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;<br />
#include &laquo;xip.h&raquo;</p>
<p>static void ext2_sync_super(struct super_block *sb,<br />
			    struct ext2_super_block *es);<br />
static int ext2_remount (struct super_block * sb, int * flags, char * data);<br />
static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf);<br />
static int ext2_sync_fs(struct super_block *sb, int wait);</p>
<p>void ext2_error (struct super_block * sb, const char * function,<br />
		 const char * fmt, &#8230;)<br />
{<br />
	va_list args;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	struct ext2_super_block *es = sbi->s_es;</p>
<p>	if (!(sb->s_flags &#038; MS_RDONLY)) {<br />
		sbi->s_mount_state |= EXT2_ERROR_FS;<br />
		es->s_state |= cpu_to_le16(EXT2_ERROR_FS);<br />
		ext2_sync_super(sb, es);<br />
	}</p>
<p>	va_start(args, fmt);<br />
	printk(KERN_CRIT &laquo;EXT2-fs error (device %s): %s: &laquo;,sb->s_id, function);<br />
	vprintk(fmt, args);<br />
	printk(&raquo;\n&raquo;);<br />
	va_end(args);</p>
<p>	if (test_opt(sb, ERRORS_PANIC))<br />
		panic(&raquo;EXT2-fs panic from previous error\n&raquo;);<br />
	if (test_opt(sb, ERRORS_RO)) {<br />
		printk(&raquo;Remounting filesystem read-only\n&raquo;);<br />
		sb->s_flags |= MS_RDONLY;<br />
	}<br />
}</p>
<p>void ext2_warning (struct super_block * sb, const char * function,<br />
		   const char * fmt, &#8230;)<br />
{<br />
	va_list args;</p>
<p>	va_start(args, fmt);<br />
	printk(KERN_WARNING &laquo;EXT2-fs warning (device %s): %s: &laquo;,<br />
	       sb->s_id, function);<br />
	vprintk(fmt, args);<br />
	printk(&raquo;\n&raquo;);<br />
	va_end(args);<br />
}</p>
<p>void ext2_update_dynamic_rev(struct super_block *sb)<br />
{<br />
	struct ext2_super_block *es = EXT2_SB(sb)->s_es;</p>
<p>	if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)<br />
		return;</p>
<p>	ext2_warning(sb, __func__,<br />
		     &laquo;updating to rev %d because of new feature flag, &raquo;<br />
		     &laquo;running e2fsck is recommended&raquo;,<br />
		     EXT2_DYNAMIC_REV);</p>
<p>	es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);<br />
	es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE);<br />
	es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV);<br />
	/* leave es->s_feature_*compat flags alone */<br />
	/* es->s_uuid will be set by e2fsck if empty */</p>
<p>	/*<br />
	 * The rest of the superblock fields should be zero, and if not it<br />
	 * means they are likely already in use, so leave them alone.  We<br />
	 * can leave it up to e2fsck to clean up any inconsistencies there.<br />
	 */<br />
}</p>
<p>static void ext2_put_super (struct super_block * sb)<br />
{<br />
	int db_count;<br />
	int i;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);</p>
<p>	lock_kernel();</p>
<p>	if (sb->s_dirt)<br />
		ext2_write_super(sb);</p>
<p>	ext2_xattr_put_super(sb);<br />
	if (!(sb->s_flags &#038; MS_RDONLY)) {<br />
		struct ext2_super_block *es = sbi->s_es;</p>
<p>		es->s_state = cpu_to_le16(sbi->s_mount_state);<br />
		ext2_sync_super(sb, es);<br />
	}<br />
	db_count = sbi->s_gdb_count;<br />
	for (i = 0; i < db_count; i++)<br />
		if (sbi->s_group_desc[i])<br />
			brelse (sbi->s_group_desc[i]);<br />
	kfree(sbi->s_group_desc);<br />
	kfree(sbi->s_debts);<br />
	percpu_counter_destroy(&#038;sbi->s_freeblocks_counter);<br />
	percpu_counter_destroy(&#038;sbi->s_freeinodes_counter);<br />
	percpu_counter_destroy(&#038;sbi->s_dirs_counter);<br />
	brelse (sbi->s_sbh);<br />
	sb->s_fs_info = NULL;<br />
	kfree(sbi->s_blockgroup_lock);<br />
	kfree(sbi);</p>
<p>	unlock_kernel();<br />
}</p>
<p>static struct kmem_cache * ext2_inode_cachep;</p>
<p>static struct inode *ext2_alloc_inode(struct super_block *sb)<br />
{<br />
	struct ext2_inode_info *ei;<br />
	ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, GFP_KERNEL);<br />
	if (!ei)<br />
		return NULL;<br />
	ei->i_block_alloc_info = NULL;<br />
	ei->vfs_inode.i_version = 1;<br />
	return &#038;ei->vfs_inode;<br />
}</p>
<p>static void ext2_destroy_inode(struct inode *inode)<br />
{<br />
	kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));<br />
}</p>
<p>static void init_once(void *foo)<br />
{<br />
	struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;</p>
<p>	rwlock_init(&#038;ei->i_meta_lock);<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	init_rwsem(&#038;ei->xattr_sem);<br />
#endif<br />
	mutex_init(&#038;ei->truncate_mutex);<br />
	inode_init_once(&#038;ei->vfs_inode);<br />
}</p>
<p>static int init_inodecache(void)<br />
{<br />
	ext2_inode_cachep = kmem_cache_create(&raquo;ext2_inode_cache&raquo;,<br />
					     sizeof(struct ext2_inode_info),<br />
					     0, (SLAB_RECLAIM_ACCOUNT|<br />
						SLAB_MEM_SPREAD),<br />
					     init_once);<br />
	if (ext2_inode_cachep == NULL)<br />
		return -ENOMEM;<br />
	return 0;<br />
}</p>
<p>static void destroy_inodecache(void)<br />
{<br />
	kmem_cache_destroy(ext2_inode_cachep);<br />
}</p>
<p>static void ext2_clear_inode(struct inode *inode)<br />
{<br />
	struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;<br />
	ext2_discard_reservation(inode);<br />
	EXT2_I(inode)->i_block_alloc_info = NULL;<br />
	if (unlikely(rsv))<br />
		kfree(rsv);<br />
}</p>
<p>static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)<br />
{<br />
	struct super_block *sb = vfs->mnt_sb;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	struct ext2_super_block *es = sbi->s_es;<br />
	unsigned long def_mount_opts;</p>
<p>	def_mount_opts = le32_to_cpu(es->s_default_mount_opts);</p>
<p>	if (sbi->s_sb_block != 1)<br />
		seq_printf(seq, &laquo;,sb=%lu&raquo;, sbi->s_sb_block);<br />
	if (test_opt(sb, MINIX_DF))<br />
		seq_puts(seq, &laquo;,minixdf&raquo;);<br />
	if (test_opt(sb, GRPID))<br />
		seq_puts(seq, &laquo;,grpid&raquo;);<br />
	if (!test_opt(sb, GRPID) &#038;&#038; (def_mount_opts &#038; EXT2_DEFM_BSDGROUPS))<br />
		seq_puts(seq, &laquo;,nogrpid&raquo;);<br />
	if (sbi->s_resuid != EXT2_DEF_RESUID ||<br />
	    le16_to_cpu(es->s_def_resuid) != EXT2_DEF_RESUID) {<br />
		seq_printf(seq, &laquo;,resuid=%u&raquo;, sbi->s_resuid);<br />
	}<br />
	if (sbi->s_resgid != EXT2_DEF_RESGID ||<br />
	    le16_to_cpu(es->s_def_resgid) != EXT2_DEF_RESGID) {<br />
		seq_printf(seq, &laquo;,resgid=%u&raquo;, sbi->s_resgid);<br />
	}<br />
	if (test_opt(sb, ERRORS_RO)) {<br />
		int def_errors = le16_to_cpu(es->s_errors);</p>
<p>		if (def_errors == EXT2_ERRORS_PANIC ||<br />
		    def_errors == EXT2_ERRORS_CONTINUE) {<br />
			seq_puts(seq, &laquo;,errors=remount-ro&raquo;);<br />
		}<br />
	}<br />
	if (test_opt(sb, ERRORS_CONT))<br />
		seq_puts(seq, &laquo;,errors=continue&raquo;);<br />
	if (test_opt(sb, ERRORS_PANIC))<br />
		seq_puts(seq, &laquo;,errors=panic&raquo;);<br />
	if (test_opt(sb, NO_UID32))<br />
		seq_puts(seq, &laquo;,nouid32&#8243;);<br />
	if (test_opt(sb, DEBUG))<br />
		seq_puts(seq, &laquo;,debug&raquo;);<br />
	if (test_opt(sb, OLDALLOC))<br />
		seq_puts(seq, &laquo;,oldalloc&raquo;);</p>
<p>#ifdef CONFIG_EXT2_FS_XATTR<br />
	if (test_opt(sb, XATTR_USER))<br />
		seq_puts(seq, &laquo;,user_xattr&raquo;);<br />
	if (!test_opt(sb, XATTR_USER) &#038;&#038;<br />
	    (def_mount_opts &#038; EXT2_DEFM_XATTR_USER)) {<br />
		seq_puts(seq, &laquo;,nouser_xattr&raquo;);<br />
	}<br />
#endif</p>
<p>#ifdef CONFIG_EXT2_FS_POSIX_ACL<br />
	if (test_opt(sb, POSIX_ACL))<br />
		seq_puts(seq, &laquo;,acl&raquo;);<br />
	if (!test_opt(sb, POSIX_ACL) &#038;&#038; (def_mount_opts &#038; EXT2_DEFM_ACL))<br />
		seq_puts(seq, &laquo;,noacl&raquo;);<br />
#endif</p>
<p>	if (test_opt(sb, NOBH))<br />
		seq_puts(seq, &laquo;,nobh&raquo;);</p>
<p>#if defined(CONFIG_QUOTA)<br />
	if (sbi->s_mount_opt &#038; EXT2_MOUNT_USRQUOTA)<br />
		seq_puts(seq, &laquo;,usrquota&raquo;);</p>
<p>	if (sbi->s_mount_opt &#038; EXT2_MOUNT_GRPQUOTA)<br />
		seq_puts(seq, &laquo;,grpquota&raquo;);<br />
#endif</p>
<p>#if defined(CONFIG_EXT2_FS_XIP)<br />
	if (sbi->s_mount_opt &#038; EXT2_MOUNT_XIP)<br />
		seq_puts(seq, &laquo;,xip&raquo;);<br />
#endif</p>
<p>	if (!test_opt(sb, RESERVATION))<br />
		seq_puts(seq, &laquo;,noreservation&raquo;);</p>
<p>	return 0;<br />
}</p>
<p>#ifdef CONFIG_QUOTA<br />
static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off);<br />
static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);<br />
#endif</p>
<p>static const struct super_operations ext2_sops = {<br />
	.alloc_inode	= ext2_alloc_inode,<br />
	.destroy_inode	= ext2_destroy_inode,<br />
	.write_inode	= ext2_write_inode,<br />
	.delete_inode	= ext2_delete_inode,<br />
	.put_super	= ext2_put_super,<br />
	.write_super	= ext2_write_super,<br />
	.sync_fs	= ext2_sync_fs,<br />
	.statfs		= ext2_statfs,<br />
	.remount_fs	= ext2_remount,<br />
	.clear_inode	= ext2_clear_inode,<br />
	.show_options	= ext2_show_options,<br />
#ifdef CONFIG_QUOTA<br />
	.quota_read	= ext2_quota_read,<br />
	.quota_write	= ext2_quota_write,<br />
#endif<br />
};</p>
<p>static struct inode *ext2_nfs_get_inode(struct super_block *sb,<br />
		u64 ino, u32 generation)<br />
{<br />
	struct inode *inode;</p>
<p>	if (ino < EXT2_FIRST_INO(sb) &#038;&#038; ino != EXT2_ROOT_INO)<br />
		return ERR_PTR(-ESTALE);<br />
	if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))<br />
		return ERR_PTR(-ESTALE);</p>
<p>	/* iget isn&#8217;t really right if the inode is currently unallocated!!<br />
	 * ext2_read_inode currently does appropriate checks, but<br />
	 * it might be &laquo;neater&raquo; to call ext2_get_inode first and check<br />
	 * if the inode is valid&#8230;..<br />
	 */<br />
	inode = ext2_iget(sb, ino);<br />
	if (IS_ERR(inode))<br />
		return ERR_CAST(inode);<br />
	if (generation &#038;&#038; inode->i_generation != generation) {<br />
		/* we didn&#8217;t find the right inode.. */<br />
		iput(inode);<br />
		return ERR_PTR(-ESTALE);<br />
	}<br />
	return inode;<br />
}</p>
<p>static struct dentry *ext2_fh_to_dentry(struct super_block *sb, struct fid *fid,<br />
		int fh_len, int fh_type)<br />
{<br />
	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,<br />
				    ext2_nfs_get_inode);<br />
}</p>
<p>static struct dentry *ext2_fh_to_parent(struct super_block *sb, struct fid *fid,<br />
		int fh_len, int fh_type)<br />
{<br />
	return generic_fh_to_parent(sb, fid, fh_len, fh_type,<br />
				    ext2_nfs_get_inode);<br />
}</p>
<p>/* Yes, most of these are left as NULL!!<br />
 * A NULL value implies the default, which works with ext2-like file<br />
 * systems, but can be improved upon.<br />
 * Currently only get_parent is required.<br />
 */<br />
static const struct export_operations ext2_export_ops = {<br />
	.fh_to_dentry = ext2_fh_to_dentry,<br />
	.fh_to_parent = ext2_fh_to_parent,<br />
	.get_parent = ext2_get_parent,<br />
};</p>
<p>static unsigned long get_sb_block(void **data)<br />
{<br />
	unsigned long 	sb_block;<br />
	char 		*options = (char *) *data;</p>
<p>	if (!options || strncmp(options, &laquo;sb=&raquo;, 3) != 0)<br />
		return 1;	/* Default location */<br />
	options += 3;<br />
	sb_block = simple_strtoul(options, &#038;options, 0);<br />
	if (*options &#038;&#038; *options != &#8216;,&#8217;) {<br />
		printk(&raquo;EXT2-fs: Invalid sb specification: %s\n&raquo;,<br />
		       (char *) *data);<br />
		return 1;<br />
	}<br />
	if (*options == &#8216;,&#8217;)<br />
		options++;<br />
	*data = (void *) options;<br />
	return sb_block;<br />
}</p>
<p>enum {<br />
	Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,<br />
	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic,<br />
	Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,<br />
	Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,<br />
	Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota,<br />
	Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation<br />
};</p>
<p>static const match_table_t tokens = {<br />
	{Opt_bsd_df, &laquo;bsddf&raquo;},<br />
	{Opt_minix_df, &laquo;minixdf&raquo;},<br />
	{Opt_grpid, &laquo;grpid&raquo;},<br />
	{Opt_grpid, &laquo;bsdgroups&raquo;},<br />
	{Opt_nogrpid, &laquo;nogrpid&raquo;},<br />
	{Opt_nogrpid, &laquo;sysvgroups&raquo;},<br />
	{Opt_resgid, &laquo;resgid=%u&raquo;},<br />
	{Opt_resuid, &laquo;resuid=%u&raquo;},<br />
	{Opt_sb, &laquo;sb=%u&raquo;},<br />
	{Opt_err_cont, &laquo;errors=continue&raquo;},<br />
	{Opt_err_panic, &laquo;errors=panic&raquo;},<br />
	{Opt_err_ro, &laquo;errors=remount-ro&raquo;},<br />
	{Opt_nouid32, &laquo;nouid32&#8243;},<br />
	{Opt_nocheck, &laquo;check=none&raquo;},<br />
	{Opt_nocheck, &laquo;nocheck&raquo;},<br />
	{Opt_debug, &laquo;debug&raquo;},<br />
	{Opt_oldalloc, &laquo;oldalloc&raquo;},<br />
	{Opt_orlov, &laquo;orlov&raquo;},<br />
	{Opt_nobh, &laquo;nobh&raquo;},<br />
	{Opt_user_xattr, &laquo;user_xattr&raquo;},<br />
	{Opt_nouser_xattr, &laquo;nouser_xattr&raquo;},<br />
	{Opt_acl, &laquo;acl&raquo;},<br />
	{Opt_noacl, &laquo;noacl&raquo;},<br />
	{Opt_xip, &laquo;xip&raquo;},<br />
	{Opt_grpquota, &laquo;grpquota&raquo;},<br />
	{Opt_ignore, &laquo;noquota&raquo;},<br />
	{Opt_quota, &laquo;quota&raquo;},<br />
	{Opt_usrquota, &laquo;usrquota&raquo;},<br />
	{Opt_reservation, &laquo;reservation&raquo;},<br />
	{Opt_noreservation, &laquo;noreservation&raquo;},<br />
	{Opt_err, NULL}<br />
};</p>
<p>static int parse_options (char * options,<br />
			  struct ext2_sb_info *sbi)<br />
{<br />
	char * p;<br />
	substring_t args[MAX_OPT_ARGS];<br />
	int option;</p>
<p>	if (!options)<br />
		return 1;</p>
<p>	while ((p = strsep (&#038;options, &laquo;,&raquo;)) != NULL) {<br />
		int token;<br />
		if (!*p)<br />
			continue;</p>
<p>		token = match_token(p, tokens, args);<br />
		switch (token) {<br />
		case Opt_bsd_df:<br />
			clear_opt (sbi->s_mount_opt, MINIX_DF);<br />
			break;<br />
		case Opt_minix_df:<br />
			set_opt (sbi->s_mount_opt, MINIX_DF);<br />
			break;<br />
		case Opt_grpid:<br />
			set_opt (sbi->s_mount_opt, GRPID);<br />
			break;<br />
		case Opt_nogrpid:<br />
			clear_opt (sbi->s_mount_opt, GRPID);<br />
			break;<br />
		case Opt_resuid:<br />
			if (match_int(&#038;args[0], &#038;option))<br />
				return 0;<br />
			sbi->s_resuid = option;<br />
			break;<br />
		case Opt_resgid:<br />
			if (match_int(&#038;args[0], &#038;option))<br />
				return 0;<br />
			sbi->s_resgid = option;<br />
			break;<br />
		case Opt_sb:<br />
			/* handled by get_sb_block() instead of here */<br />
			/* *sb_block = match_int(&#038;args[0]); */<br />
			break;<br />
		case Opt_err_panic:<br />
			clear_opt (sbi->s_mount_opt, ERRORS_CONT);<br />
			clear_opt (sbi->s_mount_opt, ERRORS_RO);<br />
			set_opt (sbi->s_mount_opt, ERRORS_PANIC);<br />
			break;<br />
		case Opt_err_ro:<br />
			clear_opt (sbi->s_mount_opt, ERRORS_CONT);<br />
			clear_opt (sbi->s_mount_opt, ERRORS_PANIC);<br />
			set_opt (sbi->s_mount_opt, ERRORS_RO);<br />
			break;<br />
		case Opt_err_cont:<br />
			clear_opt (sbi->s_mount_opt, ERRORS_RO);<br />
			clear_opt (sbi->s_mount_opt, ERRORS_PANIC);<br />
			set_opt (sbi->s_mount_opt, ERRORS_CONT);<br />
			break;<br />
		case Opt_nouid32:<br />
			set_opt (sbi->s_mount_opt, NO_UID32);<br />
			break;<br />
		case Opt_nocheck:<br />
			clear_opt (sbi->s_mount_opt, CHECK);<br />
			break;<br />
		case Opt_debug:<br />
			set_opt (sbi->s_mount_opt, DEBUG);<br />
			break;<br />
		case Opt_oldalloc:<br />
			set_opt (sbi->s_mount_opt, OLDALLOC);<br />
			break;<br />
		case Opt_orlov:<br />
			clear_opt (sbi->s_mount_opt, OLDALLOC);<br />
			break;<br />
		case Opt_nobh:<br />
			set_opt (sbi->s_mount_opt, NOBH);<br />
			break;<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
		case Opt_user_xattr:<br />
			set_opt (sbi->s_mount_opt, XATTR_USER);<br />
			break;<br />
		case Opt_nouser_xattr:<br />
			clear_opt (sbi->s_mount_opt, XATTR_USER);<br />
			break;<br />
#else<br />
		case Opt_user_xattr:<br />
		case Opt_nouser_xattr:<br />
			printk(&raquo;EXT2 (no)user_xattr options not supported\n&raquo;);<br />
			break;<br />
#endif<br />
#ifdef CONFIG_EXT2_FS_POSIX_ACL<br />
		case Opt_acl:<br />
			set_opt(sbi->s_mount_opt, POSIX_ACL);<br />
			break;<br />
		case Opt_noacl:<br />
			clear_opt(sbi->s_mount_opt, POSIX_ACL);<br />
			break;<br />
#else<br />
		case Opt_acl:<br />
		case Opt_noacl:<br />
			printk(&raquo;EXT2 (no)acl options not supported\n&raquo;);<br />
			break;<br />
#endif<br />
		case Opt_xip:<br />
#ifdef CONFIG_EXT2_FS_XIP<br />
			set_opt (sbi->s_mount_opt, XIP);<br />
#else<br />
			printk(&raquo;EXT2 xip option not supported\n&raquo;);<br />
#endif<br />
			break;</p>
<p>#if defined(CONFIG_QUOTA)<br />
		case Opt_quota:<br />
		case Opt_usrquota:<br />
			set_opt(sbi->s_mount_opt, USRQUOTA);<br />
			break;</p>
<p>		case Opt_grpquota:<br />
			set_opt(sbi->s_mount_opt, GRPQUOTA);<br />
			break;<br />
#else<br />
		case Opt_quota:<br />
		case Opt_usrquota:<br />
		case Opt_grpquota:<br />
			printk(KERN_ERR<br />
				&laquo;EXT2-fs: quota operations not supported.\n&raquo;);</p>
<p>			break;<br />
#endif</p>
<p>		case Opt_reservation:<br />
			set_opt(sbi->s_mount_opt, RESERVATION);<br />
			printk(&raquo;reservations ON\n&raquo;);<br />
			break;<br />
		case Opt_noreservation:<br />
			clear_opt(sbi->s_mount_opt, RESERVATION);<br />
			printk(&raquo;reservations OFF\n&raquo;);<br />
			break;<br />
		case Opt_ignore:<br />
			break;<br />
		default:<br />
			return 0;<br />
		}<br />
	}<br />
	return 1;<br />
}</p>
<p>static int ext2_setup_super (struct super_block * sb,<br />
			      struct ext2_super_block * es,<br />
			      int read_only)<br />
{<br />
	int res = 0;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);</p>
<p>	if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) {<br />
		printk (&raquo;EXT2-fs warning: revision level too high, &raquo;<br />
			&laquo;forcing read-only mode\n&raquo;);<br />
		res = MS_RDONLY;<br />
	}<br />
	if (read_only)<br />
		return res;<br />
	if (!(sbi->s_mount_state &#038; EXT2_VALID_FS))<br />
		printk (&raquo;EXT2-fs warning: mounting unchecked fs, &raquo;<br />
			&laquo;running e2fsck is recommended\n&raquo;);<br />
	else if ((sbi->s_mount_state &#038; EXT2_ERROR_FS))<br />
		printk (&raquo;EXT2-fs warning: mounting fs with errors, &raquo;<br />
			&laquo;running e2fsck is recommended\n&raquo;);<br />
	else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &#038;&#038;<br />
		 le16_to_cpu(es->s_mnt_count) >=<br />
		 (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))<br />
		printk (&raquo;EXT2-fs warning: maximal mount count reached, &raquo;<br />
			&laquo;running e2fsck is recommended\n&raquo;);<br />
	else if (le32_to_cpu(es->s_checkinterval) &#038;&#038;<br />
		(le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= get_seconds()))<br />
		printk ("EXT2-fs warning: checktime reached, "<br />
			"running e2fsck is recommended\n");<br />
	if (!le16_to_cpu(es->s_max_mnt_count))<br />
		es->s_max_mnt_count = cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT);<br />
	le16_add_cpu(&#038;es->s_mnt_count, 1);<br />
	ext2_write_super(sb);<br />
	if (test_opt (sb, DEBUG))<br />
		printk (&raquo;[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "<br />
			"bpg=%lu, ipg=%lu, mo=%04lx]\n&raquo;,<br />
			EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,<br />
			sbi->s_frag_size,<br />
			sbi->s_groups_count,<br />
			EXT2_BLOCKS_PER_GROUP(sb),<br />
			EXT2_INODES_PER_GROUP(sb),<br />
			sbi->s_mount_opt);<br />
	return res;<br />
}</p>
<p>static int ext2_check_descriptors(struct super_block *sb)<br />
{<br />
	int i;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);</p>
<p>	ext2_debug (&raquo;Checking group descriptors&raquo;);</p>
<p>	for (i = 0; i < sbi->s_groups_count; i++) {<br />
		struct ext2_group_desc *gdp = ext2_get_group_desc(sb, i, NULL);<br />
		ext2_fsblk_t first_block = ext2_group_first_block_no(sb, i);<br />
		ext2_fsblk_t last_block;</p>
<p>		if (i == sbi->s_groups_count &#8211; 1)<br />
			last_block = le32_to_cpu(sbi->s_es->s_blocks_count) &#8211; 1;<br />
		else<br />
			last_block = first_block +<br />
				(EXT2_BLOCKS_PER_GROUP(sb) &#8211; 1);</p>
<p>		if (le32_to_cpu(gdp->bg_block_bitmap) < first_block ||<br />
		    le32_to_cpu(gdp->bg_block_bitmap) > last_block)<br />
		{<br />
			ext2_error (sb, &laquo;ext2_check_descriptors&raquo;,<br />
				    &laquo;Block bitmap for group %d&raquo;<br />
				    &raquo; not in group (block %lu)!&raquo;,<br />
				    i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));<br />
			return 0;<br />
		}<br />
		if (le32_to_cpu(gdp->bg_inode_bitmap) < first_block ||<br />
		    le32_to_cpu(gdp->bg_inode_bitmap) > last_block)<br />
		{<br />
			ext2_error (sb, &laquo;ext2_check_descriptors&raquo;,<br />
				    &laquo;Inode bitmap for group %d&raquo;<br />
				    &raquo; not in group (block %lu)!&raquo;,<br />
				    i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));<br />
			return 0;<br />
		}<br />
		if (le32_to_cpu(gdp->bg_inode_table) < first_block ||<br />
		    le32_to_cpu(gdp->bg_inode_table) + sbi->s_itb_per_group &#8211; 1 ><br />
		    last_block)<br />
		{<br />
			ext2_error (sb, &laquo;ext2_check_descriptors&raquo;,<br />
				    &laquo;Inode table for group %d&raquo;<br />
				    &raquo; not in group (block %lu)!&raquo;,<br />
				    i, (unsigned long) le32_to_cpu(gdp->bg_inode_table));<br />
			return 0;<br />
		}<br />
	}<br />
	return 1;<br />
}</p>
<p>/*<br />
 * Maximal file size.  There is a direct, and {,double-,triple-}indirect<br />
 * block limit, and also a limit of (2^32 &#8211; 1) 512-byte sectors in i_blocks.<br />
 * We need to be 1 filesystem block less than the 2^32 sector limit.<br />
 */<br />
static loff_t ext2_max_size(int bits)<br />
{<br />
	loff_t res = EXT2_NDIR_BLOCKS;<br />
	int meta_blocks;<br />
	loff_t upper_limit;</p>
<p>	/* This is calculated to be the largest file size for a<br />
	 * dense, file such that the total number of<br />
	 * sectors in the file, including data and all indirect blocks,<br />
	 * does not exceed 2^32 -1<br />
	 * __u32 i_blocks representing the total number of<br />
	 * 512 bytes blocks of the file<br />
	 */<br />
	upper_limit = (1LL << 32) - 1;</p>
<p>	/* total blocks in file system block size */<br />
	upper_limit >>= (bits &#8211; 9);</p>
<p>	/* indirect blocks */<br />
	meta_blocks = 1;<br />
	/* double indirect blocks */<br />
	meta_blocks += 1 + (1LL << (bits-2));<br />
	/* tripple indirect blocks */<br />
	meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2)));</p>
<p>	upper_limit -= meta_blocks;<br />
	upper_limit <<= bits;</p>
<p>	res += 1LL << (bits-2);<br />
	res += 1LL << (2*(bits-2));<br />
	res += 1LL << (3*(bits-2));<br />
	res <<= bits;<br />
	if (res > upper_limit)<br />
		res = upper_limit;</p>
<p>	if (res > MAX_LFS_FILESIZE)<br />
		res = MAX_LFS_FILESIZE;</p>
<p>	return res;<br />
}</p>
<p>static unsigned long descriptor_loc(struct super_block *sb,<br />
				    unsigned long logic_sb_block,<br />
				    int nr)<br />
{<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	unsigned long bg, first_meta_bg;<br />
	int has_super = 0;</p>
<p>	first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);</p>
<p>	if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_META_BG) ||<br />
	    nr < first_meta_bg)<br />
		return (logic_sb_block + nr + 1);<br />
	bg = sbi->s_desc_per_block * nr;<br />
	if (ext2_bg_has_super(sb, bg))<br />
		has_super = 1;</p>
<p>	return ext2_group_first_block_no(sb, bg) + has_super;<br />
}</p>
<p>static int ext2_fill_super(struct super_block *sb, void *data, int silent)<br />
{<br />
	struct buffer_head * bh;<br />
	struct ext2_sb_info * sbi;<br />
	struct ext2_super_block * es;<br />
	struct inode *root;<br />
	unsigned long block;<br />
	unsigned long sb_block = get_sb_block(&#038;data);<br />
	unsigned long logic_sb_block;<br />
	unsigned long offset = 0;<br />
	unsigned long def_mount_opts;<br />
	long ret = -EINVAL;<br />
	int blocksize = BLOCK_SIZE;<br />
	int db_count;<br />
	int i, j;<br />
	__le32 features;<br />
	int err;</p>
<p>	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);<br />
	if (!sbi)<br />
		return -ENOMEM;</p>
<p>	sbi->s_blockgroup_lock =<br />
		kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);<br />
	if (!sbi->s_blockgroup_lock) {<br />
		kfree(sbi);<br />
		return -ENOMEM;<br />
	}<br />
	sb->s_fs_info = sbi;<br />
	sbi->s_sb_block = sb_block;</p>
<p>	/*<br />
	 * See what the current blocksize for the device is, and<br />
	 * use that as the blocksize.  Otherwise (or if the blocksize<br />
	 * is smaller than the default) use the default.<br />
	 * This is important for devices that have a hardware<br />
	 * sectorsize that is larger than the default.<br />
	 */<br />
	blocksize = sb_min_blocksize(sb, BLOCK_SIZE);<br />
	if (!blocksize) {<br />
		printk (&raquo;EXT2-fs: unable to set blocksize\n&raquo;);<br />
		goto failed_sbi;<br />
	}</p>
<p>	/*<br />
	 * If the superblock doesn&#8217;t start on a hardware sector boundary,<br />
	 * calculate the offset.<br />
	 */<br />
	if (blocksize != BLOCK_SIZE) {<br />
		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;<br />
		offset = (sb_block*BLOCK_SIZE) % blocksize;<br />
	} else {<br />
		logic_sb_block = sb_block;<br />
	}</p>
<p>	if (!(bh = sb_bread(sb, logic_sb_block))) {<br />
		printk (&raquo;EXT2-fs: unable to read superblock\n&raquo;);<br />
		goto failed_sbi;<br />
	}<br />
	/*<br />
	 * Note: s_es must be initialized as soon as possible because<br />
	 *       some ext2 macro-instructions depend on its value<br />
	 */<br />
	es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);<br />
	sbi->s_es = es;<br />
	sb->s_magic = le16_to_cpu(es->s_magic);</p>
<p>	if (sb->s_magic != EXT2_SUPER_MAGIC)<br />
		goto cantfind_ext2;</p>
<p>	/* Set defaults before we parse the mount options */<br />
	def_mount_opts = le32_to_cpu(es->s_default_mount_opts);<br />
	if (def_mount_opts &#038; EXT2_DEFM_DEBUG)<br />
		set_opt(sbi->s_mount_opt, DEBUG);<br />
	if (def_mount_opts &#038; EXT2_DEFM_BSDGROUPS)<br />
		set_opt(sbi->s_mount_opt, GRPID);<br />
	if (def_mount_opts &#038; EXT2_DEFM_UID16)<br />
		set_opt(sbi->s_mount_opt, NO_UID32);<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	if (def_mount_opts &#038; EXT2_DEFM_XATTR_USER)<br />
		set_opt(sbi->s_mount_opt, XATTR_USER);<br />
#endif<br />
#ifdef CONFIG_EXT2_FS_POSIX_ACL<br />
	if (def_mount_opts &#038; EXT2_DEFM_ACL)<br />
		set_opt(sbi->s_mount_opt, POSIX_ACL);<br />
#endif</p>
<p>	if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)<br />
		set_opt(sbi->s_mount_opt, ERRORS_PANIC);<br />
	else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE)<br />
		set_opt(sbi->s_mount_opt, ERRORS_CONT);<br />
	else<br />
		set_opt(sbi->s_mount_opt, ERRORS_RO);</p>
<p>	sbi->s_resuid = le16_to_cpu(es->s_def_resuid);<br />
	sbi->s_resgid = le16_to_cpu(es->s_def_resgid);</p>
<p>	set_opt(sbi->s_mount_opt, RESERVATION);</p>
<p>	if (!parse_options ((char *) data, sbi))<br />
		goto failed_mount;</p>
<p>	sb->s_flags = (sb->s_flags &#038; ~MS_POSIXACL) |<br />
		((EXT2_SB(sb)->s_mount_opt &#038; EXT2_MOUNT_POSIX_ACL) ?<br />
		 MS_POSIXACL : 0);</p>
<p>	ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset<br />
				    EXT2_MOUNT_XIP if not */</p>
<p>	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &#038;&#038;<br />
	    (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||<br />
	     EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||<br />
	     EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))<br />
		printk(&raquo;EXT2-fs warning: feature flags set on rev 0 fs, &raquo;<br />
		       &laquo;running e2fsck is recommended\n&raquo;);<br />
	/*<br />
	 * Check feature flags regardless of the revision level, since we<br />
	 * previously didn&#8217;t change the revision level when setting the flags,<br />
	 * so there is a chance incompat flags are set on a rev 0 filesystem.<br />
	 */<br />
	features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP);<br />
	if (features) {<br />
		printk(&raquo;EXT2-fs: %s: couldn&#8217;t mount because of &raquo;<br />
		       &laquo;unsupported optional features (%x).\n&raquo;,<br />
		       sb->s_id, le32_to_cpu(features));<br />
		goto failed_mount;<br />
	}<br />
	if (!(sb->s_flags &#038; MS_RDONLY) &#038;&#038;<br />
	    (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){<br />
		printk(&raquo;EXT2-fs: %s: couldn&#8217;t mount RDWR because of &raquo;<br />
		       &laquo;unsupported optional features (%x).\n&raquo;,<br />
		       sb->s_id, le32_to_cpu(features));<br />
		goto failed_mount;<br />
	}</p>
<p>	blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);</p>
<p>	if (ext2_use_xip(sb) &#038;&#038; blocksize != PAGE_SIZE) {<br />
		if (!silent)<br />
			printk(&raquo;XIP: Unsupported blocksize\n&raquo;);<br />
		goto failed_mount;<br />
	}</p>
<p>	/* If the blocksize doesn&#8217;t match, re-read the thing.. */<br />
	if (sb->s_blocksize != blocksize) {<br />
		brelse(bh);</p>
<p>		if (!sb_set_blocksize(sb, blocksize)) {<br />
			printk(KERN_ERR &laquo;EXT2-fs: blocksize too small for device.\n&raquo;);<br />
			goto failed_sbi;<br />
		}</p>
<p>		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;<br />
		offset = (sb_block*BLOCK_SIZE) % blocksize;<br />
		bh = sb_bread(sb, logic_sb_block);<br />
		if(!bh) {<br />
			printk(&raquo;EXT2-fs: Couldn&#8217;t read superblock on &raquo;<br />
			       &laquo;2nd try.\n&raquo;);<br />
			goto failed_sbi;<br />
		}<br />
		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);<br />
		sbi->s_es = es;<br />
		if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {<br />
			printk (&raquo;EXT2-fs: Magic mismatch, very weird !\n&raquo;);<br />
			goto failed_mount;<br />
		}<br />
	}</p>
<p>	sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);</p>
<p>	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {<br />
		sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;<br />
		sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;<br />
	} else {<br />
		sbi->s_inode_size = le16_to_cpu(es->s_inode_size);<br />
		sbi->s_first_ino = le32_to_cpu(es->s_first_ino);<br />
		if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||<br />
		    !is_power_of_2(sbi->s_inode_size) ||<br />
		    (sbi->s_inode_size > blocksize)) {<br />
			printk (&raquo;EXT2-fs: unsupported inode size: %d\n&raquo;,<br />
				sbi->s_inode_size);<br />
			goto failed_mount;<br />
		}<br />
	}</p>
<p>	sbi->s_frag_size = EXT2_MIN_FRAG_SIZE <<<br />
				   le32_to_cpu(es->s_log_frag_size);<br />
	if (sbi->s_frag_size == 0)<br />
		goto cantfind_ext2;<br />
	sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size;</p>
<p>	sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);<br />
	sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);<br />
	sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);</p>
<p>	if (EXT2_INODE_SIZE(sb) == 0)<br />
		goto cantfind_ext2;<br />
	sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);<br />
	if (sbi->s_inodes_per_block == 0 || sbi->s_inodes_per_group == 0)<br />
		goto cantfind_ext2;<br />
	sbi->s_itb_per_group = sbi->s_inodes_per_group /<br />
					sbi->s_inodes_per_block;<br />
	sbi->s_desc_per_block = sb->s_blocksize /<br />
					sizeof (struct ext2_group_desc);<br />
	sbi->s_sbh = bh;<br />
	sbi->s_mount_state = le16_to_cpu(es->s_state);<br />
	sbi->s_addr_per_block_bits =<br />
		ilog2 (EXT2_ADDR_PER_BLOCK(sb));<br />
	sbi->s_desc_per_block_bits =<br />
		ilog2 (EXT2_DESC_PER_BLOCK(sb));</p>
<p>	if (sb->s_magic != EXT2_SUPER_MAGIC)<br />
		goto cantfind_ext2;</p>
<p>	if (sb->s_blocksize != bh->b_size) {<br />
		if (!silent)<br />
			printk (&raquo;VFS: Unsupported blocksize on dev &raquo;<br />
				&laquo;%s.\n&raquo;, sb->s_id);<br />
		goto failed_mount;<br />
	}</p>
<p>	if (sb->s_blocksize != sbi->s_frag_size) {<br />
		printk (&raquo;EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n&raquo;,<br />
			sbi->s_frag_size, sb->s_blocksize);<br />
		goto failed_mount;<br />
	}</p>
<p>	if (sbi->s_blocks_per_group > sb->s_blocksize * <img src='http://lynyrd.ru/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> {<br />
		printk (&raquo;EXT2-fs: #blocks per group too big: %lu\n&raquo;,<br />
			sbi->s_blocks_per_group);<br />
		goto failed_mount;<br />
	}<br />
	if (sbi->s_frags_per_group > sb->s_blocksize * <img src='http://lynyrd.ru/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> {<br />
		printk (&raquo;EXT2-fs: #fragments per group too big: %lu\n&raquo;,<br />
			sbi->s_frags_per_group);<br />
		goto failed_mount;<br />
	}<br />
	if (sbi->s_inodes_per_group > sb->s_blocksize * <img src='http://lynyrd.ru/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> {<br />
		printk (&raquo;EXT2-fs: #inodes per group too big: %lu\n&raquo;,<br />
			sbi->s_inodes_per_group);<br />
		goto failed_mount;<br />
	}</p>
<p>	if (EXT2_BLOCKS_PER_GROUP(sb) == 0)<br />
		goto cantfind_ext2;<br />
 	sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) -<br />
 				le32_to_cpu(es->s_first_data_block) &#8211; 1)<br />
 					/ EXT2_BLOCKS_PER_GROUP(sb)) + 1;<br />
	db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) &#8211; 1) /<br />
		   EXT2_DESC_PER_BLOCK(sb);<br />
	sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);<br />
	if (sbi->s_group_desc == NULL) {<br />
		printk (&raquo;EXT2-fs: not enough memory\n&raquo;);<br />
		goto failed_mount;<br />
	}<br />
	bgl_lock_init(sbi->s_blockgroup_lock);<br />
	sbi->s_debts = kcalloc(sbi->s_groups_count, sizeof(*sbi->s_debts), GFP_KERNEL);<br />
	if (!sbi->s_debts) {<br />
		printk (&raquo;EXT2-fs: not enough memory\n&raquo;);<br />
		goto failed_mount_group_desc;<br />
	}<br />
	for (i = 0; i < db_count; i++) {<br />
		block = descriptor_loc(sb, logic_sb_block, i);<br />
		sbi->s_group_desc[i] = sb_bread(sb, block);<br />
		if (!sbi->s_group_desc[i]) {<br />
			for (j = 0; j < i; j++)<br />
				brelse (sbi->s_group_desc[j]);<br />
			printk (&raquo;EXT2-fs: unable to read group descriptors\n&raquo;);<br />
			goto failed_mount_group_desc;<br />
		}<br />
	}<br />
	if (!ext2_check_descriptors (sb)) {<br />
		printk (&raquo;EXT2-fs: group descriptors corrupted!\n&raquo;);<br />
		goto failed_mount2;<br />
	}<br />
	sbi->s_gdb_count = db_count;<br />
	get_random_bytes(&#038;sbi->s_next_generation, sizeof(u32));<br />
	spin_lock_init(&#038;sbi->s_next_gen_lock);</p>
<p>	/* per fileystem reservation list head &#038; lock */<br />
	spin_lock_init(&#038;sbi->s_rsv_window_lock);<br />
	sbi->s_rsv_window_root = RB_ROOT;<br />
	/*<br />
	 * Add a single, static dummy reservation to the start of the<br />
	 * reservation window list &#8212; it gives us a placeholder for<br />
	 * append-at-start-of-list which makes the allocation logic<br />
	 * _much_ simpler.<br />
	 */<br />
	sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;<br />
	sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;<br />
	sbi->s_rsv_window_head.rsv_alloc_hit = 0;<br />
	sbi->s_rsv_window_head.rsv_goal_size = 0;<br />
	ext2_rsv_window_add(sb, &#038;sbi->s_rsv_window_head);</p>
<p>	err = percpu_counter_init(&#038;sbi->s_freeblocks_counter,<br />
				ext2_count_free_blocks(sb));<br />
	if (!err) {<br />
		err = percpu_counter_init(&#038;sbi->s_freeinodes_counter,<br />
				ext2_count_free_inodes(sb));<br />
	}<br />
	if (!err) {<br />
		err = percpu_counter_init(&#038;sbi->s_dirs_counter,<br />
				ext2_count_dirs(sb));<br />
	}<br />
	if (err) {<br />
		printk(KERN_ERR &laquo;EXT2-fs: insufficient memory\n&raquo;);<br />
		goto failed_mount3;<br />
	}<br />
	/*<br />
	 * set up enough so that it can read an inode<br />
	 */<br />
	sb->s_op = &#038;ext2_sops;<br />
	sb->s_export_op = &#038;ext2_export_ops;<br />
	sb->s_xattr = ext2_xattr_handlers;<br />
	root = ext2_iget(sb, EXT2_ROOT_INO);<br />
	if (IS_ERR(root)) {<br />
		ret = PTR_ERR(root);<br />
		goto failed_mount3;<br />
	}<br />
	if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {<br />
		iput(root);<br />
		printk(KERN_ERR &laquo;EXT2-fs: corrupt root inode, run e2fsck\n&raquo;);<br />
		goto failed_mount3;<br />
	}</p>
<p>	sb->s_root = d_alloc_root(root);<br />
	if (!sb->s_root) {<br />
		iput(root);<br />
		printk(KERN_ERR &laquo;EXT2-fs: get root inode failed\n&raquo;);<br />
		ret = -ENOMEM;<br />
		goto failed_mount3;<br />
	}<br />
	if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))<br />
		ext2_warning(sb, __func__,<br />
			&laquo;mounting ext3 filesystem as ext2&#8243;);<br />
	ext2_setup_super (sb, es, sb->s_flags &#038; MS_RDONLY);<br />
	return 0;</p>
<p>cantfind_ext2:<br />
	if (!silent)<br />
		printk(&raquo;VFS: Can&#8217;t find an ext2 filesystem on dev %s.\n&raquo;,<br />
		       sb->s_id);<br />
	goto failed_mount;<br />
failed_mount3:<br />
	percpu_counter_destroy(&#038;sbi->s_freeblocks_counter);<br />
	percpu_counter_destroy(&#038;sbi->s_freeinodes_counter);<br />
	percpu_counter_destroy(&#038;sbi->s_dirs_counter);<br />
failed_mount2:<br />
	for (i = 0; i < db_count; i++)<br />
		brelse(sbi->s_group_desc[i]);<br />
failed_mount_group_desc:<br />
	kfree(sbi->s_group_desc);<br />
	kfree(sbi->s_debts);<br />
failed_mount:<br />
	brelse(bh);<br />
failed_sbi:<br />
	sb->s_fs_info = NULL;<br />
	kfree(sbi->s_blockgroup_lock);<br />
	kfree(sbi);<br />
	return ret;<br />
}</p>
<p>static void ext2_commit_super (struct super_block * sb,<br />
			       struct ext2_super_block * es)<br />
{<br />
	es->s_wtime = cpu_to_le32(get_seconds());<br />
	mark_buffer_dirty(EXT2_SB(sb)->s_sbh);<br />
	sb->s_dirt = 0;<br />
}</p>
<p>static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es)<br />
{<br />
	es->s_free_blocks_count = cpu_to_le32(ext2_count_free_blocks(sb));<br />
	es->s_free_inodes_count = cpu_to_le32(ext2_count_free_inodes(sb));<br />
	es->s_wtime = cpu_to_le32(get_seconds());<br />
	mark_buffer_dirty(EXT2_SB(sb)->s_sbh);<br />
	sync_dirty_buffer(EXT2_SB(sb)->s_sbh);<br />
	sb->s_dirt = 0;<br />
}</p>
<p>/*<br />
 * In the second extended file system, it is not necessary to<br />
 * write the super block since we use a mapping of the<br />
 * disk super block in a buffer.<br />
 *<br />
 * However, this function is still used to set the fs valid<br />
 * flags to 0.  We need to set this flag to 0 since the fs<br />
 * may have been checked while mounted and e2fsck may have<br />
 * set s_state to EXT2_VALID_FS after some corrections.<br />
 */</p>
<p>static int ext2_sync_fs(struct super_block *sb, int wait)<br />
{<br />
	struct ext2_super_block *es = EXT2_SB(sb)->s_es;</p>
<p>	lock_kernel();<br />
	if (es->s_state &#038; cpu_to_le16(EXT2_VALID_FS)) {<br />
		ext2_debug(&raquo;setting valid to 0\n&raquo;);<br />
		es->s_state &#038;= cpu_to_le16(~EXT2_VALID_FS);<br />
		es->s_free_blocks_count =<br />
			cpu_to_le32(ext2_count_free_blocks(sb));<br />
		es->s_free_inodes_count =<br />
			cpu_to_le32(ext2_count_free_inodes(sb));<br />
		es->s_mtime = cpu_to_le32(get_seconds());<br />
		ext2_sync_super(sb, es);<br />
	} else {<br />
		ext2_commit_super(sb, es);<br />
	}<br />
	sb->s_dirt = 0;<br />
	unlock_kernel();</p>
<p>	return 0;<br />
}</p>
<p>void ext2_write_super(struct super_block *sb)<br />
{<br />
	if (!(sb->s_flags &#038; MS_RDONLY))<br />
		ext2_sync_fs(sb, 1);<br />
	else<br />
		sb->s_dirt = 0;<br />
}</p>
<p>static int ext2_remount (struct super_block * sb, int * flags, char * data)<br />
{<br />
	struct ext2_sb_info * sbi = EXT2_SB(sb);<br />
	struct ext2_super_block * es;<br />
	unsigned long old_mount_opt = sbi->s_mount_opt;<br />
	struct ext2_mount_options old_opts;<br />
	unsigned long old_sb_flags;<br />
	int err;</p>
<p>	lock_kernel();</p>
<p>	/* Store the old options */<br />
	old_sb_flags = sb->s_flags;<br />
	old_opts.s_mount_opt = sbi->s_mount_opt;<br />
	old_opts.s_resuid = sbi->s_resuid;<br />
	old_opts.s_resgid = sbi->s_resgid;</p>
<p>	/*<br />
	 * Allow the &laquo;check&raquo; option to be passed as a remount option.<br />
	 */<br />
	if (!parse_options (data, sbi)) {<br />
		err = -EINVAL;<br />
		goto restore_opts;<br />
	}</p>
<p>	sb->s_flags = (sb->s_flags &#038; ~MS_POSIXACL) |<br />
		((sbi->s_mount_opt &#038; EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);</p>
<p>	ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset<br />
				    EXT2_MOUNT_XIP if not */</p>
<p>	if ((ext2_use_xip(sb)) &#038;&#038; (sb->s_blocksize != PAGE_SIZE)) {<br />
		printk(&raquo;XIP: Unsupported blocksize\n&raquo;);<br />
		err = -EINVAL;<br />
		goto restore_opts;<br />
	}</p>
<p>	es = sbi->s_es;<br />
	if (((sbi->s_mount_opt &#038; EXT2_MOUNT_XIP) !=<br />
	    (old_mount_opt &#038; EXT2_MOUNT_XIP)) &#038;&#038;<br />
	    invalidate_inodes(sb)) {<br />
		ext2_warning(sb, __func__, &laquo;refusing change of xip flag &raquo;<br />
			     &laquo;with busy inodes while remounting&raquo;);<br />
		sbi->s_mount_opt &#038;= ~EXT2_MOUNT_XIP;<br />
		sbi->s_mount_opt |= old_mount_opt &#038; EXT2_MOUNT_XIP;<br />
	}<br />
	if ((*flags &#038; MS_RDONLY) == (sb->s_flags &#038; MS_RDONLY)) {<br />
		unlock_kernel();<br />
		return 0;<br />
	}<br />
	if (*flags &#038; MS_RDONLY) {<br />
		if (le16_to_cpu(es->s_state) &#038; EXT2_VALID_FS ||<br />
		    !(sbi->s_mount_state &#038; EXT2_VALID_FS)) {<br />
			unlock_kernel();<br />
			return 0;<br />
		}<br />
		/*<br />
		 * OK, we are remounting a valid rw partition rdonly, so set<br />
		 * the rdonly flag and then mark the partition as valid again.<br />
		 */<br />
		es->s_state = cpu_to_le16(sbi->s_mount_state);<br />
		es->s_mtime = cpu_to_le32(get_seconds());<br />
	} else {<br />
		__le32 ret = EXT2_HAS_RO_COMPAT_FEATURE(sb,<br />
					       ~EXT2_FEATURE_RO_COMPAT_SUPP);<br />
		if (ret) {<br />
			printk(&raquo;EXT2-fs: %s: couldn&#8217;t remount RDWR because of &raquo;<br />
			       &laquo;unsupported optional features (%x).\n&raquo;,<br />
			       sb->s_id, le32_to_cpu(ret));<br />
			err = -EROFS;<br />
			goto restore_opts;<br />
		}<br />
		/*<br />
		 * Mounting a RDONLY partition read-write, so reread and<br />
		 * store the current valid flag.  (It may have been changed<br />
		 * by e2fsck since we originally mounted the partition.)<br />
		 */<br />
		sbi->s_mount_state = le16_to_cpu(es->s_state);<br />
		if (!ext2_setup_super (sb, es, 0))<br />
			sb->s_flags &#038;= ~MS_RDONLY;<br />
	}<br />
	ext2_sync_super(sb, es);<br />
	unlock_kernel();<br />
	return 0;<br />
restore_opts:<br />
	sbi->s_mount_opt = old_opts.s_mount_opt;<br />
	sbi->s_resuid = old_opts.s_resuid;<br />
	sbi->s_resgid = old_opts.s_resgid;<br />
	sb->s_flags = old_sb_flags;<br />
	unlock_kernel();<br />
	return err;<br />
}</p>
<p>static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf)<br />
{<br />
	struct super_block *sb = dentry->d_sb;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	struct ext2_super_block *es = sbi->s_es;<br />
	u64 fsid;</p>
<p>	if (test_opt (sb, MINIX_DF))<br />
		sbi->s_overhead_last = 0;<br />
	else if (sbi->s_blocks_last != le32_to_cpu(es->s_blocks_count)) {<br />
		unsigned long i, overhead = 0;<br />
		smp_rmb();</p>
<p>		/*<br />
		 * Compute the overhead (FS structures). This is constant<br />
		 * for a given filesystem unless the number of block groups<br />
		 * changes so we cache the previous value until it does.<br />
		 */</p>
<p>		/*<br />
		 * All of the blocks before first_data_block are<br />
		 * overhead<br />
		 */<br />
		overhead = le32_to_cpu(es->s_first_data_block);</p>
<p>		/*<br />
		 * Add the overhead attributed to the superblock and<br />
		 * block group descriptors.  If the sparse superblocks<br />
		 * feature is turned on, then not all groups have this.<br />
		 */<br />
		for (i = 0; i < sbi->s_groups_count; i++)<br />
			overhead += ext2_bg_has_super(sb, i) +<br />
				ext2_bg_num_gdb(sb, i);</p>
<p>		/*<br />
		 * Every block group has an inode bitmap, a block<br />
		 * bitmap, and an inode table.<br />
		 */<br />
		overhead += (sbi->s_groups_count *<br />
			     (2 + sbi->s_itb_per_group));<br />
		sbi->s_overhead_last = overhead;<br />
		smp_wmb();<br />
		sbi->s_blocks_last = le32_to_cpu(es->s_blocks_count);<br />
	}</p>
<p>	buf->f_type = EXT2_SUPER_MAGIC;<br />
	buf->f_bsize = sb->s_blocksize;<br />
	buf->f_blocks = le32_to_cpu(es->s_blocks_count) &#8211; sbi->s_overhead_last;<br />
	buf->f_bfree = ext2_count_free_blocks(sb);<br />
	es->s_free_blocks_count = cpu_to_le32(buf->f_bfree);<br />
	buf->f_bavail = buf->f_bfree &#8211; le32_to_cpu(es->s_r_blocks_count);<br />
	if (buf->f_bfree < le32_to_cpu(es->s_r_blocks_count))<br />
		buf->f_bavail = 0;<br />
	buf->f_files = le32_to_cpu(es->s_inodes_count);<br />
	buf->f_ffree = ext2_count_free_inodes(sb);<br />
	es->s_free_inodes_count = cpu_to_le32(buf->f_ffree);<br />
	buf->f_namelen = EXT2_NAME_LEN;<br />
	fsid = le64_to_cpup((void *)es->s_uuid) ^<br />
	       le64_to_cpup((void *)es->s_uuid + sizeof(u64));<br />
	buf->f_fsid.val[0] = fsid &#038; 0xFFFFFFFFUL;<br />
	buf->f_fsid.val[1] = (fsid >> 32) &#038; 0xFFFFFFFFUL;<br />
	return 0;<br />
}</p>
<p>static int ext2_get_sb(struct file_system_type *fs_type,<br />
	int flags, const char *dev_name, void *data, struct vfsmount *mnt)<br />
{<br />
	return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super, mnt);<br />
}</p>
<p>#ifdef CONFIG_QUOTA</p>
<p>/* Read data from quotafile &#8211; avoid pagecache and such because we cannot afford<br />
 * acquiring the locks&#8230; As quota files are never truncated and quota code<br />
 * itself serializes the operations (and noone else should touch the files)<br />
 * we don&#8217;t have to be afraid of races */<br />
static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data,<br />
			       size_t len, loff_t off)<br />
{<br />
	struct inode *inode = sb_dqopt(sb)->files[type];<br />
	sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb);<br />
	int err = 0;<br />
	int offset = off &#038; (sb->s_blocksize &#8211; 1);<br />
	int tocopy;<br />
	size_t toread;<br />
	struct buffer_head tmp_bh;<br />
	struct buffer_head *bh;<br />
	loff_t i_size = i_size_read(inode);</p>
<p>	if (off > i_size)<br />
		return 0;<br />
	if (off+len > i_size)<br />
		len = i_size-off;<br />
	toread = len;<br />
	while (toread > 0) {<br />
		tocopy = sb->s_blocksize &#8211; offset < toread ?<br />
				sb->s_blocksize &#8211; offset : toread;</p>
<p>		tmp_bh.b_state = 0;<br />
		tmp_bh.b_size = sb->s_blocksize;<br />
		err = ext2_get_block(inode, blk, &#038;tmp_bh, 0);<br />
		if (err < 0)<br />
			return err;<br />
		if (!buffer_mapped(&#038;tmp_bh))	/* A hole? */<br />
			memset(data, 0, tocopy);<br />
		else {<br />
			bh = sb_bread(sb, tmp_bh.b_blocknr);<br />
			if (!bh)<br />
				return -EIO;<br />
			memcpy(data, bh->b_data+offset, tocopy);<br />
			brelse(bh);<br />
		}<br />
		offset = 0;<br />
		toread -= tocopy;<br />
		data += tocopy;<br />
		blk++;<br />
	}<br />
	return len;<br />
}</p>
<p>/* Write to quotafile */<br />
static ssize_t ext2_quota_write(struct super_block *sb, int type,<br />
				const char *data, size_t len, loff_t off)<br />
{<br />
	struct inode *inode = sb_dqopt(sb)->files[type];<br />
	sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb);<br />
	int err = 0;<br />
	int offset = off &#038; (sb->s_blocksize &#8211; 1);<br />
	int tocopy;<br />
	size_t towrite = len;<br />
	struct buffer_head tmp_bh;<br />
	struct buffer_head *bh;</p>
<p>	mutex_lock_nested(&#038;inode->i_mutex, I_MUTEX_QUOTA);<br />
	while (towrite > 0) {<br />
		tocopy = sb->s_blocksize &#8211; offset < towrite ?<br />
				sb->s_blocksize &#8211; offset : towrite;</p>
<p>		tmp_bh.b_state = 0;<br />
		err = ext2_get_block(inode, blk, &#038;tmp_bh, 1);<br />
		if (err < 0)<br />
			goto out;<br />
		if (offset || tocopy != EXT2_BLOCK_SIZE(sb))<br />
			bh = sb_bread(sb, tmp_bh.b_blocknr);<br />
		else<br />
			bh = sb_getblk(sb, tmp_bh.b_blocknr);<br />
		if (!bh) {<br />
			err = -EIO;<br />
			goto out;<br />
		}<br />
		lock_buffer(bh);<br />
		memcpy(bh->b_data+offset, data, tocopy);<br />
		flush_dcache_page(bh->b_page);<br />
		set_buffer_uptodate(bh);<br />
		mark_buffer_dirty(bh);<br />
		unlock_buffer(bh);<br />
		brelse(bh);<br />
		offset = 0;<br />
		towrite -= tocopy;<br />
		data += tocopy;<br />
		blk++;<br />
	}<br />
out:<br />
	if (len == towrite) {<br />
		mutex_unlock(&#038;inode->i_mutex);<br />
		return err;<br />
	}<br />
	if (inode->i_size < off+len-towrite)<br />
		i_size_write(inode, off+len-towrite);<br />
	inode->i_version++;<br />
	inode->i_mtime = inode->i_ctime = CURRENT_TIME;<br />
	mark_inode_dirty(inode);<br />
	mutex_unlock(&#038;inode->i_mutex);<br />
	return len &#8211; towrite;<br />
}</p>
<p>#endif</p>
<p>static struct file_system_type ext2_fs_type = {<br />
	.owner		= THIS_MODULE,<br />
	.name		= &laquo;ext2&#8243;,<br />
	.get_sb		= ext2_get_sb,<br />
	.kill_sb	= kill_block_super,<br />
	.fs_flags	= FS_REQUIRES_DEV,<br />
};</p>
<p>static int __init init_ext2_fs(void)<br />
{<br />
	int err = init_ext2_xattr();<br />
	if (err)<br />
		return err;<br />
	err = init_inodecache();<br />
	if (err)<br />
		goto out1;<br />
        err = register_filesystem(&#038;ext2_fs_type);<br />
	if (err)<br />
		goto out;<br />
	return 0;<br />
out:<br />
	destroy_inodecache();<br />
out1:<br />
	exit_ext2_xattr();<br />
	return err;<br />
}</p>
<p>static void __exit exit_ext2_fs(void)<br />
{<br />
	unregister_filesystem(&#038;ext2_fs_type);<br />
	destroy_inodecache();<br />
	exit_ext2_xattr();<br />
}</p>
<p>module_init(init_ext2_fs)<br />
module_exit(exit_ext2_fs)</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/super-c-8/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>namei.c</title>
		<link>http://lynyrd.ru/namei-c-5</link>
		<comments>http://lynyrd.ru/namei-c-5#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:45:16 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/namei-c-5</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/namei.c
 *
 * Rewrite to pagecache. Almost all code had been changed, so blame me
 * if the things go wrong. Please, send bug reports to
 * viro@parcelfarce.linux.theplanet.co.uk
 *
 * Stuff here is basically a glue between the VFS and generic UNIXish
 * filesystem that keeps everything in pagecache. All knowledge of the
 ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/namei.c<span id="more-1156"></span><br />
 *<br />
 * Rewrite to pagecache. Almost all code had been changed, so blame me<br />
 * if the things go wrong. Please, send bug reports to<br />
 * viro@parcelfarce.linux.theplanet.co.uk<br />
 *<br />
 * Stuff here is basically a glue between the VFS and generic UNIXish<br />
 * filesystem that keeps everything in pagecache. All knowledge of the<br />
 * directory layout is in fs/ext2/dir.c &#8211; it turned out to be easily separatable<br />
 * and it&#8217;s easier to debug that way. In principle we might want to<br />
 * generalize that a bit and turn it into a library. Or not.<br />
 *<br />
 * The only non-static object here is ext2_dir_inode_operations.<br />
 *<br />
 * TODO: get rid of kmap() use, add readahead.<br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/namei.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 */</p>
<p>#include
<linux/pagemap.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;<br />
#include &laquo;xip.h&raquo;</p>
<p>static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)<br />
{<br />
	int err = ext2_add_link(dentry, inode);<br />
	if (!err) {<br />
		d_instantiate(dentry, inode);<br />
		unlock_new_inode(inode);<br />
		return 0;<br />
	}<br />
	inode_dec_link_count(inode);<br />
	unlock_new_inode(inode);<br />
	iput(inode);<br />
	return err;<br />
}</p>
<p>/*<br />
 * Methods themselves.<br />
 */</p>
<p>static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)<br />
{<br />
	struct inode * inode;<br />
	ino_t ino;</p>
<p>	if (dentry->d_name.len > EXT2_NAME_LEN)<br />
		return ERR_PTR(-ENAMETOOLONG);</p>
<p>	ino = ext2_inode_by_name(dir, &#038;dentry->d_name);<br />
	inode = NULL;<br />
	if (ino) {<br />
		inode = ext2_iget(dir->i_sb, ino);<br />
		if (unlikely(IS_ERR(inode))) {<br />
			if (PTR_ERR(inode) == -ESTALE) {<br />
				ext2_error(dir->i_sb, __func__,<br />
						&laquo;deleted inode referenced: %lu&raquo;,<br />
						(unsigned long) ino);<br />
				return ERR_PTR(-EIO);<br />
			} else {<br />
				return ERR_CAST(inode);<br />
			}<br />
		}<br />
	}<br />
	return d_splice_alias(inode, dentry);<br />
}</p>
<p>struct dentry *ext2_get_parent(struct dentry *child)<br />
{<br />
	struct qstr dotdot = {.name = &laquo;..&raquo;, .len = 2};<br />
	unsigned long ino = ext2_inode_by_name(child->d_inode, &#038;dotdot);<br />
	if (!ino)<br />
		return ERR_PTR(-ENOENT);<br />
	return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino));<br />
} </p>
<p>/*<br />
 * By the time this is called, we already have created<br />
 * the directory cache entry for the new file, but it<br />
 * is so far negative &#8211; it has no inode.<br />
 *<br />
 * If the create succeeds, we fill in the inode information<br />
 * with d_instantiate().<br />
 */<br />
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)<br />
{<br />
	struct inode * inode = ext2_new_inode (dir, mode);<br />
	int err = PTR_ERR(inode);<br />
	if (!IS_ERR(inode)) {<br />
		inode->i_op = &#038;ext2_file_inode_operations;<br />
		if (ext2_use_xip(inode->i_sb)) {<br />
			inode->i_mapping->a_ops = &#038;ext2_aops_xip;<br />
			inode->i_fop = &#038;ext2_xip_file_operations;<br />
		} else if (test_opt(inode->i_sb, NOBH)) {<br />
			inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
			inode->i_fop = &#038;ext2_file_operations;<br />
		} else {<br />
			inode->i_mapping->a_ops = &#038;ext2_aops;<br />
			inode->i_fop = &#038;ext2_file_operations;<br />
		}<br />
		mark_inode_dirty(inode);<br />
		err = ext2_add_nondir(dentry, inode);<br />
	}<br />
	return err;<br />
}</p>
<p>static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)<br />
{<br />
	struct inode * inode;<br />
	int err;</p>
<p>	if (!new_valid_dev(rdev))<br />
		return -EINVAL;</p>
<p>	inode = ext2_new_inode (dir, mode);<br />
	err = PTR_ERR(inode);<br />
	if (!IS_ERR(inode)) {<br />
		init_special_inode(inode, inode->i_mode, rdev);<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
		inode->i_op = &#038;ext2_special_inode_operations;<br />
#endif<br />
		mark_inode_dirty(inode);<br />
		err = ext2_add_nondir(dentry, inode);<br />
	}<br />
	return err;<br />
}</p>
<p>static int ext2_symlink (struct inode * dir, struct dentry * dentry,<br />
	const char * symname)<br />
{<br />
	struct super_block * sb = dir->i_sb;<br />
	int err = -ENAMETOOLONG;<br />
	unsigned l = strlen(symname)+1;<br />
	struct inode * inode;</p>
<p>	if (l > sb->s_blocksize)<br />
		goto out;</p>
<p>	inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO);<br />
	err = PTR_ERR(inode);<br />
	if (IS_ERR(inode))<br />
		goto out;</p>
<p>	if (l > sizeof (EXT2_I(inode)->i_data)) {<br />
		/* slow symlink */<br />
		inode->i_op = &#038;ext2_symlink_inode_operations;<br />
		if (test_opt(inode->i_sb, NOBH))<br />
			inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
		else<br />
			inode->i_mapping->a_ops = &#038;ext2_aops;<br />
		err = page_symlink(inode, symname, l);<br />
		if (err)<br />
			goto out_fail;<br />
	} else {<br />
		/* fast symlink */<br />
		inode->i_op = &#038;ext2_fast_symlink_inode_operations;<br />
		memcpy((char*)(EXT2_I(inode)->i_data),symname,l);<br />
		inode->i_size = l-1;<br />
	}<br />
	mark_inode_dirty(inode);</p>
<p>	err = ext2_add_nondir(dentry, inode);<br />
out:<br />
	return err;</p>
<p>out_fail:<br />
	inode_dec_link_count(inode);<br />
	unlock_new_inode(inode);<br />
	iput (inode);<br />
	goto out;<br />
}</p>
<p>static int ext2_link (struct dentry * old_dentry, struct inode * dir,<br />
	struct dentry *dentry)<br />
{<br />
	struct inode *inode = old_dentry->d_inode;<br />
	int err;</p>
<p>	if (inode->i_nlink >= EXT2_LINK_MAX)<br />
		return -EMLINK;</p>
<p>	inode->i_ctime = CURRENT_TIME_SEC;<br />
	inode_inc_link_count(inode);<br />
	atomic_inc(&#038;inode->i_count);</p>
<p>	err = ext2_add_link(dentry, inode);<br />
	if (!err) {<br />
		d_instantiate(dentry, inode);<br />
		return 0;<br />
	}<br />
	inode_dec_link_count(inode);<br />
	iput(inode);<br />
	return err;<br />
}</p>
<p>static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)<br />
{<br />
	struct inode * inode;<br />
	int err = -EMLINK;</p>
<p>	if (dir->i_nlink >= EXT2_LINK_MAX)<br />
		goto out;</p>
<p>	inode_inc_link_count(dir);</p>
<p>	inode = ext2_new_inode (dir, S_IFDIR | mode);<br />
	err = PTR_ERR(inode);<br />
	if (IS_ERR(inode))<br />
		goto out_dir;</p>
<p>	inode->i_op = &#038;ext2_dir_inode_operations;<br />
	inode->i_fop = &#038;ext2_dir_operations;<br />
	if (test_opt(inode->i_sb, NOBH))<br />
		inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
	else<br />
		inode->i_mapping->a_ops = &#038;ext2_aops;</p>
<p>	inode_inc_link_count(inode);</p>
<p>	err = ext2_make_empty(inode, dir);<br />
	if (err)<br />
		goto out_fail;</p>
<p>	err = ext2_add_link(dentry, inode);<br />
	if (err)<br />
		goto out_fail;</p>
<p>	d_instantiate(dentry, inode);<br />
	unlock_new_inode(inode);<br />
out:<br />
	return err;</p>
<p>out_fail:<br />
	inode_dec_link_count(inode);<br />
	inode_dec_link_count(inode);<br />
	unlock_new_inode(inode);<br />
	iput(inode);<br />
out_dir:<br />
	inode_dec_link_count(dir);<br />
	goto out;<br />
}</p>
<p>static int ext2_unlink(struct inode * dir, struct dentry *dentry)<br />
{<br />
	struct inode * inode = dentry->d_inode;<br />
	struct ext2_dir_entry_2 * de;<br />
	struct page * page;<br />
	int err = -ENOENT;</p>
<p>	de = ext2_find_entry (dir, &#038;dentry->d_name, &#038;page);<br />
	if (!de)<br />
		goto out;</p>
<p>	err = ext2_delete_entry (de, page);<br />
	if (err)<br />
		goto out;</p>
<p>	inode->i_ctime = dir->i_ctime;<br />
	inode_dec_link_count(inode);<br />
	err = 0;<br />
out:<br />
	return err;<br />
}</p>
<p>static int ext2_rmdir (struct inode * dir, struct dentry *dentry)<br />
{<br />
	struct inode * inode = dentry->d_inode;<br />
	int err = -ENOTEMPTY;</p>
<p>	if (ext2_empty_dir(inode)) {<br />
		err = ext2_unlink(dir, dentry);<br />
		if (!err) {<br />
			inode->i_size = 0;<br />
			inode_dec_link_count(inode);<br />
			inode_dec_link_count(dir);<br />
		}<br />
	}<br />
	return err;<br />
}</p>
<p>static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,<br />
	struct inode * new_dir,	struct dentry * new_dentry )<br />
{<br />
	struct inode * old_inode = old_dentry->d_inode;<br />
	struct inode * new_inode = new_dentry->d_inode;<br />
	struct page * dir_page = NULL;<br />
	struct ext2_dir_entry_2 * dir_de = NULL;<br />
	struct page * old_page;<br />
	struct ext2_dir_entry_2 * old_de;<br />
	int err = -ENOENT;</p>
<p>	old_de = ext2_find_entry (old_dir, &#038;old_dentry->d_name, &#038;old_page);<br />
	if (!old_de)<br />
		goto out;</p>
<p>	if (S_ISDIR(old_inode->i_mode)) {<br />
		err = -EIO;<br />
		dir_de = ext2_dotdot(old_inode, &#038;dir_page);<br />
		if (!dir_de)<br />
			goto out_old;<br />
	}</p>
<p>	if (new_inode) {<br />
		struct page *new_page;<br />
		struct ext2_dir_entry_2 *new_de;</p>
<p>		err = -ENOTEMPTY;<br />
		if (dir_de &#038;&#038; !ext2_empty_dir (new_inode))<br />
			goto out_dir;</p>
<p>		err = -ENOENT;<br />
		new_de = ext2_find_entry (new_dir, &#038;new_dentry->d_name, &#038;new_page);<br />
		if (!new_de)<br />
			goto out_dir;<br />
		inode_inc_link_count(old_inode);<br />
		ext2_set_link(new_dir, new_de, new_page, old_inode, 1);<br />
		new_inode->i_ctime = CURRENT_TIME_SEC;<br />
		if (dir_de)<br />
			drop_nlink(new_inode);<br />
		inode_dec_link_count(new_inode);<br />
	} else {<br />
		if (dir_de) {<br />
			err = -EMLINK;<br />
			if (new_dir->i_nlink >= EXT2_LINK_MAX)<br />
				goto out_dir;<br />
		}<br />
		inode_inc_link_count(old_inode);<br />
		err = ext2_add_link(new_dentry, old_inode);<br />
		if (err) {<br />
			inode_dec_link_count(old_inode);<br />
			goto out_dir;<br />
		}<br />
		if (dir_de)<br />
			inode_inc_link_count(new_dir);<br />
	}</p>
<p>	/*<br />
	 * Like most other Unix systems, set the ctime for inodes on a<br />
 	 * rename.<br />
	 * inode_dec_link_count() will mark the inode dirty.<br />
	 */<br />
	old_inode->i_ctime = CURRENT_TIME_SEC;</p>
<p>	ext2_delete_entry (old_de, old_page);<br />
	inode_dec_link_count(old_inode);</p>
<p>	if (dir_de) {<br />
		if (old_dir != new_dir)<br />
			ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0);<br />
		else {<br />
			kunmap(dir_page);<br />
			page_cache_release(dir_page);<br />
		}<br />
		inode_dec_link_count(old_dir);<br />
	}<br />
	return 0;</p>
<p>out_dir:<br />
	if (dir_de) {<br />
		kunmap(dir_page);<br />
		page_cache_release(dir_page);<br />
	}<br />
out_old:<br />
	kunmap(old_page);<br />
	page_cache_release(old_page);<br />
out:<br />
	return err;<br />
}</p>
<p>const struct inode_operations ext2_dir_inode_operations = {<br />
	.create		= ext2_create,<br />
	.lookup		= ext2_lookup,<br />
	.link		= ext2_link,<br />
	.unlink		= ext2_unlink,<br />
	.symlink	= ext2_symlink,<br />
	.mkdir		= ext2_mkdir,<br />
	.rmdir		= ext2_rmdir,<br />
	.mknod		= ext2_mknod,<br />
	.rename		= ext2_rename,<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext2_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
	.setattr	= ext2_setattr,<br />
	.check_acl	= ext2_check_acl,<br />
};</p>
<p>const struct inode_operations ext2_special_inode_operations = {<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext2_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
	.setattr	= ext2_setattr,<br />
	.check_acl	= ext2_check_acl,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/namei-c-5/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Makefile</title>
		<link>http://lynyrd.ru/makefile-15</link>
		<comments>http://lynyrd.ru/makefile-15#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:44:55 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/makefile-15</guid>
		<description><![CDATA[#
# Makefile for the linux ext2-filesystem routines.
#
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o ialloc.o inode.o \
	  ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR)	 += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY)	 += xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP)	 += xip.o
]]></description>
			<content:encoded><![CDATA[<p>#<br />
# Makefile for the linux ext2-filesystem routines.<span id="more-1155"></span><br />
#</p>
<p>obj-$(CONFIG_EXT2_FS) += ext2.o</p>
<p>ext2-y := balloc.o dir.o file.o ialloc.o inode.o \<br />
	  ioctl.o namei.o super.o symlink.o</p>
<p>ext2-$(CONFIG_EXT2_FS_XATTR)	 += xattr.o xattr_user.o xattr_trusted.o<br />
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o<br />
ext2-$(CONFIG_EXT2_FS_SECURITY)	 += xattr_security.o<br />
ext2-$(CONFIG_EXT2_FS_XIP)	 += xip.o</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/makefile-15/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kconfig</title>
		<link>http://lynyrd.ru/kconfig-16</link>
		<comments>http://lynyrd.ru/kconfig-16#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:44:39 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1153</guid>
		<description><![CDATA[config EXT2_FS
	tristate &#171;Second extended fs support&#187;
	help
	  Ext2 is a standard Linux file system for hard disks.
	  To compile this file system support as a module, choose M here: the
	  module will be called ext2.
	  If unsure, say Y.
config EXT2_FS_XATTR
	bool &#171;Ext2 extended attributes&#187;
	depends on EXT2_FS
	help
	  Extended attributes are name:value pairs associated ]]></description>
			<content:encoded><![CDATA[<p>config EXT2_FS<br />
	tristate &laquo;Second extended fs support&raquo;<span id="more-1153"></span><br />
	help<br />
	  Ext2 is a standard Linux file system for hard disks.</p>
<p>	  To compile this file system support as a module, choose M here: the<br />
	  module will be called ext2.</p>
<p>	  If unsure, say Y.</p>
<p>config EXT2_FS_XATTR<br />
	bool &laquo;Ext2 extended attributes&raquo;<br />
	depends on EXT2_FS<br />
	help<br />
	  Extended attributes are name:value pairs associated with inodes by<br />
	  the kernel or by users (see the attr(5) manual page, or visit<br />
	  <http://acl.bestbits.at/> for details).</p>
<p>	  If unsure, say N.</p>
<p>config EXT2_FS_POSIX_ACL<br />
	bool &laquo;Ext2 POSIX Access Control Lists&raquo;<br />
	depends on EXT2_FS_XATTR<br />
	select FS_POSIX_ACL<br />
	help<br />
	  Posix Access Control Lists (ACLs) support permissions for users and<br />
	  groups beyond the owner/group/world scheme.</p>
<p>	  To learn more about Access Control Lists, visit the Posix ACLs for<br />
	  Linux website <http://acl.bestbits.at/>.</p>
<p>	  If you don&#8217;t know what Access Control Lists are, say N</p>
<p>config EXT2_FS_SECURITY<br />
	bool &laquo;Ext2 Security Labels&raquo;<br />
	depends on EXT2_FS_XATTR<br />
	help<br />
	  Security labels support alternative access control models<br />
	  implemented by security modules like SELinux.  This option<br />
	  enables an extended attribute handler for file security<br />
	  labels in the ext2 filesystem.</p>
<p>	  If you are not using a security module that requires using<br />
	  extended attributes for file security labels, say N.</p>
<p>config EXT2_FS_XIP<br />
	bool &laquo;Ext2 execute in place support&raquo;<br />
	depends on EXT2_FS &#038;&#038; MMU<br />
	help<br />
	  Execute in place can be used on memory-backed block devices. If you<br />
	  enable this option, you can select to mount block devices which are<br />
	  capable of this feature without using the page cache.</p>
<p>	  If you do not use a block device that is capable of using this,<br />
	  or if unsure, say N.</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/kconfig-16/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ioctl.c</title>
		<link>http://lynyrd.ru/ioctl-c-3</link>
		<comments>http://lynyrd.ru/ioctl-c-3#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:44:15 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/ioctl-c-3</guid>
		<description><![CDATA[/*
 * linux/fs/ext2/ioctl.c
 *
 * Copyright (C) 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 */
#include &#171;ext2.h&#187;
#include

#include

#include

#include

#include

#include 
#include 
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct inode *inode = filp->f_dentry->d_inode;
	struct ext2_inode_info *ei = EXT2_I(inode);
	unsigned int flags;
	unsigned short rsv_window_size;
	int ret;
	ext2_debug ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * linux/fs/ext2/ioctl.c<span id="more-1152"></span><br />
 *<br />
 * Copyright (C) 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 */</p>
<p>#include &laquo;ext2.h&raquo;<br />
#include
<linux/capability.h>
#include
<linux/time.h>
#include
<linux/sched.h>
#include
<linux/compat.h>
#include
<linux/mount.h>
#include <asm/current.h><br />
#include <asm/uaccess.h></p>
<p>long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)<br />
{<br />
	struct inode *inode = filp->f_dentry->d_inode;<br />
	struct ext2_inode_info *ei = EXT2_I(inode);<br />
	unsigned int flags;<br />
	unsigned short rsv_window_size;<br />
	int ret;</p>
<p>	ext2_debug (&raquo;cmd = %u, arg = %lu\n&raquo;, cmd, arg);</p>
<p>	switch (cmd) {<br />
	case EXT2_IOC_GETFLAGS:<br />
		ext2_get_inode_flags(ei);<br />
		flags = ei->i_flags &#038; EXT2_FL_USER_VISIBLE;<br />
		return put_user(flags, (int __user *) arg);<br />
	case EXT2_IOC_SETFLAGS: {<br />
		unsigned int oldflags;</p>
<p>		ret = mnt_want_write(filp->f_path.mnt);<br />
		if (ret)<br />
			return ret;</p>
<p>		if (!is_owner_or_cap(inode)) {<br />
			ret = -EACCES;<br />
			goto setflags_out;<br />
		}</p>
<p>		if (get_user(flags, (int __user *) arg)) {<br />
			ret = -EFAULT;<br />
			goto setflags_out;<br />
		}</p>
<p>		flags = ext2_mask_flags(inode->i_mode, flags);</p>
<p>		mutex_lock(&#038;inode->i_mutex);<br />
		/* Is it quota file? Do not allow user to mess with it */<br />
		if (IS_NOQUOTA(inode)) {<br />
			mutex_unlock(&#038;inode->i_mutex);<br />
			ret = -EPERM;<br />
			goto setflags_out;<br />
		}<br />
		oldflags = ei->i_flags;</p>
<p>		/*<br />
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by<br />
		 * the relevant capability.<br />
		 *<br />
		 * This test looks nicer. Thanks to Pauline Middelink<br />
		 */<br />
		if ((flags ^ oldflags) &#038; (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {<br />
			if (!capable(CAP_LINUX_IMMUTABLE)) {<br />
				mutex_unlock(&#038;inode->i_mutex);<br />
				ret = -EPERM;<br />
				goto setflags_out;<br />
			}<br />
		}</p>
<p>		flags = flags &#038; EXT2_FL_USER_MODIFIABLE;<br />
		flags |= oldflags &#038; ~EXT2_FL_USER_MODIFIABLE;<br />
		ei->i_flags = flags;<br />
		mutex_unlock(&#038;inode->i_mutex);</p>
<p>		ext2_set_inode_flags(inode);<br />
		inode->i_ctime = CURRENT_TIME_SEC;<br />
		mark_inode_dirty(inode);<br />
setflags_out:<br />
		mnt_drop_write(filp->f_path.mnt);<br />
		return ret;<br />
	}<br />
	case EXT2_IOC_GETVERSION:<br />
		return put_user(inode->i_generation, (int __user *) arg);<br />
	case EXT2_IOC_SETVERSION:<br />
		if (!is_owner_or_cap(inode))<br />
			return -EPERM;<br />
		ret = mnt_want_write(filp->f_path.mnt);<br />
		if (ret)<br />
			return ret;<br />
		if (get_user(inode->i_generation, (int __user *) arg)) {<br />
			ret = -EFAULT;<br />
		} else {<br />
			inode->i_ctime = CURRENT_TIME_SEC;<br />
			mark_inode_dirty(inode);<br />
		}<br />
		mnt_drop_write(filp->f_path.mnt);<br />
		return ret;<br />
	case EXT2_IOC_GETRSVSZ:<br />
		if (test_opt(inode->i_sb, RESERVATION)<br />
			&#038;&#038; S_ISREG(inode->i_mode)<br />
			&#038;&#038; ei->i_block_alloc_info) {<br />
			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;<br />
			return put_user(rsv_window_size, (int __user *)arg);<br />
		}<br />
		return -ENOTTY;<br />
	case EXT2_IOC_SETRSVSZ: {</p>
<p>		if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))<br />
			return -ENOTTY;</p>
<p>		if (!is_owner_or_cap(inode))<br />
			return -EACCES;</p>
<p>		if (get_user(rsv_window_size, (int __user *)arg))<br />
			return -EFAULT;</p>
<p>		ret = mnt_want_write(filp->f_path.mnt);<br />
		if (ret)<br />
			return ret;</p>
<p>		if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS)<br />
			rsv_window_size = EXT2_MAX_RESERVE_BLOCKS;</p>
<p>		/*<br />
		 * need to allocate reservation structure for this inode<br />
		 * before set the window size<br />
		 */<br />
		/*<br />
		 * XXX What lock should protect the rsv_goal_size?<br />
		 * Accessed in ext2_get_block only.  ext3 uses i_truncate.<br />
		 */<br />
		mutex_lock(&#038;ei->truncate_mutex);<br />
		if (!ei->i_block_alloc_info)<br />
			ext2_init_block_alloc_info(inode);</p>
<p>		if (ei->i_block_alloc_info){<br />
			struct ext2_reserve_window_node *rsv = &#038;ei->i_block_alloc_info->rsv_window_node;<br />
			rsv->rsv_goal_size = rsv_window_size;<br />
		}<br />
		mutex_unlock(&#038;ei->truncate_mutex);<br />
		mnt_drop_write(filp->f_path.mnt);<br />
		return 0;<br />
	}<br />
	default:<br />
		return -ENOTTY;<br />
	}<br />
}</p>
<p>#ifdef CONFIG_COMPAT<br />
long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)<br />
{<br />
	/* These are just misnamed, they actually get/put from/to user an int */<br />
	switch (cmd) {<br />
	case EXT2_IOC32_GETFLAGS:<br />
		cmd = EXT2_IOC_GETFLAGS;<br />
		break;<br />
	case EXT2_IOC32_SETFLAGS:<br />
		cmd = EXT2_IOC_SETFLAGS;<br />
		break;<br />
	case EXT2_IOC32_GETVERSION:<br />
		cmd = EXT2_IOC_GETVERSION;<br />
		break;<br />
	case EXT2_IOC32_SETVERSION:<br />
		cmd = EXT2_IOC_SETVERSION;<br />
		break;<br />
	default:<br />
		return -ENOIOCTLCMD;<br />
	}<br />
	return ext2_ioctl(file, cmd, (unsigned long) compat_ptr(arg));<br />
}<br />
#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/ioctl-c-3/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>inode.c</title>
		<link>http://lynyrd.ru/inode-c-18</link>
		<comments>http://lynyrd.ru/inode-c-18#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:43:52 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/inode-c-18</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/inode.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/inode.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  Goal-directed block allocation ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/inode.c<span id="more-1151"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/inode.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  Goal-directed block allocation by Stephen Tweedie<br />
 * 	(sct@dcs.ed.ac.uk), 1993, 1998<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 *  64-bit file support on 64-bit platforms by Jakub Jelinek<br />
 * 	(jj@sunsite.ms.mff.cuni.cz)<br />
 *<br />
 *  Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000<br />
 */</p>
<p>#include
<linux/smp_lock.h>
#include
<linux/time.h>
#include
<linux/highuid.h>
#include
<linux/pagemap.h>
#include
<linux/quotaops.h>
#include
<linux/module.h>
#include
<linux/writeback.h>
#include
<linux/buffer_head.h>
#include
<linux/mpage.h>
#include
<linux/fiemap.h>
#include
<linux/namei.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;acl.h&raquo;<br />
#include &laquo;xip.h&raquo;</p>
<p>MODULE_AUTHOR(&raquo;Remy Card and others&raquo;);<br />
MODULE_DESCRIPTION(&raquo;Second Extended Filesystem&raquo;);<br />
MODULE_LICENSE(&raquo;GPL&raquo;);</p>
<p>/*<br />
 * Test whether an inode is a fast symlink.<br />
 */<br />
static inline int ext2_inode_is_fast_symlink(struct inode *inode)<br />
{<br />
	int ea_blocks = EXT2_I(inode)->i_file_acl ?<br />
		(inode->i_sb->s_blocksize >> 9) : 0;</p>
<p>	return (S_ISLNK(inode->i_mode) &#038;&#038;<br />
		inode->i_blocks &#8211; ea_blocks == 0);<br />
}</p>
<p>/*<br />
 * Called at the last iput() if i_nlink is zero.<br />
 */<br />
void ext2_delete_inode (struct inode * inode)<br />
{<br />
	truncate_inode_pages(&#038;inode->i_data, 0);</p>
<p>	if (is_bad_inode(inode))<br />
		goto no_delete;<br />
	EXT2_I(inode)->i_dtime	= get_seconds();<br />
	mark_inode_dirty(inode);<br />
	ext2_write_inode(inode, inode_needs_sync(inode));</p>
<p>	inode->i_size = 0;<br />
	if (inode->i_blocks)<br />
		ext2_truncate (inode);<br />
	ext2_free_inode (inode);</p>
<p>	return;<br />
no_delete:<br />
	clear_inode(inode);	/* We must guarantee clearing of inode&#8230; */<br />
}</p>
<p>typedef struct {<br />
	__le32	*p;<br />
	__le32	key;<br />
	struct buffer_head *bh;<br />
} Indirect;</p>
<p>static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)<br />
{<br />
	p->key = *(p->p = v);<br />
	p->bh = bh;<br />
}</p>
<p>static inline int verify_chain(Indirect *from, Indirect *to)<br />
{<br />
	while (from <= to &#038;&#038; from->key == *from->p)<br />
		from++;<br />
	return (from > to);<br />
}</p>
<p>/**<br />
 *	ext2_block_to_path &#8211; parse the block number into array of offsets<br />
 *	@inode: inode in question (we are only interested in its superblock)<br />
 *	@i_block: block number to be parsed<br />
 *	@offsets: array to store the offsets in<br />
 *      @boundary: set this non-zero if the referred-to block is likely to be<br />
 *             followed (on disk) by an indirect block.<br />
 *	To store the locations of file&#8217;s data ext2 uses a data structure common<br />
 *	for UNIX filesystems &#8211; tree of pointers anchored in the inode, with<br />
 *	data blocks at leaves and indirect blocks in intermediate nodes.<br />
 *	This function translates the block number into path in that tree -<br />
 *	return value is the path length and @offsets[n] is the offset of<br />
 *	pointer to (n+1)th node in the nth one. If @block is out of range<br />
 *	(negative or too large) warning is printed and zero returned.<br />
 *<br />
 *	Note: function doesn&#8217;t find node addresses, so no IO is needed. All<br />
 *	we need to know is the capacity of indirect blocks (taken from the<br />
 *	inode->i_sb).<br />
 */</p>
<p>/*<br />
 * Portability note: the last comparison (check that we fit into triple<br />
 * indirect block) is spelled differently, because otherwise on an<br />
 * architecture with 32-bit longs and 8Kb pages we might get into trouble<br />
 * if our filesystem had 8Kb blocks. We might use long long, but that would<br />
 * kill us on x86. Oh, well, at least the sign propagation does not matter -<br />
 * i_block would have to be negative in the very beginning, so we would not<br />
 * get there at all.<br />
 */</p>
<p>static int ext2_block_to_path(struct inode *inode,<br />
			long i_block, int offsets[4], int *boundary)<br />
{<br />
	int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);<br />
	int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);<br />
	const long direct_blocks = EXT2_NDIR_BLOCKS,<br />
		indirect_blocks = ptrs,<br />
		double_blocks = (1 << (ptrs_bits * 2));<br />
	int n = 0;<br />
	int final = 0;</p>
<p>	if (i_block < 0) {<br />
		ext2_warning (inode->i_sb, &laquo;ext2_block_to_path&raquo;, &laquo;block < 0");<br />
	} else if (i_block < direct_blocks) {<br />
		offsets[n++] = i_block;<br />
		final = direct_blocks;<br />
	} else if ( (i_block -= direct_blocks) < indirect_blocks) {<br />
		offsets[n++] = EXT2_IND_BLOCK;<br />
		offsets[n++] = i_block;<br />
		final = ptrs;<br />
	} else if ((i_block -= indirect_blocks) < double_blocks) {<br />
		offsets[n++] = EXT2_DIND_BLOCK;<br />
		offsets[n++] = i_block >> ptrs_bits;<br />
		offsets[n++] = i_block &#038; (ptrs &#8211; 1);<br />
		final = ptrs;<br />
	} else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {<br />
		offsets[n++] = EXT2_TIND_BLOCK;<br />
		offsets[n++] = i_block >> (ptrs_bits * 2);<br />
		offsets[n++] = (i_block >> ptrs_bits) &#038; (ptrs &#8211; 1);<br />
		offsets[n++] = i_block &#038; (ptrs &#8211; 1);<br />
		final = ptrs;<br />
	} else {<br />
		ext2_warning (inode->i_sb, &laquo;ext2_block_to_path&raquo;, &laquo;block > big&raquo;);<br />
	}<br />
	if (boundary)<br />
		*boundary = final &#8211; 1 &#8211; (i_block &#038; (ptrs &#8211; 1));</p>
<p>	return n;<br />
}</p>
<p>/**<br />
 *	ext2_get_branch &#8211; read the chain of indirect blocks leading to data<br />
 *	@inode: inode in question<br />
 *	@depth: depth of the chain (1 &#8211; direct pointer, etc.)<br />
 *	@offsets: offsets of pointers in inode/indirect blocks<br />
 *	@chain: place to store the result<br />
 *	@err: here we store the error value<br />
 *<br />
 *	Function fills the array of triples <key, p, bh> and returns %NULL<br />
 *	if everything went OK or the pointer to the last filled triple<br />
 *	(incomplete one) otherwise. Upon the return chain[i].key contains<br />
 *	the number of (i+1)-th block in the chain (as it is stored in memory,<br />
 *	i.e. little-endian 32-bit), chain[i].p contains the address of that<br />
 *	number (it points into struct inode for i==0 and into the bh->b_data<br />
 *	for i>0) and chain[i].bh points to the buffer_head of i-th indirect<br />
 *	block for i>0 and NULL for i==0. In other words, it holds the block<br />
 *	numbers of the chain, addresses they were taken from (and where we can<br />
 *	verify that chain did not change) and buffer_heads hosting these<br />
 *	numbers.<br />
 *<br />
 *	Function stops when it stumbles upon zero pointer (absent block)<br />
 *		(pointer to last triple returned, *@err == 0)<br />
 *	or when it gets an IO error reading an indirect block<br />
 *		(ditto, *@err == -EIO)<br />
 *	or when it notices that chain had been changed while it was reading<br />
 *		(ditto, *@err == -EAGAIN)<br />
 *	or when it reads all @depth-1 indirect blocks successfully and finds<br />
 *	the whole chain, all way to the data (returns %NULL, *err == 0).<br />
 */<br />
static Indirect *ext2_get_branch(struct inode *inode,<br />
				 int depth,<br />
				 int *offsets,<br />
				 Indirect chain[4],<br />
				 int *err)<br />
{<br />
	struct super_block *sb = inode->i_sb;<br />
	Indirect *p = chain;<br />
	struct buffer_head *bh;</p>
<p>	*err = 0;<br />
	/* i_data is not going away, no lock needed */<br />
	add_chain (chain, NULL, EXT2_I(inode)->i_data + *offsets);<br />
	if (!p->key)<br />
		goto no_block;<br />
	while (&#8211;depth) {<br />
		bh = sb_bread(sb, le32_to_cpu(p->key));<br />
		if (!bh)<br />
			goto failure;<br />
		read_lock(&#038;EXT2_I(inode)->i_meta_lock);<br />
		if (!verify_chain(chain, p))<br />
			goto changed;<br />
		add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);<br />
		read_unlock(&#038;EXT2_I(inode)->i_meta_lock);<br />
		if (!p->key)<br />
			goto no_block;<br />
	}<br />
	return NULL;</p>
<p>changed:<br />
	read_unlock(&#038;EXT2_I(inode)->i_meta_lock);<br />
	brelse(bh);<br />
	*err = -EAGAIN;<br />
	goto no_block;<br />
failure:<br />
	*err = -EIO;<br />
no_block:<br />
	return p;<br />
}</p>
<p>/**<br />
 *	ext2_find_near &#8211; find a place for allocation with sufficient locality<br />
 *	@inode: owner<br />
 *	@ind: descriptor of indirect block.<br />
 *<br />
 *	This function returns the preferred place for block allocation.<br />
 *	It is used when heuristic for sequential allocation fails.<br />
 *	Rules are:<br />
 *	  + if there is a block to the left of our position &#8211; allocate near it.<br />
 *	  + if pointer will live in indirect block &#8211; allocate near that block.<br />
 *	  + if pointer will live in inode &#8211; allocate in the same cylinder group.<br />
 *<br />
 * In the latter case we colour the starting block by the callers PID to<br />
 * prevent it from clashing with concurrent allocations for a different inode<br />
 * in the same block group.   The PID is used here so that functionally related<br />
 * files will be close-by on-disk.<br />
 *<br />
 *	Caller must make sure that @ind is valid and will stay that way.<br />
 */</p>
<p>static ext2_fsblk_t ext2_find_near(struct inode *inode, Indirect *ind)<br />
{<br />
	struct ext2_inode_info *ei = EXT2_I(inode);<br />
	__le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;<br />
	__le32 *p;<br />
	ext2_fsblk_t bg_start;<br />
	ext2_fsblk_t colour;</p>
<p>	/* Try to find previous block */<br />
	for (p = ind->p &#8211; 1; p >= start; p&#8211;)<br />
		if (*p)<br />
			return le32_to_cpu(*p);</p>
<p>	/* No such thing, so let&#8217;s try location of indirect block */<br />
	if (ind->bh)<br />
		return ind->bh->b_blocknr;</p>
<p>	/*<br />
	 * It is going to be refered from inode itself? OK, just put it into<br />
	 * the same cylinder group then.<br />
	 */<br />
	bg_start = ext2_group_first_block_no(inode->i_sb, ei->i_block_group);<br />
	colour = (current->pid % 16) *<br />
			(EXT2_BLOCKS_PER_GROUP(inode->i_sb) / 16);<br />
	return bg_start + colour;<br />
}</p>
<p>/**<br />
 *	ext2_find_goal &#8211; find a preferred place for allocation.<br />
 *	@inode: owner<br />
 *	@block:  block we want<br />
 *	@partial: pointer to the last triple within a chain<br />
 *<br />
 *	Returns preferred place for a block (the goal).<br />
 */</p>
<p>static inline ext2_fsblk_t ext2_find_goal(struct inode *inode, long block,<br />
					  Indirect *partial)<br />
{<br />
	struct ext2_block_alloc_info *block_i;</p>
<p>	block_i = EXT2_I(inode)->i_block_alloc_info;</p>
<p>	/*<br />
	 * try the heuristic for sequential allocation,<br />
	 * failing that at least try to get decent locality.<br />
	 */<br />
	if (block_i &#038;&#038; (block == block_i->last_alloc_logical_block + 1)<br />
		&#038;&#038; (block_i->last_alloc_physical_block != 0)) {<br />
		return block_i->last_alloc_physical_block + 1;<br />
	}</p>
<p>	return ext2_find_near(inode, partial);<br />
}</p>
<p>/**<br />
 *	ext2_blks_to_allocate: Look up the block map and count the number<br />
 *	of direct blocks need to be allocated for the given branch.<br />
 *<br />
 * 	@branch: chain of indirect blocks<br />
 *	@k: number of blocks need for indirect blocks<br />
 *	@blks: number of data blocks to be mapped.<br />
 *	@blocks_to_boundary:  the offset in the indirect block<br />
 *<br />
 *	return the total number of blocks to be allocate, including the<br />
 *	direct and indirect blocks.<br />
 */<br />
static int<br />
ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks,<br />
		int blocks_to_boundary)<br />
{<br />
	unsigned long count = 0;</p>
<p>	/*<br />
	 * Simple case, [t,d]Indirect block(s) has not allocated yet<br />
	 * then it&#8217;s clear blocks on that path have not allocated<br />
	 */<br />
	if (k > 0) {<br />
		/* right now don&#8217;t hanel cross boundary allocation */<br />
		if (blks < blocks_to_boundary + 1)<br />
			count += blks;<br />
		else<br />
			count += blocks_to_boundary + 1;<br />
		return count;<br />
	}</p>
<p>	count++;<br />
	while (count < blks &#038;&#038; count <= blocks_to_boundary<br />
		&#038;&#038; le32_to_cpu(*(branch[0].p + count)) == 0) {<br />
		count++;<br />
	}<br />
	return count;<br />
}</p>
<p>/**<br />
 *	ext2_alloc_blocks: multiple allocate blocks needed for a branch<br />
 *	@indirect_blks: the number of blocks need to allocate for indirect<br />
 *			blocks<br />
 *<br />
 *	@new_blocks: on return it will store the new block numbers for<br />
 *	the indirect blocks(if needed) and the first direct block,<br />
 *	@blks:	on return it will store the total number of allocated<br />
 *		direct blocks<br />
 */<br />
static int ext2_alloc_blocks(struct inode *inode,<br />
			ext2_fsblk_t goal, int indirect_blks, int blks,<br />
			ext2_fsblk_t new_blocks[4], int *err)<br />
{<br />
	int target, i;<br />
	unsigned long count = 0;<br />
	int index = 0;<br />
	ext2_fsblk_t current_block = 0;<br />
	int ret = 0;</p>
<p>	/*<br />
	 * Here we try to allocate the requested multiple blocks at once,<br />
	 * on a best-effort basis.<br />
	 * To build a branch, we should allocate blocks for<br />
	 * the indirect blocks(if not allocated yet), and at least<br />
	 * the first direct block of this branch.  That's the<br />
	 * minimum number of blocks need to allocate(required)<br />
	 */<br />
	target = blks + indirect_blks;</p>
<p>	while (1) {<br />
		count = target;<br />
		/* allocating blocks for indirect blocks and direct blocks */<br />
		current_block = ext2_new_blocks(inode,goal,&#038;count,err);<br />
		if (*err)<br />
			goto failed_out;</p>
<p>		target -= count;<br />
		/* allocate blocks for indirect blocks */<br />
		while (index < indirect_blks &#038;&#038; count) {<br />
			new_blocks[index++] = current_block++;<br />
			count--;<br />
		}</p>
<p>		if (count > 0)<br />
			break;<br />
	}</p>
<p>	/* save the new block number for the first direct block */<br />
	new_blocks[index] = current_block;</p>
<p>	/* total number of blocks allocated for direct blocks */<br />
	ret = count;<br />
	*err = 0;<br />
	return ret;<br />
failed_out:<br />
	for (i = 0; i <index; i++)<br />
		ext2_free_blocks(inode, new_blocks[i], 1);<br />
	return ret;<br />
}</p>
<p>/**<br />
 *	ext2_alloc_branch - allocate and set up a chain of blocks.<br />
 *	@inode: owner<br />
 *	@num: depth of the chain (number of blocks to allocate)<br />
 *	@offsets: offsets (in the blocks) to store the pointers to next.<br />
 *	@branch: place to store the chain in.<br />
 *<br />
 *	This function allocates @num blocks, zeroes out all but the last one,<br />
 *	links them into chain and (if we are synchronous) writes them to disk.<br />
 *	In other words, it prepares a branch that can be spliced onto the<br />
 *	inode. It stores the information about that chain in the branch[], in<br />
 *	the same format as ext2_get_branch() would do. We are calling it after<br />
 *	we had read the existing part of chain and partial points to the last<br />
 *	triple of that (one with zero ->key). Upon the exit we have the same<br />
 *	picture as after the successful ext2_get_block(), excpet that in one<br />
 *	place chain is disconnected &#8211; *branch->p is still zero (we did not<br />
 *	set the last link), but branch->key contains the number that should<br />
 *	be placed into *branch->p to fill that gap.<br />
 *<br />
 *	If allocation fails we free all blocks we&#8217;ve allocated (and forget<br />
 *	their buffer_heads) and return the error value the from failed<br />
 *	ext2_alloc_block() (normally -ENOSPC). Otherwise we set the chain<br />
 *	as described above and return 0.<br />
 */</p>
<p>static int ext2_alloc_branch(struct inode *inode,<br />
			int indirect_blks, int *blks, ext2_fsblk_t goal,<br />
			int *offsets, Indirect *branch)<br />
{<br />
	int blocksize = inode->i_sb->s_blocksize;<br />
	int i, n = 0;<br />
	int err = 0;<br />
	struct buffer_head *bh;<br />
	int num;<br />
	ext2_fsblk_t new_blocks[4];<br />
	ext2_fsblk_t current_block;</p>
<p>	num = ext2_alloc_blocks(inode, goal, indirect_blks,<br />
				*blks, new_blocks, &#038;err);<br />
	if (err)<br />
		return err;</p>
<p>	branch[0].key = cpu_to_le32(new_blocks[0]);<br />
	/*<br />
	 * metadata blocks and data blocks are allocated.<br />
	 */<br />
	for (n = 1; n <= indirect_blks;  n++) {<br />
		/*<br />
		 * Get buffer_head for parent block, zero it out<br />
		 * and set the pointer to new one, then send<br />
		 * parent to disk.<br />
		 */<br />
		bh = sb_getblk(inode->i_sb, new_blocks[n-1]);<br />
		branch[n].bh = bh;<br />
		lock_buffer(bh);<br />
		memset(bh->b_data, 0, blocksize);<br />
		branch[n].p = (__le32 *) bh->b_data + offsets[n];<br />
		branch[n].key = cpu_to_le32(new_blocks[n]);<br />
		*branch[n].p = branch[n].key;<br />
		if ( n == indirect_blks) {<br />
			current_block = new_blocks[n];<br />
			/*<br />
			 * End of chain, update the last new metablock of<br />
			 * the chain to point to the new allocated<br />
			 * data blocks numbers<br />
			 */<br />
			for (i=1; i < num; i++)<br />
				*(branch[n].p + i) = cpu_to_le32(++current_block);<br />
		}<br />
		set_buffer_uptodate(bh);<br />
		unlock_buffer(bh);<br />
		mark_buffer_dirty_inode(bh, inode);<br />
		/* We used to sync bh here if IS_SYNC(inode).<br />
		 * But we now rely upon generic_write_sync()<br />
		 * and b_inode_buffers.  But not for directories.<br />
		 */<br />
		if (S_ISDIR(inode->i_mode) &#038;&#038; IS_DIRSYNC(inode))<br />
			sync_dirty_buffer(bh);<br />
	}<br />
	*blks = num;<br />
	return err;<br />
}</p>
<p>/**<br />
 * ext2_splice_branch &#8211; splice the allocated branch onto inode.<br />
 * @inode: owner<br />
 * @block: (logical) number of block we are adding<br />
 * @where: location of missing link<br />
 * @num:   number of indirect blocks we are adding<br />
 * @blks:  number of direct blocks we are adding<br />
 *<br />
 * This function fills the missing link and does all housekeeping needed in<br />
 * inode (->i_blocks, etc.). In case of success we end up with the full<br />
 * chain to new block and return 0.<br />
 */<br />
static void ext2_splice_branch(struct inode *inode,<br />
			long block, Indirect *where, int num, int blks)<br />
{<br />
	int i;<br />
	struct ext2_block_alloc_info *block_i;<br />
	ext2_fsblk_t current_block;</p>
<p>	block_i = EXT2_I(inode)->i_block_alloc_info;</p>
<p>	/* XXX LOCKING probably should have i_meta_lock ?*/<br />
	/* That&#8217;s it */</p>
<p>	*where->p = where->key;</p>
<p>	/*<br />
	 * Update the host buffer_head or inode to point to more just allocated<br />
	 * direct blocks blocks<br />
	 */<br />
	if (num == 0 &#038;&#038; blks > 1) {<br />
		current_block = le32_to_cpu(where->key) + 1;<br />
		for (i = 1; i < blks; i++)<br />
			*(where->p + i ) = cpu_to_le32(current_block++);<br />
	}</p>
<p>	/*<br />
	 * update the most recently allocated logical &#038; physical block<br />
	 * in i_block_alloc_info, to assist find the proper goal block for next<br />
	 * allocation<br />
	 */<br />
	if (block_i) {<br />
		block_i->last_alloc_logical_block = block + blks &#8211; 1;<br />
		block_i->last_alloc_physical_block =<br />
				le32_to_cpu(where[num].key) + blks &#8211; 1;<br />
	}</p>
<p>	/* We are done with atomic stuff, now do the rest of housekeeping */</p>
<p>	/* had we spliced it onto indirect block? */<br />
	if (where->bh)<br />
		mark_buffer_dirty_inode(where->bh, inode);</p>
<p>	inode->i_ctime = CURRENT_TIME_SEC;<br />
	mark_inode_dirty(inode);<br />
}</p>
<p>/*<br />
 * Allocation strategy is simple: if we have to allocate something, we will<br />
 * have to go the whole way to leaf. So let&#8217;s do it before attaching anything<br />
 * to tree, set linkage between the newborn blocks, write them if sync is<br />
 * required, recheck the path, free and repeat if check fails, otherwise<br />
 * set the last missing link (that will protect us from any truncate-generated<br />
 * removals &#8211; all blocks on the path are immune now) and possibly force the<br />
 * write on the parent block.<br />
 * That has a nice additional property: no special recovery from the failed<br />
 * allocations is needed &#8211; we simply release blocks and do not touch anything<br />
 * reachable from inode.<br />
 *<br />
 * `handle&#8217; can be NULL if create == 0.<br />
 *<br />
 * return > 0, # of blocks mapped or allocated.<br />
 * return = 0, if plain lookup failed.<br />
 * return < 0, error case.<br />
 */<br />
static int ext2_get_blocks(struct inode *inode,<br />
			   sector_t iblock, unsigned long maxblocks,<br />
			   struct buffer_head *bh_result,<br />
			   int create)<br />
{<br />
	int err = -EIO;<br />
	int offsets[4];<br />
	Indirect chain[4];<br />
	Indirect *partial;<br />
	ext2_fsblk_t goal;<br />
	int indirect_blks;<br />
	int blocks_to_boundary = 0;<br />
	int depth;<br />
	struct ext2_inode_info *ei = EXT2_I(inode);<br />
	int count = 0;<br />
	ext2_fsblk_t first_block = 0;</p>
<p>	depth = ext2_block_to_path(inode,iblock,offsets,&#038;blocks_to_boundary);</p>
<p>	if (depth == 0)<br />
		return (err);</p>
<p>	partial = ext2_get_branch(inode, depth, offsets, chain, &#038;err);<br />
	/* Simplest case - block found, no allocation needed */<br />
	if (!partial) {<br />
		first_block = le32_to_cpu(chain[depth - 1].key);<br />
		clear_buffer_new(bh_result); /* What's this do? */<br />
		count++;<br />
		/*map more blocks*/<br />
		while (count < maxblocks &#038;&#038; count <= blocks_to_boundary) {<br />
			ext2_fsblk_t blk;</p>
<p>			if (!verify_chain(chain, chain + depth - 1)) {<br />
				/*<br />
				 * Indirect block might be removed by<br />
				 * truncate while we were reading it.<br />
				 * Handling of that case: forget what we've<br />
				 * got now, go to reread.<br />
				 */<br />
				err = -EAGAIN;<br />
				count = 0;<br />
				break;<br />
			}<br />
			blk = le32_to_cpu(*(chain[depth-1].p + count));<br />
			if (blk == first_block + count)<br />
				count++;<br />
			else<br />
				break;<br />
		}<br />
		if (err != -EAGAIN)<br />
			goto got_it;<br />
	}</p>
<p>	/* Next simple case - plain lookup or failed read of indirect block */<br />
	if (!create || err == -EIO)<br />
		goto cleanup;</p>
<p>	mutex_lock(&#038;ei->truncate_mutex);<br />
	/*<br />
	 * If the indirect block is missing while we are reading<br />
	 * the chain(ext3_get_branch() returns -EAGAIN err), or<br />
	 * if the chain has been changed after we grab the semaphore,<br />
	 * (either because another process truncated this branch, or<br />
	 * another get_block allocated this branch) re-grab the chain to see if<br />
	 * the request block has been allocated or not.<br />
	 *<br />
	 * Since we already block the truncate/other get_block<br />
	 * at this point, we will have the current copy of the chain when we<br />
	 * splice the branch into the tree.<br />
	 */<br />
	if (err == -EAGAIN || !verify_chain(chain, partial)) {<br />
		while (partial > chain) {<br />
			brelse(partial->bh);<br />
			partial&#8211;;<br />
		}<br />
		partial = ext2_get_branch(inode, depth, offsets, chain, &#038;err);<br />
		if (!partial) {<br />
			count++;<br />
			mutex_unlock(&#038;ei->truncate_mutex);<br />
			if (err)<br />
				goto cleanup;<br />
			clear_buffer_new(bh_result);<br />
			goto got_it;<br />
		}<br />
	}</p>
<p>	/*<br />
	 * Okay, we need to do block allocation.  Lazily initialize the block<br />
	 * allocation info here if necessary<br />
	*/<br />
	if (S_ISREG(inode->i_mode) &#038;&#038; (!ei->i_block_alloc_info))<br />
		ext2_init_block_alloc_info(inode);</p>
<p>	goal = ext2_find_goal(inode, iblock, partial);</p>
<p>	/* the number of blocks need to allocate for [d,t]indirect blocks */<br />
	indirect_blks = (chain + depth) &#8211; partial &#8211; 1;<br />
	/*<br />
	 * Next look up the indirect map to count the totoal number of<br />
	 * direct blocks to allocate for this branch.<br />
	 */<br />
	count = ext2_blks_to_allocate(partial, indirect_blks,<br />
					maxblocks, blocks_to_boundary);<br />
	/*<br />
	 * XXX ???? Block out ext2_truncate while we alter the tree<br />
	 */<br />
	err = ext2_alloc_branch(inode, indirect_blks, &#038;count, goal,<br />
				offsets + (partial &#8211; chain), partial);</p>
<p>	if (err) {<br />
		mutex_unlock(&#038;ei->truncate_mutex);<br />
		goto cleanup;<br />
	}</p>
<p>	if (ext2_use_xip(inode->i_sb)) {<br />
		/*<br />
		 * we need to clear the block<br />
		 */<br />
		err = ext2_clear_xip_target (inode,<br />
			le32_to_cpu(chain[depth-1].key));<br />
		if (err) {<br />
			mutex_unlock(&#038;ei->truncate_mutex);<br />
			goto cleanup;<br />
		}<br />
	}</p>
<p>	ext2_splice_branch(inode, iblock, partial, indirect_blks, count);<br />
	mutex_unlock(&#038;ei->truncate_mutex);<br />
	set_buffer_new(bh_result);<br />
got_it:<br />
	map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));<br />
	if (count > blocks_to_boundary)<br />
		set_buffer_boundary(bh_result);<br />
	err = count;<br />
	/* Clean up and exit */<br />
	partial = chain + depth &#8211; 1;	/* the whole chain */<br />
cleanup:<br />
	while (partial > chain) {<br />
		brelse(partial->bh);<br />
		partial&#8211;;<br />
	}<br />
	return err;<br />
}</p>
<p>int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)<br />
{<br />
	unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;<br />
	int ret = ext2_get_blocks(inode, iblock, max_blocks,<br />
			      bh_result, create);<br />
	if (ret > 0) {<br />
		bh_result->b_size = (ret << inode->i_blkbits);<br />
		ret = 0;<br />
	}<br />
	return ret;</p>
<p>}</p>
<p>int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,<br />
		u64 start, u64 len)<br />
{<br />
	return generic_block_fiemap(inode, fieinfo, start, len,<br />
				    ext2_get_block);<br />
}</p>
<p>static int ext2_writepage(struct page *page, struct writeback_control *wbc)<br />
{<br />
	return block_write_full_page(page, ext2_get_block, wbc);<br />
}</p>
<p>static int ext2_readpage(struct file *file, struct page *page)<br />
{<br />
	return mpage_readpage(page, ext2_get_block);<br />
}</p>
<p>static int<br />
ext2_readpages(struct file *file, struct address_space *mapping,<br />
		struct list_head *pages, unsigned nr_pages)<br />
{<br />
	return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);<br />
}</p>
<p>int __ext2_write_begin(struct file *file, struct address_space *mapping,<br />
		loff_t pos, unsigned len, unsigned flags,<br />
		struct page **pagep, void **fsdata)<br />
{<br />
	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,<br />
							ext2_get_block);<br />
}</p>
<p>static int<br />
ext2_write_begin(struct file *file, struct address_space *mapping,<br />
		loff_t pos, unsigned len, unsigned flags,<br />
		struct page **pagep, void **fsdata)<br />
{<br />
	*pagep = NULL;<br />
	return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata);<br />
}</p>
<p>static int<br />
ext2_nobh_write_begin(struct file *file, struct address_space *mapping,<br />
		loff_t pos, unsigned len, unsigned flags,<br />
		struct page **pagep, void **fsdata)<br />
{<br />
	/*<br />
	 * Dir-in-pagecache still uses ext2_write_begin. Would have to rework<br />
	 * directory handling code to pass around offsets rather than struct<br />
	 * pages in order to make this work easily.<br />
	 */<br />
	return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata,<br />
							ext2_get_block);<br />
}</p>
<p>static int ext2_nobh_writepage(struct page *page,<br />
			struct writeback_control *wbc)<br />
{<br />
	return nobh_writepage(page, ext2_get_block, wbc);<br />
}</p>
<p>static sector_t ext2_bmap(struct address_space *mapping, sector_t block)<br />
{<br />
	return generic_block_bmap(mapping,block,ext2_get_block);<br />
}</p>
<p>static ssize_t<br />
ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,<br />
			loff_t offset, unsigned long nr_segs)<br />
{<br />
	struct file *file = iocb->ki_filp;<br />
	struct inode *inode = file->f_mapping->host;</p>
<p>	return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,<br />
				offset, nr_segs, ext2_get_block, NULL);<br />
}</p>
<p>static int<br />
ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)<br />
{<br />
	return mpage_writepages(mapping, wbc, ext2_get_block);<br />
}</p>
<p>const struct address_space_operations ext2_aops = {<br />
	.readpage		= ext2_readpage,<br />
	.readpages		= ext2_readpages,<br />
	.writepage		= ext2_writepage,<br />
	.sync_page		= block_sync_page,<br />
	.write_begin		= ext2_write_begin,<br />
	.write_end		= generic_write_end,<br />
	.bmap			= ext2_bmap,<br />
	.direct_IO		= ext2_direct_IO,<br />
	.writepages		= ext2_writepages,<br />
	.migratepage		= buffer_migrate_page,<br />
	.is_partially_uptodate	= block_is_partially_uptodate,<br />
	.error_remove_page	= generic_error_remove_page,<br />
};</p>
<p>const struct address_space_operations ext2_aops_xip = {<br />
	.bmap			= ext2_bmap,<br />
	.get_xip_mem		= ext2_get_xip_mem,<br />
};</p>
<p>const struct address_space_operations ext2_nobh_aops = {<br />
	.readpage		= ext2_readpage,<br />
	.readpages		= ext2_readpages,<br />
	.writepage		= ext2_nobh_writepage,<br />
	.sync_page		= block_sync_page,<br />
	.write_begin		= ext2_nobh_write_begin,<br />
	.write_end		= nobh_write_end,<br />
	.bmap			= ext2_bmap,<br />
	.direct_IO		= ext2_direct_IO,<br />
	.writepages		= ext2_writepages,<br />
	.migratepage		= buffer_migrate_page,<br />
	.error_remove_page	= generic_error_remove_page,<br />
};</p>
<p>/*<br />
 * Probably it should be a library function&#8230; search for first non-zero word<br />
 * or memcmp with zero_page, whatever is better for particular architecture.<br />
 * Linus?<br />
 */<br />
static inline int all_zeroes(__le32 *p, __le32 *q)<br />
{<br />
	while (p < q)<br />
		if (*p++)<br />
			return 0;<br />
	return 1;<br />
}</p>
<p>/**<br />
 *	ext2_find_shared - find the indirect blocks for partial truncation.<br />
 *	@inode:	  inode in question<br />
 *	@depth:	  depth of the affected branch<br />
 *	@offsets: offsets of pointers in that branch (see ext2_block_to_path)<br />
 *	@chain:	  place to store the pointers to partial indirect blocks<br />
 *	@top:	  place to the (detached) top of branch<br />
 *<br />
 *	This is a helper function used by ext2_truncate().<br />
 *<br />
 *	When we do truncate() we may have to clean the ends of several indirect<br />
 *	blocks but leave the blocks themselves alive. Block is partially<br />
 *	truncated if some data below the new i_size is refered from it (and<br />
 *	it is on the path to the first completely truncated data block, indeed).<br />
 *	We have to free the top of that path along with everything to the right<br />
 *	of the path. Since no allocation past the truncation point is possible<br />
 *	until ext2_truncate() finishes, we may safely do the latter, but top<br />
 *	of branch may require special attention - pageout below the truncation<br />
 *	point might try to populate it.<br />
 *<br />
 *	We atomically detach the top of branch from the tree, store the block<br />
 *	number of its root in *@top, pointers to buffer_heads of partially<br />
 *	truncated blocks - in @chain[].bh and pointers to their last elements<br />
 *	that should not be removed - in @chain[].p. Return value is the pointer<br />
 *	to last filled element of @chain.<br />
 *<br />
 *	The work left to caller to do the actual freeing of subtrees:<br />
 *		a) free the subtree starting from *@top<br />
 *		b) free the subtrees whose roots are stored in<br />
 *			(@chain[i].p+1 .. end of @chain[i].bh->b_data)<br />
 *		c) free the subtrees growing from the inode past the @chain[0].p<br />
 *			(no partially truncated stuff there).<br />
 */</p>
<p>static Indirect *ext2_find_shared(struct inode *inode,<br />
				int depth,<br />
				int offsets[4],<br />
				Indirect chain[4],<br />
				__le32 *top)<br />
{<br />
	Indirect *partial, *p;<br />
	int k, err;</p>
<p>	*top = 0;<br />
	for (k = depth; k > 1 &#038;&#038; !offsets[k-1]; k&#8211;)<br />
		;<br />
	partial = ext2_get_branch(inode, k, offsets, chain, &#038;err);<br />
	if (!partial)<br />
		partial = chain + k-1;<br />
	/*<br />
	 * If the branch acquired continuation since we&#8217;ve looked at it -<br />
	 * fine, it should all survive and (new) top doesn&#8217;t belong to us.<br />
	 */<br />
	write_lock(&#038;EXT2_I(inode)->i_meta_lock);<br />
	if (!partial->key &#038;&#038; *partial->p) {<br />
		write_unlock(&#038;EXT2_I(inode)->i_meta_lock);<br />
		goto no_top;<br />
	}<br />
	for (p=partial; p>chain &#038;&#038; all_zeroes((__le32*)p->bh->b_data,p->p); p&#8211;)<br />
		;<br />
	/*<br />
	 * OK, we&#8217;ve found the last block that must survive. The rest of our<br />
	 * branch should be detached before unlocking. However, if that rest<br />
	 * of branch is all ours and does not grow immediately from the inode<br />
	 * it&#8217;s easier to cheat and just decrement partial->p.<br />
	 */<br />
	if (p == chain + k &#8211; 1 &#038;&#038; p > chain) {<br />
		p->p&#8211;;<br />
	} else {<br />
		*top = *p->p;<br />
		*p->p = 0;<br />
	}<br />
	write_unlock(&#038;EXT2_I(inode)->i_meta_lock);</p>
<p>	while(partial > p)<br />
	{<br />
		brelse(partial->bh);<br />
		partial&#8211;;<br />
	}<br />
no_top:<br />
	return partial;<br />
}</p>
<p>/**<br />
 *	ext2_free_data &#8211; free a list of data blocks<br />
 *	@inode:	inode we are dealing with<br />
 *	@p:	array of block numbers<br />
 *	@q:	points immediately past the end of array<br />
 *<br />
 *	We are freeing all blocks refered from that array (numbers are<br />
 *	stored as little-endian 32-bit) and updating @inode->i_blocks<br />
 *	appropriately.<br />
 */<br />
static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)<br />
{<br />
	unsigned long block_to_free = 0, count = 0;<br />
	unsigned long nr;</p>
<p>	for ( ; p < q ; p++) {<br />
		nr = le32_to_cpu(*p);<br />
		if (nr) {<br />
			*p = 0;<br />
			/* accumulate blocks to free if they're contiguous */<br />
			if (count == 0)<br />
				goto free_this;<br />
			else if (block_to_free == nr - count)<br />
				count++;<br />
			else {<br />
				mark_inode_dirty(inode);<br />
				ext2_free_blocks (inode, block_to_free, count);<br />
			free_this:<br />
				block_to_free = nr;<br />
				count = 1;<br />
			}<br />
		}<br />
	}<br />
	if (count > 0) {<br />
		mark_inode_dirty(inode);<br />
		ext2_free_blocks (inode, block_to_free, count);<br />
	}<br />
}</p>
<p>/**<br />
 *	ext2_free_branches &#8211; free an array of branches<br />
 *	@inode:	inode we are dealing with<br />
 *	@p:	array of block numbers<br />
 *	@q:	pointer immediately past the end of array<br />
 *	@depth:	depth of the branches to free<br />
 *<br />
 *	We are freeing all blocks refered from these branches (numbers are<br />
 *	stored as little-endian 32-bit) and updating @inode->i_blocks<br />
 *	appropriately.<br />
 */<br />
static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int depth)<br />
{<br />
	struct buffer_head * bh;<br />
	unsigned long nr;</p>
<p>	if (depth&#8211;) {<br />
		int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);<br />
		for ( ; p < q ; p++) {<br />
			nr = le32_to_cpu(*p);<br />
			if (!nr)<br />
				continue;<br />
			*p = 0;<br />
			bh = sb_bread(inode->i_sb, nr);<br />
			/*<br />
			 * A read failure? Report error and clear slot<br />
			 * (should be rare).<br />
			 */<br />
			if (!bh) {<br />
				ext2_error(inode->i_sb, &laquo;ext2_free_branches&raquo;,<br />
					&laquo;Read failure, inode=%ld, block=%ld&raquo;,<br />
					inode->i_ino, nr);<br />
				continue;<br />
			}<br />
			ext2_free_branches(inode,<br />
					   (__le32*)bh->b_data,<br />
					   (__le32*)bh->b_data + addr_per_block,<br />
					   depth);<br />
			bforget(bh);<br />
			ext2_free_blocks(inode, nr, 1);<br />
			mark_inode_dirty(inode);<br />
		}<br />
	} else<br />
		ext2_free_data(inode, p, q);<br />
}</p>
<p>void ext2_truncate(struct inode *inode)<br />
{<br />
	__le32 *i_data = EXT2_I(inode)->i_data;<br />
	struct ext2_inode_info *ei = EXT2_I(inode);<br />
	int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);<br />
	int offsets[4];<br />
	Indirect chain[4];<br />
	Indirect *partial;<br />
	__le32 nr = 0;<br />
	int n;<br />
	long iblock;<br />
	unsigned blocksize;</p>
<p>	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||<br />
	    S_ISLNK(inode->i_mode)))<br />
		return;<br />
	if (ext2_inode_is_fast_symlink(inode))<br />
		return;<br />
	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))<br />
		return;</p>
<p>	blocksize = inode->i_sb->s_blocksize;<br />
	iblock = (inode->i_size + blocksize-1)<br />
					>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);</p>
<p>	if (mapping_is_xip(inode->i_mapping))<br />
		xip_truncate_page(inode->i_mapping, inode->i_size);<br />
	else if (test_opt(inode->i_sb, NOBH))<br />
		nobh_truncate_page(inode->i_mapping,<br />
				inode->i_size, ext2_get_block);<br />
	else<br />
		block_truncate_page(inode->i_mapping,<br />
				inode->i_size, ext2_get_block);</p>
<p>	n = ext2_block_to_path(inode, iblock, offsets, NULL);<br />
	if (n == 0)<br />
		return;</p>
<p>	/*<br />
	 * From here we block out all ext2_get_block() callers who want to<br />
	 * modify the block allocation tree.<br />
	 */<br />
	mutex_lock(&#038;ei->truncate_mutex);</p>
<p>	if (n == 1) {<br />
		ext2_free_data(inode, i_data+offsets[0],<br />
					i_data + EXT2_NDIR_BLOCKS);<br />
		goto do_indirects;<br />
	}</p>
<p>	partial = ext2_find_shared(inode, n, offsets, chain, &#038;nr);<br />
	/* Kill the top of shared branch (already detached) */<br />
	if (nr) {<br />
		if (partial == chain)<br />
			mark_inode_dirty(inode);<br />
		else<br />
			mark_buffer_dirty_inode(partial->bh, inode);<br />
		ext2_free_branches(inode, &#038;nr, &#038;nr+1, (chain+n-1) &#8211; partial);<br />
	}<br />
	/* Clear the ends of indirect blocks on the shared branch */<br />
	while (partial > chain) {<br />
		ext2_free_branches(inode,<br />
				   partial->p + 1,<br />
				   (__le32*)partial->bh->b_data+addr_per_block,<br />
				   (chain+n-1) &#8211; partial);<br />
		mark_buffer_dirty_inode(partial->bh, inode);<br />
		brelse (partial->bh);<br />
		partial&#8211;;<br />
	}<br />
do_indirects:<br />
	/* Kill the remaining (whole) subtrees */<br />
	switch (offsets[0]) {<br />
		default:<br />
			nr = i_data[EXT2_IND_BLOCK];<br />
			if (nr) {<br />
				i_data[EXT2_IND_BLOCK] = 0;<br />
				mark_inode_dirty(inode);<br />
				ext2_free_branches(inode, &#038;nr, &#038;nr+1, 1);<br />
			}<br />
		case EXT2_IND_BLOCK:<br />
			nr = i_data[EXT2_DIND_BLOCK];<br />
			if (nr) {<br />
				i_data[EXT2_DIND_BLOCK] = 0;<br />
				mark_inode_dirty(inode);<br />
				ext2_free_branches(inode, &#038;nr, &#038;nr+1, 2);<br />
			}<br />
		case EXT2_DIND_BLOCK:<br />
			nr = i_data[EXT2_TIND_BLOCK];<br />
			if (nr) {<br />
				i_data[EXT2_TIND_BLOCK] = 0;<br />
				mark_inode_dirty(inode);<br />
				ext2_free_branches(inode, &#038;nr, &#038;nr+1, 3);<br />
			}<br />
		case EXT2_TIND_BLOCK:<br />
			;<br />
	}</p>
<p>	ext2_discard_reservation(inode);</p>
<p>	mutex_unlock(&#038;ei->truncate_mutex);<br />
	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;<br />
	if (inode_needs_sync(inode)) {<br />
		sync_mapping_buffers(inode->i_mapping);<br />
		ext2_sync_inode (inode);<br />
	} else {<br />
		mark_inode_dirty(inode);<br />
	}<br />
}</p>
<p>static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino,<br />
					struct buffer_head **p)<br />
{<br />
	struct buffer_head * bh;<br />
	unsigned long block_group;<br />
	unsigned long block;<br />
	unsigned long offset;<br />
	struct ext2_group_desc * gdp;</p>
<p>	*p = NULL;<br />
	if ((ino != EXT2_ROOT_INO &#038;&#038; ino < EXT2_FIRST_INO(sb)) ||<br />
	    ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))<br />
		goto Einval;</p>
<p>	block_group = (ino &#8211; 1) / EXT2_INODES_PER_GROUP(sb);<br />
	gdp = ext2_get_group_desc(sb, block_group, NULL);<br />
	if (!gdp)<br />
		goto Egdp;<br />
	/*<br />
	 * Figure out the offset within the block group inode table<br />
	 */<br />
	offset = ((ino &#8211; 1) % EXT2_INODES_PER_GROUP(sb)) * EXT2_INODE_SIZE(sb);<br />
	block = le32_to_cpu(gdp->bg_inode_table) +<br />
		(offset >> EXT2_BLOCK_SIZE_BITS(sb));<br />
	if (!(bh = sb_bread(sb, block)))<br />
		goto Eio;</p>
<p>	*p = bh;<br />
	offset &#038;= (EXT2_BLOCK_SIZE(sb) &#8211; 1);<br />
	return (struct ext2_inode *) (bh->b_data + offset);</p>
<p>Einval:<br />
	ext2_error(sb, &laquo;ext2_get_inode&raquo;, &laquo;bad inode number: %lu&raquo;,<br />
		   (unsigned long) ino);<br />
	return ERR_PTR(-EINVAL);<br />
Eio:<br />
	ext2_error(sb, &laquo;ext2_get_inode&raquo;,<br />
		   &laquo;unable to read inode block &#8211; inode=%lu, block=%lu&raquo;,<br />
		   (unsigned long) ino, block);<br />
Egdp:<br />
	return ERR_PTR(-EIO);<br />
}</p>
<p>void ext2_set_inode_flags(struct inode *inode)<br />
{<br />
	unsigned int flags = EXT2_I(inode)->i_flags;</p>
<p>	inode->i_flags &#038;= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);<br />
	if (flags &#038; EXT2_SYNC_FL)<br />
		inode->i_flags |= S_SYNC;<br />
	if (flags &#038; EXT2_APPEND_FL)<br />
		inode->i_flags |= S_APPEND;<br />
	if (flags &#038; EXT2_IMMUTABLE_FL)<br />
		inode->i_flags |= S_IMMUTABLE;<br />
	if (flags &#038; EXT2_NOATIME_FL)<br />
		inode->i_flags |= S_NOATIME;<br />
	if (flags &#038; EXT2_DIRSYNC_FL)<br />
		inode->i_flags |= S_DIRSYNC;<br />
}</p>
<p>/* Propagate flags from i_flags to EXT2_I(inode)->i_flags */<br />
void ext2_get_inode_flags(struct ext2_inode_info *ei)<br />
{<br />
	unsigned int flags = ei->vfs_inode.i_flags;</p>
<p>	ei->i_flags &#038;= ~(EXT2_SYNC_FL|EXT2_APPEND_FL|<br />
			EXT2_IMMUTABLE_FL|EXT2_NOATIME_FL|EXT2_DIRSYNC_FL);<br />
	if (flags &#038; S_SYNC)<br />
		ei->i_flags |= EXT2_SYNC_FL;<br />
	if (flags &#038; S_APPEND)<br />
		ei->i_flags |= EXT2_APPEND_FL;<br />
	if (flags &#038; S_IMMUTABLE)<br />
		ei->i_flags |= EXT2_IMMUTABLE_FL;<br />
	if (flags &#038; S_NOATIME)<br />
		ei->i_flags |= EXT2_NOATIME_FL;<br />
	if (flags &#038; S_DIRSYNC)<br />
		ei->i_flags |= EXT2_DIRSYNC_FL;<br />
}</p>
<p>struct inode *ext2_iget (struct super_block *sb, unsigned long ino)<br />
{<br />
	struct ext2_inode_info *ei;<br />
	struct buffer_head * bh;<br />
	struct ext2_inode *raw_inode;<br />
	struct inode *inode;<br />
	long ret = -EIO;<br />
	int n;</p>
<p>	inode = iget_locked(sb, ino);<br />
	if (!inode)<br />
		return ERR_PTR(-ENOMEM);<br />
	if (!(inode->i_state &#038; I_NEW))<br />
		return inode;</p>
<p>	ei = EXT2_I(inode);<br />
	ei->i_block_alloc_info = NULL;</p>
<p>	raw_inode = ext2_get_inode(inode->i_sb, ino, &#038;bh);<br />
	if (IS_ERR(raw_inode)) {<br />
		ret = PTR_ERR(raw_inode);<br />
 		goto bad_inode;<br />
	}</p>
<p>	inode->i_mode = le16_to_cpu(raw_inode->i_mode);<br />
	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);<br />
	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);<br />
	if (!(test_opt (inode->i_sb, NO_UID32))) {<br />
		inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;<br />
		inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;<br />
	}<br />
	inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);<br />
	inode->i_size = le32_to_cpu(raw_inode->i_size);<br />
	inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);<br />
	inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime);<br />
	inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime);<br />
	inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0;<br />
	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);<br />
	/* We now have enough fields to check if the inode was active or not.<br />
	 * This is needed because nfsd might try to access dead inodes<br />
	 * the test is that same one that e2fsck uses<br />
	 * NeilBrown 1999oct15<br />
	 */<br />
	if (inode->i_nlink == 0 &#038;&#038; (inode->i_mode == 0 || ei->i_dtime)) {<br />
		/* this inode is deleted */<br />
		brelse (bh);<br />
		ret = -ESTALE;<br />
		goto bad_inode;<br />
	}<br />
	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);<br />
	ei->i_flags = le32_to_cpu(raw_inode->i_flags);<br />
	ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);<br />
	ei->i_frag_no = raw_inode->i_frag;<br />
	ei->i_frag_size = raw_inode->i_fsize;<br />
	ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);<br />
	ei->i_dir_acl = 0;<br />
	if (S_ISREG(inode->i_mode))<br />
		inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;<br />
	else<br />
		ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);<br />
	ei->i_dtime = 0;<br />
	inode->i_generation = le32_to_cpu(raw_inode->i_generation);<br />
	ei->i_state = 0;<br />
	ei->i_block_group = (ino &#8211; 1) / EXT2_INODES_PER_GROUP(inode->i_sb);<br />
	ei->i_dir_start_lookup = 0;</p>
<p>	/*<br />
	 * NOTE! The in-memory inode i_data array is in little-endian order<br />
	 * even on big-endian machines: we do NOT byteswap the block numbers!<br />
	 */<br />
	for (n = 0; n < EXT2_N_BLOCKS; n++)<br />
		ei->i_data[n] = raw_inode->i_block[n];</p>
<p>	if (S_ISREG(inode->i_mode)) {<br />
		inode->i_op = &#038;ext2_file_inode_operations;<br />
		if (ext2_use_xip(inode->i_sb)) {<br />
			inode->i_mapping->a_ops = &#038;ext2_aops_xip;<br />
			inode->i_fop = &#038;ext2_xip_file_operations;<br />
		} else if (test_opt(inode->i_sb, NOBH)) {<br />
			inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
			inode->i_fop = &#038;ext2_file_operations;<br />
		} else {<br />
			inode->i_mapping->a_ops = &#038;ext2_aops;<br />
			inode->i_fop = &#038;ext2_file_operations;<br />
		}<br />
	} else if (S_ISDIR(inode->i_mode)) {<br />
		inode->i_op = &#038;ext2_dir_inode_operations;<br />
		inode->i_fop = &#038;ext2_dir_operations;<br />
		if (test_opt(inode->i_sb, NOBH))<br />
			inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
		else<br />
			inode->i_mapping->a_ops = &#038;ext2_aops;<br />
	} else if (S_ISLNK(inode->i_mode)) {<br />
		if (ext2_inode_is_fast_symlink(inode)) {<br />
			inode->i_op = &#038;ext2_fast_symlink_inode_operations;<br />
			nd_terminate_link(ei->i_data, inode->i_size,<br />
				sizeof(ei->i_data) &#8211; 1);<br />
		} else {<br />
			inode->i_op = &#038;ext2_symlink_inode_operations;<br />
			if (test_opt(inode->i_sb, NOBH))<br />
				inode->i_mapping->a_ops = &#038;ext2_nobh_aops;<br />
			else<br />
				inode->i_mapping->a_ops = &#038;ext2_aops;<br />
		}<br />
	} else {<br />
		inode->i_op = &#038;ext2_special_inode_operations;<br />
		if (raw_inode->i_block[0])<br />
			init_special_inode(inode, inode->i_mode,<br />
			   old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));<br />
		else<br />
			init_special_inode(inode, inode->i_mode,<br />
			   new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));<br />
	}<br />
	brelse (bh);<br />
	ext2_set_inode_flags(inode);<br />
	unlock_new_inode(inode);<br />
	return inode;</p>
<p>bad_inode:<br />
	iget_failed(inode);<br />
	return ERR_PTR(ret);<br />
}</p>
<p>int ext2_write_inode(struct inode *inode, int do_sync)<br />
{<br />
	struct ext2_inode_info *ei = EXT2_I(inode);<br />
	struct super_block *sb = inode->i_sb;<br />
	ino_t ino = inode->i_ino;<br />
	uid_t uid = inode->i_uid;<br />
	gid_t gid = inode->i_gid;<br />
	struct buffer_head * bh;<br />
	struct ext2_inode * raw_inode = ext2_get_inode(sb, ino, &#038;bh);<br />
	int n;<br />
	int err = 0;</p>
<p>	if (IS_ERR(raw_inode))<br />
 		return -EIO;</p>
<p>	/* For fields not not tracking in the in-memory inode,<br />
	 * initialise them to zero for new inodes. */<br />
	if (ei->i_state &#038; EXT2_STATE_NEW)<br />
		memset(raw_inode, 0, EXT2_SB(sb)->s_inode_size);</p>
<p>	ext2_get_inode_flags(ei);<br />
	raw_inode->i_mode = cpu_to_le16(inode->i_mode);<br />
	if (!(test_opt(sb, NO_UID32))) {<br />
		raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid));<br />
		raw_inode->i_gid_low = cpu_to_le16(low_16_bits(gid));<br />
/*<br />
 * Fix up interoperability with old kernels. Otherwise, old inodes get<br />
 * re-used with the upper 16 bits of the uid/gid intact<br />
 */<br />
		if (!ei->i_dtime) {<br />
			raw_inode->i_uid_high = cpu_to_le16(high_16_bits(uid));<br />
			raw_inode->i_gid_high = cpu_to_le16(high_16_bits(gid));<br />
		} else {<br />
			raw_inode->i_uid_high = 0;<br />
			raw_inode->i_gid_high = 0;<br />
		}<br />
	} else {<br />
		raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(uid));<br />
		raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(gid));<br />
		raw_inode->i_uid_high = 0;<br />
		raw_inode->i_gid_high = 0;<br />
	}<br />
	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);<br />
	raw_inode->i_size = cpu_to_le32(inode->i_size);<br />
	raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);<br />
	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);<br />
	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);</p>
<p>	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);<br />
	raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);<br />
	raw_inode->i_flags = cpu_to_le32(ei->i_flags);<br />
	raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);<br />
	raw_inode->i_frag = ei->i_frag_no;<br />
	raw_inode->i_fsize = ei->i_frag_size;<br />
	raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);<br />
	if (!S_ISREG(inode->i_mode))<br />
		raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);<br />
	else {<br />
		raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);<br />
		if (inode->i_size > 0&#215;7fffffffULL) {<br />
			if (!EXT2_HAS_RO_COMPAT_FEATURE(sb,<br />
					EXT2_FEATURE_RO_COMPAT_LARGE_FILE) ||<br />
			    EXT2_SB(sb)->s_es->s_rev_level ==<br />
					cpu_to_le32(EXT2_GOOD_OLD_REV)) {<br />
			       /* If this is the first large file<br />
				* created, add a flag to the superblock.<br />
				*/<br />
				lock_kernel();<br />
				ext2_update_dynamic_rev(sb);<br />
				EXT2_SET_RO_COMPAT_FEATURE(sb,<br />
					EXT2_FEATURE_RO_COMPAT_LARGE_FILE);<br />
				unlock_kernel();<br />
				ext2_write_super(sb);<br />
			}<br />
		}<br />
	}</p>
<p>	raw_inode->i_generation = cpu_to_le32(inode->i_generation);<br />
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {<br />
		if (old_valid_dev(inode->i_rdev)) {<br />
			raw_inode->i_block[0] =<br />
				cpu_to_le32(old_encode_dev(inode->i_rdev));<br />
			raw_inode->i_block[1] = 0;<br />
		} else {<br />
			raw_inode->i_block[0] = 0;<br />
			raw_inode->i_block[1] =<br />
				cpu_to_le32(new_encode_dev(inode->i_rdev));<br />
			raw_inode->i_block[2] = 0;<br />
		}<br />
	} else for (n = 0; n < EXT2_N_BLOCKS; n++)<br />
		raw_inode->i_block[n] = ei->i_data[n];<br />
	mark_buffer_dirty(bh);<br />
	if (do_sync) {<br />
		sync_dirty_buffer(bh);<br />
		if (buffer_req(bh) &#038;&#038; !buffer_uptodate(bh)) {<br />
			printk (&raquo;IO error syncing ext2 inode [%s:%08lx]\n&raquo;,<br />
				sb->s_id, (unsigned long) ino);<br />
			err = -EIO;<br />
		}<br />
	}<br />
	ei->i_state &#038;= ~EXT2_STATE_NEW;<br />
	brelse (bh);<br />
	return err;<br />
}</p>
<p>int ext2_sync_inode(struct inode *inode)<br />
{<br />
	struct writeback_control wbc = {<br />
		.sync_mode = WB_SYNC_ALL,<br />
		.nr_to_write = 0,	/* sys_fsync did this */<br />
	};<br />
	return sync_inode(inode, &#038;wbc);<br />
}</p>
<p>int ext2_setattr(struct dentry *dentry, struct iattr *iattr)<br />
{<br />
	struct inode *inode = dentry->d_inode;<br />
	int error;</p>
<p>	error = inode_change_ok(inode, iattr);<br />
	if (error)<br />
		return error;<br />
	if ((iattr->ia_valid &#038; ATTR_UID &#038;&#038; iattr->ia_uid != inode->i_uid) ||<br />
	    (iattr->ia_valid &#038; ATTR_GID &#038;&#038; iattr->ia_gid != inode->i_gid)) {<br />
		error = vfs_dq_transfer(inode, iattr) ? -EDQUOT : 0;<br />
		if (error)<br />
			return error;<br />
	}<br />
	error = inode_setattr(inode, iattr);<br />
	if (!error &#038;&#038; (iattr->ia_valid &#038; ATTR_MODE))<br />
		error = ext2_acl_chmod(inode);<br />
	return error;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/inode-c-18/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ialloc.c</title>
		<link>http://lynyrd.ru/ialloc-c</link>
		<comments>http://lynyrd.ru/ialloc-c#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:43:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1149</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/ialloc.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  BSD ufs-inspired inode and directory allocation by
 *  Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
 *  Big-endian to little-endian byte-swapping/bitmaps by
 * ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/ialloc.c<span id="more-1149"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  BSD ufs-inspired inode and directory allocation by<br />
 *  Stephen Tweedie (sct@dcs.ed.ac.uk), 1993<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 */</p>
<p>#include
<linux/quotaops.h>
#include
<linux/sched.h>
#include
<linux/backing-dev.h>
#include
<linux/buffer_head.h>
#include
<linux/random.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;</p>
<p>/*<br />
 * ialloc.c contains the inodes allocation and deallocation routines<br />
 */</p>
<p>/*<br />
 * The free inodes are managed by bitmaps.  A file system contains several<br />
 * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap<br />
 * block for inodes, N blocks for the inode table and data blocks.<br />
 *<br />
 * The file system contains group descriptors which are located after the<br />
 * super block.  Each descriptor contains the number of the bitmap block and<br />
 * the free blocks count in the block.<br />
 */</p>
<p>/*<br />
 * Read the inode allocation bitmap for a given block_group, reading<br />
 * into the specified slot in the superblock&#8217;s bitmap cache.<br />
 *<br />
 * Return buffer_head of bitmap on success or NULL.<br />
 */<br />
static struct buffer_head *<br />
read_inode_bitmap(struct super_block * sb, unsigned long block_group)<br />
{<br />
	struct ext2_group_desc *desc;<br />
	struct buffer_head *bh = NULL;</p>
<p>	desc = ext2_get_group_desc(sb, block_group, NULL);<br />
	if (!desc)<br />
		goto error_out;</p>
<p>	bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));<br />
	if (!bh)<br />
		ext2_error(sb, &laquo;read_inode_bitmap&raquo;,<br />
			    &laquo;Cannot read inode bitmap &#8211; &raquo;<br />
			    &laquo;block_group = %lu, inode_bitmap = %u&raquo;,<br />
			    block_group, le32_to_cpu(desc->bg_inode_bitmap));<br />
error_out:<br />
	return bh;<br />
}</p>
<p>static void ext2_release_inode(struct super_block *sb, int group, int dir)<br />
{<br />
	struct ext2_group_desc * desc;<br />
	struct buffer_head *bh;</p>
<p>	desc = ext2_get_group_desc(sb, group, &#038;bh);<br />
	if (!desc) {<br />
		ext2_error(sb, &laquo;ext2_release_inode&raquo;,<br />
			&laquo;can&#8217;t get descriptor for group %d&raquo;, group);<br />
		return;<br />
	}</p>
<p>	spin_lock(sb_bgl_lock(EXT2_SB(sb), group));<br />
	le16_add_cpu(&#038;desc->bg_free_inodes_count, 1);<br />
	if (dir)<br />
		le16_add_cpu(&#038;desc->bg_used_dirs_count, -1);<br />
	spin_unlock(sb_bgl_lock(EXT2_SB(sb), group));<br />
	if (dir)<br />
		percpu_counter_dec(&#038;EXT2_SB(sb)->s_dirs_counter);<br />
	sb->s_dirt = 1;<br />
	mark_buffer_dirty(bh);<br />
}</p>
<p>/*<br />
 * NOTE! When we get the inode, we&#8217;re the only people<br />
 * that have access to it, and as such there are no<br />
 * race conditions we have to worry about. The inode<br />
 * is not on the hash-lists, and it cannot be reached<br />
 * through the filesystem because the directory entry<br />
 * has been deleted earlier.<br />
 *<br />
 * HOWEVER: we must make sure that we get no aliases,<br />
 * which means that we have to call &laquo;clear_inode()&raquo;<br />
 * _before_ we mark the inode not in use in the inode<br />
 * bitmaps. Otherwise a newly created file might use<br />
 * the same inode number (not actually the same pointer<br />
 * though), and then we&#8217;d have two inodes sharing the<br />
 * same inode number and space on the harddisk.<br />
 */<br />
void ext2_free_inode (struct inode * inode)<br />
{<br />
	struct super_block * sb = inode->i_sb;<br />
	int is_directory;<br />
	unsigned long ino;<br />
	struct buffer_head *bitmap_bh = NULL;<br />
	unsigned long block_group;<br />
	unsigned long bit;<br />
	struct ext2_super_block * es;</p>
<p>	ino = inode->i_ino;<br />
	ext2_debug (&raquo;freeing inode %lu\n&raquo;, ino);</p>
<p>	/*<br />
	 * Note: we must free any quota before locking the superblock,<br />
	 * as writing the quota to disk may need the lock as well.<br />
	 */<br />
	if (!is_bad_inode(inode)) {<br />
		/* Quota is already initialized in iput() */<br />
		ext2_xattr_delete_inode(inode);<br />
		vfs_dq_free_inode(inode);<br />
		vfs_dq_drop(inode);<br />
	}</p>
<p>	es = EXT2_SB(sb)->s_es;<br />
	is_directory = S_ISDIR(inode->i_mode);</p>
<p>	/* Do this BEFORE marking the inode not in use or returning an error */<br />
	clear_inode (inode);</p>
<p>	if (ino < EXT2_FIRST_INO(sb) ||<br />
	    ino > le32_to_cpu(es->s_inodes_count)) {<br />
		ext2_error (sb, &laquo;ext2_free_inode&raquo;,<br />
			    &laquo;reserved or nonexistent inode %lu&raquo;, ino);<br />
		goto error_return;<br />
	}<br />
	block_group = (ino &#8211; 1) / EXT2_INODES_PER_GROUP(sb);<br />
	bit = (ino &#8211; 1) % EXT2_INODES_PER_GROUP(sb);<br />
	brelse(bitmap_bh);<br />
	bitmap_bh = read_inode_bitmap(sb, block_group);<br />
	if (!bitmap_bh)<br />
		goto error_return;</p>
<p>	/* Ok, now we can actually update the inode bitmaps.. */<br />
	if (!ext2_clear_bit_atomic(sb_bgl_lock(EXT2_SB(sb), block_group),<br />
				bit, (void *) bitmap_bh->b_data))<br />
		ext2_error (sb, &laquo;ext2_free_inode&raquo;,<br />
			      &laquo;bit already cleared for inode %lu&raquo;, ino);<br />
	else<br />
		ext2_release_inode(sb, block_group, is_directory);<br />
	mark_buffer_dirty(bitmap_bh);<br />
	if (sb->s_flags &#038; MS_SYNCHRONOUS)<br />
		sync_dirty_buffer(bitmap_bh);<br />
error_return:<br />
	brelse(bitmap_bh);<br />
}</p>
<p>/*<br />
 * We perform asynchronous prereading of the new inode&#8217;s inode block when<br />
 * we create the inode, in the expectation that the inode will be written<br />
 * back soon.  There are two reasons:<br />
 *<br />
 * &#8211; When creating a large number of files, the async prereads will be<br />
 *   nicely merged into large reads<br />
 * &#8211; When writing out a large number of inodes, we don&#8217;t need to keep on<br />
 *   stalling the writes while we read the inode block.<br />
 *<br />
 * FIXME: ext2_get_group_desc() needs to be simplified.<br />
 */<br />
static void ext2_preread_inode(struct inode *inode)<br />
{<br />
	unsigned long block_group;<br />
	unsigned long offset;<br />
	unsigned long block;<br />
	struct ext2_group_desc * gdp;<br />
	struct backing_dev_info *bdi;</p>
<p>	bdi = inode->i_mapping->backing_dev_info;<br />
	if (bdi_read_congested(bdi))<br />
		return;<br />
	if (bdi_write_congested(bdi))<br />
		return;</p>
<p>	block_group = (inode->i_ino &#8211; 1) / EXT2_INODES_PER_GROUP(inode->i_sb);<br />
	gdp = ext2_get_group_desc(inode->i_sb, block_group, NULL);<br />
	if (gdp == NULL)<br />
		return;</p>
<p>	/*<br />
	 * Figure out the offset within the block group inode table<br />
	 */<br />
	offset = ((inode->i_ino &#8211; 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *<br />
				EXT2_INODE_SIZE(inode->i_sb);<br />
	block = le32_to_cpu(gdp->bg_inode_table) +<br />
				(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));<br />
	sb_breadahead(inode->i_sb, block);<br />
}</p>
<p>/*<br />
 * There are two policies for allocating an inode.  If the new inode is<br />
 * a directory, then a forward search is made for a block group with both<br />
 * free space and a low directory-to-inode ratio; if that fails, then of<br />
 * the groups with above-average free space, that group with the fewest<br />
 * directories already is chosen.<br />
 *<br />
 * For other inodes, search forward from the parent directory\&#8217;s block<br />
 * group to find a free inode.<br />
 */<br />
static int find_group_dir(struct super_block *sb, struct inode *parent)<br />
{<br />
	int ngroups = EXT2_SB(sb)->s_groups_count;<br />
	int avefreei = ext2_count_free_inodes(sb) / ngroups;<br />
	struct ext2_group_desc *desc, *best_desc = NULL;<br />
	int group, best_group = -1;</p>
<p>	for (group = 0; group < ngroups; group++) {<br />
		desc = ext2_get_group_desc (sb, group, NULL);<br />
		if (!desc || !desc->bg_free_inodes_count)<br />
			continue;<br />
		if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)<br />
			continue;<br />
		if (!best_desc ||<br />
		    (le16_to_cpu(desc->bg_free_blocks_count) ><br />
		     le16_to_cpu(best_desc->bg_free_blocks_count))) {<br />
			best_group = group;<br />
			best_desc = desc;<br />
		}<br />
	}<br />
	if (!best_desc)<br />
		return -1;</p>
<p>	return best_group;<br />
}</p>
<p>/*<br />
 * Orlov&#8217;s allocator for directories.<br />
 *<br />
 * We always try to spread first-level directories.<br />
 *<br />
 * If there are blockgroups with both free inodes and free blocks counts<br />
 * not worse than average we return one with smallest directory count.<br />
 * Otherwise we simply return a random group.<br />
 *<br />
 * For the rest rules look so:<br />
 *<br />
 * It&#8217;s OK to put directory into a group unless<br />
 * it has too many directories already (max_dirs) or<br />
 * it has too few free inodes left (min_inodes) or<br />
 * it has too few free blocks left (min_blocks) or<br />
 * it&#8217;s already running too large debt (max_debt).<br />
 * Parent&#8217;s group is preferred, if it doesn&#8217;t satisfy these<br />
 * conditions we search cyclically through the rest. If none<br />
 * of the groups look good we just look for a group with more<br />
 * free inodes than average (starting at parent&#8217;s group).<br />
 *<br />
 * Debt is incremented each time we allocate a directory and decremented<br />
 * when we allocate an inode, within 0&#8211;255.<br />
 */ </p>
<p>#define INODE_COST 64<br />
#define BLOCK_COST 256</p>
<p>static int find_group_orlov(struct super_block *sb, struct inode *parent)<br />
{<br />
	int parent_group = EXT2_I(parent)->i_block_group;<br />
	struct ext2_sb_info *sbi = EXT2_SB(sb);<br />
	struct ext2_super_block *es = sbi->s_es;<br />
	int ngroups = sbi->s_groups_count;<br />
	int inodes_per_group = EXT2_INODES_PER_GROUP(sb);<br />
	int freei;<br />
	int avefreei;<br />
	int free_blocks;<br />
	int avefreeb;<br />
	int blocks_per_dir;<br />
	int ndirs;<br />
	int max_debt, max_dirs, min_blocks, min_inodes;<br />
	int group = -1, i;<br />
	struct ext2_group_desc *desc;</p>
<p>	freei = percpu_counter_read_positive(&#038;sbi->s_freeinodes_counter);<br />
	avefreei = freei / ngroups;<br />
	free_blocks = percpu_counter_read_positive(&#038;sbi->s_freeblocks_counter);<br />
	avefreeb = free_blocks / ngroups;<br />
	ndirs = percpu_counter_read_positive(&#038;sbi->s_dirs_counter);</p>
<p>	if ((parent == sb->s_root->d_inode) ||<br />
	    (EXT2_I(parent)->i_flags &#038; EXT2_TOPDIR_FL)) {<br />
		struct ext2_group_desc *best_desc = NULL;<br />
		int best_ndir = inodes_per_group;<br />
		int best_group = -1;</p>
<p>		get_random_bytes(&#038;group, sizeof(group));<br />
		parent_group = (unsigned)group % ngroups;<br />
		for (i = 0; i < ngroups; i++) {<br />
			group = (parent_group + i) % ngroups;<br />
			desc = ext2_get_group_desc (sb, group, NULL);<br />
			if (!desc || !desc->bg_free_inodes_count)<br />
				continue;<br />
			if (le16_to_cpu(desc->bg_used_dirs_count) >= best_ndir)<br />
				continue;<br />
			if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)<br />
				continue;<br />
			if (le16_to_cpu(desc->bg_free_blocks_count) < avefreeb)<br />
				continue;<br />
			best_group = group;<br />
			best_ndir = le16_to_cpu(desc->bg_used_dirs_count);<br />
			best_desc = desc;<br />
		}<br />
		if (best_group >= 0) {<br />
			desc = best_desc;<br />
			group = best_group;<br />
			goto found;<br />
		}<br />
		goto fallback;<br />
	}</p>
<p>	if (ndirs == 0)<br />
		ndirs = 1;	/* percpu_counters are approximate&#8230; */</p>
<p>	blocks_per_dir = (le32_to_cpu(es->s_blocks_count)-free_blocks) / ndirs;</p>
<p>	max_dirs = ndirs / ngroups + inodes_per_group / 16;<br />
	min_inodes = avefreei &#8211; inodes_per_group / 4;<br />
	min_blocks = avefreeb &#8211; EXT2_BLOCKS_PER_GROUP(sb) / 4;</p>
<p>	max_debt = EXT2_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, BLOCK_COST);<br />
	if (max_debt * INODE_COST > inodes_per_group)<br />
		max_debt = inodes_per_group / INODE_COST;<br />
	if (max_debt > 255)<br />
		max_debt = 255;<br />
	if (max_debt == 0)<br />
		max_debt = 1;</p>
<p>	for (i = 0; i < ngroups; i++) {<br />
		group = (parent_group + i) % ngroups;<br />
		desc = ext2_get_group_desc (sb, group, NULL);<br />
		if (!desc || !desc->bg_free_inodes_count)<br />
			continue;<br />
		if (sbi->s_debts[group] >= max_debt)<br />
			continue;<br />
		if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs)<br />
			continue;<br />
		if (le16_to_cpu(desc->bg_free_inodes_count) < min_inodes)<br />
			continue;<br />
		if (le16_to_cpu(desc->bg_free_blocks_count) < min_blocks)<br />
			continue;<br />
		goto found;<br />
	}</p>
<p>fallback:<br />
	for (i = 0; i < ngroups; i++) {<br />
		group = (parent_group + i) % ngroups;<br />
		desc = ext2_get_group_desc (sb, group, NULL);<br />
		if (!desc || !desc->bg_free_inodes_count)<br />
			continue;<br />
		if (le16_to_cpu(desc->bg_free_inodes_count) >= avefreei)<br />
			goto found;<br />
	}</p>
<p>	if (avefreei) {<br />
		/*<br />
		 * The free-inodes counter is approximate, and for really small<br />
		 * filesystems the above test can fail to find any blockgroups<br />
		 */<br />
		avefreei = 0;<br />
		goto fallback;<br />
	}</p>
<p>	return -1;</p>
<p>found:<br />
	return group;<br />
}</p>
<p>static int find_group_other(struct super_block *sb, struct inode *parent)<br />
{<br />
	int parent_group = EXT2_I(parent)->i_block_group;<br />
	int ngroups = EXT2_SB(sb)->s_groups_count;<br />
	struct ext2_group_desc *desc;<br />
	int group, i;</p>
<p>	/*<br />
	 * Try to place the inode in its parent directory<br />
	 */<br />
	group = parent_group;<br />
	desc = ext2_get_group_desc (sb, group, NULL);<br />
	if (desc &#038;&#038; le16_to_cpu(desc->bg_free_inodes_count) &#038;&#038;<br />
			le16_to_cpu(desc->bg_free_blocks_count))<br />
		goto found;</p>
<p>	/*<br />
	 * We&#8217;re going to place this inode in a different blockgroup from its<br />
	 * parent.  We want to cause files in a common directory to all land in<br />
	 * the same blockgroup.  But we want files which are in a different<br />
	 * directory which shares a blockgroup with our parent to land in a<br />
	 * different blockgroup.<br />
	 *<br />
	 * So add our directory&#8217;s i_ino into the starting point for the hash.<br />
	 */<br />
	group = (group + parent->i_ino) % ngroups;</p>
<p>	/*<br />
	 * Use a quadratic hash to find a group with a free inode and some<br />
	 * free blocks.<br />
	 */<br />
	for (i = 1; i < ngroups; i <<= 1) {<br />
		group += i;<br />
		if (group >= ngroups)<br />
			group -= ngroups;<br />
		desc = ext2_get_group_desc (sb, group, NULL);<br />
		if (desc &#038;&#038; le16_to_cpu(desc->bg_free_inodes_count) &#038;&#038;<br />
				le16_to_cpu(desc->bg_free_blocks_count))<br />
			goto found;<br />
	}</p>
<p>	/*<br />
	 * That failed: try linear search for a free inode, even if that group<br />
	 * has no free blocks.<br />
	 */<br />
	group = parent_group;<br />
	for (i = 0; i < ngroups; i++) {<br />
		if (++group >= ngroups)<br />
			group = 0;<br />
		desc = ext2_get_group_desc (sb, group, NULL);<br />
		if (desc &#038;&#038; le16_to_cpu(desc->bg_free_inodes_count))<br />
			goto found;<br />
	}</p>
<p>	return -1;</p>
<p>found:<br />
	return group;<br />
}</p>
<p>struct inode *ext2_new_inode(struct inode *dir, int mode)<br />
{<br />
	struct super_block *sb;<br />
	struct buffer_head *bitmap_bh = NULL;<br />
	struct buffer_head *bh2;<br />
	int group, i;<br />
	ino_t ino = 0;<br />
	struct inode * inode;<br />
	struct ext2_group_desc *gdp;<br />
	struct ext2_super_block *es;<br />
	struct ext2_inode_info *ei;<br />
	struct ext2_sb_info *sbi;<br />
	int err;</p>
<p>	sb = dir->i_sb;<br />
	inode = new_inode(sb);<br />
	if (!inode)<br />
		return ERR_PTR(-ENOMEM);</p>
<p>	ei = EXT2_I(inode);<br />
	sbi = EXT2_SB(sb);<br />
	es = sbi->s_es;<br />
	if (S_ISDIR(mode)) {<br />
		if (test_opt(sb, OLDALLOC))<br />
			group = find_group_dir(sb, dir);<br />
		else<br />
			group = find_group_orlov(sb, dir);<br />
	} else<br />
		group = find_group_other(sb, dir);</p>
<p>	if (group == -1) {<br />
		err = -ENOSPC;<br />
		goto fail;<br />
	}</p>
<p>	for (i = 0; i < sbi->s_groups_count; i++) {<br />
		gdp = ext2_get_group_desc(sb, group, &#038;bh2);<br />
		brelse(bitmap_bh);<br />
		bitmap_bh = read_inode_bitmap(sb, group);<br />
		if (!bitmap_bh) {<br />
			err = -EIO;<br />
			goto fail;<br />
		}<br />
		ino = 0;</p>
<p>repeat_in_this_group:<br />
		ino = ext2_find_next_zero_bit((unsigned long *)bitmap_bh->b_data,<br />
					      EXT2_INODES_PER_GROUP(sb), ino);<br />
		if (ino >= EXT2_INODES_PER_GROUP(sb)) {<br />
			/*<br />
			 * Rare race: find_group_xx() decided that there were<br />
			 * free inodes in this group, but by the time we tried<br />
			 * to allocate one, they&#8217;re all gone.  This can also<br />
			 * occur because the counters which find_group_orlov()<br />
			 * uses are approximate.  So just go and search the<br />
			 * next block group.<br />
			 */<br />
			if (++group == sbi->s_groups_count)<br />
				group = 0;<br />
			continue;<br />
		}<br />
		if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group),<br />
						ino, bitmap_bh->b_data)) {<br />
			/* we lost this inode */<br />
			if (++ino >= EXT2_INODES_PER_GROUP(sb)) {<br />
				/* this group is exhausted, try next group */<br />
				if (++group == sbi->s_groups_count)<br />
					group = 0;<br />
				continue;<br />
			}<br />
			/* try to find free inode in the same group */<br />
			goto repeat_in_this_group;<br />
		}<br />
		goto got;<br />
	}</p>
<p>	/*<br />
	 * Scanned all blockgroups.<br />
	 */<br />
	err = -ENOSPC;<br />
	goto fail;<br />
got:<br />
	mark_buffer_dirty(bitmap_bh);<br />
	if (sb->s_flags &#038; MS_SYNCHRONOUS)<br />
		sync_dirty_buffer(bitmap_bh);<br />
	brelse(bitmap_bh);</p>
<p>	ino += group * EXT2_INODES_PER_GROUP(sb) + 1;<br />
	if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {<br />
		ext2_error (sb, &laquo;ext2_new_inode&raquo;,<br />
			    &laquo;reserved inode or inode > inodes count &#8211; &raquo;<br />
			    &laquo;block_group = %d,inode=%lu&raquo;, group,<br />
			    (unsigned long) ino);<br />
		err = -EIO;<br />
		goto fail;<br />
	}</p>
<p>	percpu_counter_add(&#038;sbi->s_freeinodes_counter, -1);<br />
	if (S_ISDIR(mode))<br />
		percpu_counter_inc(&#038;sbi->s_dirs_counter);</p>
<p>	spin_lock(sb_bgl_lock(sbi, group));<br />
	le16_add_cpu(&#038;gdp->bg_free_inodes_count, -1);<br />
	if (S_ISDIR(mode)) {<br />
		if (sbi->s_debts[group] < 255)<br />
			sbi->s_debts[group]++;<br />
		le16_add_cpu(&#038;gdp->bg_used_dirs_count, 1);<br />
	} else {<br />
		if (sbi->s_debts[group])<br />
			sbi->s_debts[group]&#8211;;<br />
	}<br />
	spin_unlock(sb_bgl_lock(sbi, group));</p>
<p>	sb->s_dirt = 1;<br />
	mark_buffer_dirty(bh2);<br />
	inode->i_uid = current_fsuid();<br />
	if (test_opt (sb, GRPID))<br />
		inode->i_gid = dir->i_gid;<br />
	else if (dir->i_mode &#038; S_ISGID) {<br />
		inode->i_gid = dir->i_gid;<br />
		if (S_ISDIR(mode))<br />
			mode |= S_ISGID;<br />
	} else<br />
		inode->i_gid = current_fsgid();<br />
	inode->i_mode = mode;</p>
<p>	inode->i_ino = ino;<br />
	inode->i_blocks = 0;<br />
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;<br />
	memset(ei->i_data, 0, sizeof(ei->i_data));<br />
	ei->i_flags =<br />
		ext2_mask_flags(mode, EXT2_I(dir)->i_flags &#038; EXT2_FL_INHERITED);<br />
	ei->i_faddr = 0;<br />
	ei->i_frag_no = 0;<br />
	ei->i_frag_size = 0;<br />
	ei->i_file_acl = 0;<br />
	ei->i_dir_acl = 0;<br />
	ei->i_dtime = 0;<br />
	ei->i_block_alloc_info = NULL;<br />
	ei->i_block_group = group;<br />
	ei->i_dir_start_lookup = 0;<br />
	ei->i_state = EXT2_STATE_NEW;<br />
	ext2_set_inode_flags(inode);<br />
	spin_lock(&#038;sbi->s_next_gen_lock);<br />
	inode->i_generation = sbi->s_next_generation++;<br />
	spin_unlock(&#038;sbi->s_next_gen_lock);<br />
	if (insert_inode_locked(inode) < 0) {<br />
		err = -EINVAL;<br />
		goto fail_drop;<br />
	}</p>
<p>	if (vfs_dq_alloc_inode(inode)) {<br />
		err = -EDQUOT;<br />
		goto fail_drop;<br />
	}</p>
<p>	err = ext2_init_acl(inode, dir);<br />
	if (err)<br />
		goto fail_free_drop;</p>
<p>	err = ext2_init_security(inode,dir);<br />
	if (err)<br />
		goto fail_free_drop;</p>
<p>	mark_inode_dirty(inode);<br />
	ext2_debug("allocating inode %lu\n", inode->i_ino);<br />
	ext2_preread_inode(inode);<br />
	return inode;</p>
<p>fail_free_drop:<br />
	vfs_dq_free_inode(inode);</p>
<p>fail_drop:<br />
	vfs_dq_drop(inode);<br />
	inode->i_flags |= S_NOQUOTA;<br />
	inode->i_nlink = 0;<br />
	unlock_new_inode(inode);<br />
	iput(inode);<br />
	return ERR_PTR(err);</p>
<p>fail:<br />
	make_bad_inode(inode);<br />
	iput(inode);<br />
	return ERR_PTR(err);<br />
}</p>
<p>unsigned long ext2_count_free_inodes (struct super_block * sb)<br />
{<br />
	struct ext2_group_desc *desc;<br />
	unsigned long desc_count = 0;<br />
	int i;	</p>
<p>#ifdef EXT2FS_DEBUG<br />
	struct ext2_super_block *es;<br />
	unsigned long bitmap_count = 0;<br />
	struct buffer_head *bitmap_bh = NULL;</p>
<p>	es = EXT2_SB(sb)->s_es;<br />
	for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {<br />
		unsigned x;</p>
<p>		desc = ext2_get_group_desc (sb, i, NULL);<br />
		if (!desc)<br />
			continue;<br />
		desc_count += le16_to_cpu(desc->bg_free_inodes_count);<br />
		brelse(bitmap_bh);<br />
		bitmap_bh = read_inode_bitmap(sb, i);<br />
		if (!bitmap_bh)<br />
			continue;</p>
<p>		x = ext2_count_free(bitmap_bh, EXT2_INODES_PER_GROUP(sb) / 8);<br />
		printk(&raquo;group %d: stored = %d, counted = %u\n&raquo;,<br />
			i, le16_to_cpu(desc->bg_free_inodes_count), x);<br />
		bitmap_count += x;<br />
	}<br />
	brelse(bitmap_bh);<br />
	printk(&raquo;ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n&raquo;,<br />
		percpu_counter_read(&#038;EXT2_SB(sb)->s_freeinodes_counter),<br />
		desc_count, bitmap_count);<br />
	return desc_count;<br />
#else<br />
	for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {<br />
		desc = ext2_get_group_desc (sb, i, NULL);<br />
		if (!desc)<br />
			continue;<br />
		desc_count += le16_to_cpu(desc->bg_free_inodes_count);<br />
	}<br />
	return desc_count;<br />
#endif<br />
}</p>
<p>/* Called at mount-time, super-block is locked */<br />
unsigned long ext2_count_dirs (struct super_block * sb)<br />
{<br />
	unsigned long count = 0;<br />
	int i;</p>
<p>	for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {<br />
		struct ext2_group_desc *gdp = ext2_get_group_desc (sb, i, NULL);<br />
		if (!gdp)<br />
			continue;<br />
		count += le16_to_cpu(gdp->bg_used_dirs_count);<br />
	}<br />
	return count;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/ialloc-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>file.c</title>
		<link>http://lynyrd.ru/file-c-14</link>
		<comments>http://lynyrd.ru/file-c-14#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:43:12 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/file-c-14</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/file.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/file.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  ext2 fs regular ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/file.c<span id="more-1148"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/file.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  ext2 fs regular file handling primitives<br />
 *<br />
 *  64-bit file support on 64-bit platforms by Jakub Jelinek<br />
 * 	(jj@sunsite.ms.mff.cuni.cz)<br />
 */</p>
<p>#include
<linux/time.h>
#include &laquo;ext2.h&raquo;<br />
#include &laquo;xattr.h&raquo;<br />
#include &laquo;acl.h&raquo;</p>
<p>/*<br />
 * Called when filp is released. This happens when all file descriptors<br />
 * for a single struct file are closed. Note that different open() calls<br />
 * for the same file yield different struct file structures.<br />
 */<br />
static int ext2_release_file (struct inode * inode, struct file * filp)<br />
{<br />
	if (filp->f_mode &#038; FMODE_WRITE) {<br />
		mutex_lock(&#038;EXT2_I(inode)->truncate_mutex);<br />
		ext2_discard_reservation(inode);<br />
		mutex_unlock(&#038;EXT2_I(inode)->truncate_mutex);<br />
	}<br />
	return 0;<br />
}</p>
<p>/*<br />
 * We have mostly NULL&#8217;s here: the current defaults are ok for<br />
 * the ext2 filesystem.<br />
 */<br />
const struct file_operations ext2_file_operations = {<br />
	.llseek		= generic_file_llseek,<br />
	.read		= do_sync_read,<br />
	.write		= do_sync_write,<br />
	.aio_read	= generic_file_aio_read,<br />
	.aio_write	= generic_file_aio_write,<br />
	.unlocked_ioctl = ext2_ioctl,<br />
#ifdef CONFIG_COMPAT<br />
	.compat_ioctl	= ext2_compat_ioctl,<br />
#endif<br />
	.mmap		= generic_file_mmap,<br />
	.open		= generic_file_open,<br />
	.release	= ext2_release_file,<br />
	.fsync		= simple_fsync,<br />
	.splice_read	= generic_file_splice_read,<br />
	.splice_write	= generic_file_splice_write,<br />
};</p>
<p>#ifdef CONFIG_EXT2_FS_XIP<br />
const struct file_operations ext2_xip_file_operations = {<br />
	.llseek		= generic_file_llseek,<br />
	.read		= xip_file_read,<br />
	.write		= xip_file_write,<br />
	.unlocked_ioctl = ext2_ioctl,<br />
#ifdef CONFIG_COMPAT<br />
	.compat_ioctl	= ext2_compat_ioctl,<br />
#endif<br />
	.mmap		= xip_file_mmap,<br />
	.open		= generic_file_open,<br />
	.release	= ext2_release_file,<br />
	.fsync		= simple_fsync,<br />
};<br />
#endif</p>
<p>const struct inode_operations ext2_file_inode_operations = {<br />
	.truncate	= ext2_truncate,<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	.setxattr	= generic_setxattr,<br />
	.getxattr	= generic_getxattr,<br />
	.listxattr	= ext2_listxattr,<br />
	.removexattr	= generic_removexattr,<br />
#endif<br />
	.setattr	= ext2_setattr,<br />
	.check_acl	= ext2_check_acl,<br />
	.fiemap		= ext2_fiemap,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/file-c-14/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ext2.h</title>
		<link>http://lynyrd.ru/ext2-h</link>
		<comments>http://lynyrd.ru/ext2-h#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:42:50 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1146</guid>
		<description><![CDATA[#include

#include

/*
 * ext2 mount options
 */
struct ext2_mount_options {
	unsigned long s_mount_opt;
	uid_t s_resuid;
	gid_t s_resgid;
};
/*
 * second extended file system inode data in memory
 */
struct ext2_inode_info {
	__le32	i_data[15];
	__u32	i_flags;
	__u32	i_faddr;
	__u8	i_frag_no;
	__u8	i_frag_size;
	__u16	i_state;
	__u32	i_file_acl;
	__u32	i_dir_acl;
	__u32	i_dtime;
	/*
	 * i_block_group is the number of the block group which contains
	 * this file&#8217;s inode.  Constant across the lifetime of the inode,
	 * it is used for making block allocation ]]></description>
			<content:encoded><![CDATA[<p>#include
<linux/fs.h>
#include
<linux/ext2_fs.h><span id="more-1146"></span></p>
<p>/*<br />
 * ext2 mount options<br />
 */<br />
struct ext2_mount_options {<br />
	unsigned long s_mount_opt;<br />
	uid_t s_resuid;<br />
	gid_t s_resgid;<br />
};</p>
<p>/*<br />
 * second extended file system inode data in memory<br />
 */<br />
struct ext2_inode_info {<br />
	__le32	i_data[15];<br />
	__u32	i_flags;<br />
	__u32	i_faddr;<br />
	__u8	i_frag_no;<br />
	__u8	i_frag_size;<br />
	__u16	i_state;<br />
	__u32	i_file_acl;<br />
	__u32	i_dir_acl;<br />
	__u32	i_dtime;</p>
<p>	/*<br />
	 * i_block_group is the number of the block group which contains<br />
	 * this file&#8217;s inode.  Constant across the lifetime of the inode,<br />
	 * it is used for making block allocation decisions &#8211; we try to<br />
	 * place a file&#8217;s data blocks near its inode block, and new inodes<br />
	 * near to their parent directory&#8217;s inode.<br />
	 */<br />
	__u32	i_block_group;</p>
<p>	/* block reservation info */<br />
	struct ext2_block_alloc_info *i_block_alloc_info;</p>
<p>	__u32	i_dir_start_lookup;<br />
#ifdef CONFIG_EXT2_FS_XATTR<br />
	/*<br />
	 * Extended attributes can be read independently of the main file<br />
	 * data. Taking i_mutex even when reading would cause contention<br />
	 * between readers of EAs and writers of regular file data, so<br />
	 * instead we synchronize on xattr_sem when reading or changing<br />
	 * EAs.<br />
	 */<br />
	struct rw_semaphore xattr_sem;<br />
#endif<br />
	rwlock_t i_meta_lock;</p>
<p>	/*<br />
	 * truncate_mutex is for serialising ext2_truncate() against<br />
	 * ext2_getblock().  It also protects the internals of the inode&#8217;s<br />
	 * reservation data structures: ext2_reserve_window and<br />
	 * ext2_reserve_window_node.<br />
	 */<br />
	struct mutex truncate_mutex;<br />
	struct inode	vfs_inode;<br />
	struct list_head i_orphan;	/* unlinked but open inodes */<br />
};</p>
<p>/*<br />
 * Inode dynamic state flags<br />
 */<br />
#define EXT2_STATE_NEW			0&#215;00000001 /* inode is newly created */</p>
<p>/*<br />
 * Function prototypes<br />
 */</p>
<p>/*<br />
 * Ok, these declarations are also in
<linux/kernel.h> but none of the<br />
 * ext2 source programs needs to include it so they are duplicated here.<br />
 */</p>
<p>static inline struct ext2_inode_info *EXT2_I(struct inode *inode)<br />
{<br />
	return container_of(inode, struct ext2_inode_info, vfs_inode);<br />
}</p>
<p>/* balloc.c */<br />
extern int ext2_bg_has_super(struct super_block *sb, int group);<br />
extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);<br />
extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);<br />
extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,<br />
				unsigned long *, int *);<br />
extern void ext2_free_blocks (struct inode *, unsigned long,<br />
			      unsigned long);<br />
extern unsigned long ext2_count_free_blocks (struct super_block *);<br />
extern unsigned long ext2_count_dirs (struct super_block *);<br />
extern void ext2_check_blocks_bitmap (struct super_block *);<br />
extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,<br />
						    unsigned int block_group,<br />
						    struct buffer_head ** bh);<br />
extern void ext2_discard_reservation (struct inode *);<br />
extern int ext2_should_retry_alloc(struct super_block *sb, int *retries);<br />
extern void ext2_init_block_alloc_info(struct inode *);<br />
extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv);</p>
<p>/* dir.c */<br />
extern int ext2_add_link (struct dentry *, struct inode *);<br />
extern ino_t ext2_inode_by_name(struct inode *, struct qstr *);<br />
extern int ext2_make_empty(struct inode *, struct inode *);<br />
extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct qstr *, struct page **);<br />
extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);<br />
extern int ext2_empty_dir (struct inode *);<br />
extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);<br />
extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *, int);</p>
<p>/* ialloc.c */<br />
extern struct inode * ext2_new_inode (struct inode *, int);<br />
extern void ext2_free_inode (struct inode *);<br />
extern unsigned long ext2_count_free_inodes (struct super_block *);<br />
extern void ext2_check_inodes_bitmap (struct super_block *);<br />
extern unsigned long ext2_count_free (struct buffer_head *, unsigned);</p>
<p>/* inode.c */<br />
extern struct inode *ext2_iget (struct super_block *, unsigned long);<br />
extern int ext2_write_inode (struct inode *, int);<br />
extern void ext2_delete_inode (struct inode *);<br />
extern int ext2_sync_inode (struct inode *);<br />
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);<br />
extern void ext2_truncate (struct inode *);<br />
extern int ext2_setattr (struct dentry *, struct iattr *);<br />
extern void ext2_set_inode_flags(struct inode *inode);<br />
extern void ext2_get_inode_flags(struct ext2_inode_info *);<br />
extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,<br />
		       u64 start, u64 len);<br />
int __ext2_write_begin(struct file *file, struct address_space *mapping,<br />
		loff_t pos, unsigned len, unsigned flags,<br />
		struct page **pagep, void **fsdata);</p>
<p>/* ioctl.c */<br />
extern long ext2_ioctl(struct file *, unsigned int, unsigned long);<br />
extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long);</p>
<p>/* namei.c */<br />
struct dentry *ext2_get_parent(struct dentry *child);</p>
<p>/* super.c */<br />
extern void ext2_error (struct super_block *, const char *, const char *, &#8230;)<br />
	__attribute__ ((format (printf, 3, 4)));<br />
extern void ext2_warning (struct super_block *, const char *, const char *, &#8230;)<br />
	__attribute__ ((format (printf, 3, 4)));<br />
extern void ext2_update_dynamic_rev (struct super_block *sb);<br />
extern void ext2_write_super (struct super_block *);</p>
<p>/*<br />
 * Inodes and files operations<br />
 */</p>
<p>/* dir.c */<br />
extern const struct file_operations ext2_dir_operations;</p>
<p>/* file.c */<br />
extern const struct inode_operations ext2_file_inode_operations;<br />
extern const struct file_operations ext2_file_operations;<br />
extern const struct file_operations ext2_xip_file_operations;</p>
<p>/* inode.c */<br />
extern const struct address_space_operations ext2_aops;<br />
extern const struct address_space_operations ext2_aops_xip;<br />
extern const struct address_space_operations ext2_nobh_aops;</p>
<p>/* namei.c */<br />
extern const struct inode_operations ext2_dir_inode_operations;<br />
extern const struct inode_operations ext2_special_inode_operations;</p>
<p>/* symlink.c */<br />
extern const struct inode_operations ext2_fast_symlink_inode_operations;<br />
extern const struct inode_operations ext2_symlink_inode_operations;</p>
<p>static inline ext2_fsblk_t<br />
ext2_group_first_block_no(struct super_block *sb, unsigned long group_no)<br />
{<br />
	return group_no * (ext2_fsblk_t)EXT2_BLOCKS_PER_GROUP(sb) +<br />
		le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/ext2-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>dir.c</title>
		<link>http://lynyrd.ru/dir-c-12</link>
		<comments>http://lynyrd.ru/dir-c-12#comments</comments>
		<pubDate>Sun, 31 Jan 2010 05:42:28 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[ext2]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=1144</guid>
		<description><![CDATA[/*
 *  linux/fs/ext2/dir.c
 *
 * Copyright (C) 1992, 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI &#8211; Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 *
 *  from
 *
 *  linux/fs/minix/dir.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  ext2 directory handling ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 *  linux/fs/ext2/dir.c<span id="more-1144"></span><br />
 *<br />
 * Copyright (C) 1992, 1993, 1994, 1995<br />
 * Remy Card (card@masi.ibp.fr)<br />
 * Laboratoire MASI &#8211; Institut Blaise Pascal<br />
 * Universite Pierre et Marie Curie (Paris VI)<br />
 *<br />
 *  from<br />
 *<br />
 *  linux/fs/minix/dir.c<br />
 *<br />
 *  Copyright (C) 1991, 1992  Linus Torvalds<br />
 *<br />
 *  ext2 directory handling functions<br />
 *<br />
 *  Big-endian to little-endian byte-swapping/bitmaps by<br />
 *        David S. Miller (davem@caip.rutgers.edu), 1995<br />
 *<br />
 * All code that works with directory layout had been switched to pagecache<br />
 * and moved here. AV<br />
 */</p>
<p>#include &laquo;ext2.h&raquo;<br />
#include
<linux/buffer_head.h>
#include
<linux/pagemap.h>
#include
<linux/swap.h>
<p>typedef struct ext2_dir_entry_2 ext2_dirent;</p>
<p>static inline unsigned ext2_rec_len_from_disk(__le16 dlen)<br />
{<br />
	unsigned len = le16_to_cpu(dlen);</p>
<p>	if (len == EXT2_MAX_REC_LEN)<br />
		return 1 << 16;<br />
	return len;<br />
}</p>
<p>static inline __le16 ext2_rec_len_to_disk(unsigned len)<br />
{<br />
	if (len == (1 << 16))<br />
		return cpu_to_le16(EXT2_MAX_REC_LEN);<br />
	else<br />
		BUG_ON(len > (1 << 16));<br />
	return cpu_to_le16(len);<br />
}</p>
<p>/*<br />
 * ext2 uses block-sized chunks. Arguably, sector-sized ones would be<br />
 * more robust, but we have what we have<br />
 */<br />
static inline unsigned ext2_chunk_size(struct inode *inode)<br />
{<br />
	return inode->i_sb->s_blocksize;<br />
}</p>
<p>static inline void ext2_put_page(struct page *page)<br />
{<br />
	kunmap(page);<br />
	page_cache_release(page);<br />
}</p>
<p>static inline unsigned long dir_pages(struct inode *inode)<br />
{<br />
	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;<br />
}</p>
<p>/*<br />
 * Return the offset into page `page_nr&#8217; of the last valid<br />
 * byte in that page, plus one.<br />
 */<br />
static unsigned<br />
ext2_last_byte(struct inode *inode, unsigned long page_nr)<br />
{<br />
	unsigned last_byte = inode->i_size;</p>
<p>	last_byte -= page_nr << PAGE_CACHE_SHIFT;<br />
	if (last_byte > PAGE_CACHE_SIZE)<br />
		last_byte = PAGE_CACHE_SIZE;<br />
	return last_byte;<br />
}</p>
<p>static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)<br />
{<br />
	struct address_space *mapping = page->mapping;<br />
	struct inode *dir = mapping->host;<br />
	int err = 0;</p>
<p>	dir->i_version++;<br />
	block_write_end(NULL, mapping, pos, len, len, page, NULL);</p>
<p>	if (pos+len > dir->i_size) {<br />
		i_size_write(dir, pos+len);<br />
		mark_inode_dirty(dir);<br />
	}</p>
<p>	if (IS_DIRSYNC(dir)) {<br />
		err = write_one_page(page, 1);<br />
		if (!err)<br />
			err = ext2_sync_inode(dir);<br />
	} else {<br />
		unlock_page(page);<br />
	}</p>
<p>	return err;<br />
}</p>
<p>static void ext2_check_page(struct page *page, int quiet)<br />
{<br />
	struct inode *dir = page->mapping->host;<br />
	struct super_block *sb = dir->i_sb;<br />
	unsigned chunk_size = ext2_chunk_size(dir);<br />
	char *kaddr = page_address(page);<br />
	u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count);<br />
	unsigned offs, rec_len;<br />
	unsigned limit = PAGE_CACHE_SIZE;<br />
	ext2_dirent *p;<br />
	char *error;</p>
<p>	if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) {<br />
		limit = dir->i_size &#038; ~PAGE_CACHE_MASK;<br />
		if (limit &#038; (chunk_size &#8211; 1))<br />
			goto Ebadsize;<br />
		if (!limit)<br />
			goto out;<br />
	}<br />
	for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {<br />
		p = (ext2_dirent *)(kaddr + offs);<br />
		rec_len = ext2_rec_len_from_disk(p->rec_len);</p>
<p>		if (rec_len < EXT2_DIR_REC_LEN(1))<br />
			goto Eshort;<br />
		if (rec_len &#038; 3)<br />
			goto Ealign;<br />
		if (rec_len < EXT2_DIR_REC_LEN(p->name_len))<br />
			goto Enamelen;<br />
		if (((offs + rec_len &#8211; 1) ^ offs) &#038; ~(chunk_size-1))<br />
			goto Espan;<br />
		if (le32_to_cpu(p->inode) > max_inumber)<br />
			goto Einumber;<br />
	}<br />
	if (offs != limit)<br />
		goto Eend;<br />
out:<br />
	SetPageChecked(page);<br />
	return;</p>
<p>	/* Too bad, we had an error */</p>
<p>Ebadsize:<br />
	if (!quiet)<br />
		ext2_error(sb, __func__,<br />
			&laquo;size of directory #%lu is not a multiple &raquo;<br />
			&laquo;of chunk size&raquo;, dir->i_ino);<br />
	goto fail;<br />
Eshort:<br />
	error = &laquo;rec_len is smaller than minimal&raquo;;<br />
	goto bad_entry;<br />
Ealign:<br />
	error = &laquo;unaligned directory entry&raquo;;<br />
	goto bad_entry;<br />
Enamelen:<br />
	error = &laquo;rec_len is too small for name_len&raquo;;<br />
	goto bad_entry;<br />
Espan:<br />
	error = &laquo;directory entry across blocks&raquo;;<br />
	goto bad_entry;<br />
Einumber:<br />
	error = &laquo;inode out of bounds&raquo;;<br />
bad_entry:<br />
	if (!quiet)<br />
		ext2_error(sb, __func__, &laquo;bad entry in directory #%lu: : %s &#8211; &raquo;<br />
			&laquo;offset=%lu, inode=%lu, rec_len=%d, name_len=%d&raquo;,<br />
			dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,<br />
			(unsigned long) le32_to_cpu(p->inode),<br />
			rec_len, p->name_len);<br />
	goto fail;<br />
Eend:<br />
	if (!quiet) {<br />
		p = (ext2_dirent *)(kaddr + offs);<br />
		ext2_error(sb, &laquo;ext2_check_page&raquo;,<br />
			&laquo;entry in directory #%lu spans the page boundary&raquo;<br />
			&laquo;offset=%lu, inode=%lu&raquo;,<br />
			dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,<br />
			(unsigned long) le32_to_cpu(p->inode));<br />
	}<br />
fail:<br />
	SetPageChecked(page);<br />
	SetPageError(page);<br />
}</p>
<p>static struct page * ext2_get_page(struct inode *dir, unsigned long n,<br />
				   int quiet)<br />
{<br />
	struct address_space *mapping = dir->i_mapping;<br />
	struct page *page = read_mapping_page(mapping, n, NULL);<br />
	if (!IS_ERR(page)) {<br />
		kmap(page);<br />
		if (!PageChecked(page))<br />
			ext2_check_page(page, quiet);<br />
		if (PageError(page))<br />
			goto fail;<br />
	}<br />
	return page;</p>
<p>fail:<br />
	ext2_put_page(page);<br />
	return ERR_PTR(-EIO);<br />
}</p>
<p>/*<br />
 * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.<br />
 *<br />
 * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.<br />
 */<br />
static inline int ext2_match (int len, const char * const name,<br />
					struct ext2_dir_entry_2 * de)<br />
{<br />
	if (len != de->name_len)<br />
		return 0;<br />
	if (!de->inode)<br />
		return 0;<br />
	return !memcmp(name, de->name, len);<br />
}</p>
<p>/*<br />
 * p is at least 6 bytes before the end of page<br />
 */<br />
static inline ext2_dirent *ext2_next_entry(ext2_dirent *p)<br />
{<br />
	return (ext2_dirent *)((char *)p +<br />
			ext2_rec_len_from_disk(p->rec_len));<br />
}</p>
<p>static inline unsigned<br />
ext2_validate_entry(char *base, unsigned offset, unsigned mask)<br />
{<br />
	ext2_dirent *de = (ext2_dirent*)(base + offset);<br />
	ext2_dirent *p = (ext2_dirent*)(base + (offset&#038;mask));<br />
	while ((char*)p < (char*)de) {<br />
		if (p->rec_len == 0)<br />
			break;<br />
		p = ext2_next_entry(p);<br />
	}<br />
	return (char *)p &#8211; base;<br />
}</p>
<p>static unsigned char ext2_filetype_table[EXT2_FT_MAX] = {<br />
	[EXT2_FT_UNKNOWN]	= DT_UNKNOWN,<br />
	[EXT2_FT_REG_FILE]	= DT_REG,<br />
	[EXT2_FT_DIR]		= DT_DIR,<br />
	[EXT2_FT_CHRDEV]	= DT_CHR,<br />
	[EXT2_FT_BLKDEV]	= DT_BLK,<br />
	[EXT2_FT_FIFO]		= DT_FIFO,<br />
	[EXT2_FT_SOCK]		= DT_SOCK,<br />
	[EXT2_FT_SYMLINK]	= DT_LNK,<br />
};</p>
<p>#define S_SHIFT 12<br />
static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = {<br />
	[S_IFREG >> S_SHIFT]	= EXT2_FT_REG_FILE,<br />
	[S_IFDIR >> S_SHIFT]	= EXT2_FT_DIR,<br />
	[S_IFCHR >> S_SHIFT]	= EXT2_FT_CHRDEV,<br />
	[S_IFBLK >> S_SHIFT]	= EXT2_FT_BLKDEV,<br />
	[S_IFIFO >> S_SHIFT]	= EXT2_FT_FIFO,<br />
	[S_IFSOCK >> S_SHIFT]	= EXT2_FT_SOCK,<br />
	[S_IFLNK >> S_SHIFT]	= EXT2_FT_SYMLINK,<br />
};</p>
<p>static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)<br />
{<br />
	mode_t mode = inode->i_mode;<br />
	if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE))<br />
		de->file_type = ext2_type_by_mode[(mode &#038; S_IFMT)>>S_SHIFT];<br />
	else<br />
		de->file_type = 0;<br />
}</p>
<p>static int<br />
ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)<br />
{<br />
	loff_t pos = filp->f_pos;<br />
	struct inode *inode = filp->f_path.dentry->d_inode;<br />
	struct super_block *sb = inode->i_sb;<br />
	unsigned int offset = pos &#038; ~PAGE_CACHE_MASK;<br />
	unsigned long n = pos >> PAGE_CACHE_SHIFT;<br />
	unsigned long npages = dir_pages(inode);<br />
	unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);<br />
	unsigned char *types = NULL;<br />
	int need_revalidate = filp->f_version != inode->i_version;</p>
<p>	if (pos > inode->i_size &#8211; EXT2_DIR_REC_LEN(1))<br />
		return 0;</p>
<p>	if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))<br />
		types = ext2_filetype_table;</p>
<p>	for ( ; n < npages; n++, offset = 0) {<br />
		char *kaddr, *limit;<br />
		ext2_dirent *de;<br />
		struct page *page = ext2_get_page(inode, n, 0);</p>
<p>		if (IS_ERR(page)) {<br />
			ext2_error(sb, __func__,<br />
				   "bad page in #%lu",<br />
				   inode->i_ino);<br />
			filp->f_pos += PAGE_CACHE_SIZE &#8211; offset;<br />
			return PTR_ERR(page);<br />
		}<br />
		kaddr = page_address(page);<br />
		if (unlikely(need_revalidate)) {<br />
			if (offset) {<br />
				offset = ext2_validate_entry(kaddr, offset, chunk_mask);<br />
				filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;<br />
			}<br />
			filp->f_version = inode->i_version;<br />
			need_revalidate = 0;<br />
		}<br />
		de = (ext2_dirent *)(kaddr+offset);<br />
		limit = kaddr + ext2_last_byte(inode, n) &#8211; EXT2_DIR_REC_LEN(1);<br />
		for ( ;(char*)de <= limit; de = ext2_next_entry(de)) {<br />
			if (de->rec_len == 0) {<br />
				ext2_error(sb, __func__,<br />
					&laquo;zero-length directory entry&raquo;);<br />
				ext2_put_page(page);<br />
				return -EIO;<br />
			}<br />
			if (de->inode) {<br />
				int over;<br />
				unsigned char d_type = DT_UNKNOWN;</p>
<p>				if (types &#038;&#038; de->file_type < EXT2_FT_MAX)<br />
					d_type = types[de->file_type];</p>
<p>				offset = (char *)de &#8211; kaddr;<br />
				over = filldir(dirent, de->name, de->name_len,<br />
						(n<<PAGE_CACHE_SHIFT) | offset,<br />
						le32_to_cpu(de->inode), d_type);<br />
				if (over) {<br />
					ext2_put_page(page);<br />
					return 0;<br />
				}<br />
			}<br />
			filp->f_pos += ext2_rec_len_from_disk(de->rec_len);<br />
		}<br />
		ext2_put_page(page);<br />
	}<br />
	return 0;<br />
}</p>
<p>/*<br />
 *	ext2_find_entry()<br />
 *<br />
 * finds an entry in the specified directory with the wanted name. It<br />
 * returns the page in which the entry was found, and the entry itself<br />
 * (as a parameter &#8211; res_dir). Page is returned mapped and unlocked.<br />
 * Entry is guaranteed to be valid.<br />
 */<br />
struct ext2_dir_entry_2 *ext2_find_entry (struct inode * dir,<br />
			struct qstr *child, struct page ** res_page)<br />
{<br />
	const char *name = child->name;<br />
	int namelen = child->len;<br />
	unsigned reclen = EXT2_DIR_REC_LEN(namelen);<br />
	unsigned long start, n;<br />
	unsigned long npages = dir_pages(dir);<br />
	struct page *page = NULL;<br />
	struct ext2_inode_info *ei = EXT2_I(dir);<br />
	ext2_dirent * de;<br />
	int dir_has_error = 0;</p>
<p>	if (npages == 0)<br />
		goto out;</p>
<p>	/* OFFSET_CACHE */<br />
	*res_page = NULL;</p>
<p>	start = ei->i_dir_start_lookup;<br />
	if (start >= npages)<br />
		start = 0;<br />
	n = start;<br />
	do {<br />
		char *kaddr;<br />
		page = ext2_get_page(dir, n, dir_has_error);<br />
		if (!IS_ERR(page)) {<br />
			kaddr = page_address(page);<br />
			de = (ext2_dirent *) kaddr;<br />
			kaddr += ext2_last_byte(dir, n) &#8211; reclen;<br />
			while ((char *) de <= kaddr) {<br />
				if (de->rec_len == 0) {<br />
					ext2_error(dir->i_sb, __func__,<br />
						&laquo;zero-length directory entry&raquo;);<br />
					ext2_put_page(page);<br />
					goto out;<br />
				}<br />
				if (ext2_match (namelen, name, de))<br />
					goto found;<br />
				de = ext2_next_entry(de);<br />
			}<br />
			ext2_put_page(page);<br />
		} else<br />
			dir_has_error = 1;</p>
<p>		if (++n >= npages)<br />
			n = 0;<br />
		/* next page is past the blocks we&#8217;ve got */<br />
		if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT &#8211; 9)))) {<br />
			ext2_error(dir->i_sb, __func__,<br />
				&laquo;dir %lu size %lld exceeds block count %llu&raquo;,<br />
				dir->i_ino, dir->i_size,<br />
				(unsigned long long)dir->i_blocks);<br />
			goto out;<br />
		}<br />
	} while (n != start);<br />
out:<br />
	return NULL;</p>
<p>found:<br />
	*res_page = page;<br />
	ei->i_dir_start_lookup = n;<br />
	return de;<br />
}</p>
<p>struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)<br />
{<br />
	struct page *page = ext2_get_page(dir, 0, 0);<br />
	ext2_dirent *de = NULL;</p>
<p>	if (!IS_ERR(page)) {<br />
		de = ext2_next_entry((ext2_dirent *) page_address(page));<br />
		*p = page;<br />
	}<br />
	return de;<br />
}</p>
<p>ino_t ext2_inode_by_name(struct inode *dir, struct qstr *child)<br />
{<br />
	ino_t res = 0;<br />
	struct ext2_dir_entry_2 *de;<br />
	struct page *page;</p>
<p>	de = ext2_find_entry (dir, child, &#038;page);<br />
	if (de) {<br />
		res = le32_to_cpu(de->inode);<br />
		ext2_put_page(page);<br />
	}<br />
	return res;<br />
}</p>
<p>/* Releases the page */<br />
void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,<br />
		   struct page *page, struct inode *inode, int update_times)<br />
{<br />
	loff_t pos = page_offset(page) +<br />
			(char *) de &#8211; (char *) page_address(page);<br />
	unsigned len = ext2_rec_len_from_disk(de->rec_len);<br />
	int err;</p>
<p>	lock_page(page);<br />
	err = __ext2_write_begin(NULL, page->mapping, pos, len,<br />
				AOP_FLAG_UNINTERRUPTIBLE, &#038;page, NULL);<br />
	BUG_ON(err);<br />
	de->inode = cpu_to_le32(inode->i_ino);<br />
	ext2_set_de_type(de, inode);<br />
	err = ext2_commit_chunk(page, pos, len);<br />
	ext2_put_page(page);<br />
	if (update_times)<br />
		dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;<br />
	EXT2_I(dir)->i_flags &#038;= ~EXT2_BTREE_FL;<br />
	mark_inode_dirty(dir);<br />
}</p>
<p>/*<br />
 *	Parent is locked.<br />
 */<br />
int ext2_add_link (struct dentry *dentry, struct inode *inode)<br />
{<br />
	struct inode *dir = dentry->d_parent->d_inode;<br />
	const char *name = dentry->d_name.name;<br />
	int namelen = dentry->d_name.len;<br />
	unsigned chunk_size = ext2_chunk_size(dir);<br />
	unsigned reclen = EXT2_DIR_REC_LEN(namelen);<br />
	unsigned short rec_len, name_len;<br />
	struct page *page = NULL;<br />
	ext2_dirent * de;<br />
	unsigned long npages = dir_pages(dir);<br />
	unsigned long n;<br />
	char *kaddr;<br />
	loff_t pos;<br />
	int err;</p>
<p>	/*<br />
	 * We take care of directory expansion in the same loop.<br />
	 * This code plays outside i_size, so it locks the page<br />
	 * to protect that region.<br />
	 */<br />
	for (n = 0; n <= npages; n++) {<br />
		char *dir_end;</p>
<p>		page = ext2_get_page(dir, n, 0);<br />
		err = PTR_ERR(page);<br />
		if (IS_ERR(page))<br />
			goto out;<br />
		lock_page(page);<br />
		kaddr = page_address(page);<br />
		dir_end = kaddr + ext2_last_byte(dir, n);<br />
		de = (ext2_dirent *)kaddr;<br />
		kaddr += PAGE_CACHE_SIZE - reclen;<br />
		while ((char *)de <= kaddr) {<br />
			if ((char *)de == dir_end) {<br />
				/* We hit i_size */<br />
				name_len = 0;<br />
				rec_len = chunk_size;<br />
				de->rec_len = ext2_rec_len_to_disk(chunk_size);<br />
				de->inode = 0;<br />
				goto got_it;<br />
			}<br />
			if (de->rec_len == 0) {<br />
				ext2_error(dir->i_sb, __func__,<br />
					&laquo;zero-length directory entry&raquo;);<br />
				err = -EIO;<br />
				goto out_unlock;<br />
			}<br />
			err = -EEXIST;<br />
			if (ext2_match (namelen, name, de))<br />
				goto out_unlock;<br />
			name_len = EXT2_DIR_REC_LEN(de->name_len);<br />
			rec_len = ext2_rec_len_from_disk(de->rec_len);<br />
			if (!de->inode &#038;&#038; rec_len >= reclen)<br />
				goto got_it;<br />
			if (rec_len >= name_len + reclen)<br />
				goto got_it;<br />
			de = (ext2_dirent *) ((char *) de + rec_len);<br />
		}<br />
		unlock_page(page);<br />
		ext2_put_page(page);<br />
	}<br />
	BUG();<br />
	return -EINVAL;</p>
<p>got_it:<br />
	pos = page_offset(page) +<br />
		(char*)de &#8211; (char*)page_address(page);<br />
	err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0,<br />
							&#038;page, NULL);<br />
	if (err)<br />
		goto out_unlock;<br />
	if (de->inode) {<br />
		ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);<br />
		de1->rec_len = ext2_rec_len_to_disk(rec_len &#8211; name_len);<br />
		de->rec_len = ext2_rec_len_to_disk(name_len);<br />
		de = de1;<br />
	}<br />
	de->name_len = namelen;<br />
	memcpy(de->name, name, namelen);<br />
	de->inode = cpu_to_le32(inode->i_ino);<br />
	ext2_set_de_type (de, inode);<br />
	err = ext2_commit_chunk(page, pos, rec_len);<br />
	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;<br />
	EXT2_I(dir)->i_flags &#038;= ~EXT2_BTREE_FL;<br />
	mark_inode_dirty(dir);<br />
	/* OFFSET_CACHE */<br />
out_put:<br />
	ext2_put_page(page);<br />
out:<br />
	return err;<br />
out_unlock:<br />
	unlock_page(page);<br />
	goto out_put;<br />
}</p>
<p>/*<br />
 * ext2_delete_entry deletes a directory entry by merging it with the<br />
 * previous entry. Page is up-to-date. Releases the page.<br />
 */<br />
int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page )<br />
{<br />
	struct address_space *mapping = page->mapping;<br />
	struct inode *inode = mapping->host;<br />
	char *kaddr = page_address(page);<br />
	unsigned from = ((char*)dir &#8211; kaddr) &#038; ~(ext2_chunk_size(inode)-1);<br />
	unsigned to = ((char *)dir &#8211; kaddr) +<br />
				ext2_rec_len_from_disk(dir->rec_len);<br />
	loff_t pos;<br />
	ext2_dirent * pde = NULL;<br />
	ext2_dirent * de = (ext2_dirent *) (kaddr + from);<br />
	int err;</p>
<p>	while ((char*)de < (char*)dir) {<br />
		if (de->rec_len == 0) {<br />
			ext2_error(inode->i_sb, __func__,<br />
				&laquo;zero-length directory entry&raquo;);<br />
			err = -EIO;<br />
			goto out;<br />
		}<br />
		pde = de;<br />
		de = ext2_next_entry(de);<br />
	}<br />
	if (pde)<br />
		from = (char*)pde &#8211; (char*)page_address(page);<br />
	pos = page_offset(page) + from;<br />
	lock_page(page);<br />
	err = __ext2_write_begin(NULL, page->mapping, pos, to &#8211; from, 0,<br />
							&#038;page, NULL);<br />
	BUG_ON(err);<br />
	if (pde)<br />
		pde->rec_len = ext2_rec_len_to_disk(to &#8211; from);<br />
	dir->inode = 0;<br />
	err = ext2_commit_chunk(page, pos, to &#8211; from);<br />
	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;<br />
	EXT2_I(inode)->i_flags &#038;= ~EXT2_BTREE_FL;<br />
	mark_inode_dirty(inode);<br />
out:<br />
	ext2_put_page(page);<br />
	return err;<br />
}</p>
<p>/*<br />
 * Set the first fragment of directory.<br />
 */<br />
int ext2_make_empty(struct inode *inode, struct inode *parent)<br />
{<br />
	struct address_space *mapping = inode->i_mapping;<br />
	struct page *page = grab_cache_page(mapping, 0);<br />
	unsigned chunk_size = ext2_chunk_size(inode);<br />
	struct ext2_dir_entry_2 * de;<br />
	int err;<br />
	void *kaddr;</p>
<p>	if (!page)<br />
		return -ENOMEM;</p>
<p>	err = __ext2_write_begin(NULL, page->mapping, 0, chunk_size, 0,<br />
							&#038;page, NULL);<br />
	if (err) {<br />
		unlock_page(page);<br />
		goto fail;<br />
	}<br />
	kaddr = kmap_atomic(page, KM_USER0);<br />
	memset(kaddr, 0, chunk_size);<br />
	de = (struct ext2_dir_entry_2 *)kaddr;<br />
	de->name_len = 1;<br />
	de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1));<br />
	memcpy (de->name, &laquo;.\0\0&#8243;, 4);<br />
	de->inode = cpu_to_le32(inode->i_ino);<br />
	ext2_set_de_type (de, inode);</p>
<p>	de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1));<br />
	de->name_len = 2;<br />
	de->rec_len = ext2_rec_len_to_disk(chunk_size &#8211; EXT2_DIR_REC_LEN(1));<br />
	de->inode = cpu_to_le32(parent->i_ino);<br />
	memcpy (de->name, &laquo;..\0&#8243;, 4);<br />
	ext2_set_de_type (de, inode);<br />
	kunmap_atomic(kaddr, KM_USER0);<br />
	err = ext2_commit_chunk(page, 0, chunk_size);<br />
fail:<br />
	page_cache_release(page);<br />
	return err;<br />
}</p>
<p>/*<br />
 * routine to check that the specified directory is empty (for rmdir)<br />
 */<br />
int ext2_empty_dir (struct inode * inode)<br />
{<br />
	struct page *page = NULL;<br />
	unsigned long i, npages = dir_pages(inode);<br />
	int dir_has_error = 0;</p>
<p>	for (i = 0; i < npages; i++) {<br />
		char *kaddr;<br />
		ext2_dirent * de;<br />
		page = ext2_get_page(inode, i, dir_has_error);</p>
<p>		if (IS_ERR(page)) {<br />
			dir_has_error = 1;<br />
			continue;<br />
		}</p>
<p>		kaddr = page_address(page);<br />
		de = (ext2_dirent *)kaddr;<br />
		kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1);</p>
<p>		while ((char *)de <= kaddr) {<br />
			if (de->rec_len == 0) {<br />
				ext2_error(inode->i_sb, __func__,<br />
					&laquo;zero-length directory entry&raquo;);<br />
				printk(&raquo;kaddr=%p, de=%p\n&raquo;, kaddr, de);<br />
				goto not_empty;<br />
			}<br />
			if (de->inode != 0) {<br />
				/* check for . and .. */<br />
				if (de->name[0] != &#8216;.&#8217;)<br />
					goto not_empty;<br />
				if (de->name_len > 2)<br />
					goto not_empty;<br />
				if (de->name_len < 2) {<br />
					if (de->inode !=<br />
					    cpu_to_le32(inode->i_ino))<br />
						goto not_empty;<br />
				} else if (de->name[1] != &#8216;.&#8217;)<br />
					goto not_empty;<br />
			}<br />
			de = ext2_next_entry(de);<br />
		}<br />
		ext2_put_page(page);<br />
	}<br />
	return 1;</p>
<p>not_empty:<br />
	ext2_put_page(page);<br />
	return 0;<br />
}</p>
<p>const struct file_operations ext2_dir_operations = {<br />
	.llseek		= generic_file_llseek,<br />
	.read		= generic_read_dir,<br />
	.readdir	= ext2_readdir,<br />
	.unlocked_ioctl = ext2_ioctl,<br />
#ifdef CONFIG_COMPAT<br />
	.compat_ioctl	= ext2_compat_ioctl,<br />
#endif<br />
	.fsync		= simple_fsync,<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/dir-c-12/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
