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