OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/fs/ext2
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/inode.c
0,0 → 1,634
/*
* linux/fs/ext2/inode.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - 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 by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/mm.h>
 
static int ext2_update_inode(struct inode * inode, int do_sync);
 
void ext2_put_inode (struct inode * inode)
{
ext2_discard_prealloc (inode);
if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
return;
inode->u.ext2_i.i_dtime = CURRENT_TIME;
inode->i_dirt = 1;
ext2_update_inode(inode, IS_SYNC(inode));
inode->i_size = 0;
if (inode->i_blocks)
ext2_truncate (inode);
ext2_free_inode (inode);
}
 
#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
 
static inline int block_bmap (struct buffer_head * bh, int nr)
{
int tmp;
 
if (!bh)
return 0;
tmp = swab32(((u32 *) bh->b_data)[nr]);
brelse (bh);
return tmp;
}
 
/*
* ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
* superblock in the same manner as are ext2_free_blocks and
* ext2_new_block. We just wait on the super rather than locking it
* here, since ext2_new_block will do the necessary locking and we
* can't block until then.
*/
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
unsigned short total;
 
if (inode->u.ext2_i.i_prealloc_count) {
total = inode->u.ext2_i.i_prealloc_count;
inode->u.ext2_i.i_prealloc_count = 0;
ext2_free_blocks (inode, inode->u.ext2_i.i_prealloc_block, total);
}
#endif
}
 
static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
{
#ifdef EXT2FS_DEBUG
static unsigned long alloc_hits = 0, alloc_attempts = 0;
#endif
unsigned long result;
struct buffer_head * bh;
 
wait_on_super (inode->i_sb);
 
#ifdef EXT2_PREALLOCATE
if (inode->u.ext2_i.i_prealloc_count &&
(goal == inode->u.ext2_i.i_prealloc_block ||
goal + 1 == inode->u.ext2_i.i_prealloc_block))
{
result = inode->u.ext2_i.i_prealloc_block++;
inode->u.ext2_i.i_prealloc_count--;
ext2_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
 
/* It doesn't matter if we block in getblk() since
we have already atomically allocated the block, and
are only clearing it now. */
if (!(bh = getblk (inode->i_sb->s_dev, result,
inode->i_sb->s_blocksize))) {
ext2_error (inode->i_sb, "ext2_alloc_block",
"cannot get block %lu", result);
return 0;
}
memset(bh->b_data, 0, inode->i_sb->s_blocksize);
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse (bh);
} else {
ext2_discard_prealloc (inode);
ext2_debug ("preallocation miss (%lu/%lu).\n",
alloc_hits, ++alloc_attempts);
if (S_ISREG(inode->i_mode))
result = ext2_new_block (inode, goal,
&inode->u.ext2_i.i_prealloc_count,
&inode->u.ext2_i.i_prealloc_block, err);
else
result = ext2_new_block (inode, goal, 0, 0, err);
}
#else
result = ext2_new_block (inode, goal, 0, 0, err);
#endif
 
return result;
}
 
 
int ext2_bmap (struct inode * inode, int block)
{
int i;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
 
if (block < 0) {
ext2_warning (inode->i_sb, "ext2_bmap", "block < 0");
return 0;
}
if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
(1 << (addr_per_block_bits * 2)) +
((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
ext2_warning (inode->i_sb, "ext2_bmap", "block > big");
return 0;
}
if (block < EXT2_NDIR_BLOCKS)
return inode_bmap (inode, block);
block -= EXT2_NDIR_BLOCKS;
if (block < addr_per_block) {
i = inode_bmap (inode, EXT2_IND_BLOCK);
if (!i)
return 0;
return block_bmap (bread (inode->i_dev, i,
inode->i_sb->s_blocksize), block);
}
block -= addr_per_block;
if (block < (1 << (addr_per_block_bits * 2))) {
i = inode_bmap (inode, EXT2_DIND_BLOCK);
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i,
inode->i_sb->s_blocksize),
block >> addr_per_block_bits);
if (!i)
return 0;
return block_bmap (bread (inode->i_dev, i,
inode->i_sb->s_blocksize),
block & (addr_per_block - 1));
}
block -= (1 << (addr_per_block_bits * 2));
i = inode_bmap (inode, EXT2_TIND_BLOCK);
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
block >> (addr_per_block_bits * 2));
if (!i)
return 0;
i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
(block >> addr_per_block_bits) & (addr_per_block - 1));
if (!i)
return 0;
return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
block & (addr_per_block - 1));
}
 
static struct buffer_head * inode_getblk (struct inode * inode, int nr,
int create, int new_block, int * err)
{
u32 * p;
int tmp, goal = 0;
struct buffer_head * result;
int blocks = inode->i_sb->s_blocksize / 512;
 
p = inode->u.ext2_i.i_data + nr;
repeat:
tmp = *p;
if (tmp) {
result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp == *p)
return result;
brelse (result);
goto repeat;
}
if (!create || new_block >=
(current->rlim[RLIMIT_FSIZE].rlim_cur >>
EXT2_BLOCK_SIZE_BITS(inode->i_sb))) {
*err = -EFBIG;
return NULL;
}
if (inode->u.ext2_i.i_next_alloc_block == new_block)
goal = inode->u.ext2_i.i_next_alloc_goal;
 
ext2_debug ("hint = %d,", goal);
 
if (!goal) {
for (tmp = nr - 1; tmp >= 0; tmp--) {
if (inode->u.ext2_i.i_data[tmp]) {
goal = inode->u.ext2_i.i_data[tmp];
break;
}
}
if (!goal)
goal = (inode->u.ext2_i.i_block_group *
EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
swab32(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
}
 
ext2_debug ("goal = %d.\n", goal);
 
tmp = ext2_alloc_block (inode, goal, err);
if (!tmp)
return NULL;
result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (*p) {
ext2_free_blocks (inode, tmp, 1);
brelse (result);
goto repeat;
}
*p = tmp;
inode->u.ext2_i.i_next_alloc_block = new_block;
inode->u.ext2_i.i_next_alloc_goal = tmp;
inode->i_ctime = CURRENT_TIME;
inode->i_blocks += blocks;
if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
ext2_sync_inode (inode);
else
inode->i_dirt = 1;
return result;
}
 
static struct buffer_head * block_getblk (struct inode * inode,
struct buffer_head * bh, int nr,
int create, int blocksize,
int new_block, int * err)
{
int tmp, goal = 0;
u32 * p;
struct buffer_head * result;
int blocks = inode->i_sb->s_blocksize / 512;
 
if (!bh)
return NULL;
if (!buffer_uptodate(bh)) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
if (!buffer_uptodate(bh)) {
brelse (bh);
return NULL;
}
}
p = (u32 *) bh->b_data + nr;
repeat:
tmp = swab32(*p);
if (tmp) {
result = getblk (bh->b_dev, tmp, blocksize);
if (tmp == swab32(*p)) {
brelse (bh);
return result;
}
brelse (result);
goto repeat;
}
if (!create || new_block >=
(current->rlim[RLIMIT_FSIZE].rlim_cur >>
EXT2_BLOCK_SIZE_BITS(inode->i_sb))) {
brelse (bh);
*err = -EFBIG;
return NULL;
}
if (inode->u.ext2_i.i_next_alloc_block == new_block)
goal = inode->u.ext2_i.i_next_alloc_goal;
if (!goal) {
for (tmp = nr - 1; tmp >= 0; tmp--) {
if (swab32(((u32 *) bh->b_data)[tmp])) {
goal = swab32(((u32 *)bh->b_data)[tmp]);
break;
}
}
if (!goal)
goal = bh->b_blocknr;
}
tmp = ext2_alloc_block (inode, goal, err);
if (!tmp) {
brelse (bh);
return NULL;
}
result = getblk (bh->b_dev, tmp, blocksize);
if (swab32(*p)) {
ext2_free_blocks (inode, tmp, 1);
brelse (result);
goto repeat;
}
*p = swab32(tmp);
mark_buffer_dirty(bh, 1);
if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
inode->i_ctime = CURRENT_TIME;
inode->i_blocks += blocks;
inode->i_dirt = 1;
inode->u.ext2_i.i_next_alloc_block = new_block;
inode->u.ext2_i.i_next_alloc_goal = tmp;
brelse (bh);
return result;
}
 
struct buffer_head * ext2_getblk (struct inode * inode, long block,
int create, int * err)
{
struct buffer_head * bh;
unsigned long b;
unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
 
*err = -EIO;
if (block < 0) {
ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
return NULL;
}
if (block > EXT2_NDIR_BLOCKS + addr_per_block +
(1 << (addr_per_block_bits * 2)) +
((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
return NULL;
}
/*
* If this is a sequential block allocation, set the next_alloc_block
* to this block now so that all the indblock and data block
* allocations use the same goal zone
*/
 
ext2_debug ("block %lu, next %lu, goal %lu.\n", block,
inode->u.ext2_i.i_next_alloc_block,
inode->u.ext2_i.i_next_alloc_goal);
 
if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
inode->u.ext2_i.i_next_alloc_block++;
inode->u.ext2_i.i_next_alloc_goal++;
}
 
*err = -ENOSPC;
b = block;
if (block < EXT2_NDIR_BLOCKS)
return inode_getblk (inode, block, create, b, err);
block -= EXT2_NDIR_BLOCKS;
if (block < addr_per_block) {
bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err);
return block_getblk (inode, bh, block, create,
inode->i_sb->s_blocksize, b, err);
}
block -= addr_per_block;
if (block < (1 << (addr_per_block_bits * 2))) {
bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err);
bh = block_getblk (inode, bh, block >> addr_per_block_bits,
create, inode->i_sb->s_blocksize, b, err);
return block_getblk (inode, bh, block & (addr_per_block - 1),
create, inode->i_sb->s_blocksize, b, err);
}
block -= (1 << (addr_per_block_bits * 2));
bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err);
bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
create, inode->i_sb->s_blocksize, b, err);
bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
create, inode->i_sb->s_blocksize, b, err);
return block_getblk (inode, bh, block & (addr_per_block - 1), create,
inode->i_sb->s_blocksize, b, err);
}
 
struct buffer_head * ext2_bread (struct inode * inode, int block,
int create, int *err)
{
struct buffer_head * bh;
 
bh = ext2_getblk (inode, block, create, err);
if (!bh || buffer_uptodate(bh))
return bh;
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
if (buffer_uptodate(bh))
return bh;
brelse (bh);
*err = -EIO;
return NULL;
}
 
void ext2_read_inode (struct inode * inode)
{
struct buffer_head * bh;
struct ext2_inode * raw_inode;
unsigned long block_group;
unsigned long group_desc;
unsigned long desc;
unsigned long block;
unsigned long offset;
struct ext2_group_desc * gdp;
 
if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
inode->i_ino != EXT2_ACL_DATA_INO &&
inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
inode->i_ino > swab32(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (inode->i_sb, "ext2_read_inode",
"bad inode number: %lu", inode->i_ino);
return;
}
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
ext2_panic (inode->i_sb, "ext2_read_inode",
"group >= groups count");
group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
if (!bh) {
ext2_error (inode->i_sb, "ext2_read_inode",
"Descriptor not loaded");
goto bad_inode;
}
gdp = (struct ext2_group_desc *) bh->b_data;
/*
* Figure out the offset within the block group inode table
*/
offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
EXT2_INODE_SIZE(inode->i_sb);
block = swab32(gdp[desc].bg_inode_table) +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
ext2_error (inode->i_sb, "ext2_read_inode",
"unable to read i-node block - "
"inode=%lu, block=%lu", inode->i_ino, block);
goto bad_inode;
}
offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
inode->i_mode = swab16(raw_inode->i_mode);
inode->i_uid = swab16(raw_inode->i_uid);
inode->i_gid = swab16(raw_inode->i_gid);
inode->i_nlink = swab16(raw_inode->i_links_count);
inode->i_size = swab32(raw_inode->i_size);
inode->i_atime = swab32(raw_inode->i_atime);
inode->i_ctime = swab32(raw_inode->i_ctime);
inode->i_mtime = swab32(raw_inode->i_mtime);
inode->u.ext2_i.i_dtime = swab32(raw_inode->i_dtime);
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = swab32(raw_inode->i_blocks);
inode->i_version = ++event;
inode->u.ext2_i.i_new_inode = 0;
inode->u.ext2_i.i_flags = swab32(raw_inode->i_flags);
inode->u.ext2_i.i_faddr = swab32(raw_inode->i_faddr);
inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
inode->u.ext2_i.i_osync = 0;
inode->u.ext2_i.i_file_acl = swab32(raw_inode->i_file_acl);
inode->u.ext2_i.i_dir_acl = swab32(raw_inode->i_dir_acl);
inode->u.ext2_i.i_version = swab32(raw_inode->i_version);
inode->u.ext2_i.i_block_group = block_group;
inode->u.ext2_i.i_next_alloc_block = 0;
inode->u.ext2_i.i_next_alloc_goal = 0;
if (inode->u.ext2_i.i_prealloc_count)
ext2_error (inode->i_sb, "ext2_read_inode",
"New inode has non-zero prealloc count!");
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
inode->i_rdev = to_kdev_t(swab32(raw_inode->i_block[0]));
else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
for (block = 0; block < EXT2_N_BLOCKS; block++)
inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
else for (block = 0; block < EXT2_N_BLOCKS; block++)
inode->u.ext2_i.i_data[block] = swab32(raw_inode->i_block[block]);
brelse (bh);
inode->i_op = NULL;
if (inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
/* Nothing to do */ ;
else if (S_ISREG(inode->i_mode))
inode->i_op = &ext2_file_inode_operations;
else if (S_ISDIR(inode->i_mode))
inode->i_op = &ext2_dir_inode_operations;
else if (S_ISLNK(inode->i_mode))
inode->i_op = &ext2_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
inode->i_flags |= MS_SYNCHRONOUS;
if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL)
inode->i_flags |= MS_NOATIME;
return;
bad_inode:
make_bad_inode(inode);
return;
}
 
static int ext2_update_inode(struct inode * inode, int do_sync)
{
struct buffer_head * bh;
struct ext2_inode * raw_inode;
unsigned long block_group;
unsigned long group_desc;
unsigned long desc;
unsigned long block;
unsigned long offset;
int err = 0;
struct ext2_group_desc * gdp;
 
if ((inode->i_ino != EXT2_ROOT_INO &&
inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
inode->i_ino > swab32(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (inode->i_sb, "ext2_write_inode",
"bad inode number: %lu", inode->i_ino);
return 0;
}
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
ext2_panic (inode->i_sb, "ext2_write_inode",
"group >= groups count");
group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
if (!bh)
ext2_panic (inode->i_sb, "ext2_write_inode",
"Descriptor not loaded");
gdp = (struct ext2_group_desc *) bh->b_data;
/*
* Figure out the offset within the block group inode table
*/
offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
EXT2_INODE_SIZE(inode->i_sb);
block = swab32(gdp[desc].bg_inode_table) +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
ext2_error (inode->i_sb, "ext2_write_inode",
"unable to read i-node block - "
"inode=%lu, block=%lu", inode->i_ino, block);
/*
* Unfortunately we're in a lose-lose situation. I think that
* keeping the inode in-core with the dirty bit set is
* the worse option, since that will soak up inodes until
* the end of the world. Clearing the dirty bit is nasty if
* we haven't succeeded in writing out, but it's less nasty
* than the alternative. -- sct
*/
inode->i_dirt = 0;
return -EIO;
}
offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
raw_inode = (struct ext2_inode *) (bh->b_data + offset);
 
raw_inode->i_mode = swab16(inode->i_mode);
raw_inode->i_uid = swab16(inode->i_uid);
raw_inode->i_gid = swab16(inode->i_gid);
raw_inode->i_links_count = swab16(inode->i_nlink);
raw_inode->i_size = swab32(inode->i_size);
raw_inode->i_atime = swab32(inode->i_atime);
raw_inode->i_ctime = swab32(inode->i_ctime);
raw_inode->i_mtime = swab32(inode->i_mtime);
raw_inode->i_blocks = swab32(inode->i_blocks);
raw_inode->i_dtime = swab32(inode->u.ext2_i.i_dtime);
raw_inode->i_flags = swab32(inode->u.ext2_i.i_flags);
raw_inode->i_faddr = swab32(inode->u.ext2_i.i_faddr);
raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
raw_inode->i_file_acl = swab32(inode->u.ext2_i.i_file_acl);
raw_inode->i_dir_acl = swab32(inode->u.ext2_i.i_dir_acl);
raw_inode->i_version = swab32(inode->u.ext2_i.i_version);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
raw_inode->i_block[0] = swab32(kdev_t_to_nr(inode->i_rdev));
else if (S_ISLNK(inode->i_mode) && !inode->i_blocks)
for (block = 0; block < EXT2_N_BLOCKS; block++)
raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
else for (block = 0; block < EXT2_N_BLOCKS; block++)
raw_inode->i_block[block] = swab32(inode->u.ext2_i.i_data[block]);
mark_buffer_dirty(bh, 1);
inode->i_dirt = 0;
if (do_sync) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
if (buffer_req(bh) && !buffer_uptodate(bh)) {
ext2_error (inode->i_sb,
"IO error syncing ext2 inode ["
"%s:%08lx]\n",
kdevname(inode->i_dev), inode->i_ino);
err = -EIO;
}
}
brelse (bh);
return err;
}
 
void ext2_write_inode (struct inode * inode)
{
ext2_update_inode (inode, 0);
}
 
int ext2_sync_inode (struct inode *inode)
{
return ext2_update_inode (inode, 1);
}
 
/super.c
0,0 → 1,746
/*
* linux/fs/ext2/super.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - 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 byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
#include <linux/module.h>
 
#include <stdarg.h>
 
#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
 
static char error_buf[1024];
 
void ext2_error (struct super_block * sb, const char * function,
const char * fmt, ...)
{
va_list args;
 
if (!(sb->s_flags & MS_RDONLY)) {
sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
sb->u.ext2_sb.s_es->s_state =
swab16(swab16(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
}
va_start (args, fmt);
vsprintf (error_buf, fmt, args);
va_end (args);
if (test_opt (sb, ERRORS_PANIC) ||
(swab16(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
!test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
panic ("EXT2-fs panic (device %s): %s: %s\n",
kdevname(sb->s_dev), function, error_buf);
printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
kdevname(sb->s_dev), function, error_buf);
if (test_opt (sb, ERRORS_RO) ||
(swab16(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
!test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
printk ("Remounting filesystem read-only\n");
sb->s_flags |= MS_RDONLY;
}
}
 
NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
const char * fmt, ...)
{
va_list args;
 
if (!(sb->s_flags & MS_RDONLY)) {
sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
sb->u.ext2_sb.s_es->s_state =
swab16(swab16(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
}
va_start (args, fmt);
vsprintf (error_buf, fmt, args);
va_end (args);
/* this is to prevent panic from syncing this filesystem */
if (sb->s_lock)
sb->s_lock=0;
sb->s_flags |= MS_RDONLY;
panic ("EXT2-fs panic (device %s): %s: %s\n",
kdevname(sb->s_dev), function, error_buf);
}
 
void ext2_warning (struct super_block * sb, const char * function,
const char * fmt, ...)
{
va_list args;
 
va_start (args, fmt);
vsprintf (error_buf, fmt, args);
va_end (args);
printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
kdevname(sb->s_dev), function, error_buf);
}
 
void ext2_put_super (struct super_block * sb)
{
int db_count;
int i;
 
lock_super (sb);
if (!(sb->s_flags & MS_RDONLY)) {
sb->u.ext2_sb.s_es->s_state = swab16(sb->u.ext2_sb.s_mount_state);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
}
sb->s_dev = 0;
db_count = sb->u.ext2_sb.s_db_per_group;
for (i = 0; i < db_count; i++)
if (sb->u.ext2_sb.s_group_desc[i])
brelse (sb->u.ext2_sb.s_group_desc[i]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
if (sb->u.ext2_sb.s_inode_bitmap[i])
brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
if (sb->u.ext2_sb.s_block_bitmap[i])
brelse (sb->u.ext2_sb.s_block_bitmap[i]);
brelse (sb->u.ext2_sb.s_sbh);
unlock_super (sb);
MOD_DEC_USE_COUNT;
return;
}
 
static struct super_operations ext2_sops = {
ext2_read_inode,
NULL,
ext2_write_inode,
ext2_put_inode,
ext2_put_super,
ext2_write_super,
ext2_statfs,
ext2_remount
};
 
/*
* This function has been shamelessly adapted from the msdos fs
*/
static int parse_options (char * options, unsigned long * sb_block,
unsigned short *resuid, unsigned short * resgid,
unsigned long * mount_options)
{
char * this_char;
char * value;
 
if (!options)
return 1;
for (this_char = strtok (options, ",");
this_char != NULL;
this_char = strtok (NULL, ",")) {
if ((value = strchr (this_char, '=')) != NULL)
*value++ = 0;
if (!strcmp (this_char, "bsddf"))
clear_opt (*mount_options, MINIX_DF);
else if (!strcmp (this_char, "check")) {
if (!value || !*value)
set_opt (*mount_options, CHECK_NORMAL);
else if (!strcmp (value, "none")) {
clear_opt (*mount_options, CHECK_NORMAL);
clear_opt (*mount_options, CHECK_STRICT);
}
else if (!strcmp (value, "normal"))
set_opt (*mount_options, CHECK_NORMAL);
else if (!strcmp (value, "strict")) {
set_opt (*mount_options, CHECK_NORMAL);
set_opt (*mount_options, CHECK_STRICT);
}
else {
printk ("EXT2-fs: Invalid check option: %s\n",
value);
return 0;
}
}
else if (!strcmp (this_char, "debug"))
set_opt (*mount_options, DEBUG);
else if (!strcmp (this_char, "errors")) {
if (!value || !*value) {
printk ("EXT2-fs: the errors option requires "
"an argument");
return 0;
}
if (!strcmp (value, "continue")) {
clear_opt (*mount_options, ERRORS_RO);
clear_opt (*mount_options, ERRORS_PANIC);
set_opt (*mount_options, ERRORS_CONT);
}
else if (!strcmp (value, "remount-ro")) {
clear_opt (*mount_options, ERRORS_CONT);
clear_opt (*mount_options, ERRORS_PANIC);
set_opt (*mount_options, ERRORS_RO);
}
else if (!strcmp (value, "panic")) {
clear_opt (*mount_options, ERRORS_CONT);
clear_opt (*mount_options, ERRORS_RO);
set_opt (*mount_options, ERRORS_PANIC);
}
else {
printk ("EXT2-fs: Invalid errors option: %s\n",
value);
return 0;
}
}
else if (!strcmp (this_char, "grpid") ||
!strcmp (this_char, "bsdgroups"))
set_opt (*mount_options, GRPID);
else if (!strcmp (this_char, "minixdf"))
set_opt (*mount_options, MINIX_DF);
else if (!strcmp (this_char, "nocheck")) {
clear_opt (*mount_options, CHECK_NORMAL);
clear_opt (*mount_options, CHECK_STRICT);
}
else if (!strcmp (this_char, "nogrpid") ||
!strcmp (this_char, "sysvgroups"))
clear_opt (*mount_options, GRPID);
else if (!strcmp (this_char, "resgid")) {
if (!value || !*value) {
printk ("EXT2-fs: the resgid option requires "
"an argument");
return 0;
}
*resgid = simple_strtoul (value, &value, 0);
if (*value) {
printk ("EXT2-fs: Invalid resgid option: %s\n",
value);
return 0;
}
}
else if (!strcmp (this_char, "resuid")) {
if (!value || !*value) {
printk ("EXT2-fs: the resuid option requires "
"an argument");
return 0;
}
*resuid = simple_strtoul (value, &value, 0);
if (*value) {
printk ("EXT2-fs: Invalid resuid option: %s\n",
value);
return 0;
}
}
else if (!strcmp (this_char, "sb")) {
if (!value || !*value) {
printk ("EXT2-fs: the sb option requires "
"an argument");
return 0;
}
*sb_block = simple_strtoul (value, &value, 0);
if (*value) {
printk ("EXT2-fs: Invalid sb option: %s\n",
value);
return 0;
}
}
/* Silently ignore the quota options */
else if (!strcmp (this_char, "grpquota")
|| !strcmp (this_char, "noquota")
|| !strcmp (this_char, "quota")
|| !strcmp (this_char, "usrquota"))
/* Don't do anything ;-) */ ;
else {
printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
return 0;
}
}
return 1;
}
 
static void ext2_setup_super (struct super_block * sb,
struct ext2_super_block * es)
{
if (swab32(es->s_rev_level) > EXT2_MAX_SUPP_REV) {
printk ("EXT2-fs warning: revision level too high, "
"forcing read/only mode\n");
sb->s_flags |= MS_RDONLY;
}
if (!(sb->s_flags & MS_RDONLY)) {
if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
printk ("EXT2-fs warning: mounting unchecked fs, "
"running e2fsck is recommended\n");
else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
printk ("EXT2-fs warning: mounting fs with errors, "
"running e2fsck is recommended\n");
else if ((__s16) swab16(es->s_max_mnt_count) >= 0 &&
swab16(es->s_mnt_count) >=
(unsigned short) (__s16) swab16(es->s_max_mnt_count))
printk ("EXT2-fs warning: maximal mount count reached, "
"running e2fsck is recommended\n");
else if (swab32(es->s_checkinterval) &&
(swab32(es->s_lastcheck) + swab32(es->s_checkinterval) <= CURRENT_TIME))
printk ("EXT2-fs warning: checktime reached, "
"running e2fsck is recommended\n");
es->s_state = swab16(swab16(es->s_state) & ~EXT2_VALID_FS);
if (!(__s16) swab16(es->s_max_mnt_count))
es->s_max_mnt_count = (__s16) swab16(EXT2_DFL_MAX_MNT_COUNT);
es->s_mnt_count=swab16(swab16(es->s_mnt_count) + 1);
es->s_mtime = swab32(CURRENT_TIME);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
if (test_opt (sb, DEBUG))
printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
"bpg=%lu, ipg=%lu, mo=%04lx]\n",
EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
sb->u.ext2_sb.s_frag_size,
sb->u.ext2_sb.s_groups_count,
EXT2_BLOCKS_PER_GROUP(sb),
EXT2_INODES_PER_GROUP(sb),
sb->u.ext2_sb.s_mount_opt);
if (test_opt (sb, CHECK)) {
ext2_check_blocks_bitmap (sb);
ext2_check_inodes_bitmap (sb);
}
}
}
 
static int ext2_check_descriptors (struct super_block * sb)
{
int i;
int desc_block = 0;
unsigned long block = swab32(sb->u.ext2_sb.s_es->s_first_data_block);
struct ext2_group_desc * gdp = NULL;
 
ext2_debug ("Checking group descriptors");
 
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++)
{
if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
if (swab32(gdp->bg_block_bitmap) < block ||
swab32(gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Block bitmap for group %d"
" not in group (block %lu)!",
i, (unsigned long) swab32(gdp->bg_block_bitmap));
return 0;
}
if (swab32(gdp->bg_inode_bitmap) < block ||
swab32(gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode bitmap for group %d"
" not in group (block %lu)!",
i, (unsigned long) swab32(gdp->bg_inode_bitmap));
return 0;
}
if (swab32(gdp->bg_inode_table) < block ||
swab32(gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >=
block + EXT2_BLOCKS_PER_GROUP(sb))
{
ext2_error (sb, "ext2_check_descriptors",
"Inode table for group %d"
" not in group (block %lu)!",
i, (unsigned long) swab32(gdp->bg_inode_table));
return 0;
}
block += EXT2_BLOCKS_PER_GROUP(sb);
gdp++;
}
return 1;
}
 
#define log2(n) ffz(~(n))
 
struct super_block * ext2_read_super (struct super_block * sb, void * data,
int silent)
{
struct buffer_head * bh;
struct ext2_super_block * es;
unsigned long sb_block = 1;
unsigned short resuid = EXT2_DEF_RESUID;
unsigned short resgid = EXT2_DEF_RESGID;
unsigned long logic_sb_block = 1;
kdev_t dev = sb->s_dev;
int db_count;
int i, j;
 
sb->u.ext2_sb.s_mount_opt = 0;
set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
&sb->u.ext2_sb.s_mount_opt)) {
sb->s_dev = 0;
return NULL;
}
 
MOD_INC_USE_COUNT;
lock_super (sb);
set_blocksize (dev, BLOCK_SIZE);
if (!(bh = bread (dev, sb_block, BLOCK_SIZE))) {
sb->s_dev = 0;
unlock_super (sb);
printk ("EXT2-fs: unable to read superblock\n");
MOD_DEC_USE_COUNT;
return NULL;
}
/*
* Note: s_es must be initialized s_es as soon as possible because
* some ext2 macro-instructions depend on its value
*/
es = (struct ext2_super_block *) bh->b_data;
sb->u.ext2_sb.s_es = es;
sb->s_magic = swab16(es->s_magic);
if (sb->s_magic != EXT2_SUPER_MAGIC) {
if (!silent)
printk ("VFS: Can't find an ext2 filesystem on dev "
"%s.\n", kdevname(dev));
failed_mount:
sb->s_dev = 0;
unlock_super (sb);
if (bh)
brelse(bh);
MOD_DEC_USE_COUNT;
return NULL;
}
if (swab32(es->s_rev_level) > EXT2_GOOD_OLD_REV) {
if (swab32(es->s_feature_incompat) & ~EXT2_FEATURE_INCOMPAT_SUPP) {
printk("EXT2-fs: %s: couldn't mount because of "
"unsupported optional features.\n",
kdevname(dev));
goto failed_mount;
}
if (!(sb->s_flags & MS_RDONLY) &&
(swab32(es->s_feature_ro_compat) & ~EXT2_FEATURE_RO_COMPAT_SUPP)) {
printk("EXT2-fs: %s: couldn't mount RDWR because of "
"unsupported optional features.\n",
kdevname(dev));
goto failed_mount;
}
}
sb->s_blocksize_bits = swab32(sb->u.ext2_sb.s_es->s_log_block_size) + 10;
sb->s_blocksize = 1 << sb->s_blocksize_bits;
if (sb->s_blocksize != BLOCK_SIZE &&
(sb->s_blocksize == 1024 || sb->s_blocksize == 2048 ||
sb->s_blocksize == 4096)) {
unsigned long offset;
 
brelse (bh);
set_blocksize (dev, sb->s_blocksize);
logic_sb_block = (sb_block*BLOCK_SIZE) / sb->s_blocksize;
offset = (sb_block*BLOCK_SIZE) % sb->s_blocksize;
bh = bread (dev, logic_sb_block, sb->s_blocksize);
if(!bh) {
printk("EXT2-fs: Couldn't read superblock on "
"2nd try.\n");
goto failed_mount;
}
es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
sb->u.ext2_sb.s_es = es;
if (es->s_magic != swab16(EXT2_SUPER_MAGIC)) {
printk ("EXT2-fs: Magic mismatch, very weird !\n");
goto failed_mount;
}
}
if (swab32(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
sb->u.ext2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
sb->u.ext2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
} else {
sb->u.ext2_sb.s_inode_size = swab16(es->s_inode_size);
sb->u.ext2_sb.s_first_ino = swab32(es->s_first_ino);
if (sb->u.ext2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
printk ("EXT2-fs: unsupported inode size: %d\n",
sb->u.ext2_sb.s_inode_size);
goto failed_mount;
}
}
sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
(__s32) swab32(es->s_log_frag_size);
if (sb->u.ext2_sb.s_frag_size)
sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
sb->u.ext2_sb.s_frag_size;
else
sb->s_magic = 0;
sb->u.ext2_sb.s_blocks_per_group = swab32(es->s_blocks_per_group);
sb->u.ext2_sb.s_frags_per_group = swab32(es->s_frags_per_group);
sb->u.ext2_sb.s_inodes_per_group = swab32(es->s_inodes_per_group);
sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
EXT2_INODE_SIZE(sb);
sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
sb->u.ext2_sb.s_inodes_per_block;
sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
sizeof (struct ext2_group_desc);
sb->u.ext2_sb.s_sbh = bh;
if (resuid != EXT2_DEF_RESUID)
sb->u.ext2_sb.s_resuid = resuid;
else
sb->u.ext2_sb.s_resuid = swab16(es->s_def_resuid);
if (resgid != EXT2_DEF_RESGID)
sb->u.ext2_sb.s_resgid = resgid;
else
sb->u.ext2_sb.s_resgid = swab16(es->s_def_resgid);
sb->u.ext2_sb.s_mount_state = swab16(es->s_state);
sb->u.ext2_sb.s_rename_lock = 0;
sb->u.ext2_sb.s_rename_wait = NULL;
sb->u.ext2_sb.s_addr_per_block_bits =
log2 (EXT2_ADDR_PER_BLOCK(sb));
sb->u.ext2_sb.s_desc_per_block_bits =
log2 (EXT2_DESC_PER_BLOCK(sb));
if (sb->s_magic != EXT2_SUPER_MAGIC) {
if (!silent)
printk ("VFS: Can't find an ext2 filesystem on dev "
"%s.\n",
kdevname(dev));
goto failed_mount;
}
if (sb->s_blocksize != bh->b_size) {
if (!silent)
printk ("VFS: Unsupported blocksize on dev "
"%s.\n", kdevname(dev));
goto failed_mount;
}
 
if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
goto failed_mount;
}
 
if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
printk ("EXT2-fs: #blocks per group too big: %lu\n",
sb->u.ext2_sb.s_blocks_per_group);
goto failed_mount;
}
if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) {
printk ("EXT2-fs: #fragments per group too big: %lu\n",
sb->u.ext2_sb.s_frags_per_group);
goto failed_mount;
}
if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
printk ("EXT2-fs: #inodes per group too big: %lu\n",
sb->u.ext2_sb.s_inodes_per_group);
goto failed_mount;
}
 
sb->u.ext2_sb.s_groups_count = (swab32(es->s_blocks_count) -
swab32(es->s_first_data_block) +
EXT2_BLOCKS_PER_GROUP(sb) - 1) /
EXT2_BLOCKS_PER_GROUP(sb);
db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
EXT2_DESC_PER_BLOCK(sb);
sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
if (sb->u.ext2_sb.s_group_desc == NULL) {
printk ("EXT2-fs: not enough memory\n");
goto failed_mount;
}
for (i = 0; i < db_count; i++) {
sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
sb->s_blocksize);
if (!sb->u.ext2_sb.s_group_desc[i]) {
for (j = 0; j < i; j++)
brelse (sb->u.ext2_sb.s_group_desc[j]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
printk ("EXT2-fs: unable to read group descriptors\n");
goto failed_mount;
}
}
if (!ext2_check_descriptors (sb)) {
for (j = 0; j < db_count; j++)
brelse (sb->u.ext2_sb.s_group_desc[j]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
printk ("EXT2-fs: group descriptors corrupted !\n");
goto failed_mount;
}
for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
sb->u.ext2_sb.s_inode_bitmap[i] = NULL;
sb->u.ext2_sb.s_block_bitmap_number[i] = 0;
sb->u.ext2_sb.s_block_bitmap[i] = NULL;
}
sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
sb->u.ext2_sb.s_db_per_group = db_count;
unlock_super (sb);
/*
* set up enough so that it can read an inode
*/
sb->s_dev = dev;
sb->s_op = &ext2_sops;
if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) {
sb->s_dev = 0;
for (i = 0; i < db_count; i++)
if (sb->u.ext2_sb.s_group_desc[i])
brelse (sb->u.ext2_sb.s_group_desc[i]);
kfree_s (sb->u.ext2_sb.s_group_desc,
db_count * sizeof (struct buffer_head *));
brelse (bh);
printk ("EXT2-fs: get root inode failed\n");
MOD_DEC_USE_COUNT;
return NULL;
}
ext2_setup_super (sb, es);
return sb;
}
 
static void ext2_commit_super (struct super_block * sb,
struct ext2_super_block * es)
{
es->s_wtime = swab32(CURRENT_TIME);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 0;
}
 
/*
* In the second extended file system, it is not necessary to
* write the super block since we use a mapping of the
* disk super block in a buffer.
*
* However, this function is still used to set the fs valid
* flags to 0. We need to set this flag to 0 since the fs
* may have been checked while mounted and e2fsck may have
* set s_state to EXT2_VALID_FS after some corrections.
*/
 
void ext2_write_super (struct super_block * sb)
{
struct ext2_super_block * es;
 
if (!(sb->s_flags & MS_RDONLY)) {
es = sb->u.ext2_sb.s_es;
 
ext2_debug ("setting valid to 0\n");
 
if (swab16(es->s_state) & EXT2_VALID_FS) {
es->s_state = swab16(swab16(es->s_state) & ~EXT2_VALID_FS);
es->s_mtime = swab32(CURRENT_TIME);
}
ext2_commit_super (sb, es);
}
sb->s_dirt = 0;
}
 
int ext2_remount (struct super_block * sb, int * flags, char * data)
{
struct ext2_super_block * es;
unsigned short resuid = sb->u.ext2_sb.s_resuid;
unsigned short resgid = sb->u.ext2_sb.s_resgid;
unsigned long new_mount_opt;
unsigned long tmp;
 
/*
* Allow the "check" option to be passed as a remount option.
*/
new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
if (!parse_options (data, &tmp, &resuid, &resgid,
&new_mount_opt))
return -EINVAL;
 
sb->u.ext2_sb.s_mount_opt = new_mount_opt;
sb->u.ext2_sb.s_resuid = resuid;
sb->u.ext2_sb.s_resgid = resgid;
es = sb->u.ext2_sb.s_es;
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
if (*flags & MS_RDONLY) {
if (swab16(es->s_state) & EXT2_VALID_FS ||
!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
return 0;
/*
* OK, we are remounting a valid rw partition rdonly, so set
* the rdonly flag and then mark the partition as valid again.
*/
es->s_state = swab16(sb->u.ext2_sb.s_mount_state);
es->s_mtime = swab32(CURRENT_TIME);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
ext2_commit_super (sb, es);
}
else {
/*
* Mounting a RDONLY partition read-write, so reread and
* store the current valid flag. (It may have been changed
* by e2fsck since we originally mounted the partition.)
*/
sb->u.ext2_sb.s_mount_state = swab16(es->s_state);
sb->s_flags &= ~MS_RDONLY;
ext2_setup_super (sb, es);
}
return 0;
}
 
static struct file_system_type ext2_fs_type = {
ext2_read_super, "ext2", 1, NULL
};
 
int init_ext2_fs(void)
{
return register_filesystem(&ext2_fs_type);
}
 
#ifdef MODULE
int init_module(void)
{
int status;
 
if ((status = init_ext2_fs()) == 0)
register_symtab(0);
return status;
}
 
void cleanup_module(void)
{
unregister_filesystem(&ext2_fs_type);
}
 
#endif
 
void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
{
unsigned long overhead;
unsigned long overhead_per_group;
struct statfs tmp;
 
if (test_opt (sb, MINIX_DF))
overhead = 0;
else {
/*
* Compute the overhead (FS structures)
*/
overhead_per_group = 1 /* super block */ +
sb->u.ext2_sb.s_db_per_group /* descriptors */ +
1 /* block bitmap */ +
1 /* inode bitmap */ +
sb->u.ext2_sb.s_itb_per_group /* inode table */;
overhead = swab32(sb->u.ext2_sb.s_es->s_first_data_block) +
sb->u.ext2_sb.s_groups_count * overhead_per_group;
}
 
tmp.f_type = EXT2_SUPER_MAGIC;
tmp.f_bsize = sb->s_blocksize;
tmp.f_blocks = swab32(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
tmp.f_bfree = ext2_count_free_blocks (sb);
tmp.f_bavail = tmp.f_bfree - swab32(sb->u.ext2_sb.s_es->s_r_blocks_count);
if (tmp.f_bfree < swab32(sb->u.ext2_sb.s_es->s_r_blocks_count))
tmp.f_bavail = 0;
tmp.f_files = swab32(sb->u.ext2_sb.s_es->s_inodes_count);
tmp.f_ffree = ext2_count_free_inodes (sb);
tmp.f_namelen = EXT2_NAME_LEN;
memcpy_tofs(buf, &tmp, bufsiz);
}
/acl.c
0,0 → 1,58
/*
* linux/fs/ext2/acl.c
*
* Copyright (C) 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*/
 
/*
* This file will contain the Access Control Lists management for the
* second extended file system.
*/
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
 
/*
* ext2_permission ()
*
* Check for access rights
*/
int ext2_permission (struct inode * inode, int mask)
{
unsigned short mode = inode->i_mode;
 
/*
* Nobody gets write access to a file on a readonly-fs
*/
if ((mask & S_IWOTH) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) &&
IS_RDONLY(inode))
return -EROFS;
/*
* Nobody gets write access to an immutable file
*/
if ((mask & S_IWOTH) && IS_IMMUTABLE(inode))
return -EACCES;
/*
* Special case, access is always granted for root
*/
if (fsuser())
return 0;
/*
* If no ACL, checks using the file mode
*/
else if (current->fsuid == inode->i_uid)
mode >>= 6;
else if (in_group_p (inode->i_gid))
mode >>= 3;
if (((mode & mask & S_IRWXO) == mask))
return 0;
else
return -EACCES;
}
/ialloc.c
0,0 → 1,587
/*
* linux/fs/ext2/ialloc.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - 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
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
/*
* ialloc.c contains the inodes allocation and deallocation routines
*/
 
/*
* The free inodes are managed by bitmaps. A file system contains several
* blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
* block for inodes, N blocks for the inode table and data blocks.
*
* The file system contains group descriptors which are located after the
* super block. Each descriptor contains the number of the bitmap block and
* the free blocks count in the block. The descriptors are loaded in memory
* when a file system is mounted (see ext2_read_super).
*/
 
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
 
#include <asm/bitops.h>
#include <asm/byteorder.h>
 
static struct ext2_group_desc * get_group_desc (struct super_block * sb,
unsigned int block_group,
struct buffer_head ** bh)
{
unsigned long group_desc;
unsigned long desc;
struct ext2_group_desc * gdp;
 
if (block_group >= sb->u.ext2_sb.s_groups_count)
ext2_panic (sb, "get_group_desc",
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
block_group, sb->u.ext2_sb.s_groups_count);
 
group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
desc = block_group % EXT2_DESC_PER_BLOCK(sb);
if (!sb->u.ext2_sb.s_group_desc[group_desc])
ext2_panic (sb, "get_group_desc",
"Group descriptor not loaded - "
"block_group = %d, group_desc = %lu, desc = %lu",
block_group, group_desc, desc);
gdp = (struct ext2_group_desc *)
sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
if (bh)
*bh = sb->u.ext2_sb.s_group_desc[group_desc];
return gdp + desc;
}
 
/*
* Read the inode allocation bitmap for a given block_group, reading
* into the specified slot in the superblock's bitmap cache.
*
* Return >=0 on success or a -ve error code.
*/
 
static int read_inode_bitmap (struct super_block * sb,
unsigned long block_group,
unsigned int bitmap_nr)
{
struct ext2_group_desc * gdp;
struct buffer_head * bh;
int retval = 0;
 
gdp = get_group_desc (sb, block_group, NULL);
bh = bread (sb->s_dev, swab32(gdp->bg_inode_bitmap), sb->s_blocksize);
if (!bh) {
ext2_error (sb, "read_inode_bitmap",
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %lu",
block_group, (unsigned long) swab32(gdp->bg_inode_bitmap));
retval = -EIO;
}
/*
* On IO error, just leave a zero in the superblock's block pointer for
* this group. The IO will be retried next time.
*/
sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
return retval;
}
 
/*
* load_inode_bitmap loads the inode bitmap for a blocks group
*
* It maintains a cache for the last bitmaps loaded. This cache is managed
* with a LRU algorithm.
*
* Notes:
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
*
* Return the slot used to store the bitmap, or a -ve error code.
*/
static int load_inode_bitmap (struct super_block * sb,
unsigned int block_group)
{
int i, j, retval = 0;
unsigned long inode_bitmap_number;
struct buffer_head * inode_bitmap;
 
if (block_group >= sb->u.ext2_sb.s_groups_count)
ext2_panic (sb, "load_inode_bitmap",
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
block_group, sb->u.ext2_sb.s_groups_count);
if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 &&
sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group)
return 0;
if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
if (sb->u.ext2_sb.s_inode_bitmap[block_group]) {
if (sb->u.ext2_sb.s_inode_bitmap_number[block_group] != block_group)
ext2_panic (sb, "load_inode_bitmap",
"block_group != inode_bitmap_number");
else
return block_group;
} else {
retval = read_inode_bitmap (sb, block_group, block_group);
if (retval < 0)
return retval;
return block_group;
}
}
 
for (i = 0; i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
sb->u.ext2_sb.s_inode_bitmap_number[i] != block_group;
i++)
;
if (i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
sb->u.ext2_sb.s_inode_bitmap_number[i] == block_group) {
inode_bitmap_number = sb->u.ext2_sb.s_inode_bitmap_number[i];
inode_bitmap = sb->u.ext2_sb.s_inode_bitmap[i];
for (j = i; j > 0; j--) {
sb->u.ext2_sb.s_inode_bitmap_number[j] =
sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
sb->u.ext2_sb.s_inode_bitmap[j] =
sb->u.ext2_sb.s_inode_bitmap[j - 1];
}
sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap;
 
/*
* There's still one special case here --- if inode_bitmap == 0
* then our last attempt to read the bitmap failed and we have
* just ended up caching that failure. Try again to read it.
*/
if (!inode_bitmap)
retval = read_inode_bitmap (sb, block_group, 0);
} else {
if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
sb->u.ext2_sb.s_loaded_inode_bitmaps++;
else
brelse (sb->u.ext2_sb.s_inode_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
for (j = sb->u.ext2_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) {
sb->u.ext2_sb.s_inode_bitmap_number[j] =
sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
sb->u.ext2_sb.s_inode_bitmap[j] =
sb->u.ext2_sb.s_inode_bitmap[j - 1];
}
retval = read_inode_bitmap (sb, block_group, 0);
}
return retval;
}
 
void ext2_free_inode (struct inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
unsigned long block_group;
unsigned long bit;
int bitmap_nr;
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
 
if (!inode)
return;
if (!inode->i_dev) {
printk ("ext2_free_inode: inode has no device\n");
return;
}
if (inode->i_count > 1) {
printk ("ext2_free_inode: inode has count=%ld\n",
inode->i_count);
return;
}
if (inode->i_nlink) {
printk ("ext2_free_inode: inode has nlink=%d\n",
inode->i_nlink);
return;
}
sb = inode->i_sb;
if (!sb) {
printk("ext2_free_inode: inode on nonexistent device\n");
return;
}
 
ext2_debug ("freeing inode %lu\n", inode->i_ino);
 
/* We need to kill quota references now, before grabbing the
* superblock lock because writing the quota out to disk
* may need to lock the superblock as well.
*
* It is safe to do this early instead of the original
* places because we cannot be here in ext2_free_inode
* if any other references to this inode exist at all.
*
* Based upon a 2.1.x fix by Bill Hawes. --DaveM
*/
if (sb->dq_op) {
sb->dq_op->free_inode (inode, 1);
if (IS_WRITABLE (inode))
sb->dq_op->drop(inode);
}
 
lock_super (sb);
if (inode->i_ino < EXT2_FIRST_INO(sb) ||
inode->i_ino > swab32(sb->u.ext2_sb.s_es->s_inodes_count)) {
ext2_error (sb, "free_inode",
"reserved inode or nonexistent inode");
unlock_super (sb);
return;
}
es = sb->u.ext2_sb.s_es;
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
bitmap_nr = load_inode_bitmap (sb, block_group);
if (bitmap_nr < 0) {
unlock_super (sb);
return;
}
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
if (!__ext2_clear_bit (bit, bh->b_data))
ext2_warning (sb, "ext2_free_inode",
"bit already cleared for inode %lu", inode->i_ino);
else {
gdp = get_group_desc (sb, block_group, &bh2);
gdp->bg_free_inodes_count =
swab16(swab16(gdp->bg_free_inodes_count) + 1);
if (S_ISDIR(inode->i_mode))
gdp->bg_used_dirs_count =
swab16(swab16(gdp->bg_used_dirs_count) - 1);
mark_buffer_dirty(bh2, 1);
es->s_free_inodes_count =
swab32(swab32(es->s_free_inodes_count) + 1);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
inode->i_dirt = 0;
}
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
sb->s_dirt = 1;
clear_inode (inode);
unlock_super (sb);
}
 
/*
* This function increments the inode version number
*
* This may be used one day by the NFS server
*/
static void inc_inode_version (struct inode * inode,
struct ext2_group_desc *gdp,
int mode)
{
inode->u.ext2_i.i_version++;
inode->i_dirt = 1;
 
return;
}
 
/*
* There are two policies for allocating an inode. If the new inode is
* a directory, then a forward search is made for a block group with both
* free space and a low directory-to-inode ratio; if that fails, then of
* the groups with above-average free space, that group with the fewest
* directories already is chosen.
*
* For other inodes, search forward from the parent directory\'s block
* group to find a free inode.
*/
struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
{
struct super_block * sb;
struct buffer_head * bh;
struct buffer_head * bh2;
int i, j, avefreei;
struct inode * inode;
int bitmap_nr;
struct ext2_group_desc * gdp;
struct ext2_group_desc * tmp;
struct ext2_super_block * es;
 
if (!dir || !(inode = get_empty_inode ()))
{
*err=-ENOMEM;
return NULL;
}
sb = dir->i_sb;
inode->i_sb = sb;
inode->i_flags = sb->s_flags;
lock_super (sb);
es = sb->u.ext2_sb.s_es;
repeat:
gdp = NULL; i=0;
*err = -ENOSPC;
if (S_ISDIR(mode)) {
avefreei = swab32(es->s_free_inodes_count) /
sb->u.ext2_sb.s_groups_count;
/* I am not yet convinced that this next bit is necessary.
i = dir->u.ext2_i.i_block_group;
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
tmp = get_group_desc (sb, i, &bh2);
if ((swab16(tmp->bg_used_dirs_count) << 8) <
swab16(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
else
i = ++i % sb->u.ext2_sb.s_groups_count;
}
*/
if (!gdp) {
for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) {
tmp = get_group_desc (sb, j, &bh2);
if (swab16(tmp->bg_free_inodes_count) &&
swab16(tmp->bg_free_inodes_count) >= avefreei) {
if (!gdp ||
(swab16(tmp->bg_free_blocks_count) >
swab16(gdp->bg_free_blocks_count))) {
i = j;
gdp = tmp;
}
}
}
}
}
else
{
/*
* Try to place the inode in its parent directory
*/
i = dir->u.ext2_i.i_block_group;
tmp = get_group_desc (sb, i, &bh2);
if (swab16(tmp->bg_free_inodes_count))
gdp = tmp;
else
{
/*
* Use a quadratic hash to find a group with a
* free inode
*/
for (j = 1; j < sb->u.ext2_sb.s_groups_count; j <<= 1) {
i += j;
if (i >= sb->u.ext2_sb.s_groups_count)
i -= sb->u.ext2_sb.s_groups_count;
tmp = get_group_desc (sb, i, &bh2);
if (swab16(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
}
}
if (!gdp) {
/*
* That failed: try linear search for a free inode
*/
i = dir->u.ext2_i.i_block_group + 1;
for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) {
if (++i >= sb->u.ext2_sb.s_groups_count)
i = 0;
tmp = get_group_desc (sb, i, &bh2);
if (swab16(tmp->bg_free_inodes_count)) {
gdp = tmp;
break;
}
}
}
}
 
if (!gdp) {
unlock_super (sb);
iput(inode);
return NULL;
}
bitmap_nr = load_inode_bitmap (sb, i);
if (bitmap_nr < 0) {
unlock_super (sb);
iput(inode);
*err = -EIO;
return NULL;
}
bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
if ((j = __ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_INODES_PER_GROUP(sb))) <
EXT2_INODES_PER_GROUP(sb)) {
if (__ext2_set_bit (j, bh->b_data)) {
ext2_warning (sb, "ext2_new_inode",
"bit already set for inode %d", j);
goto repeat;
}
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
} else {
if (swab16(gdp->bg_free_inodes_count) != 0) {
ext2_error (sb, "ext2_new_inode",
"Free inodes count corrupted in group %d",
i);
unlock_super (sb);
iput (inode);
return NULL;
}
goto repeat;
}
j += i * EXT2_INODES_PER_GROUP(sb) + 1;
if (j < EXT2_FIRST_INO(sb) || j > swab32(es->s_inodes_count)) {
ext2_error (sb, "ext2_new_inode",
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%d", i, j);
unlock_super (sb);
iput (inode);
return NULL;
}
gdp->bg_free_inodes_count =
swab16(swab16(gdp->bg_free_inodes_count) - 1);
if (S_ISDIR(mode))
gdp->bg_used_dirs_count =
swab16(swab16(gdp->bg_used_dirs_count) + 1);
mark_buffer_dirty(bh2, 1);
es->s_free_inodes_count =
swab32(swab32(es->s_free_inodes_count) - 1);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
inode->i_mode = mode;
inode->i_sb = sb;
inode->i_count = 1;
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
if (test_opt (sb, GRPID))
inode->i_gid = dir->i_gid;
else if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
if (S_ISDIR(mode))
mode |= S_ISGID;
} else
inode->i_gid = current->fsgid;
inode->i_dirt = 1;
inode->i_ino = j;
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->u.ext2_i.i_new_inode = 1;
inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags;
if (S_ISLNK(mode))
inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL | EXT2_APPEND_FL);
inode->u.ext2_i.i_faddr = 0;
inode->u.ext2_i.i_frag_no = 0;
inode->u.ext2_i.i_frag_size = 0;
inode->u.ext2_i.i_file_acl = 0;
inode->u.ext2_i.i_dir_acl = 0;
inode->u.ext2_i.i_dtime = 0;
inode->u.ext2_i.i_block_group = i;
inode->i_op = NULL;
if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL)
inode->i_flags |= MS_SYNCHRONOUS;
insert_inode_hash(inode);
inc_inode_version (inode, gdp, mode);
 
unlock_super (sb);
if (sb->dq_op) {
sb->dq_op->initialize (inode, -1);
if (sb->dq_op->alloc_inode (inode, 1)) {
sb->dq_op->drop (inode);
inode->i_nlink = 0;
iput (inode);
*err = -EDQUOT;
return NULL;
}
inode->i_flags |= S_WRITE;
}
ext2_debug ("allocating inode %lu\n", inode->i_ino);
 
*err = 0;
return inode;
}
 
unsigned long ext2_count_free_inodes (struct super_block * sb)
{
#ifdef EXT2FS_DEBUG
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
int bitmap_nr;
struct ext2_group_desc * gdp;
int i;
 
lock_super (sb);
es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
desc_count += swab16(gdp->bg_free_inodes_count);
bitmap_nr = load_inode_bitmap (sb, i);
if (bitmap_nr < 0)
continue;
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
printk ("group %d: stored = %d, counted = %lu\n",
i, swab16(gdp->bg_free_inodes_count), x);
bitmap_count += x;
}
printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
swab32(es->s_free_inodes_count), desc_count, bitmap_count);
unlock_super (sb);
return desc_count;
#else
return swab32(sb->u.ext2_sb.s_es->s_free_inodes_count);
#endif
}
 
void ext2_check_inodes_bitmap (struct super_block * sb)
{
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
int bitmap_nr;
struct ext2_group_desc * gdp;
int i;
 
lock_super (sb);
es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
desc_count += swab16(gdp->bg_free_inodes_count);
bitmap_nr = load_inode_bitmap (sb, i);
if (bitmap_nr < 0)
continue;
x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
EXT2_INODES_PER_GROUP(sb) / 8);
if (swab16(gdp->bg_free_inodes_count) != x)
ext2_error (sb, "ext2_check_inodes_bitmap",
"Wrong free inodes count in group %d, "
"stored = %d, counted = %lu", i,
swab16(gdp->bg_free_inodes_count), x);
bitmap_count += x;
}
if (swab32(es->s_free_inodes_count) != bitmap_count)
ext2_error (sb, "ext2_check_inodes_bitmap",
"Wrong free inodes count in super block, "
"stored = %lu, counted = %lu",
(unsigned long) swab32(es->s_free_inodes_count),
bitmap_count);
unlock_super (sb);
}
/ioctl.c
0,0 → 1,91
/*
* linux/fs/ext2/ioctl.c
*
* Copyright (C) 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*/
 
#include <asm/segment.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
 
int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
unsigned long arg)
{
int err;
unsigned long flags;
 
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
 
switch (cmd) {
case EXT2_IOC_GETFLAGS:
err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
if (err)
return err;
put_user(inode->u.ext2_i.i_flags, (int *) arg);
return 0;
case EXT2_IOC_SETFLAGS:
err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
if (err)
return err;
flags = get_user((int *) arg);
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the super user when the security level is zero.
*/
if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^
(inode->u.ext2_i.i_flags &
(EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
/* This test looks nicer. Thanks to Pauline Middelink */
if (!fsuser() || securelevel > 0)
return -EPERM;
} else
if ((current->fsuid != inode->i_uid) && !fsuser())
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
inode->u.ext2_i.i_flags = flags;
if (flags & EXT2_APPEND_FL)
inode->i_flags |= S_APPEND;
else
inode->i_flags &= ~S_APPEND;
if (flags & EXT2_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
else
inode->i_flags &= ~S_IMMUTABLE;
if (flags & EXT2_NOATIME_FL)
inode->i_flags |= MS_NOATIME;
else
inode->i_flags &= ~MS_NOATIME;
inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
return 0;
case EXT2_IOC_GETVERSION:
err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int));
if (err)
return err;
put_user(inode->u.ext2_i.i_version, (int *) arg);
return 0;
case EXT2_IOC_SETVERSION:
if ((current->fsuid != inode->i_uid) && !fsuser())
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
err = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
if (err)
return err;
inode->u.ext2_i.i_version = get_user((int *) arg);
inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
return 0;
default:
return -ENOTTY;
}
}
/bitmap.c
0,0 → 1,26
/*
* linux/fs/ext2/bitmap.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*/
 
#include <linux/fs.h>
#include <linux/ext2_fs.h>
 
static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
 
unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
{
unsigned int i;
unsigned long sum = 0;
if (!map)
return (0);
for (i = 0; i < numchars; i++)
sum += nibblemap[map->b_data[i] & 0xf] +
nibblemap[(map->b_data[i] >> 4) & 0xf];
return (sum);
}
/dir.c
0,0 → 1,216
/*
* linux/fs/ext2/dir.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - 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 functions
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
#include <asm/segment.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
 
static int ext2_dir_read (struct inode * inode, struct file * filp,
char * buf, int count)
{
return -EISDIR;
}
 
static int ext2_readdir (struct inode *, struct file *, void *, filldir_t);
 
static struct file_operations ext2_dir_operations = {
NULL, /* lseek - default */
ext2_dir_read, /* read */
NULL, /* write - bad */
ext2_readdir, /* readdir */
NULL, /* select - default */
ext2_ioctl, /* ioctl */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
ext2_sync_file, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL /* revalidate */
};
 
/*
* directories can handle most operations...
*/
struct inode_operations ext2_dir_inode_operations = {
&ext2_dir_operations, /* default directory file-ops */
ext2_create, /* create */
ext2_lookup, /* lookup */
ext2_link, /* link */
ext2_unlink, /* unlink */
ext2_symlink, /* symlink */
ext2_mkdir, /* mkdir */
ext2_rmdir, /* rmdir */
ext2_mknod, /* mknod */
ext2_rename, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
ext2_truncate, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
 
int ext2_check_dir_entry (const char * function, struct inode * dir,
struct ext2_dir_entry * de, struct buffer_head * bh,
unsigned long offset)
{
const char * error_msg = NULL;
 
if (swab16(de->rec_len) < EXT2_DIR_REC_LEN(1))
error_msg = "rec_len is smaller than minimal";
else if (swab16(de->rec_len) % 4 != 0)
error_msg = "rec_len % 4 != 0";
else if (swab16(de->rec_len) < EXT2_DIR_REC_LEN(swab16(de->name_len)))
error_msg = "rec_len is too small for name_len";
else if (dir && ((char *) de - bh->b_data) + swab16(de->rec_len) >
dir->i_sb->s_blocksize)
error_msg = "directory entry across blocks";
else if (dir && swab32(de->inode) > swab32(dir->i_sb->u.ext2_sb.s_es->s_inodes_count))
error_msg = "inode out of bounds";
 
if (error_msg != NULL)
ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - "
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
dir->i_ino, error_msg, offset,
(unsigned long) swab32(de->inode),
swab16(de->rec_len), swab16(de->name_len));
return error_msg == NULL ? 1 : 0;
}
 
static int ext2_readdir (struct inode * inode, struct file * filp,
void * dirent, filldir_t filldir)
{
int error = 0;
unsigned long offset, blk;
int i, num, stored;
struct buffer_head * bh, * tmp, * bha[16];
struct ext2_dir_entry * de;
struct super_block * sb;
int err;
 
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
sb = inode->i_sb;
 
stored = 0;
bh = NULL;
offset = filp->f_pos & (sb->s_blocksize - 1);
 
while (!error && !stored && filp->f_pos < inode->i_size) {
blk = (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb);
bh = ext2_bread (inode, blk, 0, &err);
if (!bh) {
ext2_error (sb, "ext2_readdir",
"directory #%lu contains a hole at offset %lu",
inode->i_ino, (unsigned long)filp->f_pos);
filp->f_pos += sb->s_blocksize - offset;
continue;
}
 
/*
* Do the readahead
*/
if (!offset) {
for (i = 16 >> (EXT2_BLOCK_SIZE_BITS(sb) - 9), num = 0;
i > 0; i--) {
tmp = ext2_getblk (inode, ++blk, 0, &err);
if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
bha[num++] = tmp;
else
brelse (tmp);
}
if (num) {
ll_rw_block (READA, num, bha);
for (i = 0; i < num; i++)
brelse (bha[i]);
}
}
revalidate:
/* If the dir block has changed since the last call to
* readdir(2), then we might be pointing to an invalid
* dirent right now. Scan from the start of the block
* to make sure. */
if (filp->f_version != inode->i_version) {
for (i = 0; i < sb->s_blocksize && i < offset; ) {
de = (struct ext2_dir_entry *)
(bh->b_data + i);
/* It's too expensive to do a full
* dirent test each time round this
* loop, but we do have to test at
* least that it is non-zero. A
* failure will be detected in the
* dirent test below. */
if (swab16(de->rec_len) < EXT2_DIR_REC_LEN(1))
break;
i += swab16(de->rec_len);
}
offset = i;
filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
| offset;
filp->f_version = inode->i_version;
}
while (!error && filp->f_pos < inode->i_size
&& offset < sb->s_blocksize) {
de = (struct ext2_dir_entry *) (bh->b_data + offset);
if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
bh, offset)) {
/* On error, skip the f_pos to the
next block. */
filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
+ sb->s_blocksize;
brelse (bh);
return stored;
}
offset += swab16(de->rec_len);
if (swab32(de->inode)) {
/* We might block in the next section
* if the data destination is
* currently swapped out. So, use a
* version stamp to detect whether or
* not the directory has been modified
* during the copy operation. */
unsigned long version;
dcache_add(inode, de->name, swab16(de->name_len),
swab32(de->inode));
version = inode->i_version;
error = filldir(dirent, de->name, swab16(de->name_len), filp->f_pos, swab32(de->inode));
if (error)
break;
if (version != inode->i_version)
goto revalidate;
stored ++;
}
filp->f_pos += swab16(de->rec_len);
}
offset = 0;
brelse (bh);
}
UPDATE_ATIME(inode);
return 0;
}
/file.c
0,0 → 1,209
/*
* linux/fs/ext2/file.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - 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 file handling primitives
*/
 
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
 
#define NBUF 32
 
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
 
static int ext2_file_write (struct inode *, struct file *, const char *, int);
static void ext2_release_file (struct inode *, struct file *);
 
/*
* We have mostly NULL's here: the current defaults are ok for
* the ext2 filesystem.
*/
static struct file_operations ext2_file_operations = {
NULL, /* lseek - default */
generic_file_read, /* read */
ext2_file_write, /* write */
NULL, /* readdir - bad */
NULL, /* select - default */
ext2_ioctl, /* ioctl */
generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
ext2_release_file, /* release */
ext2_sync_file, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL /* revalidate */
};
 
struct inode_operations ext2_file_inode_operations = {
&ext2_file_operations,/* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
ext2_bmap, /* bmap */
ext2_truncate, /* truncate */
ext2_permission, /* permission */
NULL /* smap */
};
 
static int ext2_file_write (struct inode * inode, struct file * filp,
const char * buf, int count)
{
const loff_t two_gb = 2147483647;
loff_t pos;
off_t pos2;
long block;
int offset;
int written, c;
struct buffer_head * bh, *bufferlist[NBUF];
struct super_block * sb;
int err;
int i,buffercount,write_error;
 
write_error = buffercount = 0;
if (!inode) {
printk("ext2_file_write: inode = NULL\n");
return -EINVAL;
}
sb = inode->i_sb;
if (sb->s_flags & MS_RDONLY)
/*
* This fs has been automatically remounted ro because of errors
*/
return -ENOSPC;
 
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_write", "mode = %07o",
inode->i_mode);
return -EINVAL;
}
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
pos2 = (off_t) pos;
/*
* If a file has been opened in synchronous mode, we have to ensure
* that meta-data will also be written synchronously. Thus, we
* set the i_osync field. This field is tested by the allocation
* routines.
*/
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync++;
block = pos2 >> EXT2_BLOCK_SIZE_BITS(sb);
offset = pos2 & (sb->s_blocksize - 1);
c = sb->s_blocksize - offset;
written = 0;
while (count > 0) {
if (pos > two_gb) {
if (!written)
written = -EFBIG;
break;
}
bh = ext2_getblk (inode, block, 1, &err);
if (!bh) {
if (!written)
written = err;
break;
}
count -= c;
if (count < 0)
c += count;
if (c != sb->s_blocksize && !buffer_uptodate(bh)) {
ll_rw_block (READ, 1, &bh);
wait_on_buffer (bh);
if (!buffer_uptodate(bh)) {
brelse (bh);
if (!written)
written = -EIO;
break;
}
}
memcpy_fromfs (bh->b_data + offset, buf, c);
update_vm_cache(inode, pos, bh->b_data + offset, c);
pos2 += c;
pos += c;
written += c;
buf += c;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh;
else
brelse(bh);
if (buffercount == NBUF){
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
buffercount=0;
}
if(write_error)
break;
block++;
offset = 0;
c = sb->s_blocksize;
}
if ( buffercount ){
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[i]);
if (!buffer_uptodate(bufferlist[i]))
write_error=1;
brelse(bufferlist[i]);
}
}
if (pos > inode->i_size)
inode->i_size = pos;
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync--;
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
return written;
}
 
/*
* Called when a inode is released. Note that this is different
* from ext2_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
static void ext2_release_file (struct inode * inode, struct file * filp)
{
if (filp->f_mode & 2)
ext2_discard_prealloc (inode);
}
/fsync.c
0,0 → 1,301
/*
* linux/fs/ext2/fsync.c
*
* Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
* from
* Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
* from
* linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2fs fsync primitive
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*
* Fast 'fsync' on large files (Scott Laird <laird@pacificrim.net>)
*/
 
#include <asm/segment.h>
#include <asm/system.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
 
 
#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
 
static __inline__ int sync_block (struct inode * inode, u32 * block, int wait)
{
struct buffer_head * bh;
int tmp;
if (!*block)
return 0;
tmp = *block;
bh = get_hash_table (inode->i_dev, *block, blocksize);
if (!bh)
return 0;
if (*block != tmp) {
brelse (bh);
return 1;
}
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse (bh);
return -1;
}
if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
brelse (bh);
return 0;
}
ll_rw_block (WRITE, 1, &bh);
bh->b_count--;
return 0;
}
 
static __inline__ int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
{
struct buffer_head * bh;
int tmp;
if (!swab32(*block))
return 0;
tmp = swab32(*block);
bh = get_hash_table (inode->i_dev, swab32(*block), blocksize);
if (!bh)
return 0;
if (swab32(*block) != tmp) {
brelse (bh);
return 1;
}
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse (bh);
return -1;
}
if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
brelse (bh);
return 0;
}
ll_rw_block (WRITE, 1, &bh);
bh->b_count--;
return 0;
}
 
static __inline__ int sync_iblock (struct inode * inode, u32 * iblock,
struct buffer_head ** bh, int wait)
{
int rc, tmp;
*bh = NULL;
tmp = *iblock;
if (!tmp)
return 0;
rc = sync_block (inode, iblock, wait);
if (rc)
return rc;
*bh = bread (inode->i_dev, tmp, blocksize);
if (tmp != *iblock) {
brelse (*bh);
*bh = NULL;
return 1;
}
if (!*bh)
return -1;
return 0;
}
 
static __inline__ int sync_iblock_swab32 (struct inode * inode, u32 * iblock,
struct buffer_head ** bh, int wait)
{
int rc, tmp;
*bh = NULL;
tmp = swab32(*iblock);
if (!tmp)
return 0;
rc = sync_block_swab32 (inode, iblock, wait);
if (rc)
return rc;
*bh = bread (inode->i_dev, tmp, blocksize);
if (tmp != swab32(*iblock)) {
brelse (*bh);
*bh = NULL;
return 1;
}
if (!*bh)
return -1;
return 0;
}
 
 
static __inline__ int sync_direct (struct inode * inode, int wait)
{
int i;
int rc, err = 0;
 
for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
return err;
}
 
static __inline__ int sync_indirect (struct inode * inode, u32 * iblock, int wait)
{
int i;
struct buffer_head * ind_bh;
int rc, err = 0;
 
rc = sync_iblock (inode, iblock, &ind_bh, wait);
if (rc || !ind_bh)
return rc;
for (i = 0; i < addr_per_block; i++) {
rc = sync_block_swab32 (inode,
((u32 *) ind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse (ind_bh);
return err;
}
 
static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait)
{
int i;
struct buffer_head * ind_bh;
int rc, err = 0;
 
rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait);
if (rc || !ind_bh)
return rc;
for (i = 0; i < addr_per_block; i++) {
rc = sync_block_swab32 (inode,
((u32 *) ind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse (ind_bh);
return err;
}
 
static __inline__ int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
{
int i;
struct buffer_head * dind_bh;
int rc, err = 0;
 
rc = sync_iblock (inode, diblock, &dind_bh, wait);
if (rc || !dind_bh)
return rc;
for (i = 0; i < addr_per_block; i++) {
rc = sync_indirect_swab32 (inode,
((u32 *) dind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse (dind_bh);
return err;
}
 
static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait)
{
int i;
struct buffer_head * dind_bh;
int rc, err = 0;
 
rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait);
if (rc || !dind_bh)
return rc;
for (i = 0; i < addr_per_block; i++) {
rc = sync_indirect_swab32 (inode,
((u32 *) dind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse (dind_bh);
return err;
}
 
static __inline__ int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
{
int i;
struct buffer_head * tind_bh;
int rc, err = 0;
 
rc = sync_iblock (inode, tiblock, &tind_bh, wait);
if (rc || !tind_bh)
return rc;
for (i = 0; i < addr_per_block; i++) {
rc = sync_dindirect_swab32 (inode,
((u32 *) tind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse (tind_bh);
return err;
}
 
int ext2_sync_file (struct inode * inode, struct file * file)
{
int wait, err = 0;
 
if (S_ISLNK(inode->i_mode) && !(inode->i_blocks))
/*
* Don't sync fast links!
*/
goto skip;
 
/* fsync on large files is *slow*, so fall back to sync() if
* the file's over 10M */
if (inode->i_size>10000000) {
file_fsync(inode,file);
goto skip;
}
 
for (wait=0; wait<=1; wait++)
{
err |= sync_direct (inode, wait);
err |= sync_indirect (inode,
inode->u.ext2_i.i_data+EXT2_IND_BLOCK,
wait);
err |= sync_dindirect (inode,
inode->u.ext2_i.i_data+EXT2_DIND_BLOCK,
wait);
err |= sync_tindirect (inode,
inode->u.ext2_i.i_data+EXT2_TIND_BLOCK,
wait);
}
skip:
err |= ext2_sync_inode (inode);
return (err < 0) ? -EIO : 0;
}
/truncate.c
0,0 → 1,514
/*
* linux/fs/ext2/truncate.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/fs/minix/truncate.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
/*
* Real random numbers for secure rm added 94/02/18
* Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
*/
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/locks.h>
#include <linux/string.h>
 
#if 0
 
/*
* Secure deletion currently doesn't work. It interacts very badly
* with buffers shared with memory mappings, and for that reason
* can't be done in the truncate() routines. It should instead be
* done separately in "release()" before calling the truncate routines
* that will release the actual file blocks.
*
* Linus
*/
static int ext2_secrm_seed = 152; /* Random generator base */
 
#define RANDOM_INT (ext2_secrm_seed = ext2_secrm_seed * 69069l +1)
#endif
 
/*
* Truncate has the most races in the whole filesystem: coding it is
* a pain in the a**. Especially as I don't do any locking...
*
* The code may look a bit weird, but that's just because I've tried to
* handle things like file-size changes in a somewhat graceful manner.
* Anyway, truncating a file at the same time somebody else writes to it
* is likely to result in pretty weird behaviour...
*
* The new code handles normal truncates (size = 0) as well as the more
* general case (size = XXX). I hope.
*/
 
static int trunc_direct (struct inode * inode)
{
u32 * p;
int i, tmp;
struct buffer_head * bh;
unsigned long block_to_free = 0;
unsigned long free_count = 0;
int retry = 0;
int blocks = inode->i_sb->s_blocksize / 512;
#define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \
inode->i_sb->s_blocksize)
int direct_block = DIRECT_BLOCK;
 
repeat:
for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
p = inode->u.ext2_i.i_data + i;
tmp = *p;
if (!tmp)
continue;
bh = get_hash_table (inode->i_dev, tmp,
inode->i_sb->s_blocksize);
if (i < direct_block) {
brelse (bh);
goto repeat;
}
if ((bh && bh->b_count != 1) || tmp != *p) {
retry = 1;
brelse (bh);
continue;
}
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
free_count++;
} else if (free_count > 0 && block_to_free == tmp - free_count)
free_count++;
else {
ext2_free_blocks (inode, block_to_free, free_count);
block_to_free = tmp;
free_count = 1;
}
/* ext2_free_blocks (inode, tmp, 1); */
}
if (free_count > 0)
ext2_free_blocks (inode, block_to_free, free_count);
return retry;
}
 
static int trunc_indirect (struct inode * inode, int offset, u32 * p)
{
int i, tmp;
struct buffer_head * bh;
struct buffer_head * ind_bh;
u32 * ind;
unsigned long block_to_free = 0;
unsigned long free_count = 0;
int retry = 0;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int blocks = inode->i_sb->s_blocksize / 512;
#define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
int indirect_block = INDIRECT_BLOCK;
 
tmp = *p;
if (!tmp)
return 0;
ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp != *p) {
brelse (ind_bh);
return 1;
}
if (!ind_bh) {
*p = 0;
return 0;
}
repeat:
for (i = indirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < indirect_block)
goto repeat;
ind = i + (u32 *) ind_bh->b_data;
tmp = swab32(*ind);
if (!tmp)
continue;
bh = get_hash_table (inode->i_dev, tmp,
inode->i_sb->s_blocksize);
if (i < indirect_block) {
brelse (bh);
goto repeat;
}
if ((bh && bh->b_count != 1) || tmp != swab32(*ind)) {
retry = 1;
brelse (bh);
continue;
}
*ind = swab32(0);
mark_buffer_dirty(ind_bh, 1);
bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
free_count++;
} else if (free_count > 0 && block_to_free == tmp - free_count)
free_count++;
else {
ext2_free_blocks (inode, block_to_free, free_count);
block_to_free = tmp;
free_count = 1;
}
/* ext2_free_blocks (inode, tmp, 1); */
inode->i_blocks -= blocks;
inode->i_dirt = 1;
}
if (free_count > 0)
ext2_free_blocks (inode, block_to_free, free_count);
ind = (u32 *) ind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
if (swab32(*(ind++)))
break;
if (i >= addr_per_block)
if (ind_bh->b_count != 1)
retry = 1;
else {
tmp = *p;
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
ext2_free_blocks (inode, tmp, 1);
}
if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
ll_rw_block (WRITE, 1, &ind_bh);
wait_on_buffer (ind_bh);
}
brelse (ind_bh);
return retry;
}
 
static int trunc_indirect_swab32 (struct inode * inode, int offset, u32 * p)
{
int i, tmp;
struct buffer_head * bh;
struct buffer_head * ind_bh;
u32 * ind;
unsigned long block_to_free = 0;
unsigned long free_count = 0;
int retry = 0;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int blocks = inode->i_sb->s_blocksize / 512;
int indirect_block = INDIRECT_BLOCK;
 
tmp = swab32(*p);
if (!tmp)
return 0;
ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp != swab32(*p)) {
brelse (ind_bh);
return 1;
}
if (!ind_bh) {
*p = swab32(0);
return 0;
}
repeat:
for (i = indirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < indirect_block)
goto repeat;
ind = i + (u32 *) ind_bh->b_data;
tmp = swab32(*ind);
if (!tmp)
continue;
bh = get_hash_table (inode->i_dev, tmp,
inode->i_sb->s_blocksize);
if (i < indirect_block) {
brelse (bh);
goto repeat;
}
if ((bh && bh->b_count != 1) || tmp != swab32(*ind)) {
retry = 1;
brelse (bh);
continue;
}
*ind = swab32(0);
mark_buffer_dirty(ind_bh, 1);
bforget(bh);
if (free_count == 0) {
block_to_free = tmp;
free_count++;
} else if (free_count > 0 && block_to_free == tmp - free_count)
free_count++;
else {
ext2_free_blocks (inode, block_to_free, free_count);
block_to_free = tmp;
free_count = 1;
}
/* ext2_free_blocks (inode, tmp, 1); */
inode->i_blocks -= blocks;
inode->i_dirt = 1;
}
if (free_count > 0)
ext2_free_blocks (inode, block_to_free, free_count);
ind = (u32 *) ind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
if (swab32(*(ind++)))
break;
if (i >= addr_per_block)
if (ind_bh->b_count != 1)
retry = 1;
else {
tmp = swab32(*p);
*p = swab32(0);
inode->i_blocks -= blocks;
inode->i_dirt = 1;
ext2_free_blocks (inode, tmp, 1);
}
if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
ll_rw_block (WRITE, 1, &ind_bh);
wait_on_buffer (ind_bh);
}
brelse (ind_bh);
return retry;
}
 
static int trunc_dindirect (struct inode * inode, int offset,
u32 * p)
{
int i, tmp;
struct buffer_head * dind_bh;
u32 * dind;
int retry = 0;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int blocks = inode->i_sb->s_blocksize / 512;
#define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
int dindirect_block = DINDIRECT_BLOCK;
 
tmp = *p;
if (!tmp)
return 0;
dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp != *p) {
brelse (dind_bh);
return 1;
}
if (!dind_bh) {
*p = 0;
return 0;
}
repeat:
for (i = dindirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < dindirect_block)
goto repeat;
dind = i + (u32 *) dind_bh->b_data;
tmp = swab32(*dind);
if (!tmp)
continue;
retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
dind);
mark_buffer_dirty(dind_bh, 1);
}
dind = (u32 *) dind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
if (swab32(*(dind++)))
break;
if (i >= addr_per_block)
if (dind_bh->b_count != 1)
retry = 1;
else {
tmp = *p;
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
ext2_free_blocks (inode, tmp, 1);
}
if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
ll_rw_block (WRITE, 1, &dind_bh);
wait_on_buffer (dind_bh);
}
brelse (dind_bh);
return retry;
}
 
static int trunc_dindirect_swab32 (struct inode * inode, int offset,
u32 * p)
{
int i, tmp;
struct buffer_head * dind_bh;
u32 * dind;
int retry = 0;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int blocks = inode->i_sb->s_blocksize / 512;
int dindirect_block = DINDIRECT_BLOCK;
 
tmp = swab32(*p);
if (!tmp)
return 0;
dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp != swab32(*p)) {
brelse (dind_bh);
return 1;
}
if (!dind_bh) {
*p = swab32(0);
return 0;
}
repeat:
for (i = dindirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < dindirect_block)
goto repeat;
dind = i + (u32 *) dind_bh->b_data;
tmp = swab32(*dind);
if (!tmp)
continue;
retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block),
dind);
mark_buffer_dirty(dind_bh, 1);
}
dind = (u32 *) dind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
if (swab32(*(dind++)))
break;
if (i >= addr_per_block)
if (dind_bh->b_count != 1)
retry = 1;
else {
tmp = swab32(*p);
*p = swab32(0);
inode->i_blocks -= blocks;
inode->i_dirt = 1;
ext2_free_blocks (inode, tmp, 1);
}
if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
ll_rw_block (WRITE, 1, &dind_bh);
wait_on_buffer (dind_bh);
}
brelse (dind_bh);
return retry;
}
 
static int trunc_tindirect (struct inode * inode)
{
int i, tmp;
struct buffer_head * tind_bh;
u32 * tind, * p;
int retry = 0;
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int blocks = inode->i_sb->s_blocksize / 512;
#define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \
addr_per_block + EXT2_NDIR_BLOCKS)) / \
(addr_per_block * addr_per_block))
int tindirect_block = TINDIRECT_BLOCK;
 
p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
if (!(tmp = *p))
return 0;
tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
if (tmp != *p) {
brelse (tind_bh);
return 1;
}
if (!tind_bh) {
*p = 0;
return 0;
}
repeat:
for (i = tindirect_block ; i < addr_per_block ; i++) {
if (i < 0)
i = 0;
if (i < tindirect_block)
goto repeat;
tind = i + (u32 *) tind_bh->b_data;
retry |= trunc_dindirect_swab32(inode, EXT2_NDIR_BLOCKS +
addr_per_block + (i + 1) * addr_per_block * addr_per_block,
tind);
mark_buffer_dirty(tind_bh, 1);
}
tind = (u32 *) tind_bh->b_data;
for (i = 0; i < addr_per_block; i++)
if (swab32(*(tind++)))
break;
if (i >= addr_per_block)
if (tind_bh->b_count != 1)
retry = 1;
else {
tmp = *p;
*p = 0;
inode->i_blocks -= blocks;
inode->i_dirt = 1;
ext2_free_blocks (inode, tmp, 1);
}
if (IS_SYNC(inode) && buffer_dirty(tind_bh)) {
ll_rw_block (WRITE, 1, &tind_bh);
wait_on_buffer (tind_bh);
}
brelse (tind_bh);
return retry;
}
void ext2_truncate (struct inode * inode)
{
int retry;
struct buffer_head * bh;
int err;
int offset;
 
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return;
ext2_discard_prealloc(inode);
while (1) {
retry = trunc_direct(inode);
retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
(u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
EXT2_ADDR_PER_BLOCK(inode->i_sb),
(u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
retry |= trunc_tindirect (inode);
if (!retry)
break;
if (IS_SYNC(inode) && inode->i_dirt)
ext2_sync_inode (inode);
current->counter = 0;
schedule ();
}
/*
* If the file is not being truncated to a block boundary, the
* contents of the partial block following the end of the file must be
* zeroed in case it ever becomes accessible again because of
* subsequent file growth.
*/
offset = inode->i_size & (inode->i_sb->s_blocksize - 1);
if (offset) {
bh = ext2_bread (inode,
inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb),
0, &err);
if (bh) {
memset (bh->b_data + offset, 0,
inode->i_sb->s_blocksize - offset);
mark_buffer_dirty (bh, 0);
brelse (bh);
}
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
}
/symlink.c
0,0 → 1,132
/*
* linux/fs/ext2/symlink.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/fs/minix/symlink.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2 symlink handling code
*/
 
#include <asm/segment.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
 
static int ext2_readlink (struct inode *, char *, int);
static int ext2_follow_link (struct inode *, struct inode *, int, int,
struct inode **);
 
/*
* symlinks can't do much...
*/
struct inode_operations ext2_symlink_inode_operations = {
NULL, /* no file-operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
ext2_readlink, /* readlink */
ext2_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL /* smap */
};
 
static int ext2_follow_link(struct inode * dir, struct inode * inode,
int flag, int mode, struct inode ** res_inode)
{
int error;
struct buffer_head * bh = NULL;
char * link;
 
*res_inode = NULL;
if (!dir) {
dir = current->fs->root;
dir->i_count++;
}
if (!inode) {
iput (dir);
return -ENOENT;
}
if (!S_ISLNK(inode->i_mode)) {
iput (dir);
*res_inode = inode;
return 0;
}
if (current->link_count > 5) {
iput (dir);
iput (inode);
return -ELOOP;
}
if (inode->i_blocks) {
if (!(bh = ext2_bread (inode, 0, 0, &error))) {
iput (dir);
iput (inode);
return -EIO;
}
link = bh->b_data;
} else
link = (char *) inode->u.ext2_i.i_data;
UPDATE_ATIME(inode);
current->link_count++;
error = open_namei (link, flag, mode, res_inode, dir);
current->link_count--;
iput (inode);
if (bh)
brelse (bh);
return error;
}
 
static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
{
struct buffer_head * bh = NULL;
char * link;
int i, err;
char c;
 
if (!S_ISLNK(inode->i_mode)) {
iput (inode);
return -EINVAL;
}
if (buflen > inode->i_sb->s_blocksize - 1)
buflen = inode->i_sb->s_blocksize - 1;
if (inode->i_blocks) {
bh = ext2_bread (inode, 0, 0, &err);
if (!bh) {
iput (inode);
return 0;
}
link = bh->b_data;
}
else
link = (char *) inode->u.ext2_i.i_data;
i = 0;
while (i < buflen && (c = link[i])) {
i++;
put_user (c, buffer++);
}
UPDATE_ATIME(inode);
iput (inode);
if (bh)
brelse (bh);
return i;
}
/CHANGES
0,0 → 1,157
Changes from version 0.5a to version 0.5b
=========================================
- Now that we have sysctl(), the immutable flag cannot be changed when
the system is running at security level > 0.
- Some cleanups in the code.
- More consistency checks on directories.
- The ext2.diff patch from Tom May <ftom@netcom.com> has been
integrated. This patch replaces expensive "/" and "%" with
cheap ">>" and "&" where possible.
 
Changes from version 0.5 to version 0.5a
========================================
- Zero the partial block following the end of the file when a file
is truncated.
- Dates updated in the copyright.
- More checks when the filesystem is mounted: the count of blocks,
fragments, and inodes per group is checked against the block size.
- The buffers used by the error routines are now static variables, to
avoid using space on the kernel stack, as requested by Linus.
- Some cleanups in the error messages (some versions of syslog contain
a bug which truncates an error message if it contains '\n').
- Check that no data can be written to a file past the 2GB limit.
- The famous readdir() bug has been fixed by Stephen Tweedie.
- Added a revision level in the superblock.
- Full support for O_SYNC flag of the open system call.
- New mount options: `resuid=#uid' and `resgid=#gid'. `resuid' causes
ext2fs to consider user #uid like root for the reserved blocks.
`resgid' acts the same way with group #gid. New fields in the
superblock contain default values for resuid and resgid and can
be modified by tune2fs.
Idea comes from Rene Cougnenc <cougnenc@renux.frmug.fr.net>.
- New mount options: `bsddf' and `minixdf'. `bsddf' causes ext2fs
to remove the blocks used for FS structures from the total block
count in statfs. With `minixdf', ext2fs mimics Minix behavior
in statfs (i.e. it returns the total number of blocks on the
partition). This is intended to make bde happy :-)
- New file attributes:
- Immutable files cannot be modified. Data cannot be written to
these files. They cannot be removed, renamed and new links cannot
be created. Even root cannot modify the files. He has to remove
the immutable attribute first.
- Append-only files: can only be written in append-mode when writing.
They cannot be removed, renamed and new links cannot be created.
Note: files may only be added to an append-only directory.
- No-dump files: the attribute is not used by the kernel. My port
of dump uses it to avoid backing up files which are not important.
- New check in ext2_check_dir_entry: the inode number is checked.
- Support for big file systems: the copy of the FS descriptor is now
dynamically allocated (previous versions used a fixed size array).
This allows to mount 2GB+ FS.
- Reorganization of the ext2_inode structure to allow other operating
systems to create specific fields if they use ext2fs as their native
file system. Currently, ext2fs is only implemented in Linux but
will soon be part of Gnu Hurd and of Masix.
 
Changes from version 0.4b to version 0.5
========================================
- New superblock fields: s_lastcheck and s_checkinterval added
by Uwe Ohse <uwe@tirka.gun.de> to implement timedependent checks
of the file system
- Real random numbers for secure rm added by Pierre del Perugia
<delperug@gla.ecoledoc.ibp.fr>
- The mount warnings related to the state of a fs are not printed
if the fs is mounted read-only, idea by Nick Holloway
<alfie@dcs.warwick.ac.uk>
 
Changes from version 0.4a to version 0.4b
=========================================
- Copyrights changed to include the name of my laboratory.
- Clean up of balloc.c and ialloc.c.
- More consistency checks.
- Block preallocation added by Stephen Tweedie.
- Direct reads of directories disallowed.
- Readahead implemented in readdir by Stephen Tweedie.
- Bugs in block and inodes allocation fixed.
- Readahead implemented in ext2_find_entry by Chip Salzenberg.
- New mount options:
`check=none|normal|strict'
`debug'
`errors=continue|remount-ro|panic'
`grpid', `bsdgroups'
`nocheck'
`nogrpid', `sysvgroups'
- truncate() now tries to deallocate contiguous blocks in a single call
to ext2_free_blocks().
- lots of cosmetic changes.
 
Changes from version 0.4 to version 0.4a
========================================
- the `sync' option support is now complete. Version 0.4 was not
supporting it when truncating a file. I have tested the synchronous
writes and they work but they make the system very slow :-( I have
to work again on this to make it faster.
- when detecting an error on a mounted filesystem, version 0.4 used
to try to write a flag in the super block even if the filesystem had
been mounted read-only. This is fixed.
- the `sb=#' option now causes the kernel code to use the filesystem
descriptors located at block #+1. Version 0.4 used the superblock
backup located at block # but used the main copy of the descriptors.
- a new file attribute `S' is supported. This attribute causes
synchronous writes but is applied to a file not to the entire file
system (thanks to Michael Kraehe <kraehe@bakunin.north.de> for
suggesting it).
- the directory cache is inhibited by default. The cache management
code seems to be buggy and I have to look at it carefully before
using it again.
- deleting a file with the `s' attribute (secure deletion) causes its
blocks to be overwritten with random values not with zeros (thanks to
Michael A. Griffith <grif@cs.ucr.edu> for suggesting it).
- lots of cosmetic changes have been made.
 
Changes from version 0.3 to version 0.4
=======================================
- Three new mount options are supported: `check', `sync' and `sb=#'.
`check' tells the kernel code to make more consistency checks
when the file system is mounted. Currently, the kernel code checks
that the blocks and inodes bitmaps are consistent with the free
blocks and inodes counts. More checks will be added in future
releases.
`sync' tells the kernel code to use synchronous writes when updating
an inode, a bitmap, a directory entry or an indirect block. This
can make the file system much slower but can be a big win for files
recovery in case of a crash (and we can now say to the BSD folks
that Linux also supports synchronous updates :-).
`sb=#' tells the kernel code to use an alternate super block instead
of its master copy. `#' is the number of the block (counted in
1024 bytes blocks) which contains the alternate super block.
An ext2 file system typically contains backups of the super block
at blocks 8193, 16385, and so on.
- I have change the meaning of the valid flag used by e2fsck. it
now contains the state of the file system. If the kernel code
detects an inconsistency while the file system is mounted, it flags
it as erroneous and e2fsck will detect that on next run.
- The super block now contains a mount counter. This counter is
incremented each time the file system is mounted read/write. When
this counter becomes bigger than a maximal mount counts (also stored
in the super block), e2fsck checks the file system, even if it had
been unmounted cleanly, and resets this counter to 0.
- File attributes are now supported. One can associate a set of
attributes to a file. Three attributes are defined:
`c': the file is marked for automatic compression,
`s': the file is marked for secure deletion: when the file is
deleted, its blocks are zeroed and written back to the disk,
`u': the file is marked for undeletion: when the file is deleted,
its contents are saved to allow a future undeletion.
Currently, only the `s' attribute is implemented in the kernel
code. Support for the other attributes will be added in a future
release.
- a few bugs related to times updates have been fixed by Bruce
Evans and me.
- a bug related to the links count of deleted inodes has been fixed.
Previous versions used to keep the links count set to 1 when a file
was deleted. The new version now sets links_count to 0 when deleting
the last link.
- a race condition when deallocating an inode has been fixed by
Stephen Tweedie.
 
/namei.c
0,0 → 1,1147
/*
* linux/fs/ext2/namei.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* from
*
* linux/fs/minix/namei.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*
*/
 
#include <asm/segment.h>
 
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/locks.h>
 
/*
* define how far ahead to read directories while searching them.
*/
#define NAMEI_RA_CHUNKS 2
#define NAMEI_RA_BLOCKS 4
#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b))
 
/*
* NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
*/
static int ext2_match (int len, const char * const name,
struct ext2_dir_entry * de)
{
if (!de || !swab32(de->inode) || len > EXT2_NAME_LEN)
return 0;
/*
* "" means "." ---> so paths like "/usr/lib//libc.a" work
*/
if (!len && swab16(de->name_len) == 1 && (de->name[0] == '.') &&
(de->name[1] == '\0'))
return 1;
if (len != swab16(de->name_len))
return 0;
return !memcmp(name, de->name, len);
}
 
/*
* ext2_find_entry()
*
* finds an entry in the specified directory with the wanted name. It
* returns the cache buffer in which the entry was found, and the entry
* itself (as a parameter - res_dir). It does NOT read the inode of the
* entry - you'll have to do that yourself if you want to.
*/
static struct buffer_head * ext2_find_entry (struct inode * dir,
const char * const name, int namelen,
struct ext2_dir_entry ** res_dir)
{
struct super_block * sb;
struct buffer_head * bh_use[NAMEI_RA_SIZE];
struct buffer_head * bh_read[NAMEI_RA_SIZE];
unsigned long offset;
int block, toread, i, err;
 
*res_dir = NULL;
if (!dir)
return NULL;
sb = dir->i_sb;
 
if (namelen > EXT2_NAME_LEN)
return NULL;
 
memset (bh_use, 0, sizeof (bh_use));
toread = 0;
for (block = 0; block < NAMEI_RA_SIZE; ++block) {
struct buffer_head * bh;
 
if ((block << EXT2_BLOCK_SIZE_BITS (sb)) >= dir->i_size)
break;
bh = ext2_getblk (dir, block, 0, &err);
bh_use[block] = bh;
if (bh && !buffer_uptodate(bh))
bh_read[toread++] = bh;
}
 
for (block = 0, offset = 0; offset < dir->i_size; block++) {
struct buffer_head * bh;
struct ext2_dir_entry * de;
char * dlimit;
 
if ((block % NAMEI_RA_BLOCKS) == 0 && toread) {
ll_rw_block (READ, toread, bh_read);
toread = 0;
}
bh = bh_use[block % NAMEI_RA_SIZE];
 
if (!bh) {
ext2_error (sb, "ext2_find_entry",
"directory #%lu contains a hole at offset %lu",
dir->i_ino, offset);
offset += sb->s_blocksize;
continue;
}
wait_on_buffer (bh);
if (!buffer_uptodate(bh)) {
/*
* read error: all bets are off
*/
break;
}
 
de = (struct ext2_dir_entry *) bh->b_data;
dlimit = bh->b_data + sb->s_blocksize;
while ((char *) de < dlimit) {
if (!ext2_check_dir_entry ("ext2_find_entry", dir,
de, bh, offset))
goto failure;
if (swab32(de->inode) != 0 && ext2_match (namelen, name, de)) {
for (i = 0; i < NAMEI_RA_SIZE; ++i) {
if (bh_use[i] != bh)
brelse (bh_use[i]);
}
*res_dir = de;
return bh;
}
offset += swab16(de->rec_len);
de = (struct ext2_dir_entry *)
((char *) de + swab16(de->rec_len));
}
 
brelse (bh);
if (((block + NAMEI_RA_SIZE) << EXT2_BLOCK_SIZE_BITS (sb)) >=
dir->i_size)
bh = NULL;
else
bh = ext2_getblk (dir, block + NAMEI_RA_SIZE, 0, &err);
bh_use[block % NAMEI_RA_SIZE] = bh;
if (bh && !buffer_uptodate(bh))
bh_read[toread++] = bh;
}
 
failure:
for (i = 0; i < NAMEI_RA_SIZE; ++i)
brelse (bh_use[i]);
return NULL;
}
 
int ext2_lookup (struct inode * dir, const char * name, int len,
struct inode ** result)
{
unsigned long ino;
struct ext2_dir_entry * de;
struct buffer_head * bh;
 
*result = NULL;
if (!dir)
return -ENOENT;
if (!S_ISDIR(dir->i_mode)) {
iput (dir);
return -ENOTDIR;
}
if (len > EXT2_NAME_LEN) {
iput (dir);
return -ENAMETOOLONG;
}
if (dcache_lookup(dir, name, len, &ino)) {
if (!ino) {
iput(dir);
return -ENOENT;
}
if (!(*result = iget (dir->i_sb, ino))) {
iput (dir);
return -EACCES;
}
iput (dir);
return 0;
}
ino = dir->i_version;
if (!(bh = ext2_find_entry (dir, name, len, &de))) {
if (ino == dir->i_version)
dcache_add(dir, name, len, 0);
iput (dir);
return -ENOENT;
}
ino = swab32(de->inode);
dcache_add(dir, name, len, ino);
brelse (bh);
if (!(*result = iget (dir->i_sb, ino))) {
iput (dir);
return -EACCES;
}
iput (dir);
return 0;
}
 
/*
* ext2_add_entry()
*
* adds a file entry to the specified directory, using the same
* semantics as ext2_find_entry(). It returns NULL if it failed.
*
* NOTE!! The inode part of 'de' is left at 0 - which means you
* may not sleep between calling this and putting something into
* the entry, as someone else might have used it while you slept.
*/
static struct buffer_head * ext2_add_entry (struct inode * dir,
const char * name, int namelen,
struct ext2_dir_entry ** res_dir,
int *err)
{
unsigned long offset;
unsigned short rec_len;
struct buffer_head * bh;
struct ext2_dir_entry * de, * de1;
struct super_block * sb;
 
*err = -EINVAL;
*res_dir = NULL;
if (!dir)
return NULL;
sb = dir->i_sb;
 
if (namelen > EXT2_NAME_LEN)
{
*err = -ENAMETOOLONG;
return NULL;
}
 
if (!namelen)
return NULL;
/*
* Is this a busy deleted directory? Can't create new files if so
*/
if (dir->i_size == 0)
{
*err = -ENOENT;
return NULL;
}
bh = ext2_bread (dir, 0, 0, err);
if (!bh)
return NULL;
rec_len = EXT2_DIR_REC_LEN(namelen);
offset = 0;
de = (struct ext2_dir_entry *) bh->b_data;
*err = -ENOSPC;
while (1) {
if ((char *)de >= sb->s_blocksize + bh->b_data) {
brelse (bh);
bh = NULL;
bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, err);
if (!bh)
return NULL;
if (dir->i_size <= offset) {
if (dir->i_size == 0) {
*err = -ENOENT;
return NULL;
}
 
ext2_debug ("creating next block\n");
 
de = (struct ext2_dir_entry *) bh->b_data;
de->inode = swab32(0);
de->rec_len = swab16(sb->s_blocksize);
dir->i_size = offset + sb->s_blocksize;
dir->i_dirt = 1;
} else {
 
ext2_debug ("skipping to next block\n");
 
de = (struct ext2_dir_entry *) bh->b_data;
}
}
if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh,
offset)) {
*err = -ENOENT;
brelse (bh);
return NULL;
}
if (swab32(de->inode) != 0 && ext2_match (namelen, name, de)) {
*err = -EEXIST;
brelse (bh);
return NULL;
}
if ((swab32(de->inode) == 0 && swab16(de->rec_len) >= rec_len) ||
(swab16(de->rec_len) >= EXT2_DIR_REC_LEN(swab16(de->name_len)) + rec_len)) {
offset += swab16(de->rec_len);
if (swab32(de->inode)) {
de1 = (struct ext2_dir_entry *) ((char *) de +
EXT2_DIR_REC_LEN(swab16(de->name_len)));
de1->rec_len = swab16(swab16(de->rec_len) -
EXT2_DIR_REC_LEN(swab16(de->name_len)));
de->rec_len = swab16(EXT2_DIR_REC_LEN(swab16(de->name_len)));
de = de1;
}
de->inode = swab32(0);
de->name_len = swab16(namelen);
memcpy (de->name, name, namelen);
/*
* XXX shouldn't update any times until successful
* completion of syscall, but too many callers depend
* on this.
*
* XXX similarly, too many callers depend on
* ext2_new_inode() setting the times, but error
* recovery deletes the inode, so the worst that can
* happen is that the times are slightly out of date
* and/or different from the directory change time.
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
dir->i_dirt = 1;
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
*res_dir = de;
*err = 0;
return bh;
}
offset += swab16(de->rec_len);
de = (struct ext2_dir_entry *) ((char *) de + swab16(de->rec_len));
}
brelse (bh);
return NULL;
}
 
/*
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry
*/
static int ext2_delete_entry (struct ext2_dir_entry * dir,
struct buffer_head * bh)
{
struct ext2_dir_entry * de, * pde;
int i;
 
i = 0;
pde = NULL;
de = (struct ext2_dir_entry *) bh->b_data;
while (i < bh->b_size) {
if (!ext2_check_dir_entry ("ext2_delete_entry", NULL,
de, bh, i))
return -EIO;
if (de == dir) {
if (pde)
pde->rec_len =
swab16(swab16(pde->rec_len) +
swab16(dir->rec_len));
dir->inode = swab32(0);
return 0;
}
i += swab16(de->rec_len);
pde = de;
de = (struct ext2_dir_entry *) ((char *) de + swab16(de->rec_len));
}
return -ENOENT;
}
 
int ext2_create (struct inode * dir,const char * name, int len, int mode,
struct inode ** result)
{
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
int err;
 
*result = NULL;
if (!dir)
return -ENOENT;
inode = ext2_new_inode (dir, mode, &err);
if (!inode) {
iput (dir);
return err;
}
inode->i_op = &ext2_file_inode_operations;
inode->i_mode = mode;
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
return err;
}
de->inode = swab32(inode->i_ino);
dir->i_version = ++event;
dcache_add(dir, de->name, swab16(de->name_len), swab32(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
brelse (bh);
iput (dir);
*result = inode;
return 0;
}
 
int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
int rdev)
{
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
int err;
 
if (!dir)
return -ENOENT;
 
if (len > EXT2_NAME_LEN) {
iput (dir);
return -ENAMETOOLONG;
}
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
return -EEXIST;
}
inode = ext2_new_inode (dir, mode, &err);
if (!inode) {
iput (dir);
return err;
}
inode->i_uid = current->fsuid;
inode->i_mode = mode;
inode->i_op = NULL;
if (S_ISREG(inode->i_mode))
inode->i_op = &ext2_file_inode_operations;
else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &ext2_dir_inode_operations;
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
}
else if (S_ISLNK(inode->i_mode))
inode->i_op = &ext2_symlink_inode_operations;
else if (S_ISCHR(inode->i_mode))
inode->i_op = &chrdev_inode_operations;
else if (S_ISBLK(inode->i_mode))
inode->i_op = &blkdev_inode_operations;
else if (S_ISFIFO(inode->i_mode))
init_fifo(inode);
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = to_kdev_t(rdev);
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
return err;
}
de->inode = swab32(inode->i_ino);
dir->i_version = ++event;
dcache_add(dir, de->name, swab16(de->name_len), swab32(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
brelse (bh);
iput (dir);
iput (inode);
return 0;
}
 
int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
{
struct inode * inode;
struct buffer_head * bh, * dir_block;
struct ext2_dir_entry * de;
int err;
 
if (!dir)
return -ENOENT;
if (len > EXT2_NAME_LEN) {
iput (dir);
return -ENAMETOOLONG;
}
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
return -EEXIST;
}
if (dir->i_nlink >= EXT2_LINK_MAX) {
iput (dir);
return -EMLINK;
}
inode = ext2_new_inode (dir, S_IFDIR, &err);
if (!inode) {
iput (dir);
return err;
}
inode->i_op = &ext2_dir_inode_operations;
inode->i_size = inode->i_sb->s_blocksize;
dir_block = ext2_bread (inode, 0, 1, &err);
if (!dir_block) {
iput (dir);
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
return err;
}
inode->i_blocks = inode->i_sb->s_blocksize / 512;
de = (struct ext2_dir_entry *) dir_block->b_data;
de->inode = swab32(inode->i_ino);
de->name_len = swab16(1);
de->rec_len = swab16(EXT2_DIR_REC_LEN(swab16(de->name_len)));
strcpy (de->name, ".");
de = (struct ext2_dir_entry *) ((char *) de + swab16(de->rec_len));
de->inode = swab32(dir->i_ino);
de->rec_len = swab16(inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1));
de->name_len = swab16(2);
strcpy (de->name, "..");
inode->i_nlink = 2;
mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
inode->i_dirt = 1;
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
iput (dir);
inode->i_nlink = 0;
inode->i_dirt = 1;
iput (inode);
return err;
}
de->inode = swab32(inode->i_ino);
dir->i_version = ++event;
dcache_add(dir, de->name, swab16(de->name_len), swab32(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
dir->i_nlink++;
dir->i_dirt = 1;
iput (dir);
iput (inode);
brelse (bh);
return 0;
}
 
/*
* routine to check that the specified directory is empty (for rmdir)
*/
static int empty_dir (struct inode * inode)
{
unsigned long offset;
struct buffer_head * bh;
struct ext2_dir_entry * de, * de1;
struct super_block * sb;
int err;
 
sb = inode->i_sb;
if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
!(bh = ext2_bread (inode, 0, 0, &err))) {
ext2_warning (inode->i_sb, "empty_dir",
"bad directory (dir #%lu) - no data block",
inode->i_ino);
return 1;
}
de = (struct ext2_dir_entry *) bh->b_data;
de1 = (struct ext2_dir_entry *) ((char *) de + swab16(de->rec_len));
if (swab32(de->inode) != inode->i_ino || !swab32(de1->inode) ||
strcmp (".", de->name) || strcmp ("..", de1->name)) {
ext2_warning (inode->i_sb, "empty_dir",
"bad directory (dir #%lu) - no `.' or `..'",
inode->i_ino);
brelse (bh);
return 1;
}
offset = swab16(de->rec_len) + swab16(de1->rec_len);
de = (struct ext2_dir_entry *) ((char *) de1 + swab16(de1->rec_len));
while (offset < inode->i_size ) {
if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
brelse (bh);
bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err);
if (!bh) {
ext2_error (sb, "empty_dir",
"directory #%lu contains a hole at offset %lu",
inode->i_ino, offset);
offset += sb->s_blocksize;
continue;
}
de = (struct ext2_dir_entry *) bh->b_data;
}
if (!ext2_check_dir_entry ("empty_dir", inode, de, bh,
offset)) {
brelse (bh);
return 1;
}
if (swab32(de->inode)) {
brelse (bh);
return 0;
}
offset += swab16(de->rec_len);
de = (struct ext2_dir_entry *) ((char *) de + swab16(de->rec_len));
}
brelse (bh);
return 1;
}
 
int ext2_rmdir (struct inode * dir, const char * name, int len)
{
int retval;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
 
repeat:
if (!dir)
return -ENOENT;
inode = NULL;
if (len > EXT2_NAME_LEN) {
iput (dir);
return -ENAMETOOLONG;
}
bh = ext2_find_entry (dir, name, len, &de);
retval = -ENOENT;
if (!bh)
goto end_rmdir;
retval = -EPERM;
if (!(inode = iget (dir->i_sb, swab32(de->inode))))
goto end_rmdir;
if (inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize (inode, -1);
if (inode->i_dev != dir->i_dev) {
retval = -EBUSY;
goto end_rmdir;
}
if (swab32(de->inode) != inode->i_ino) {
iput(inode);
brelse(bh);
current->counter = 0;
schedule();
goto repeat;
}
if ((dir->i_mode & S_ISVTX) && !fsuser() &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid)
goto end_rmdir;
if (inode == dir) /* we may not delete ".", but "../dir" is ok */
goto end_rmdir;
if (!S_ISDIR(inode->i_mode)) {
retval = -ENOTDIR;
goto end_rmdir;
}
down(&inode->i_sem);
if (!empty_dir (inode))
retval = -ENOTEMPTY;
else if (swab32(de->inode) != inode->i_ino)
retval = -ENOENT;
else {
if (inode->i_count > 1) {
/*
* Are we deleting the last instance of a busy directory?
* Better clean up if so.
*
* Make directory empty (it will be truncated when finally
* dereferenced). This also inhibits ext2_add_entry.
*/
inode->i_size = 0;
}
retval = ext2_delete_entry (de, bh);
dir->i_version = ++event;
}
up(&inode->i_sem);
if (retval)
goto end_rmdir;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
if (inode->i_nlink != 2)
ext2_warning (inode->i_sb, "ext2_rmdir",
"empty directory has nlink!=2 (%d)",
inode->i_nlink);
inode->i_version = ++event;
inode->i_nlink = 0;
inode->i_dirt = 1;
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
end_rmdir:
iput (dir);
iput (inode);
brelse (bh);
return retval;
}
 
int ext2_unlink (struct inode * dir, const char * name, int len)
{
int retval;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
 
repeat:
if (!dir)
return -ENOENT;
retval = -ENOENT;
inode = NULL;
if (len > EXT2_NAME_LEN) {
iput (dir);
return -ENAMETOOLONG;
}
bh = ext2_find_entry (dir, name, len, &de);
if (!bh)
goto end_unlink;
if (!(inode = iget (dir->i_sb, swab32(de->inode))))
goto end_unlink;
if (inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize (inode, -1);
retval = -EPERM;
if (S_ISDIR(inode->i_mode))
goto end_unlink;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
goto end_unlink;
if (swab32(de->inode) != inode->i_ino) {
iput(inode);
brelse(bh);
current->counter = 0;
schedule();
goto repeat;
}
if ((dir->i_mode & S_ISVTX) && !fsuser() &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid)
goto end_unlink;
if (!inode->i_nlink) {
ext2_warning (inode->i_sb, "ext2_unlink",
"Deleting nonexistent file (%lu), %d",
inode->i_ino, inode->i_nlink);
inode->i_nlink = 1;
}
retval = ext2_delete_entry (de, bh);
if (retval)
goto end_unlink;
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
inode->i_nlink--;
inode->i_dirt = 1;
inode->i_ctime = dir->i_ctime;
retval = 0;
end_unlink:
brelse (bh);
iput (inode);
iput (dir);
return retval;
}
 
int ext2_symlink (struct inode * dir, const char * name, int len,
const char * symname)
{
struct ext2_dir_entry * de;
struct inode * inode = NULL;
struct buffer_head * bh = NULL, * name_block = NULL;
char * link;
int i, err;
int l;
char c;
 
if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
iput (dir);
return err;
}
inode->i_mode = S_IFLNK | S_IRWXUGO;
inode->i_op = &ext2_symlink_inode_operations;
for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
symname [l]; l++)
;
if (l >= sizeof (inode->u.ext2_i.i_data)) {
 
ext2_debug ("l=%d, normal symlink\n", l);
 
name_block = ext2_bread (inode, 0, 1, &err);
if (!name_block) {
iput (dir);
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
return err;
}
link = name_block->b_data;
} else {
link = (char *) inode->u.ext2_i.i_data;
 
ext2_debug ("l=%d, fast symlink\n", l);
 
}
i = 0;
while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
link[i++] = c;
link[i] = 0;
if (name_block) {
mark_buffer_dirty(name_block, 1);
brelse (name_block);
}
inode->i_size = i;
inode->i_dirt = 1;
 
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
brelse (bh);
iput (dir);
return -EEXIST;
}
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
iput (dir);
return err;
}
de->inode = swab32(inode->i_ino);
dir->i_version = ++event;
dcache_add(dir, de->name, swab16(de->name_len), swab32(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
brelse (bh);
iput (dir);
iput (inode);
return 0;
}
 
int ext2_link (struct inode * oldinode, struct inode * dir,
const char * name, int len)
{
struct ext2_dir_entry * de;
struct buffer_head * bh;
int err;
 
if (S_ISDIR(oldinode->i_mode)) {
iput (oldinode);
iput (dir);
return -EPERM;
}
if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) {
iput (oldinode);
iput (dir);
return -EPERM;
}
if (oldinode->i_nlink >= EXT2_LINK_MAX) {
iput (oldinode);
iput (dir);
return -EMLINK;
}
bh = ext2_find_entry (dir, name, len, &de);
if (bh) {
brelse (bh);
iput (dir);
iput (oldinode);
return -EEXIST;
}
bh = ext2_add_entry (dir, name, len, &de, &err);
if (!bh) {
iput (dir);
iput (oldinode);
return err;
}
de->inode = swab32(oldinode->i_ino);
dir->i_version = ++event;
dcache_add(dir, de->name, swab16(de->name_len), swab32(de->inode));
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
brelse (bh);
iput (dir);
oldinode->i_nlink++;
oldinode->i_ctime = CURRENT_TIME;
oldinode->i_dirt = 1;
iput (oldinode);
return 0;
}
 
static int subdir (struct inode * new_inode, struct inode * old_inode)
{
int ino;
int result;
 
new_inode->i_count++;
result = 0;
for (;;) {
if (new_inode == old_inode) {
result = 1;
break;
}
if (new_inode->i_dev != old_inode->i_dev)
break;
ino = new_inode->i_ino;
if (ext2_lookup (new_inode, "..", 2, &new_inode))
break;
if (new_inode->i_ino == ino)
break;
}
iput (new_inode);
return result;
}
 
#define PARENT_INO(buffer) \
((struct ext2_dir_entry *) ((char *) buffer + \
swab16(((struct ext2_dir_entry *) buffer)->rec_len)))->inode
 
#define PARENT_NAME(buffer) \
((struct ext2_dir_entry *) ((char *) buffer + \
swab16(((struct ext2_dir_entry *) buffer)->rec_len)))->name
 
/*
* rename uses retrying to avoid race-conditions: at least they should be
* minimal.
* it tries to allocate all the blocks, then sanity-checks, and if the sanity-
* checks fail, it tries to restart itself again. Very practical - no changes
* are done until we know everything works ok.. and then all the changes can be
* done in one fell swoop when we have claimed all the buffers needed.
*
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
*/
static int do_ext2_rename (struct inode * old_dir, const char * old_name,
int old_len, struct inode * new_dir,
const char * new_name, int new_len,
int must_be_dir)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
struct ext2_dir_entry * old_de, * new_de;
int retval;
 
goto start_up;
try_again:
if (new_bh && new_de) {
ext2_delete_entry(new_de, new_bh);
new_dir->i_version = ++event;
}
brelse (old_bh);
brelse (new_bh);
brelse (dir_bh);
iput (old_inode);
iput (new_inode);
current->counter = 0;
schedule ();
start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
new_de = NULL;
retval = -ENAMETOOLONG;
if (old_len > EXT2_NAME_LEN)
goto end_rename;
 
old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de);
retval = -ENOENT;
if (!old_bh)
goto end_rename;
old_inode = __iget (old_dir->i_sb, swab32(old_de->inode), 0); /* don't cross mnt-points */
if (!old_inode)
goto end_rename;
if (must_be_dir && !S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
current->fsuid != old_dir->i_uid && !fsuser())
goto end_rename;
if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode))
goto end_rename;
new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de);
if (new_bh) {
new_inode = __iget (new_dir->i_sb, swab32(new_de->inode), 0); /* no mntp cross */
if (!new_inode) {
brelse (new_bh);
new_bh = NULL;
} else {
if (new_inode->i_sb->dq_op)
new_inode->i_sb->dq_op->initialize (new_inode, -1);
}
}
if (new_inode == old_inode) {
retval = 0;
goto end_rename;
}
if (new_inode && S_ISDIR(new_inode->i_mode)) {
retval = -EISDIR;
if (!S_ISDIR(old_inode->i_mode))
goto end_rename;
retval = -EINVAL;
if (subdir (new_dir, old_inode))
goto end_rename;
retval = -ENOTEMPTY;
if (!empty_dir (new_inode))
goto end_rename;
retval = -EBUSY;
if (new_inode->i_count > 1)
goto end_rename;
}
retval = -EPERM;
if (new_inode) {
if ((new_dir->i_mode & S_ISVTX) &&
current->fsuid != new_inode->i_uid &&
current->fsuid != new_dir->i_uid && !fsuser())
goto end_rename;
if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
goto end_rename;
}
if (S_ISDIR(old_inode->i_mode)) {
retval = -ENOTDIR;
if (new_inode && !S_ISDIR(new_inode->i_mode))
goto end_rename;
retval = -EINVAL;
if (subdir (new_dir, old_inode))
goto end_rename;
dir_bh = ext2_bread (old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
if (swab32(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX)
goto end_rename;
}
if (!new_bh)
new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de,
&retval);
if (!new_bh)
goto end_rename;
new_dir->i_version = ++event;
/*
* sanity checking before doing the rename - avoid races
*/
if (new_inode && (swab32(new_de->inode) != new_inode->i_ino))
goto try_again;
if (swab32(new_de->inode) && !new_inode)
goto try_again;
if (swab32(old_de->inode) != old_inode->i_ino)
goto try_again;
/*
* ok, that's it
*/
new_de->inode = swab32(old_inode->i_ino);
dcache_add(new_dir, new_de->name, swab16(new_de->name_len), swab32(new_de->inode));
retval = ext2_delete_entry (old_de, old_bh);
if (retval == -ENOENT)
goto try_again;
if (retval)
goto end_rename;
old_dir->i_version = ++event;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
new_inode->i_dirt = 1;
}
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
old_dir->i_dirt = 1;
if (dir_bh) {
PARENT_INO(dir_bh->b_data) = swab32(new_dir->i_ino);
dcache_add(old_inode, "..", 2, new_dir->i_ino);
mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
old_dir->i_dirt = 1;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_dirt = 1;
} else {
new_dir->i_nlink++;
new_dir->i_dirt = 1;
}
}
mark_buffer_dirty(old_bh, 1);
if (IS_SYNC(old_dir)) {
ll_rw_block (WRITE, 1, &old_bh);
wait_on_buffer (old_bh);
}
mark_buffer_dirty(new_bh, 1);
if (IS_SYNC(new_dir)) {
ll_rw_block (WRITE, 1, &new_bh);
wait_on_buffer (new_bh);
}
retval = 0;
end_rename:
brelse (dir_bh);
brelse (old_bh);
brelse (new_bh);
iput (old_inode);
iput (new_inode);
iput (old_dir);
iput (new_dir);
return retval;
}
 
/*
* Ok, rename also locks out other renames, as they can change the parent of
* a directory, and we don't want any races. Other races are checked for by
* "do_rename()", which restarts if there are inconsistencies.
*
* Note that there is no race between different filesystems: it's only within
* the same device that races occur: many renames can happen at once, as long
* as they are on different partitions.
*
* In the second extended file system, we use a lock flag stored in the memory
* super-block. This way, we really lock other renames only if they occur
* on the same file system
*/
int ext2_rename (struct inode * old_dir, const char * old_name, int old_len,
struct inode * new_dir, const char * new_name, int new_len,
int must_be_dir)
{
int result;
 
while (old_dir->i_sb->u.ext2_sb.s_rename_lock)
sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 1;
result = do_ext2_rename (old_dir, old_name, old_len, new_dir,
new_name, new_len, must_be_dir);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 0;
wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
return result;
}
/.depend
0,0 → 1,114
acl.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h
balloc.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/bitops.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/byteorder.h
bitmap.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h
dir.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h
file.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h
fsync.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h
ialloc.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/bitops.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/byteorder.h
inode.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h
ioctl.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ioctl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h
namei.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h
super.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/bitops.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h
symlink.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h
truncate.o: \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ext2_fs.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h
/Makefile
0,0 → 1,15
#
# Makefile for the linux ext2-filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
 
O_TARGET := ext2.o
O_OBJS := acl.o balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o truncate.o
M_OBJS := $(O_TARGET)
 
include $(TOPDIR)/Rules.make
/balloc.c
0,0 → 1,723
/*
* linux/fs/ext2/balloc.c
*
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
 
/*
* balloc.c contains the blocks allocation and deallocation routines
*/
 
/*
* The free blocks are managed by bitmaps. A file system contains several
* blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
* block for inodes, N blocks for the inode table and data blocks.
*
* The file system contains group descriptors which are located after the
* super block. Each descriptor contains the number of the bitmap block and
* the free blocks count in the block. The descriptors are loaded in memory
* when a file system is mounted (see ext2_read_super).
*/
 
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/locks.h>
 
#include <asm/bitops.h>
#include <asm/byteorder.h>
 
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
 
static struct ext2_group_desc * get_group_desc (struct super_block * sb,
unsigned int block_group,
struct buffer_head ** bh)
{
unsigned long group_desc;
unsigned long desc;
struct ext2_group_desc * gdp;
 
/*
* This panic should never trigger on a bad filesystem: the caller
* should have verified the block/group number already.
*/
if (block_group >= sb->u.ext2_sb.s_groups_count)
ext2_panic (sb, "get_group_desc",
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
block_group, sb->u.ext2_sb.s_groups_count);
 
group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
desc = block_group % EXT2_DESC_PER_BLOCK(sb);
if (!sb->u.ext2_sb.s_group_desc[group_desc])
ext2_panic (sb, "get_group_desc",
"Group descriptor not loaded - "
"block_group = %d, group_desc = %lu, desc = %lu",
block_group, group_desc, desc);
gdp = (struct ext2_group_desc *)
sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
if (bh)
*bh = sb->u.ext2_sb.s_group_desc[group_desc];
return gdp + desc;
}
 
/*
* Read the bitmap for a given block_group, reading into the specified
* slot in the superblock's bitmap cache.
*
* Return >=0 on success or a -ve error code.
*/
 
static int read_block_bitmap (struct super_block * sb,
unsigned int block_group,
unsigned long bitmap_nr)
{
struct ext2_group_desc * gdp;
struct buffer_head * bh;
int retval = 0;
gdp = get_group_desc (sb, block_group, NULL);
bh = bread (sb->s_dev, swab32(gdp->bg_block_bitmap), sb->s_blocksize);
if (!bh) {
ext2_error (sb, "read_block_bitmap",
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %lu",
block_group, (unsigned long) swab32(gdp->bg_block_bitmap));
retval = -EIO;
}
/*
* On IO error, just leave a zero in the superblock's block pointer for
* this group. The IO will be retried next time.
*/
sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
return retval;
}
 
/*
* load_block_bitmap loads the block bitmap for a blocks group
*
* It maintains a cache for the last bitmaps loaded. This cache is managed
* with a LRU algorithm.
*
* Notes:
* 1/ There is one cache per mounted file system.
* 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
* this function reads the bitmap without maintaining a LRU cache.
*
* Return the slot used to store the bitmap, or a -ve error code.
*/
static int load__block_bitmap (struct super_block * sb,
unsigned int block_group)
{
int i, j, retval = 0;
unsigned long block_bitmap_number;
struct buffer_head * block_bitmap;
 
if (block_group >= sb->u.ext2_sb.s_groups_count)
ext2_panic (sb, "load_block_bitmap",
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
block_group, sb->u.ext2_sb.s_groups_count);
 
if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
if (sb->u.ext2_sb.s_block_bitmap_number[block_group] !=
block_group)
ext2_panic (sb, "load_block_bitmap",
"block_group != block_bitmap_number");
else
return block_group;
} else {
retval = read_block_bitmap (sb, block_group, block_group);
if (retval < 0)
return retval;
return block_group;
}
}
 
for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
sb->u.ext2_sb.s_block_bitmap_number[i] != block_group; i++)
;
if (i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
sb->u.ext2_sb.s_block_bitmap_number[i] == block_group) {
block_bitmap_number = sb->u.ext2_sb.s_block_bitmap_number[i];
block_bitmap = sb->u.ext2_sb.s_block_bitmap[i];
for (j = i; j > 0; j--) {
sb->u.ext2_sb.s_block_bitmap_number[j] =
sb->u.ext2_sb.s_block_bitmap_number[j - 1];
sb->u.ext2_sb.s_block_bitmap[j] =
sb->u.ext2_sb.s_block_bitmap[j - 1];
}
sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
 
/*
* There's still one special case here --- if block_bitmap == 0
* then our last attempt to read the bitmap failed and we have
* just ended up caching that failure. Try again to read it.
*/
if (!block_bitmap)
retval = read_block_bitmap (sb, block_group, 0);
} else {
if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
sb->u.ext2_sb.s_loaded_block_bitmaps++;
else
brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) {
sb->u.ext2_sb.s_block_bitmap_number[j] =
sb->u.ext2_sb.s_block_bitmap_number[j - 1];
sb->u.ext2_sb.s_block_bitmap[j] =
sb->u.ext2_sb.s_block_bitmap[j - 1];
}
retval = read_block_bitmap (sb, block_group, 0);
}
return retval;
}
 
/*
* Load the block bitmap for a given block group. First of all do a couple
* of fast lookups for common cases and then pass the request onto the guts
* of the bitmap loader.
*
* Return the slot number of the group in the superblock bitmap cache's on
* success, or a -ve error code.
*
* There is still one inconsistancy here --- if the number of groups in this
* filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of
* differentiating between a group for which we have never performed a bitmap
* IO request, and a group for which the last bitmap read request failed.
*/
static inline int load_block_bitmap (struct super_block * sb,
unsigned int block_group)
{
int slot;
/*
* Do the lookup for the slot. First of all, check if we're asking
* for the same slot as last time, and did we succeed that last time?
*/
if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
sb->u.ext2_sb.s_block_bitmap[block_group]) {
slot = 0;
}
/*
* Or can we do a fast lookup based on a loaded group on a filesystem
* small enough to be mapped directly into the superblock?
*/
else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED &&
sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
sb->u.ext2_sb.s_block_bitmap[block_group]) {
slot = block_group;
}
/*
* If not, then do a full lookup for this block group.
*/
else {
slot = load__block_bitmap (sb, block_group);
}
 
/*
* <0 means we just got an error
*/
if (slot < 0)
return slot;
/*
* If it's a valid slot, we may still have cached a previous IO error,
* in which case the bh in the superblock cache will be zero.
*/
if (!sb->u.ext2_sb.s_block_bitmap[slot])
return -EIO;
/*
* Must have been read in OK to get this far.
*/
return slot;
}
 
void ext2_free_blocks (const struct inode * inode, unsigned long block,
unsigned long count)
{
struct buffer_head * bh;
struct buffer_head * bh2;
unsigned long block_group;
unsigned long bit;
unsigned long i;
int bitmap_nr;
struct super_block * sb;
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
 
sb = inode->i_sb;
if (!sb) {
printk ("ext2_free_blocks: nonexistent device");
return;
}
lock_super (sb);
es = sb->u.ext2_sb.s_es;
if (block < swab32(es->s_first_data_block) ||
(block + count) > swab32(es->s_blocks_count)) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks not in datazone - "
"block = %lu, count = %lu", block, count);
unlock_super (sb);
return;
}
 
ext2_debug ("freeing block %lu\n", block);
 
block_group = (block - swab32(es->s_first_data_block)) /
EXT2_BLOCKS_PER_GROUP(sb);
bit = (block - swab32(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb);
if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks across group boundary - "
"Block = %lu, count = %lu",
block, count);
unlock_super (sb);
return;
}
bitmap_nr = load_block_bitmap (sb, block_group);
if (bitmap_nr < 0)
goto error_return;
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
gdp = get_group_desc (sb, block_group, &bh2);
 
if (test_opt (sb, CHECK_STRICT) &&
(in_range (swab32(gdp->bg_block_bitmap), block, count) ||
in_range (swab32(gdp->bg_inode_bitmap), block, count) ||
in_range (block, swab32(gdp->bg_inode_table),
sb->u.ext2_sb.s_itb_per_group) ||
in_range (block + count - 1, swab32(gdp->bg_inode_table),
sb->u.ext2_sb.s_itb_per_group))) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks in system zones - "
"Block = %lu, count = %lu",
block, count);
unlock_super (sb);
return;
}
 
for (i = 0; i < count; i++) {
if (!__ext2_clear_bit (bit + i, bh->b_data))
ext2_warning (sb, "ext2_free_blocks",
"bit already cleared for block %lu",
block);
else {
if (sb->dq_op)
sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
gdp->bg_free_blocks_count =
swab16(swab16(gdp->bg_free_blocks_count)+1);
es->s_free_blocks_count =
swab32(swab32(es->s_free_blocks_count)+1);
}
}
mark_buffer_dirty(bh2, 1);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
sb->s_dirt = 1;
error_return:
unlock_super (sb);
return;
}
 
/*
* ext2_new_block uses a goal block to assist allocation. If the goal is
* free, or there is a free block within 32 blocks of the goal, that block
* is allocated. Otherwise a forward search is made for a free block; within
* each block group the search first looks for an entire free byte in the block
* bitmap, and then for any free bit if that fails.
*/
int ext2_new_block (const struct inode * inode, unsigned long goal,
u32 * prealloc_count, u32 * prealloc_block, int * err)
{
struct buffer_head * bh;
struct buffer_head * bh2;
char * p, * r;
int i, j, k, tmp;
int bitmap_nr;
struct super_block * sb;
struct ext2_group_desc * gdp;
struct ext2_super_block * es;
 
*err = -ENOSPC;
#ifdef EXT2FS_DEBUG
static int goal_hits = 0, goal_attempts = 0;
#endif
sb = inode->i_sb;
if (!sb) {
printk ("ext2_new_block: nonexistent device");
return 0;
}
lock_super (sb);
es = sb->u.ext2_sb.s_es;
if (swab32(es->s_free_blocks_count) <= swab32(es->s_r_blocks_count) &&
(!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) &&
(sb->u.ext2_sb.s_resgid == 0 ||
!in_group_p (sb->u.ext2_sb.s_resgid)))) {
unlock_super (sb);
return 0;
}
 
ext2_debug ("goal=%lu.\n", goal);
 
repeat:
/*
* First, test whether the goal block is free.
*/
if (goal < swab32(es->s_first_data_block) || goal >= swab32(es->s_blocks_count))
goal = swab32(es->s_first_data_block);
i = (goal - swab32(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb);
gdp = get_group_desc (sb, i, &bh2);
if (swab16(gdp->bg_free_blocks_count) > 0) {
j = ((goal - swab32(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb));
#ifdef EXT2FS_DEBUG
if (j)
goal_attempts++;
#endif
bitmap_nr = load_block_bitmap (sb, i);
if (bitmap_nr < 0) {
*err = -EIO;
unlock_super (sb);
return 0;
}
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
ext2_debug ("goal is at %d:%d.\n", i, j);
 
if (!__ext2_test_bit(j, bh->b_data)) {
#ifdef EXT2FS_DEBUG
goal_hits++;
ext2_debug ("goal bit allocated.\n");
#endif
goto got_block;
}
if (j) {
/*
* The goal was occupied; search forward for a free
* block within the next XX blocks.
*
* end_goal is more or less random, but it has to be
* less than EXT2_BLOCKS_PER_GROUP. Aligning up to the
* next 64-bit boundary is simple..
*/
int end_goal = (j + 63) & ~63;
j = __ext2_find_next_zero_bit(bh->b_data, end_goal, j);
if (j < end_goal)
goto got_block;
}
ext2_debug ("Bit not found near goal\n");
 
/*
* There has been no free block found in the near vicinity
* of the goal: do a search forward through the block groups,
* searching in each group first for an entire free byte in
* the bitmap and then for any free bit.
*
* Search first in the remainder of the current group; then,
* cyclicly search through the rest of the groups.
*/
p = ((char *) bh->b_data) + (j >> 3);
r = memscan(p, 0, (EXT2_BLOCKS_PER_GROUP(sb) - j + 7) >> 3);
k = (r - ((char *) bh->b_data)) << 3;
if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
j = k;
goto search_back;
}
k = __ext2_find_next_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb),
j);
if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
j = k;
goto got_block;
}
}
 
ext2_debug ("Bit not found in block group %d.\n", i);
 
/*
* Now search the rest of the groups. We assume that
* i and gdp correctly point to the last group visited.
*/
for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) {
i++;
if (i >= sb->u.ext2_sb.s_groups_count)
i = 0;
gdp = get_group_desc (sb, i, &bh2);
if (swab16(gdp->bg_free_blocks_count) > 0)
break;
}
if (k >= sb->u.ext2_sb.s_groups_count) {
unlock_super (sb);
return 0;
}
 
bitmap_nr = load_block_bitmap (sb, i);
if (bitmap_nr < 0) {
*err = -EIO;
unlock_super (sb);
return 0;
}
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
j = (r - bh->b_data) << 3;
if (j < EXT2_BLOCKS_PER_GROUP(sb))
goto search_back;
else
j = __ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
EXT2_BLOCKS_PER_GROUP(sb));
if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
ext2_error (sb, "ext2_new_block",
"Free blocks count corrupted for block group %d", i);
unlock_super (sb);
return 0;
}
 
search_back:
/*
* We have succeeded in finding a free byte in the block
* bitmap. Now search backwards up to 7 bits to find the
* start of this group of free blocks.
*/
for (k = 0; k < 7 && j > 0 && !__ext2_test_bit (j - 1, bh->b_data); k++, j--);
got_block:
 
ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
 
/*
* Check quota for allocation of this block.
*/
if (sb->dq_op)
if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, sb->s_blocksize))) {
unlock_super (sb);
*err = -EDQUOT;
return 0;
}
 
tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + swab32(es->s_first_data_block);
 
if (test_opt (sb, CHECK_STRICT) &&
(tmp == swab32(gdp->bg_block_bitmap) ||
tmp == swab32(gdp->bg_inode_bitmap) ||
in_range (tmp, swab32(gdp->bg_inode_table), sb->u.ext2_sb.s_itb_per_group))) {
ext2_error (sb, "ext2_new_block",
"Allocating block in system zone - "
"block = %u", tmp);
unlock_super (sb);
*err = -EIO;
return 0;
}
 
if (__ext2_set_bit (j, bh->b_data)) {
ext2_warning (sb, "ext2_new_block",
"bit already set for block %d", j);
if (sb->dq_op)
sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
goto repeat;
}
 
ext2_debug ("found bit %d\n", j);
 
/*
* Do block preallocation now if required.
*/
#ifdef EXT2_PREALLOCATE
if (prealloc_block) {
*prealloc_count = 0;
*prealloc_block = tmp + 1;
for (k = 1;
k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
if (sb->dq_op)
if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
break;
if (__ext2_set_bit (j + k, bh->b_data)) {
if (sb->dq_op)
sb->dq_op->free_block(inode, fs_to_dq_blocks(1, sb->s_blocksize));
break;
}
(*prealloc_count)++;
}
gdp->bg_free_blocks_count =
swab16(swab16(gdp->bg_free_blocks_count) -
*prealloc_count);
es->s_free_blocks_count =
swab32(swab32(es->s_free_blocks_count) -
*prealloc_count);
ext2_debug ("Preallocated a further %lu bits.\n",
*prealloc_count);
}
#endif
 
j = tmp;
 
mark_buffer_dirty(bh, 1);
if (sb->s_flags & MS_SYNCHRONOUS) {
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
 
if (j >= swab32(es->s_blocks_count)) {
ext2_error (sb, "ext2_new_block",
"block >= blocks count - "
"block_group = %d, block=%d", i, j);
unlock_super (sb);
return 0;
}
if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
ext2_error (sb, "ext2_new_block", "cannot get block %d", j);
unlock_super (sb);
return 0;
}
memset(bh->b_data, 0, sb->s_blocksize);
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 1);
brelse (bh);
 
ext2_debug ("allocating block %d. "
"Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
 
gdp->bg_free_blocks_count = swab16(swab16(gdp->bg_free_blocks_count) - 1);
mark_buffer_dirty(bh2, 1);
es->s_free_blocks_count = swab32(swab32(es->s_free_blocks_count) - 1);
mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
sb->s_dirt = 1;
unlock_super (sb);
*err = 0;
return j;
}
 
unsigned long ext2_count_free_blocks (struct super_block * sb)
{
#ifdef EXT2FS_DEBUG
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
int bitmap_nr;
struct ext2_group_desc * gdp;
int i;
lock_super (sb);
es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
desc_count += swab16(gdp->bg_free_blocks_count);
bitmap_nr = load_block_bitmap (sb, i);
if (bitmap_nr < 0)
continue;
 
x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
sb->s_blocksize);
printk ("group %d: stored = %d, counted = %lu\n",
i, swab16(gdp->bg_free_blocks_count), x);
bitmap_count += x;
}
printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
swab32(es->s_free_blocks_count), desc_count, bitmap_count);
unlock_super (sb);
return bitmap_count;
#else
return swab32(sb->u.ext2_sb.s_es->s_free_blocks_count);
#endif
}
 
static inline int block_in_use (unsigned long block,
struct super_block * sb,
unsigned char * map)
{
return __ext2_test_bit ((block - swab32(sb->u.ext2_sb.s_es->s_first_data_block)) %
EXT2_BLOCKS_PER_GROUP(sb), map);
}
 
void ext2_check_blocks_bitmap (struct super_block * sb)
{
struct buffer_head * bh;
struct ext2_super_block * es;
unsigned long desc_count, bitmap_count, x;
unsigned long desc_blocks;
int bitmap_nr;
struct ext2_group_desc * gdp;
int i, j;
 
lock_super (sb);
es = sb->u.ext2_sb.s_es;
desc_count = 0;
bitmap_count = 0;
gdp = NULL;
desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
EXT2_DESC_PER_BLOCK(sb);
for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
gdp = get_group_desc (sb, i, NULL);
desc_count += swab16(gdp->bg_free_blocks_count);
bitmap_nr = load_block_bitmap (sb, i);
if (bitmap_nr < 0)
continue;
 
bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
if (!__ext2_test_bit (0, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Superblock in group %d is marked free", i);
 
for (j = 0; j < desc_blocks; j++)
if (!__ext2_test_bit (j + 1, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Descriptor block #%d in group "
"%d is marked free", j, i);
 
if (!block_in_use (swab32(gdp->bg_block_bitmap), sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Block bitmap for group %d is marked free",
i);
 
if (!block_in_use (swab32(gdp->bg_inode_bitmap), sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Inode bitmap for group %d is marked free",
i);
 
for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++)
if (!block_in_use (swab32(gdp->bg_inode_table) + j, sb, bh->b_data))
ext2_error (sb, "ext2_check_blocks_bitmap",
"Block #%d of the inode table in "
"group %d is marked free", j, i);
 
x = ext2_count_free (bh, sb->s_blocksize);
if (swab16(gdp->bg_free_blocks_count) != x)
ext2_error (sb, "ext2_check_blocks_bitmap",
"Wrong free blocks count for group %d, "
"stored = %d, counted = %lu", i,
swab16(gdp->bg_free_blocks_count), x);
bitmap_count += x;
}
if (swab32(es->s_free_blocks_count) != bitmap_count)
ext2_error (sb, "ext2_check_blocks_bitmap",
"Wrong free blocks count in super block, "
"stored = %lu, counted = %lu",
(unsigned long) swab32(es->s_free_blocks_count), bitmap_count);
unlock_super (sb);
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.