Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

13
fs/ext2/Makefile Normal file
View File

@@ -0,0 +1,13 @@
#
# Makefile for the linux ext2-filesystem routines.
#
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o
ext2-$(CONFIG_EXT2_FS_XIP) += xip.o

519
fs/ext2/acl.c Normal file
View File

@@ -0,0 +1,519 @@
/*
* linux/fs/ext2/acl.c
*
* Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
*/
#include <linux/capability.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
/*
* Convert from filesystem to in-memory representation.
*/
static struct posix_acl *
ext2_acl_from_disk(const void *value, size_t size)
{
const char *end = (char *)value + size;
int n, count;
struct posix_acl *acl;
if (!value)
return NULL;
if (size < sizeof(ext2_acl_header))
return ERR_PTR(-EINVAL);
if (((ext2_acl_header *)value)->a_version !=
cpu_to_le32(EXT2_ACL_VERSION))
return ERR_PTR(-EINVAL);
value = (char *)value + sizeof(ext2_acl_header);
count = ext2_acl_count(size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
return NULL;
acl = posix_acl_alloc(count, GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
for (n=0; n < count; n++) {
ext2_acl_entry *entry =
(ext2_acl_entry *)value;
if ((char *)value + sizeof(ext2_acl_entry_short) > end)
goto fail;
acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);
acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
value = (char *)value +
sizeof(ext2_acl_entry_short);
acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
value = (char *)value + sizeof(ext2_acl_entry);
if ((char *)value > end)
goto fail;
acl->a_entries[n].e_id =
le32_to_cpu(entry->e_id);
break;
default:
goto fail;
}
}
if (value != end)
goto fail;
return acl;
fail:
posix_acl_release(acl);
return ERR_PTR(-EINVAL);
}
/*
* Convert from in-memory to filesystem representation.
*/
static void *
ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
{
ext2_acl_header *ext_acl;
char *e;
size_t n;
*size = ext2_acl_size(acl->a_count);
ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
sizeof(ext2_acl_entry), GFP_KERNEL);
if (!ext_acl)
return ERR_PTR(-ENOMEM);
ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
e = (char *)ext_acl + sizeof(ext2_acl_header);
for (n=0; n < acl->a_count; n++) {
ext2_acl_entry *entry = (ext2_acl_entry *)e;
entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag);
entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
switch(acl->a_entries[n].e_tag) {
case ACL_USER:
case ACL_GROUP:
entry->e_id =
cpu_to_le32(acl->a_entries[n].e_id);
e += sizeof(ext2_acl_entry);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
e += sizeof(ext2_acl_entry_short);
break;
default:
goto fail;
}
}
return (char *)ext_acl;
fail:
kfree(ext_acl);
return ERR_PTR(-EINVAL);
}
static inline struct posix_acl *
ext2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
{
struct posix_acl *acl = EXT2_ACL_NOT_CACHED;
spin_lock(&inode->i_lock);
if (*i_acl != EXT2_ACL_NOT_CACHED)
acl = posix_acl_dup(*i_acl);
spin_unlock(&inode->i_lock);
return acl;
}
static inline void
ext2_iset_acl(struct inode *inode, struct posix_acl **i_acl,
struct posix_acl *acl)
{
spin_lock(&inode->i_lock);
if (*i_acl != EXT2_ACL_NOT_CACHED)
posix_acl_release(*i_acl);
*i_acl = posix_acl_dup(acl);
spin_unlock(&inode->i_lock);
}
/*
* inode->i_mutex: don't care
*/
static struct posix_acl *
ext2_get_acl(struct inode *inode, int type)
{
struct ext2_inode_info *ei = EXT2_I(inode);
int name_index;
char *value = NULL;
struct posix_acl *acl;
int retval;
if (!test_opt(inode->i_sb, POSIX_ACL))
return NULL;
switch(type) {
case ACL_TYPE_ACCESS:
acl = ext2_iget_acl(inode, &ei->i_acl);
if (acl != EXT2_ACL_NOT_CACHED)
return acl;
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
acl = ext2_iget_acl(inode, &ei->i_default_acl);
if (acl != EXT2_ACL_NOT_CACHED)
return acl;
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
break;
default:
return ERR_PTR(-EINVAL);
}
retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
if (retval > 0) {
value = kmalloc(retval, GFP_KERNEL);
if (!value)
return ERR_PTR(-ENOMEM);
retval = ext2_xattr_get(inode, name_index, "", value, retval);
}
if (retval > 0)
acl = ext2_acl_from_disk(value, retval);
else if (retval == -ENODATA || retval == -ENOSYS)
acl = NULL;
else
acl = ERR_PTR(retval);
kfree(value);
if (!IS_ERR(acl)) {
switch(type) {
case ACL_TYPE_ACCESS:
ext2_iset_acl(inode, &ei->i_acl, acl);
break;
case ACL_TYPE_DEFAULT:
ext2_iset_acl(inode, &ei->i_default_acl, acl);
break;
}
}
return acl;
}
/*
* inode->i_mutex: down
*/
static int
ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
{
struct ext2_inode_info *ei = EXT2_I(inode);
int name_index;
void *value = NULL;
size_t size = 0;
int error;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
switch(type) {
case ACL_TYPE_ACCESS:
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
mode_t mode = inode->i_mode;
error = posix_acl_equiv_mode(acl, &mode);
if (error < 0)
return error;
else {
inode->i_mode = mode;
mark_inode_dirty(inode);
if (error == 0)
acl = NULL;
}
}
break;
case ACL_TYPE_DEFAULT:
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
if (!S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
break;
default:
return -EINVAL;
}
if (acl) {
value = ext2_acl_to_disk(acl, &size);
if (IS_ERR(value))
return (int)PTR_ERR(value);
}
error = ext2_xattr_set(inode, name_index, "", value, size, 0);
kfree(value);
if (!error) {
switch(type) {
case ACL_TYPE_ACCESS:
ext2_iset_acl(inode, &ei->i_acl, acl);
break;
case ACL_TYPE_DEFAULT:
ext2_iset_acl(inode, &ei->i_default_acl, acl);
break;
}
}
return error;
}
static int
ext2_check_acl(struct inode *inode, int mask)
{
struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
int error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
return error;
}
return -EAGAIN;
}
int
ext2_permission(struct inode *inode, int mask, struct nameidata *nd)
{
return generic_permission(inode, mask, ext2_check_acl);
}
/*
* Initialize the ACLs of a new inode. Called from ext2_new_inode.
*
* dir->i_mutex: down
* inode->i_mutex: up (access to inode is still exclusive)
*/
int
ext2_init_acl(struct inode *inode, struct inode *dir)
{
struct posix_acl *acl = NULL;
int error = 0;
if (!S_ISLNK(inode->i_mode)) {
if (test_opt(dir->i_sb, POSIX_ACL)) {
acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
}
if (!acl)
inode->i_mode &= ~current->fs->umask;
}
if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
struct posix_acl *clone;
mode_t mode;
if (S_ISDIR(inode->i_mode)) {
error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
if (error)
goto cleanup;
}
clone = posix_acl_clone(acl, GFP_KERNEL);
error = -ENOMEM;
if (!clone)
goto cleanup;
mode = inode->i_mode;
error = posix_acl_create_masq(clone, &mode);
if (error >= 0) {
inode->i_mode = mode;
if (error > 0) {
/* This is an extended ACL */
error = ext2_set_acl(inode,
ACL_TYPE_ACCESS, clone);
}
}
posix_acl_release(clone);
}
cleanup:
posix_acl_release(acl);
return error;
}
/*
* Does chmod for an inode that may have an Access Control List. The
* inode->i_mode field must be updated to the desired value by the caller
* before calling this function.
* Returns 0 on success, or a negative error number.
*
* We change the ACL rather than storing some ACL entries in the file
* mode permission bits (which would be more efficient), because that
* would break once additional permissions (like ACL_APPEND, ACL_DELETE
* for directories) are added. There are no more bits available in the
* file mode.
*
* inode->i_mutex: down
*/
int
ext2_acl_chmod(struct inode *inode)
{
struct posix_acl *acl, *clone;
int error;
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
if (S_ISLNK(inode->i_mode))
return -EOPNOTSUPP;
acl = ext2_get_acl(inode, ACL_TYPE_ACCESS);
if (IS_ERR(acl) || !acl)
return PTR_ERR(acl);
clone = posix_acl_clone(acl, GFP_KERNEL);
posix_acl_release(acl);
if (!clone)
return -ENOMEM;
error = posix_acl_chmod_masq(clone, inode->i_mode);
if (!error)
error = ext2_set_acl(inode, ACL_TYPE_ACCESS, clone);
posix_acl_release(clone);
return error;
}
/*
* Extended attribut handlers
*/
static size_t
ext2_xattr_list_acl_access(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
if (list && size <= list_size)
memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
return size;
}
static size_t
ext2_xattr_list_acl_default(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
if (!test_opt(inode->i_sb, POSIX_ACL))
return 0;
if (list && size <= list_size)
memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
return size;
}
static int
ext2_xattr_get_acl(struct inode *inode, int type, void *buffer, size_t size)
{
struct posix_acl *acl;
int error;
if (!test_opt(inode->i_sb, POSIX_ACL))
return -EOPNOTSUPP;
acl = ext2_get_acl(inode, type);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl == NULL)
return -ENODATA;
error = posix_acl_to_xattr(acl, buffer, size);
posix_acl_release(acl);
return error;
}
static int
ext2_xattr_get_acl_access(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext2_xattr_get_acl(inode, ACL_TYPE_ACCESS, buffer, size);
}
static int
ext2_xattr_get_acl_default(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext2_xattr_get_acl(inode, ACL_TYPE_DEFAULT, buffer, size);
}
static int
ext2_xattr_set_acl(struct inode *inode, int type, const void *value,
size_t size)
{
struct posix_acl *acl;
int error;
if (!test_opt(inode->i_sb, POSIX_ACL))
return -EOPNOTSUPP;
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EPERM;
if (value) {
acl = posix_acl_from_xattr(value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
else if (acl) {
error = posix_acl_valid(acl);
if (error)
goto release_and_out;
}
} else
acl = NULL;
error = ext2_set_acl(inode, type, acl);
release_and_out:
posix_acl_release(acl);
return error;
}
static int
ext2_xattr_set_acl_access(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext2_xattr_set_acl(inode, ACL_TYPE_ACCESS, value, size);
}
static int
ext2_xattr_set_acl_default(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") != 0)
return -EINVAL;
return ext2_xattr_set_acl(inode, ACL_TYPE_DEFAULT, value, size);
}
struct xattr_handler ext2_xattr_acl_access_handler = {
.prefix = POSIX_ACL_XATTR_ACCESS,
.list = ext2_xattr_list_acl_access,
.get = ext2_xattr_get_acl_access,
.set = ext2_xattr_set_acl_access,
};
struct xattr_handler ext2_xattr_acl_default_handler = {
.prefix = POSIX_ACL_XATTR_DEFAULT,
.list = ext2_xattr_list_acl_default,
.get = ext2_xattr_get_acl_default,
.set = ext2_xattr_set_acl_default,
};

82
fs/ext2/acl.h Normal file
View File

@@ -0,0 +1,82 @@
/*
File: fs/ext2/acl.h
(C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/posix_acl_xattr.h>
#define EXT2_ACL_VERSION 0x0001
typedef struct {
__le16 e_tag;
__le16 e_perm;
__le32 e_id;
} ext2_acl_entry;
typedef struct {
__le16 e_tag;
__le16 e_perm;
} ext2_acl_entry_short;
typedef struct {
__le32 a_version;
} ext2_acl_header;
static inline size_t ext2_acl_size(int count)
{
if (count <= 4) {
return sizeof(ext2_acl_header) +
count * sizeof(ext2_acl_entry_short);
} else {
return sizeof(ext2_acl_header) +
4 * sizeof(ext2_acl_entry_short) +
(count - 4) * sizeof(ext2_acl_entry);
}
}
static inline int ext2_acl_count(size_t size)
{
ssize_t s;
size -= sizeof(ext2_acl_header);
s = size - 4 * sizeof(ext2_acl_entry_short);
if (s < 0) {
if (size % sizeof(ext2_acl_entry_short))
return -1;
return size / sizeof(ext2_acl_entry_short);
} else {
if (s % sizeof(ext2_acl_entry))
return -1;
return s / sizeof(ext2_acl_entry) + 4;
}
}
#ifdef CONFIG_EXT2_FS_POSIX_ACL
/* Value for inode->u.ext2_i.i_acl and inode->u.ext2_i.i_default_acl
if the ACL has not been cached */
#define EXT2_ACL_NOT_CACHED ((void *)-1)
/* acl.c */
extern int ext2_permission (struct inode *, int, struct nameidata *);
extern int ext2_acl_chmod (struct inode *);
extern int ext2_init_acl (struct inode *, struct inode *);
#else
#include <linux/sched.h>
#define ext2_permission NULL
#define ext2_get_acl NULL
#define ext2_set_acl NULL
static inline int
ext2_acl_chmod (struct inode *inode)
{
return 0;
}
static inline int ext2_init_acl (struct inode *inode, struct inode *dir)
{
return 0;
}
#endif

643
fs/ext2/balloc.c Normal file
View File

@@ -0,0 +1,643 @@
/*
* 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@redhat.com), 1993
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
#include "ext2.h"
#include <linux/quotaops.h>
#include <linux/sched.h>
#include <linux/buffer_head.h>
#include <linux/capability.h>
/*
* 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_fill_super).
*/
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
unsigned int block_group,
struct buffer_head ** bh)
{
unsigned long group_desc;
unsigned long offset;
struct ext2_group_desc * desc;
struct ext2_sb_info *sbi = EXT2_SB(sb);
if (block_group >= sbi->s_groups_count) {
ext2_error (sb, "ext2_get_group_desc",
"block_group >= groups_count - "
"block_group = %d, groups_count = %lu",
block_group, sbi->s_groups_count);
return NULL;
}
group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(sb);
offset = block_group & (EXT2_DESC_PER_BLOCK(sb) - 1);
if (!sbi->s_group_desc[group_desc]) {
ext2_error (sb, "ext2_get_group_desc",
"Group descriptor not loaded - "
"block_group = %d, group_desc = %lu, desc = %lu",
block_group, group_desc, offset);
return NULL;
}
desc = (struct ext2_group_desc *) sbi->s_group_desc[group_desc]->b_data;
if (bh)
*bh = sbi->s_group_desc[group_desc];
return desc + offset;
}
/*
* Read the bitmap for a given block_group, reading into the specified
* slot in the superblock's bitmap cache.
*
* Return buffer_head on success or NULL in case of failure.
*/
static struct buffer_head *
read_block_bitmap(struct super_block *sb, unsigned int block_group)
{
struct ext2_group_desc * desc;
struct buffer_head * bh = NULL;
desc = ext2_get_group_desc (sb, block_group, NULL);
if (!desc)
goto error_out;
bh = sb_bread(sb, le32_to_cpu(desc->bg_block_bitmap));
if (!bh)
ext2_error (sb, "read_block_bitmap",
"Cannot read block bitmap - "
"block_group = %d, block_bitmap = %u",
block_group, le32_to_cpu(desc->bg_block_bitmap));
error_out:
return bh;
}
/*
* Set sb->s_dirt here because the superblock was "logically" altered. We
* need to recalculate its free blocks count and flush it out.
*/
static int reserve_blocks(struct super_block *sb, int count)
{
struct ext2_sb_info *sbi = EXT2_SB(sb);
struct ext2_super_block *es = sbi->s_es;
unsigned free_blocks;
unsigned root_blocks;
free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
root_blocks = le32_to_cpu(es->s_r_blocks_count);
if (free_blocks < count)
count = free_blocks;
if (free_blocks < root_blocks + count && !capable(CAP_SYS_RESOURCE) &&
sbi->s_resuid != current->fsuid &&
(sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
/*
* We are too close to reserve and we are not privileged.
* Can we allocate anything at all?
*/
if (free_blocks > root_blocks)
count = free_blocks - root_blocks;
else
return 0;
}
percpu_counter_mod(&sbi->s_freeblocks_counter, -count);
sb->s_dirt = 1;
return count;
}
static void release_blocks(struct super_block *sb, int count)
{
if (count) {
struct ext2_sb_info *sbi = EXT2_SB(sb);
percpu_counter_mod(&sbi->s_freeblocks_counter, count);
sb->s_dirt = 1;
}
}
static int group_reserve_blocks(struct ext2_sb_info *sbi, int group_no,
struct ext2_group_desc *desc, struct buffer_head *bh, int count)
{
unsigned free_blocks;
if (!desc->bg_free_blocks_count)
return 0;
spin_lock(sb_bgl_lock(sbi, group_no));
free_blocks = le16_to_cpu(desc->bg_free_blocks_count);
if (free_blocks < count)
count = free_blocks;
desc->bg_free_blocks_count = cpu_to_le16(free_blocks - count);
spin_unlock(sb_bgl_lock(sbi, group_no));
mark_buffer_dirty(bh);
return count;
}
static void group_release_blocks(struct super_block *sb, int group_no,
struct ext2_group_desc *desc, struct buffer_head *bh, int count)
{
if (count) {
struct ext2_sb_info *sbi = EXT2_SB(sb);
unsigned free_blocks;
spin_lock(sb_bgl_lock(sbi, group_no));
free_blocks = le16_to_cpu(desc->bg_free_blocks_count);
desc->bg_free_blocks_count = cpu_to_le16(free_blocks + count);
spin_unlock(sb_bgl_lock(sbi, group_no));
sb->s_dirt = 1;
mark_buffer_dirty(bh);
}
}
/* Free given blocks, update quota and i_blocks field */
void ext2_free_blocks (struct inode * inode, unsigned long block,
unsigned long count)
{
struct buffer_head *bitmap_bh = NULL;
struct buffer_head * bh2;
unsigned long block_group;
unsigned long bit;
unsigned long i;
unsigned long overflow;
struct super_block * sb = inode->i_sb;
struct ext2_sb_info * sbi = EXT2_SB(sb);
struct ext2_group_desc * desc;
struct ext2_super_block * es = sbi->s_es;
unsigned freed = 0, group_freed;
if (block < le32_to_cpu(es->s_first_data_block) ||
block + count < block ||
block + count > le32_to_cpu(es->s_blocks_count)) {
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks not in datazone - "
"block = %lu, count = %lu", block, count);
goto error_return;
}
ext2_debug ("freeing block(s) %lu-%lu\n", block, block + count - 1);
do_more:
overflow = 0;
block_group = (block - le32_to_cpu(es->s_first_data_block)) /
EXT2_BLOCKS_PER_GROUP(sb);
bit = (block - le32_to_cpu(es->s_first_data_block)) %
EXT2_BLOCKS_PER_GROUP(sb);
/*
* Check to see if we are freeing blocks across a group
* boundary.
*/
if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
overflow = bit + count - EXT2_BLOCKS_PER_GROUP(sb);
count -= overflow;
}
brelse(bitmap_bh);
bitmap_bh = read_block_bitmap(sb, block_group);
if (!bitmap_bh)
goto error_return;
desc = ext2_get_group_desc (sb, block_group, &bh2);
if (!desc)
goto error_return;
if (in_range (le32_to_cpu(desc->bg_block_bitmap), block, count) ||
in_range (le32_to_cpu(desc->bg_inode_bitmap), block, count) ||
in_range (block, le32_to_cpu(desc->bg_inode_table),
sbi->s_itb_per_group) ||
in_range (block + count - 1, le32_to_cpu(desc->bg_inode_table),
sbi->s_itb_per_group))
ext2_error (sb, "ext2_free_blocks",
"Freeing blocks in system zones - "
"Block = %lu, count = %lu",
block, count);
for (i = 0, group_freed = 0; i < count; i++) {
if (!ext2_clear_bit_atomic(sb_bgl_lock(sbi, block_group),
bit + i, bitmap_bh->b_data)) {
ext2_error(sb, __FUNCTION__,
"bit already cleared for block %lu", block + i);
} else {
group_freed++;
}
}
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
group_release_blocks(sb, block_group, desc, bh2, group_freed);
freed += group_freed;
if (overflow) {
block += count;
count = overflow;
goto do_more;
}
error_return:
brelse(bitmap_bh);
release_blocks(sb, freed);
DQUOT_FREE_BLOCK(inode, freed);
}
static int grab_block(spinlock_t *lock, char *map, unsigned size, int goal)
{
int k;
char *p, *r;
if (!ext2_test_bit(goal, map))
goto got_it;
repeat:
if (goal) {
/*
* 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..
*/
k = (goal + 63) & ~63;
goal = ext2_find_next_zero_bit(map, k, goal);
if (goal < k)
goto got_it;
/*
* Search in the remainder of the current group.
*/
}
p = map + (goal >> 3);
r = memscan(p, 0, (size - goal + 7) >> 3);
k = (r - map) << 3;
if (k < size) {
/*
* We have succeeded in finding a free byte in the block
* bitmap. Now search backwards to find the start of this
* group of free blocks - won't take more than 7 iterations.
*/
for (goal = k; goal && !ext2_test_bit (goal - 1, map); goal--)
;
goto got_it;
}
k = ext2_find_next_zero_bit ((u32 *)map, size, goal);
if (k < size) {
goal = k;
goto got_it;
}
return -1;
got_it:
if (ext2_set_bit_atomic(lock, goal, (void *) map))
goto repeat;
return goal;
}
/*
* 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.
* This function also updates quota and i_blocks field.
*/
int ext2_new_block(struct inode *inode, unsigned long goal,
u32 *prealloc_count, u32 *prealloc_block, int *err)
{
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *gdp_bh; /* bh2 */
struct ext2_group_desc *desc;
int group_no; /* i */
int ret_block; /* j */
int group_idx; /* k */
int target_block; /* tmp */
int block = 0;
struct super_block *sb = inode->i_sb;
struct ext2_sb_info *sbi = EXT2_SB(sb);
struct ext2_super_block *es = sbi->s_es;
unsigned group_size = EXT2_BLOCKS_PER_GROUP(sb);
unsigned prealloc_goal = es->s_prealloc_blocks;
unsigned group_alloc = 0, es_alloc, dq_alloc;
int nr_scanned_groups;
if (!prealloc_goal--)
prealloc_goal = EXT2_DEFAULT_PREALLOC_BLOCKS - 1;
if (!prealloc_count || *prealloc_count)
prealloc_goal = 0;
if (DQUOT_ALLOC_BLOCK(inode, 1)) {
*err = -EDQUOT;
goto out;
}
while (prealloc_goal && DQUOT_PREALLOC_BLOCK(inode, prealloc_goal))
prealloc_goal--;
dq_alloc = prealloc_goal + 1;
es_alloc = reserve_blocks(sb, dq_alloc);
if (!es_alloc) {
*err = -ENOSPC;
goto out_dquot;
}
ext2_debug ("goal=%lu.\n", goal);
if (goal < le32_to_cpu(es->s_first_data_block) ||
goal >= le32_to_cpu(es->s_blocks_count))
goal = le32_to_cpu(es->s_first_data_block);
group_no = (goal - le32_to_cpu(es->s_first_data_block)) / group_size;
desc = ext2_get_group_desc (sb, group_no, &gdp_bh);
if (!desc) {
/*
* gdp_bh may still be uninitialised. But group_release_blocks
* will not touch it because group_alloc is zero.
*/
goto io_error;
}
group_alloc = group_reserve_blocks(sbi, group_no, desc,
gdp_bh, es_alloc);
if (group_alloc) {
ret_block = ((goal - le32_to_cpu(es->s_first_data_block)) %
group_size);
brelse(bitmap_bh);
bitmap_bh = read_block_bitmap(sb, group_no);
if (!bitmap_bh)
goto io_error;
ext2_debug("goal is at %d:%d.\n", group_no, ret_block);
ret_block = grab_block(sb_bgl_lock(sbi, group_no),
bitmap_bh->b_data, group_size, ret_block);
if (ret_block >= 0)
goto got_block;
group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc);
group_alloc = 0;
}
ext2_debug ("Bit not found in block group %d.\n", group_no);
/*
* Now search the rest of the groups. We assume that
* i and desc correctly point to the last group visited.
*/
nr_scanned_groups = 0;
retry:
for (group_idx = 0; !group_alloc &&
group_idx < sbi->s_groups_count; group_idx++) {
group_no++;
if (group_no >= sbi->s_groups_count)
group_no = 0;
desc = ext2_get_group_desc(sb, group_no, &gdp_bh);
if (!desc)
goto io_error;
group_alloc = group_reserve_blocks(sbi, group_no, desc,
gdp_bh, es_alloc);
}
if (!group_alloc) {
*err = -ENOSPC;
goto out_release;
}
brelse(bitmap_bh);
bitmap_bh = read_block_bitmap(sb, group_no);
if (!bitmap_bh)
goto io_error;
ret_block = grab_block(sb_bgl_lock(sbi, group_no), bitmap_bh->b_data,
group_size, 0);
if (ret_block < 0) {
/*
* If a free block counter is corrupted we can loop inifintely.
* Detect that here.
*/
nr_scanned_groups++;
if (nr_scanned_groups > 2 * sbi->s_groups_count) {
ext2_error(sb, "ext2_new_block",
"corrupted free blocks counters");
goto io_error;
}
/*
* Someone else grabbed the last free block in this blockgroup
* before us. Retry the scan.
*/
group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc);
group_alloc = 0;
goto retry;
}
got_block:
ext2_debug("using block group %d(%d)\n",
group_no, desc->bg_free_blocks_count);
target_block = ret_block + group_no * group_size +
le32_to_cpu(es->s_first_data_block);
if (target_block == le32_to_cpu(desc->bg_block_bitmap) ||
target_block == le32_to_cpu(desc->bg_inode_bitmap) ||
in_range(target_block, le32_to_cpu(desc->bg_inode_table),
sbi->s_itb_per_group))
ext2_error (sb, "ext2_new_block",
"Allocating block in system zone - "
"block = %u", target_block);
if (target_block >= le32_to_cpu(es->s_blocks_count)) {
ext2_error (sb, "ext2_new_block",
"block(%d) >= blocks count(%d) - "
"block_group = %d, es == %p ", ret_block,
le32_to_cpu(es->s_blocks_count), group_no, es);
goto io_error;
}
block = target_block;
/* OK, we _had_ allocated something */
ext2_debug("found bit %d\n", ret_block);
dq_alloc--;
es_alloc--;
group_alloc--;
/*
* Do block preallocation now if required.
*/
write_lock(&EXT2_I(inode)->i_meta_lock);
if (group_alloc && !*prealloc_count) {
unsigned n;
for (n = 0; n < group_alloc && ++ret_block < group_size; n++) {
if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group_no),
ret_block,
(void*) bitmap_bh->b_data))
break;
}
*prealloc_block = block + 1;
*prealloc_count = n;
es_alloc -= n;
dq_alloc -= n;
group_alloc -= n;
}
write_unlock(&EXT2_I(inode)->i_meta_lock);
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
ext2_debug ("allocating block %d. ", block);
*err = 0;
out_release:
group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc);
release_blocks(sb, es_alloc);
out_dquot:
DQUOT_FREE_BLOCK(inode, dq_alloc);
out:
brelse(bitmap_bh);
return block;
io_error:
*err = -EIO;
goto out_release;
}
#ifdef EXT2FS_DEBUG
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);
}
#endif /* EXT2FS_DEBUG */
unsigned long ext2_count_free_blocks (struct super_block * sb)
{
struct ext2_group_desc * desc;
unsigned long desc_count = 0;
int i;
#ifdef EXT2FS_DEBUG
unsigned long bitmap_count, x;
struct ext2_super_block *es;
es = EXT2_SB(sb)->s_es;
desc_count = 0;
bitmap_count = 0;
desc = NULL;
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
struct buffer_head *bitmap_bh;
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
desc_count += le16_to_cpu(desc->bg_free_blocks_count);
bitmap_bh = read_block_bitmap(sb, i);
if (!bitmap_bh)
continue;
x = ext2_count_free(bitmap_bh, sb->s_blocksize);
printk ("group %d: stored = %d, counted = %lu\n",
i, le16_to_cpu(desc->bg_free_blocks_count), x);
bitmap_count += x;
brelse(bitmap_bh);
}
printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
(long)le32_to_cpu(es->s_free_blocks_count),
desc_count, bitmap_count);
return bitmap_count;
#else
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
desc_count += le16_to_cpu(desc->bg_free_blocks_count);
}
return desc_count;
#endif
}
static inline int
block_in_use(unsigned long block, struct super_block *sb, unsigned char *map)
{
return ext2_test_bit ((block -
le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block)) %
EXT2_BLOCKS_PER_GROUP(sb), map);
}
static inline int test_root(int a, int b)
{
int num = b;
while (a > num)
num *= b;
return num == a;
}
static int ext2_group_sparse(int group)
{
if (group <= 1)
return 1;
return (test_root(group, 3) || test_root(group, 5) ||
test_root(group, 7));
}
/**
* ext2_bg_has_super - number of blocks used by the superblock in group
* @sb: superblock for filesystem
* @group: group number to check
*
* Return the number of blocks used by the superblock (primary or backup)
* in this group. Currently this will be only 0 or 1.
*/
int ext2_bg_has_super(struct super_block *sb, int group)
{
if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
!ext2_group_sparse(group))
return 0;
return 1;
}
/**
* ext2_bg_num_gdb - number of blocks used by the group table in group
* @sb: superblock for filesystem
* @group: group number to check
*
* Return the number of blocks used by the group descriptor table
* (primary or backup) in this group. In the future there may be a
* different number of descriptor blocks in each group.
*/
unsigned long ext2_bg_num_gdb(struct super_block *sb, int group)
{
if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
!ext2_group_sparse(group))
return 0;
return EXT2_SB(sb)->s_gdb_count;
}

676
fs/ext2/dir.c Normal file
View File

@@ -0,0 +1,676 @@
/*
* 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
*
* All code that works with directory layout had been switched to pagecache
* and moved here. AV
*/
#include "ext2.h"
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
typedef struct ext2_dir_entry_2 ext2_dirent;
/*
* ext2 uses block-sized chunks. Arguably, sector-sized ones would be
* more robust, but we have what we have
*/
static inline unsigned ext2_chunk_size(struct inode *inode)
{
return inode->i_sb->s_blocksize;
}
static inline void ext2_put_page(struct page *page)
{
kunmap(page);
page_cache_release(page);
}
static inline unsigned long dir_pages(struct inode *inode)
{
return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
}
/*
* Return the offset into page `page_nr' of the last valid
* byte in that page, plus one.
*/
static unsigned
ext2_last_byte(struct inode *inode, unsigned long page_nr)
{
unsigned last_byte = inode->i_size;
last_byte -= page_nr << PAGE_CACHE_SHIFT;
if (last_byte > PAGE_CACHE_SIZE)
last_byte = PAGE_CACHE_SIZE;
return last_byte;
}
static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to)
{
struct inode *dir = page->mapping->host;
int err = 0;
dir->i_version++;
page->mapping->a_ops->commit_write(NULL, page, from, to);
if (IS_DIRSYNC(dir))
err = write_one_page(page, 1);
else
unlock_page(page);
return err;
}
static void ext2_check_page(struct page *page)
{
struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb;
unsigned chunk_size = ext2_chunk_size(dir);
char *kaddr = page_address(page);
u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count);
unsigned offs, rec_len;
unsigned limit = PAGE_CACHE_SIZE;
ext2_dirent *p;
char *error;
if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) {
limit = dir->i_size & ~PAGE_CACHE_MASK;
if (limit & (chunk_size - 1))
goto Ebadsize;
if (!limit)
goto out;
}
for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
p = (ext2_dirent *)(kaddr + offs);
rec_len = le16_to_cpu(p->rec_len);
if (rec_len < EXT2_DIR_REC_LEN(1))
goto Eshort;
if (rec_len & 3)
goto Ealign;
if (rec_len < EXT2_DIR_REC_LEN(p->name_len))
goto Enamelen;
if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1))
goto Espan;
if (le32_to_cpu(p->inode) > max_inumber)
goto Einumber;
}
if (offs != limit)
goto Eend;
out:
SetPageChecked(page);
return;
/* Too bad, we had an error */
Ebadsize:
ext2_error(sb, "ext2_check_page",
"size of directory #%lu is not a multiple of chunk size",
dir->i_ino
);
goto fail;
Eshort:
error = "rec_len is smaller than minimal";
goto bad_entry;
Ealign:
error = "unaligned directory entry";
goto bad_entry;
Enamelen:
error = "rec_len is too small for name_len";
goto bad_entry;
Espan:
error = "directory entry across blocks";
goto bad_entry;
Einumber:
error = "inode out of bounds";
bad_entry:
ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - "
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
(unsigned long) le32_to_cpu(p->inode),
rec_len, p->name_len);
goto fail;
Eend:
p = (ext2_dirent *)(kaddr + offs);
ext2_error (sb, "ext2_check_page",
"entry in directory #%lu spans the page boundary"
"offset=%lu, inode=%lu",
dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,
(unsigned long) le32_to_cpu(p->inode));
fail:
SetPageChecked(page);
SetPageError(page);
}
static struct page * ext2_get_page(struct inode *dir, unsigned long n)
{
struct address_space *mapping = dir->i_mapping;
struct page *page = read_mapping_page(mapping, n, NULL);
if (!IS_ERR(page)) {
wait_on_page_locked(page);
kmap(page);
if (!PageUptodate(page))
goto fail;
if (!PageChecked(page))
ext2_check_page(page);
if (PageError(page))
goto fail;
}
return page;
fail:
ext2_put_page(page);
return ERR_PTR(-EIO);
}
/*
* NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
*
* len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
*/
static inline int ext2_match (int len, const char * const name,
struct ext2_dir_entry_2 * de)
{
if (len != de->name_len)
return 0;
if (!de->inode)
return 0;
return !memcmp(name, de->name, len);
}
/*
* p is at least 6 bytes before the end of page
*/
static inline ext2_dirent *ext2_next_entry(ext2_dirent *p)
{
return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len));
}
static inline unsigned
ext2_validate_entry(char *base, unsigned offset, unsigned mask)
{
ext2_dirent *de = (ext2_dirent*)(base + offset);
ext2_dirent *p = (ext2_dirent*)(base + (offset&mask));
while ((char*)p < (char*)de) {
if (p->rec_len == 0)
break;
p = ext2_next_entry(p);
}
return (char *)p - base;
}
static unsigned char ext2_filetype_table[EXT2_FT_MAX] = {
[EXT2_FT_UNKNOWN] = DT_UNKNOWN,
[EXT2_FT_REG_FILE] = DT_REG,
[EXT2_FT_DIR] = DT_DIR,
[EXT2_FT_CHRDEV] = DT_CHR,
[EXT2_FT_BLKDEV] = DT_BLK,
[EXT2_FT_FIFO] = DT_FIFO,
[EXT2_FT_SOCK] = DT_SOCK,
[EXT2_FT_SYMLINK] = DT_LNK,
};
#define S_SHIFT 12
static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = {
[S_IFREG >> S_SHIFT] = EXT2_FT_REG_FILE,
[S_IFDIR >> S_SHIFT] = EXT2_FT_DIR,
[S_IFCHR >> S_SHIFT] = EXT2_FT_CHRDEV,
[S_IFBLK >> S_SHIFT] = EXT2_FT_BLKDEV,
[S_IFIFO >> S_SHIFT] = EXT2_FT_FIFO,
[S_IFSOCK >> S_SHIFT] = EXT2_FT_SOCK,
[S_IFLNK >> S_SHIFT] = EXT2_FT_SYMLINK,
};
static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
{
mode_t mode = inode->i_mode;
if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
else
de->file_type = 0;
}
static int
ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
{
loff_t pos = filp->f_pos;
struct inode *inode = filp->f_path.dentry->d_inode;
struct super_block *sb = inode->i_sb;
unsigned int offset = pos & ~PAGE_CACHE_MASK;
unsigned long n = pos >> PAGE_CACHE_SHIFT;
unsigned long npages = dir_pages(inode);
unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
unsigned char *types = NULL;
int need_revalidate = filp->f_version != inode->i_version;
if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
return 0;
if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
types = ext2_filetype_table;
for ( ; n < npages; n++, offset = 0) {
char *kaddr, *limit;
ext2_dirent *de;
struct page *page = ext2_get_page(inode, n);
if (IS_ERR(page)) {
ext2_error(sb, __FUNCTION__,
"bad page in #%lu",
inode->i_ino);
filp->f_pos += PAGE_CACHE_SIZE - offset;
return -EIO;
}
kaddr = page_address(page);
if (unlikely(need_revalidate)) {
if (offset) {
offset = ext2_validate_entry(kaddr, offset, chunk_mask);
filp->f_pos = (n<<PAGE_CACHE_SHIFT) + offset;
}
filp->f_version = inode->i_version;
need_revalidate = 0;
}
de = (ext2_dirent *)(kaddr+offset);
limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1);
for ( ;(char*)de <= limit; de = ext2_next_entry(de)) {
if (de->rec_len == 0) {
ext2_error(sb, __FUNCTION__,
"zero-length directory entry");
ext2_put_page(page);
return -EIO;
}
if (de->inode) {
int over;
unsigned char d_type = DT_UNKNOWN;
if (types && de->file_type < EXT2_FT_MAX)
d_type = types[de->file_type];
offset = (char *)de - kaddr;
over = filldir(dirent, de->name, de->name_len,
(n<<PAGE_CACHE_SHIFT) | offset,
le32_to_cpu(de->inode), d_type);
if (over) {
ext2_put_page(page);
return 0;
}
}
filp->f_pos += le16_to_cpu(de->rec_len);
}
ext2_put_page(page);
}
return 0;
}
/*
* ext2_find_entry()
*
* finds an entry in the specified directory with the wanted name. It
* returns the page in which the entry was found, and the entry itself
* (as a parameter - res_dir). Page is returned mapped and unlocked.
* Entry is guaranteed to be valid.
*/
struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
struct dentry *dentry, struct page ** res_page)
{
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
unsigned reclen = EXT2_DIR_REC_LEN(namelen);
unsigned long start, n;
unsigned long npages = dir_pages(dir);
struct page *page = NULL;
struct ext2_inode_info *ei = EXT2_I(dir);
ext2_dirent * de;
if (npages == 0)
goto out;
/* OFFSET_CACHE */
*res_page = NULL;
start = ei->i_dir_start_lookup;
if (start >= npages)
start = 0;
n = start;
do {
char *kaddr;
page = ext2_get_page(dir, n);
if (!IS_ERR(page)) {
kaddr = page_address(page);
de = (ext2_dirent *) kaddr;
kaddr += ext2_last_byte(dir, n) - reclen;
while ((char *) de <= kaddr) {
if (de->rec_len == 0) {
ext2_error(dir->i_sb, __FUNCTION__,
"zero-length directory entry");
ext2_put_page(page);
goto out;
}
if (ext2_match (namelen, name, de))
goto found;
de = ext2_next_entry(de);
}
ext2_put_page(page);
}
if (++n >= npages)
n = 0;
/* next page is past the blocks we've got */
if (unlikely(n > (dir->i_blocks >> (PAGE_CACHE_SHIFT - 9)))) {
ext2_error(dir->i_sb, __FUNCTION__,
"dir %lu size %lld exceeds block count %llu",
dir->i_ino, dir->i_size,
(unsigned long long)dir->i_blocks);
goto out;
}
} while (n != start);
out:
return NULL;
found:
*res_page = page;
ei->i_dir_start_lookup = n;
return de;
}
struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)
{
struct page *page = ext2_get_page(dir, 0);
ext2_dirent *de = NULL;
if (!IS_ERR(page)) {
de = ext2_next_entry((ext2_dirent *) page_address(page));
*p = page;
}
return de;
}
ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry)
{
ino_t res = 0;
struct ext2_dir_entry_2 * de;
struct page *page;
de = ext2_find_entry (dir, dentry, &page);
if (de) {
res = le32_to_cpu(de->inode);
ext2_put_page(page);
}
return res;
}
/* Releases the page */
void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
struct page *page, struct inode *inode)
{
unsigned from = (char *) de - (char *) page_address(page);
unsigned to = from + le16_to_cpu(de->rec_len);
int err;
lock_page(page);
err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
BUG_ON(err);
de->inode = cpu_to_le32(inode->i_ino);
ext2_set_de_type (de, inode);
err = ext2_commit_chunk(page, from, to);
ext2_put_page(page);
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
}
/*
* Parent is locked.
*/
int ext2_add_link (struct dentry *dentry, struct inode *inode)
{
struct inode *dir = dentry->d_parent->d_inode;
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
unsigned chunk_size = ext2_chunk_size(dir);
unsigned reclen = EXT2_DIR_REC_LEN(namelen);
unsigned short rec_len, name_len;
struct page *page = NULL;
ext2_dirent * de;
unsigned long npages = dir_pages(dir);
unsigned long n;
char *kaddr;
unsigned from, to;
int err;
/*
* We take care of directory expansion in the same loop.
* This code plays outside i_size, so it locks the page
* to protect that region.
*/
for (n = 0; n <= npages; n++) {
char *dir_end;
page = ext2_get_page(dir, n);
err = PTR_ERR(page);
if (IS_ERR(page))
goto out;
lock_page(page);
kaddr = page_address(page);
dir_end = kaddr + ext2_last_byte(dir, n);
de = (ext2_dirent *)kaddr;
kaddr += PAGE_CACHE_SIZE - reclen;
while ((char *)de <= kaddr) {
if ((char *)de == dir_end) {
/* We hit i_size */
name_len = 0;
rec_len = chunk_size;
de->rec_len = cpu_to_le16(chunk_size);
de->inode = 0;
goto got_it;
}
if (de->rec_len == 0) {
ext2_error(dir->i_sb, __FUNCTION__,
"zero-length directory entry");
err = -EIO;
goto out_unlock;
}
err = -EEXIST;
if (ext2_match (namelen, name, de))
goto out_unlock;
name_len = EXT2_DIR_REC_LEN(de->name_len);
rec_len = le16_to_cpu(de->rec_len);
if (!de->inode && rec_len >= reclen)
goto got_it;
if (rec_len >= name_len + reclen)
goto got_it;
de = (ext2_dirent *) ((char *) de + rec_len);
}
unlock_page(page);
ext2_put_page(page);
}
BUG();
return -EINVAL;
got_it:
from = (char*)de - (char*)page_address(page);
to = from + rec_len;
err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
if (err)
goto out_unlock;
if (de->inode) {
ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
de1->rec_len = cpu_to_le16(rec_len - name_len);
de->rec_len = cpu_to_le16(name_len);
de = de1;
}
de->name_len = namelen;
memcpy (de->name, name, namelen);
de->inode = cpu_to_le32(inode->i_ino);
ext2_set_de_type (de, inode);
err = ext2_commit_chunk(page, from, to);
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
/* OFFSET_CACHE */
out_put:
ext2_put_page(page);
out:
return err;
out_unlock:
unlock_page(page);
goto out_put;
}
/*
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry. Page is up-to-date. Releases the page.
*/
int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page )
{
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
char *kaddr = page_address(page);
unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len);
ext2_dirent * pde = NULL;
ext2_dirent * de = (ext2_dirent *) (kaddr + from);
int err;
while ((char*)de < (char*)dir) {
if (de->rec_len == 0) {
ext2_error(inode->i_sb, __FUNCTION__,
"zero-length directory entry");
err = -EIO;
goto out;
}
pde = de;
de = ext2_next_entry(de);
}
if (pde)
from = (char*)pde - (char*)page_address(page);
lock_page(page);
err = mapping->a_ops->prepare_write(NULL, page, from, to);
BUG_ON(err);
if (pde)
pde->rec_len = cpu_to_le16(to-from);
dir->inode = 0;
err = ext2_commit_chunk(page, from, to);
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(inode);
out:
ext2_put_page(page);
return err;
}
/*
* Set the first fragment of directory.
*/
int ext2_make_empty(struct inode *inode, struct inode *parent)
{
struct address_space *mapping = inode->i_mapping;
struct page *page = grab_cache_page(mapping, 0);
unsigned chunk_size = ext2_chunk_size(inode);
struct ext2_dir_entry_2 * de;
int err;
void *kaddr;
if (!page)
return -ENOMEM;
err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size);
if (err) {
unlock_page(page);
goto fail;
}
kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, chunk_size);
de = (struct ext2_dir_entry_2 *)kaddr;
de->name_len = 1;
de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1));
memcpy (de->name, ".\0\0", 4);
de->inode = cpu_to_le32(inode->i_ino);
ext2_set_de_type (de, inode);
de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1));
de->name_len = 2;
de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1));
de->inode = cpu_to_le32(parent->i_ino);
memcpy (de->name, "..\0", 4);
ext2_set_de_type (de, inode);
kunmap_atomic(kaddr, KM_USER0);
err = ext2_commit_chunk(page, 0, chunk_size);
fail:
page_cache_release(page);
return err;
}
/*
* routine to check that the specified directory is empty (for rmdir)
*/
int ext2_empty_dir (struct inode * inode)
{
struct page *page = NULL;
unsigned long i, npages = dir_pages(inode);
for (i = 0; i < npages; i++) {
char *kaddr;
ext2_dirent * de;
page = ext2_get_page(inode, i);
if (IS_ERR(page))
continue;
kaddr = page_address(page);
de = (ext2_dirent *)kaddr;
kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1);
while ((char *)de <= kaddr) {
if (de->rec_len == 0) {
ext2_error(inode->i_sb, __FUNCTION__,
"zero-length directory entry");
printk("kaddr=%p, de=%p\n", kaddr, de);
goto not_empty;
}
if (de->inode != 0) {
/* check for . and .. */
if (de->name[0] != '.')
goto not_empty;
if (de->name_len > 2)
goto not_empty;
if (de->name_len < 2) {
if (de->inode !=
cpu_to_le32(inode->i_ino))
goto not_empty;
} else if (de->name[1] != '.')
goto not_empty;
}
de = ext2_next_entry(de);
}
ext2_put_page(page);
}
return 1;
not_empty:
ext2_put_page(page);
return 0;
}
const struct file_operations ext2_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = ext2_readdir,
.ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
.fsync = ext2_sync_file,
};

176
fs/ext2/ext2.h Normal file
View File

@@ -0,0 +1,176 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
/*
* ext2 mount options
*/
struct ext2_mount_options {
unsigned long s_mount_opt;
uid_t s_resuid;
gid_t s_resgid;
};
/*
* second extended file system inode data in memory
*/
struct ext2_inode_info {
__le32 i_data[15];
__u32 i_flags;
__u32 i_faddr;
__u8 i_frag_no;
__u8 i_frag_size;
__u16 i_state;
__u32 i_file_acl;
__u32 i_dir_acl;
__u32 i_dtime;
/*
* i_block_group is the number of the block group which contains
* this file's inode. Constant across the lifetime of the inode,
* it is ued for making block allocation decisions - we try to
* place a file's data blocks near its inode block, and new inodes
* near to their parent directory's inode.
*/
__u32 i_block_group;
/*
* i_next_alloc_block is the logical (file-relative) number of the
* most-recently-allocated block in this file. Yes, it is misnamed.
* We use this for detecting linearly ascending allocation requests.
*/
__u32 i_next_alloc_block;
/*
* i_next_alloc_goal is the *physical* companion to i_next_alloc_block.
* it the the physical block number of the block which was most-recently
* allocated to this file. This give us the goal (target) for the next
* allocation when we detect linearly ascending requests.
*/
__u32 i_next_alloc_goal;
__u32 i_prealloc_block;
__u32 i_prealloc_count;
__u32 i_dir_start_lookup;
#ifdef CONFIG_EXT2_FS_XATTR
/*
* Extended attributes can be read independently of the main file
* data. Taking i_mutex even when reading would cause contention
* between readers of EAs and writers of regular file data, so
* instead we synchronize on xattr_sem when reading or changing
* EAs.
*/
struct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT2_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
rwlock_t i_meta_lock;
struct inode vfs_inode;
};
/*
* Inode dynamic state flags
*/
#define EXT2_STATE_NEW 0x00000001 /* inode is newly created */
/*
* Function prototypes
*/
/*
* Ok, these declarations are also in <linux/kernel.h> but none of the
* ext2 source programs needs to include it so they are duplicated here.
*/
static inline struct ext2_inode_info *EXT2_I(struct inode *inode)
{
return container_of(inode, struct ext2_inode_info, vfs_inode);
}
/* balloc.c */
extern int ext2_bg_has_super(struct super_block *sb, int group);
extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
extern int ext2_new_block (struct inode *, unsigned long,
__u32 *, __u32 *, int *);
extern void ext2_free_blocks (struct inode *, unsigned long,
unsigned long);
extern unsigned long ext2_count_free_blocks (struct super_block *);
extern unsigned long ext2_count_dirs (struct super_block *);
extern void ext2_check_blocks_bitmap (struct super_block *);
extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
unsigned int block_group,
struct buffer_head ** bh);
/* dir.c */
extern int ext2_add_link (struct dentry *, struct inode *);
extern ino_t ext2_inode_by_name(struct inode *, struct dentry *);
extern int ext2_make_empty(struct inode *, struct inode *);
extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct dentry *, struct page **);
extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
extern int ext2_empty_dir (struct inode *);
extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *);
/* fsync.c */
extern int ext2_sync_file (struct file *, struct dentry *, int);
/* ialloc.c */
extern struct inode * ext2_new_inode (struct inode *, int);
extern void ext2_free_inode (struct inode *);
extern unsigned long ext2_count_free_inodes (struct super_block *);
extern void ext2_check_inodes_bitmap (struct super_block *);
extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
/* inode.c */
extern void ext2_read_inode (struct inode *);
extern int ext2_write_inode (struct inode *, int);
extern void ext2_put_inode (struct inode *);
extern void ext2_delete_inode (struct inode *);
extern int ext2_sync_inode (struct inode *);
extern void ext2_discard_prealloc (struct inode *);
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern void ext2_truncate (struct inode *);
extern int ext2_setattr (struct dentry *, struct iattr *);
extern void ext2_set_inode_flags(struct inode *inode);
/* ioctl.c */
extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
unsigned long);
extern long ext2_compat_ioctl(struct file *, unsigned int, unsigned long);
/* namei.c */
struct dentry *ext2_get_parent(struct dentry *child);
/* super.c */
extern void ext2_error (struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
extern void ext2_warning (struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
extern void ext2_update_dynamic_rev (struct super_block *sb);
extern void ext2_write_super (struct super_block *);
/*
* Inodes and files operations
*/
/* dir.c */
extern const struct file_operations ext2_dir_operations;
/* file.c */
extern const struct inode_operations ext2_file_inode_operations;
extern const struct file_operations ext2_file_operations;
extern const struct file_operations ext2_xip_file_operations;
/* inode.c */
extern const struct address_space_operations ext2_aops;
extern const struct address_space_operations ext2_aops_xip;
extern const struct address_space_operations ext2_nobh_aops;
/* namei.c */
extern const struct inode_operations ext2_dir_inode_operations;
extern const struct inode_operations ext2_special_inode_operations;
/* symlink.c */
extern const struct inode_operations ext2_fast_symlink_inode_operations;
extern const struct inode_operations ext2_symlink_inode_operations;

88
fs/ext2/file.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* 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
*
* 64-bit file support on 64-bit platforms by Jakub Jelinek
* (jj@sunsite.ms.mff.cuni.cz)
*/
#include <linux/time.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
/*
* Called when an inode is released. Note that this is different
* from ext2_open_file: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
static int ext2_release_file (struct inode * inode, struct file * filp)
{
if (filp->f_mode & FMODE_WRITE)
ext2_discard_prealloc (inode);
return 0;
}
/*
* We have mostly NULL's here: the current defaults are ok for
* the ext2 filesystem.
*/
const struct file_operations ext2_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write,
.ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
.mmap = generic_file_mmap,
.open = generic_file_open,
.release = ext2_release_file,
.fsync = ext2_sync_file,
.sendfile = generic_file_sendfile,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
};
#ifdef CONFIG_EXT2_FS_XIP
const struct file_operations ext2_xip_file_operations = {
.llseek = generic_file_llseek,
.read = xip_file_read,
.write = xip_file_write,
.ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext2_compat_ioctl,
#endif
.mmap = xip_file_mmap,
.open = generic_file_open,
.release = ext2_release_file,
.fsync = ext2_sync_file,
.sendfile = xip_file_sendfile,
};
#endif
const struct inode_operations ext2_file_inode_operations = {
.truncate = ext2_truncate,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.permission = ext2_permission,
};

51
fs/ext2/fsync.c Normal file
View File

@@ -0,0 +1,51 @@
/*
* 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
*
* Removed unnecessary code duplication for little endian machines
* and excessive __inline__s.
* Andi Kleen, 1997
*
* Major simplications and cleanup - we only need to do the metadata, because
* we can depend on generic_block_fdatasync() to sync the data blocks.
*/
#include "ext2.h"
#include <linux/smp_lock.h>
#include <linux/buffer_head.h> /* for sync_mapping_buffers() */
/*
* File may be NULL when we are called. Perhaps we shouldn't
* even pass file to fsync ?
*/
int ext2_sync_file(struct file *file, struct dentry *dentry, int datasync)
{
struct inode *inode = dentry->d_inode;
int err;
int ret;
ret = sync_mapping_buffers(inode->i_mapping);
if (!(inode->i_state & I_DIRTY))
return ret;
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
return ret;
err = ext2_sync_inode(inode);
if (ret == 0)
ret = err;
return ret;
}

698
fs/ext2/ialloc.c Normal file
View File

@@ -0,0 +1,698 @@
/*
* 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
*/
#include <linux/quotaops.h>
#include <linux/sched.h>
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include <linux/random.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
/*
* 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.
*/
/*
* Read the inode allocation bitmap for a given block_group, reading
* into the specified slot in the superblock's bitmap cache.
*
* Return buffer_head of bitmap on success or NULL.
*/
static struct buffer_head *
read_inode_bitmap(struct super_block * sb, unsigned long block_group)
{
struct ext2_group_desc *desc;
struct buffer_head *bh = NULL;
desc = ext2_get_group_desc(sb, block_group, NULL);
if (!desc)
goto error_out;
bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));
if (!bh)
ext2_error(sb, "read_inode_bitmap",
"Cannot read inode bitmap - "
"block_group = %lu, inode_bitmap = %u",
block_group, le32_to_cpu(desc->bg_inode_bitmap));
error_out:
return bh;
}
static void ext2_release_inode(struct super_block *sb, int group, int dir)
{
struct ext2_group_desc * desc;
struct buffer_head *bh;
desc = ext2_get_group_desc(sb, group, &bh);
if (!desc) {
ext2_error(sb, "ext2_release_inode",
"can't get descriptor for group %d", group);
return;
}
spin_lock(sb_bgl_lock(EXT2_SB(sb), group));
desc->bg_free_inodes_count =
cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) + 1);
if (dir)
desc->bg_used_dirs_count =
cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) - 1);
spin_unlock(sb_bgl_lock(EXT2_SB(sb), group));
if (dir)
percpu_counter_dec(&EXT2_SB(sb)->s_dirs_counter);
sb->s_dirt = 1;
mark_buffer_dirty(bh);
}
/*
* NOTE! When we get the inode, we're the only people
* that have access to it, and as such there are no
* race conditions we have to worry about. The inode
* is not on the hash-lists, and it cannot be reached
* through the filesystem because the directory entry
* has been deleted earlier.
*
* HOWEVER: we must make sure that we get no aliases,
* which means that we have to call "clear_inode()"
* _before_ we mark the inode not in use in the inode
* bitmaps. Otherwise a newly created file might use
* the same inode number (not actually the same pointer
* though), and then we'd have two inodes sharing the
* same inode number and space on the harddisk.
*/
void ext2_free_inode (struct inode * inode)
{
struct super_block * sb = inode->i_sb;
int is_directory;
unsigned long ino;
struct buffer_head *bitmap_bh = NULL;
unsigned long block_group;
unsigned long bit;
struct ext2_super_block * es;
ino = inode->i_ino;
ext2_debug ("freeing inode %lu\n", ino);
/*
* Note: we must free any quota before locking the superblock,
* as writing the quota to disk may need the lock as well.
*/
if (!is_bad_inode(inode)) {
/* Quota is already initialized in iput() */
ext2_xattr_delete_inode(inode);
DQUOT_FREE_INODE(inode);
DQUOT_DROP(inode);
}
es = EXT2_SB(sb)->s_es;
is_directory = S_ISDIR(inode->i_mode);
/* Do this BEFORE marking the inode not in use or returning an error */
clear_inode (inode);
if (ino < EXT2_FIRST_INO(sb) ||
ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_free_inode",
"reserved or nonexistent inode %lu", ino);
goto error_return;
}
block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
brelse(bitmap_bh);
bitmap_bh = read_inode_bitmap(sb, block_group);
if (!bitmap_bh)
goto error_return;
/* Ok, now we can actually update the inode bitmaps.. */
if (!ext2_clear_bit_atomic(sb_bgl_lock(EXT2_SB(sb), block_group),
bit, (void *) bitmap_bh->b_data))
ext2_error (sb, "ext2_free_inode",
"bit already cleared for inode %lu", ino);
else
ext2_release_inode(sb, block_group, is_directory);
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
error_return:
brelse(bitmap_bh);
}
/*
* We perform asynchronous prereading of the new inode's inode block when
* we create the inode, in the expectation that the inode will be written
* back soon. There are two reasons:
*
* - When creating a large number of files, the async prereads will be
* nicely merged into large reads
* - When writing out a large number of inodes, we don't need to keep on
* stalling the writes while we read the inode block.
*
* FIXME: ext2_get_group_desc() needs to be simplified.
*/
static void ext2_preread_inode(struct inode *inode)
{
unsigned long block_group;
unsigned long offset;
unsigned long block;
struct buffer_head *bh;
struct ext2_group_desc * gdp;
struct backing_dev_info *bdi;
bdi = inode->i_mapping->backing_dev_info;
if (bdi_read_congested(bdi))
return;
if (bdi_write_congested(bdi))
return;
block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
gdp = ext2_get_group_desc(inode->i_sb, block_group, &bh);
if (gdp == NULL)
return;
/*
* 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 = le32_to_cpu(gdp->bg_inode_table) +
(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
sb_breadahead(inode->i_sb, block);
}
/*
* 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.
*/
static int find_group_dir(struct super_block *sb, struct inode *parent)
{
int ngroups = EXT2_SB(sb)->s_groups_count;
int avefreei = ext2_count_free_inodes(sb) / ngroups;
struct ext2_group_desc *desc, *best_desc = NULL;
struct buffer_head *bh, *best_bh = NULL;
int group, best_group = -1;
for (group = 0; group < ngroups; group++) {
desc = ext2_get_group_desc (sb, group, &bh);
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
continue;
if (!best_desc ||
(le16_to_cpu(desc->bg_free_blocks_count) >
le16_to_cpu(best_desc->bg_free_blocks_count))) {
best_group = group;
best_desc = desc;
best_bh = bh;
}
}
if (!best_desc)
return -1;
return best_group;
}
/*
* Orlov's allocator for directories.
*
* We always try to spread first-level directories.
*
* If there are blockgroups with both free inodes and free blocks counts
* not worse than average we return one with smallest directory count.
* Otherwise we simply return a random group.
*
* For the rest rules look so:
*
* It's OK to put directory into a group unless
* it has too many directories already (max_dirs) or
* it has too few free inodes left (min_inodes) or
* it has too few free blocks left (min_blocks) or
* it's already running too large debt (max_debt).
* Parent's group is prefered, if it doesn't satisfy these
* conditions we search cyclically through the rest. If none
* of the groups look good we just look for a group with more
* free inodes than average (starting at parent's group).
*
* Debt is incremented each time we allocate a directory and decremented
* when we allocate an inode, within 0--255.
*/
#define INODE_COST 64
#define BLOCK_COST 256
static int find_group_orlov(struct super_block *sb, struct inode *parent)
{
int parent_group = EXT2_I(parent)->i_block_group;
struct ext2_sb_info *sbi = EXT2_SB(sb);
struct ext2_super_block *es = sbi->s_es;
int ngroups = sbi->s_groups_count;
int inodes_per_group = EXT2_INODES_PER_GROUP(sb);
int freei;
int avefreei;
int free_blocks;
int avefreeb;
int blocks_per_dir;
int ndirs;
int max_debt, max_dirs, min_blocks, min_inodes;
int group = -1, i;
struct ext2_group_desc *desc;
struct buffer_head *bh;
freei = percpu_counter_read_positive(&sbi->s_freeinodes_counter);
avefreei = freei / ngroups;
free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
avefreeb = free_blocks / ngroups;
ndirs = percpu_counter_read_positive(&sbi->s_dirs_counter);
if ((parent == sb->s_root->d_inode) ||
(EXT2_I(parent)->i_flags & EXT2_TOPDIR_FL)) {
struct ext2_group_desc *best_desc = NULL;
struct buffer_head *best_bh = NULL;
int best_ndir = inodes_per_group;
int best_group = -1;
get_random_bytes(&group, sizeof(group));
parent_group = (unsigned)group % ngroups;
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, &bh);
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_used_dirs_count) >= best_ndir)
continue;
if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
continue;
if (le16_to_cpu(desc->bg_free_blocks_count) < avefreeb)
continue;
best_group = group;
best_ndir = le16_to_cpu(desc->bg_used_dirs_count);
best_desc = desc;
best_bh = bh;
}
if (best_group >= 0) {
desc = best_desc;
bh = best_bh;
group = best_group;
goto found;
}
goto fallback;
}
if (ndirs == 0)
ndirs = 1; /* percpu_counters are approximate... */
blocks_per_dir = (le32_to_cpu(es->s_blocks_count)-free_blocks) / ndirs;
max_dirs = ndirs / ngroups + inodes_per_group / 16;
min_inodes = avefreei - inodes_per_group / 4;
min_blocks = avefreeb - EXT2_BLOCKS_PER_GROUP(sb) / 4;
max_debt = EXT2_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, BLOCK_COST);
if (max_debt * INODE_COST > inodes_per_group)
max_debt = inodes_per_group / INODE_COST;
if (max_debt > 255)
max_debt = 255;
if (max_debt == 0)
max_debt = 1;
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, &bh);
if (!desc || !desc->bg_free_inodes_count)
continue;
if (sbi->s_debts[group] >= max_debt)
continue;
if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs)
continue;
if (le16_to_cpu(desc->bg_free_inodes_count) < min_inodes)
continue;
if (le16_to_cpu(desc->bg_free_blocks_count) < min_blocks)
continue;
goto found;
}
fallback:
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext2_get_group_desc (sb, group, &bh);
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_free_inodes_count) >= avefreei)
goto found;
}
if (avefreei) {
/*
* The free-inodes counter is approximate, and for really small
* filesystems the above test can fail to find any blockgroups
*/
avefreei = 0;
goto fallback;
}
return -1;
found:
return group;
}
static int find_group_other(struct super_block *sb, struct inode *parent)
{
int parent_group = EXT2_I(parent)->i_block_group;
int ngroups = EXT2_SB(sb)->s_groups_count;
struct ext2_group_desc *desc;
struct buffer_head *bh;
int group, i;
/*
* Try to place the inode in its parent directory
*/
group = parent_group;
desc = ext2_get_group_desc (sb, group, &bh);
if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
le16_to_cpu(desc->bg_free_blocks_count))
goto found;
/*
* We're going to place this inode in a different blockgroup from its
* parent. We want to cause files in a common directory to all land in
* the same blockgroup. But we want files which are in a different
* directory which shares a blockgroup with our parent to land in a
* different blockgroup.
*
* So add our directory's i_ino into the starting point for the hash.
*/
group = (group + parent->i_ino) % ngroups;
/*
* Use a quadratic hash to find a group with a free inode and some
* free blocks.
*/
for (i = 1; i < ngroups; i <<= 1) {
group += i;
if (group >= ngroups)
group -= ngroups;
desc = ext2_get_group_desc (sb, group, &bh);
if (desc && le16_to_cpu(desc->bg_free_inodes_count) &&
le16_to_cpu(desc->bg_free_blocks_count))
goto found;
}
/*
* That failed: try linear search for a free inode, even if that group
* has no free blocks.
*/
group = parent_group;
for (i = 0; i < ngroups; i++) {
if (++group >= ngroups)
group = 0;
desc = ext2_get_group_desc (sb, group, &bh);
if (desc && le16_to_cpu(desc->bg_free_inodes_count))
goto found;
}
return -1;
found:
return group;
}
struct inode *ext2_new_inode(struct inode *dir, int mode)
{
struct super_block *sb;
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *bh2;
int group, i;
ino_t ino = 0;
struct inode * inode;
struct ext2_group_desc *gdp;
struct ext2_super_block *es;
struct ext2_inode_info *ei;
struct ext2_sb_info *sbi;
int err;
sb = dir->i_sb;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
ei = EXT2_I(inode);
sbi = EXT2_SB(sb);
es = sbi->s_es;
if (S_ISDIR(mode)) {
if (test_opt(sb, OLDALLOC))
group = find_group_dir(sb, dir);
else
group = find_group_orlov(sb, dir);
} else
group = find_group_other(sb, dir);
if (group == -1) {
err = -ENOSPC;
goto fail;
}
for (i = 0; i < sbi->s_groups_count; i++) {
gdp = ext2_get_group_desc(sb, group, &bh2);
brelse(bitmap_bh);
bitmap_bh = read_inode_bitmap(sb, group);
if (!bitmap_bh) {
err = -EIO;
goto fail;
}
ino = 0;
repeat_in_this_group:
ino = ext2_find_next_zero_bit((unsigned long *)bitmap_bh->b_data,
EXT2_INODES_PER_GROUP(sb), ino);
if (ino >= EXT2_INODES_PER_GROUP(sb)) {
/*
* Rare race: find_group_xx() decided that there were
* free inodes in this group, but by the time we tried
* to allocate one, they're all gone. This can also
* occur because the counters which find_group_orlov()
* uses are approximate. So just go and search the
* next block group.
*/
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
if (ext2_set_bit_atomic(sb_bgl_lock(sbi, group),
ino, bitmap_bh->b_data)) {
/* we lost this inode */
if (++ino >= EXT2_INODES_PER_GROUP(sb)) {
/* this group is exhausted, try next group */
if (++group == sbi->s_groups_count)
group = 0;
continue;
}
/* try to find free inode in the same group */
goto repeat_in_this_group;
}
goto got;
}
/*
* Scanned all blockgroups.
*/
err = -ENOSPC;
goto fail;
got:
mark_buffer_dirty(bitmap_bh);
if (sb->s_flags & MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
brelse(bitmap_bh);
ino += group * EXT2_INODES_PER_GROUP(sb) + 1;
if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
ext2_error (sb, "ext2_new_inode",
"reserved inode or inode > inodes count - "
"block_group = %d,inode=%lu", group,
(unsigned long) ino);
err = -EIO;
goto fail;
}
percpu_counter_mod(&sbi->s_freeinodes_counter, -1);
if (S_ISDIR(mode))
percpu_counter_inc(&sbi->s_dirs_counter);
spin_lock(sb_bgl_lock(sbi, group));
gdp->bg_free_inodes_count =
cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
if (S_ISDIR(mode)) {
if (sbi->s_debts[group] < 255)
sbi->s_debts[group]++;
gdp->bg_used_dirs_count =
cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
} else {
if (sbi->s_debts[group])
sbi->s_debts[group]--;
}
spin_unlock(sb_bgl_lock(sbi, group));
sb->s_dirt = 1;
mark_buffer_dirty(bh2);
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_mode = mode;
inode->i_ino = ino;
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
memset(ei->i_data, 0, sizeof(ei->i_data));
ei->i_flags = EXT2_I(dir)->i_flags & ~EXT2_BTREE_FL;
if (S_ISLNK(mode))
ei->i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
/* dirsync is only applied to directories */
if (!S_ISDIR(mode))
ei->i_flags &= ~EXT2_DIRSYNC_FL;
ei->i_faddr = 0;
ei->i_frag_no = 0;
ei->i_frag_size = 0;
ei->i_file_acl = 0;
ei->i_dir_acl = 0;
ei->i_dtime = 0;
ei->i_block_group = group;
ei->i_next_alloc_block = 0;
ei->i_next_alloc_goal = 0;
ei->i_prealloc_block = 0;
ei->i_prealloc_count = 0;
ei->i_dir_start_lookup = 0;
ei->i_state = EXT2_STATE_NEW;
ext2_set_inode_flags(inode);
spin_lock(&sbi->s_next_gen_lock);
inode->i_generation = sbi->s_next_generation++;
spin_unlock(&sbi->s_next_gen_lock);
insert_inode_hash(inode);
if (DQUOT_ALLOC_INODE(inode)) {
err = -EDQUOT;
goto fail_drop;
}
err = ext2_init_acl(inode, dir);
if (err)
goto fail_free_drop;
err = ext2_init_security(inode,dir);
if (err)
goto fail_free_drop;
mark_inode_dirty(inode);
ext2_debug("allocating inode %lu\n", inode->i_ino);
ext2_preread_inode(inode);
return inode;
fail_free_drop:
DQUOT_FREE_INODE(inode);
fail_drop:
DQUOT_DROP(inode);
inode->i_flags |= S_NOQUOTA;
inode->i_nlink = 0;
iput(inode);
return ERR_PTR(err);
fail:
make_bad_inode(inode);
iput(inode);
return ERR_PTR(err);
}
unsigned long ext2_count_free_inodes (struct super_block * sb)
{
struct ext2_group_desc *desc;
unsigned long desc_count = 0;
int i;
#ifdef EXT2FS_DEBUG
struct ext2_super_block *es;
unsigned long bitmap_count = 0;
struct buffer_head *bitmap_bh = NULL;
es = EXT2_SB(sb)->s_es;
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
unsigned x;
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
desc_count += le16_to_cpu(desc->bg_free_inodes_count);
brelse(bitmap_bh);
bitmap_bh = read_inode_bitmap(sb, i);
if (!bitmap_bh)
continue;
x = ext2_count_free(bitmap_bh, EXT2_INODES_PER_GROUP(sb) / 8);
printk("group %d: stored = %d, counted = %u\n",
i, le16_to_cpu(desc->bg_free_inodes_count), x);
bitmap_count += x;
}
brelse(bitmap_bh);
printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
percpu_counter_read(&EXT2_SB(sb)->s_freeinodes_counter),
desc_count, bitmap_count);
return desc_count;
#else
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
desc = ext2_get_group_desc (sb, i, NULL);
if (!desc)
continue;
desc_count += le16_to_cpu(desc->bg_free_inodes_count);
}
return desc_count;
#endif
}
/* Called at mount-time, super-block is locked */
unsigned long ext2_count_dirs (struct super_block * sb)
{
unsigned long count = 0;
int i;
for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) {
struct ext2_group_desc *gdp = ext2_get_group_desc (sb, i, NULL);
if (!gdp)
continue;
count += le16_to_cpu(gdp->bg_used_dirs_count);
}
return count;
}

1307
fs/ext2/inode.c Normal file

File diff suppressed because it is too large Load Diff

118
fs/ext2/ioctl.c Normal file
View File

@@ -0,0 +1,118 @@
/*
* 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 "ext2.h"
#include <linux/capability.h>
#include <linux/time.h>
#include <linux/sched.h>
#include <linux/compat.h>
#include <linux/smp_lock.h>
#include <asm/current.h>
#include <asm/uaccess.h>
int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
unsigned long arg)
{
struct ext2_inode_info *ei = EXT2_I(inode);
unsigned int flags;
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
switch (cmd) {
case EXT2_IOC_GETFLAGS:
flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
return put_user(flags, (int __user *) arg);
case EXT2_IOC_SETFLAGS: {
unsigned int oldflags;
if (IS_RDONLY(inode))
return -EROFS;
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EACCES;
if (get_user(flags, (int __user *) arg))
return -EFAULT;
if (!S_ISDIR(inode->i_mode))
flags &= ~EXT2_DIRSYNC_FL;
mutex_lock(&inode->i_mutex);
oldflags = ei->i_flags;
/*
* The IMMUTABLE and APPEND_ONLY flags can only be changed by
* the relevant capability.
*
* This test looks nicer. Thanks to Pauline Middelink
*/
if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
if (!capable(CAP_LINUX_IMMUTABLE)) {
mutex_unlock(&inode->i_mutex);
return -EPERM;
}
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
ei->i_flags = flags;
mutex_unlock(&inode->i_mutex);
ext2_set_inode_flags(inode);
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return 0;
}
case EXT2_IOC_GETVERSION:
return put_user(inode->i_generation, (int __user *) arg);
case EXT2_IOC_SETVERSION:
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EPERM;
if (IS_RDONLY(inode))
return -EROFS;
if (get_user(inode->i_generation, (int __user *) arg))
return -EFAULT;
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return 0;
default:
return -ENOTTY;
}
}
#ifdef CONFIG_COMPAT
long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
int ret;
/* These are just misnamed, they actually get/put from/to user an int */
switch (cmd) {
case EXT2_IOC32_GETFLAGS:
cmd = EXT2_IOC_GETFLAGS;
break;
case EXT2_IOC32_SETFLAGS:
cmd = EXT2_IOC_SETFLAGS;
break;
case EXT2_IOC32_GETVERSION:
cmd = EXT2_IOC_GETVERSION;
break;
case EXT2_IOC32_SETVERSION:
cmd = EXT2_IOC_SETVERSION;
break;
default:
return -ENOIOCTLCMD;
}
lock_kernel();
ret = ext2_ioctl(inode, file, cmd, (unsigned long) compat_ptr(arg));
unlock_kernel();
return ret;
}
#endif

405
fs/ext2/namei.c Normal file
View File

@@ -0,0 +1,405 @@
/*
* linux/fs/ext2/namei.c
*
* Rewrite to pagecache. Almost all code had been changed, so blame me
* if the things go wrong. Please, send bug reports to
* viro@parcelfarce.linux.theplanet.co.uk
*
* Stuff here is basically a glue between the VFS and generic UNIXish
* filesystem that keeps everything in pagecache. All knowledge of the
* directory layout is in fs/ext2/dir.c - it turned out to be easily separatable
* and it's easier to debug that way. In principle we might want to
* generalize that a bit and turn it into a library. Or not.
*
* The only non-static object here is ext2_dir_inode_operations.
*
* TODO: get rid of kmap() use, add readahead.
*
* 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 <linux/pagemap.h>
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
#include "xip.h"
static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
int err = ext2_add_link(dentry, inode);
if (!err) {
d_instantiate(dentry, inode);
return 0;
}
inode_dec_link_count(inode);
iput(inode);
return err;
}
/*
* Methods themselves.
*/
static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
{
struct inode * inode;
ino_t ino;
if (dentry->d_name.len > EXT2_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
ino = ext2_inode_by_name(dir, dentry);
inode = NULL;
if (ino) {
inode = iget(dir->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
}
return d_splice_alias(inode, dentry);
}
struct dentry *ext2_get_parent(struct dentry *child)
{
unsigned long ino;
struct dentry *parent;
struct inode *inode;
struct dentry dotdot;
dotdot.d_name.name = "..";
dotdot.d_name.len = 2;
ino = ext2_inode_by_name(child->d_inode, &dotdot);
if (!ino)
return ERR_PTR(-ENOENT);
inode = iget(child->d_inode->i_sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
parent = d_alloc_anon(inode);
if (!parent) {
iput(inode);
parent = ERR_PTR(-ENOMEM);
}
return parent;
}
/*
* By the time this is called, we already have created
* the directory cache entry for the new file, but it
* is so far negative - it has no inode.
*
* If the create succeeds, we fill in the inode information
* with d_instantiate().
*/
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
{
struct inode * inode = ext2_new_inode (dir, mode);
int err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
inode->i_op = &ext2_file_inode_operations;
if (ext2_use_xip(inode->i_sb)) {
inode->i_mapping->a_ops = &ext2_aops_xip;
inode->i_fop = &ext2_xip_file_operations;
} else if (test_opt(inode->i_sb, NOBH)) {
inode->i_mapping->a_ops = &ext2_nobh_aops;
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops;
inode->i_fop = &ext2_file_operations;
}
mark_inode_dirty(inode);
err = ext2_add_nondir(dentry, inode);
}
return err;
}
static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
{
struct inode * inode;
int err;
if (!new_valid_dev(rdev))
return -EINVAL;
inode = ext2_new_inode (dir, mode);
err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
init_special_inode(inode, inode->i_mode, rdev);
#ifdef CONFIG_EXT2_FS_XATTR
inode->i_op = &ext2_special_inode_operations;
#endif
mark_inode_dirty(inode);
err = ext2_add_nondir(dentry, inode);
}
return err;
}
static int ext2_symlink (struct inode * dir, struct dentry * dentry,
const char * symname)
{
struct super_block * sb = dir->i_sb;
int err = -ENAMETOOLONG;
unsigned l = strlen(symname)+1;
struct inode * inode;
if (l > sb->s_blocksize)
goto out;
inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out;
if (l > sizeof (EXT2_I(inode)->i_data)) {
/* slow symlink */
inode->i_op = &ext2_symlink_inode_operations;
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
inode->i_mapping->a_ops = &ext2_aops;
err = page_symlink(inode, symname, l);
if (err)
goto out_fail;
} else {
/* fast symlink */
inode->i_op = &ext2_fast_symlink_inode_operations;
memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
inode->i_size = l-1;
}
mark_inode_dirty(inode);
err = ext2_add_nondir(dentry, inode);
out:
return err;
out_fail:
inode_dec_link_count(inode);
iput (inode);
goto out;
}
static int ext2_link (struct dentry * old_dentry, struct inode * dir,
struct dentry *dentry)
{
struct inode *inode = old_dentry->d_inode;
if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;
inode->i_ctime = CURRENT_TIME_SEC;
inode_inc_link_count(inode);
atomic_inc(&inode->i_count);
return ext2_add_nondir(dentry, inode);
}
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
int err = -EMLINK;
if (dir->i_nlink >= EXT2_LINK_MAX)
goto out;
inode_inc_link_count(dir);
inode = ext2_new_inode (dir, S_IFDIR | mode);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_dir;
inode->i_op = &ext2_dir_inode_operations;
inode->i_fop = &ext2_dir_operations;
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
inode->i_mapping->a_ops = &ext2_aops;
inode_inc_link_count(inode);
err = ext2_make_empty(inode, dir);
if (err)
goto out_fail;
err = ext2_add_link(dentry, inode);
if (err)
goto out_fail;
d_instantiate(dentry, inode);
out:
return err;
out_fail:
inode_dec_link_count(inode);
inode_dec_link_count(inode);
iput(inode);
out_dir:
inode_dec_link_count(dir);
goto out;
}
static int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
struct ext2_dir_entry_2 * de;
struct page * page;
int err = -ENOENT;
de = ext2_find_entry (dir, dentry, &page);
if (!de)
goto out;
err = ext2_delete_entry (de, page);
if (err)
goto out;
inode->i_ctime = dir->i_ctime;
inode_dec_link_count(inode);
err = 0;
out:
return err;
}
static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
int err = -ENOTEMPTY;
if (ext2_empty_dir(inode)) {
err = ext2_unlink(dir, dentry);
if (!err) {
inode->i_size = 0;
inode_dec_link_count(inode);
inode_dec_link_count(dir);
}
}
return err;
}
static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
struct inode * new_dir, struct dentry * new_dentry )
{
struct inode * old_inode = old_dentry->d_inode;
struct inode * new_inode = new_dentry->d_inode;
struct page * dir_page = NULL;
struct ext2_dir_entry_2 * dir_de = NULL;
struct page * old_page;
struct ext2_dir_entry_2 * old_de;
int err = -ENOENT;
old_de = ext2_find_entry (old_dir, old_dentry, &old_page);
if (!old_de)
goto out;
if (S_ISDIR(old_inode->i_mode)) {
err = -EIO;
dir_de = ext2_dotdot(old_inode, &dir_page);
if (!dir_de)
goto out_old;
}
if (new_inode) {
struct page *new_page;
struct ext2_dir_entry_2 *new_de;
err = -ENOTEMPTY;
if (dir_de && !ext2_empty_dir (new_inode))
goto out_dir;
err = -ENOENT;
new_de = ext2_find_entry (new_dir, new_dentry, &new_page);
if (!new_de)
goto out_dir;
inode_inc_link_count(old_inode);
ext2_set_link(new_dir, new_de, new_page, old_inode);
new_inode->i_ctime = CURRENT_TIME_SEC;
if (dir_de)
drop_nlink(new_inode);
inode_dec_link_count(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= EXT2_LINK_MAX)
goto out_dir;
}
inode_inc_link_count(old_inode);
err = ext2_add_link(new_dentry, old_inode);
if (err) {
inode_dec_link_count(old_inode);
goto out_dir;
}
if (dir_de)
inode_inc_link_count(new_dir);
}
/*
* Like most other Unix systems, set the ctime for inodes on a
* rename.
* inode_dec_link_count() will mark the inode dirty.
*/
old_inode->i_ctime = CURRENT_TIME_SEC;
ext2_delete_entry (old_de, old_page);
inode_dec_link_count(old_inode);
if (dir_de) {
ext2_set_link(old_inode, dir_de, dir_page, new_dir);
inode_dec_link_count(old_dir);
}
return 0;
out_dir:
if (dir_de) {
kunmap(dir_page);
page_cache_release(dir_page);
}
out_old:
kunmap(old_page);
page_cache_release(old_page);
out:
return err;
}
const struct inode_operations ext2_dir_inode_operations = {
.create = ext2_create,
.lookup = ext2_lookup,
.link = ext2_link,
.unlink = ext2_unlink,
.symlink = ext2_symlink,
.mkdir = ext2_mkdir,
.rmdir = ext2_rmdir,
.mknod = ext2_mknod,
.rename = ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.permission = ext2_permission,
};
const struct inode_operations ext2_special_inode_operations = {
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
.setattr = ext2_setattr,
.permission = ext2_permission,
};

1293
fs/ext2/super.c Normal file

File diff suppressed because it is too large Load Diff

52
fs/ext2/symlink.c Normal file
View File

@@ -0,0 +1,52 @@
/*
* linux/fs/ext2/symlink.c
*
* Only fast symlinks left here - the rest is done by generic code. AV, 1999
*
* 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 "ext2.h"
#include "xattr.h"
#include <linux/namei.h>
static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
nd_set_link(nd, (char *)ei->i_data);
return NULL;
}
const struct inode_operations ext2_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.put_link = page_put_link,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
};
const struct inode_operations ext2_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = ext2_follow_link,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
};

1037
fs/ext2/xattr.c Normal file

File diff suppressed because it is too large Load Diff

125
fs/ext2/xattr.h Normal file
View File

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

75
fs/ext2/xattr_security.c Normal file
View File

@@ -0,0 +1,75 @@
/*
* linux/fs/ext2/xattr_security.c
* Handler for storing security labels as extended attributes.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/ext2_fs.h>
#include <linux/security.h>
#include "xattr.h"
static size_t
ext2_xattr_security_list(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const int prefix_len = sizeof(XATTR_SECURITY_PREFIX)-1;
const size_t total_len = prefix_len + name_len + 1;
if (list && total_len <= list_size) {
memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
memcpy(list+prefix_len, name, name_len);
list[prefix_len + name_len] = '\0';
}
return total_len;
}
static int
ext2_xattr_security_get(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return ext2_xattr_get(inode, EXT2_XATTR_INDEX_SECURITY, name,
buffer, size);
}
static int
ext2_xattr_security_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY, name,
value, size, flags);
}
int
ext2_init_security(struct inode *inode, struct inode *dir)
{
int err;
size_t len;
void *value;
char *name;
err = security_inode_init_security(inode, dir, &name, &value, &len);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
name, value, len, 0);
kfree(name);
kfree(value);
return err;
}
struct xattr_handler ext2_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.list = ext2_xattr_security_list,
.get = ext2_xattr_security_get,
.set = ext2_xattr_security_set,
};

61
fs/ext2/xattr_trusted.c Normal file
View File

@@ -0,0 +1,61 @@
/*
* linux/fs/ext2/xattr_trusted.c
* Handler for trusted extended attributes.
*
* Copyright (C) 2003 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/capability.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/ext2_fs.h>
#include "xattr.h"
#define XATTR_TRUSTED_PREFIX "trusted."
static size_t
ext2_xattr_trusted_list(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const int prefix_len = sizeof(XATTR_TRUSTED_PREFIX)-1;
const size_t total_len = prefix_len + name_len + 1;
if (!capable(CAP_SYS_ADMIN))
return 0;
if (list && total_len <= list_size) {
memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
memcpy(list+prefix_len, name, name_len);
list[prefix_len + name_len] = '\0';
}
return total_len;
}
static int
ext2_xattr_trusted_get(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return ext2_xattr_get(inode, EXT2_XATTR_INDEX_TRUSTED, name,
buffer, size);
}
static int
ext2_xattr_trusted_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") == 0)
return -EINVAL;
return ext2_xattr_set(inode, EXT2_XATTR_INDEX_TRUSTED, name,
value, size, flags);
}
struct xattr_handler ext2_xattr_trusted_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
.list = ext2_xattr_trusted_list,
.get = ext2_xattr_trusted_get,
.set = ext2_xattr_trusted_set,
};

63
fs/ext2/xattr_user.c Normal file
View File

@@ -0,0 +1,63 @@
/*
* linux/fs/ext2/xattr_user.c
* Handler for extended user attributes.
*
* Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
#include "ext2.h"
#include "xattr.h"
#define XATTR_USER_PREFIX "user."
static size_t
ext2_xattr_user_list(struct inode *inode, char *list, size_t list_size,
const char *name, size_t name_len)
{
const size_t prefix_len = sizeof(XATTR_USER_PREFIX)-1;
const size_t total_len = prefix_len + name_len + 1;
if (!test_opt(inode->i_sb, XATTR_USER))
return 0;
if (list && total_len <= list_size) {
memcpy(list, XATTR_USER_PREFIX, prefix_len);
memcpy(list+prefix_len, name, name_len);
list[prefix_len + name_len] = '\0';
}
return total_len;
}
static int
ext2_xattr_user_get(struct inode *inode, const char *name,
void *buffer, size_t size)
{
if (strcmp(name, "") == 0)
return -EINVAL;
if (!test_opt(inode->i_sb, XATTR_USER))
return -EOPNOTSUPP;
return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name, buffer, size);
}
static int
ext2_xattr_user_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
if (strcmp(name, "") == 0)
return -EINVAL;
if (!test_opt(inode->i_sb, XATTR_USER))
return -EOPNOTSUPP;
return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
value, size, flags);
}
struct xattr_handler ext2_xattr_user_handler = {
.prefix = XATTR_USER_PREFIX,
.list = ext2_xattr_user_list,
.get = ext2_xattr_user_get,
.set = ext2_xattr_user_set,
};

93
fs/ext2/xip.c Normal file
View File

@@ -0,0 +1,93 @@
/*
* linux/fs/ext2/xip.c
*
* Copyright (C) 2005 IBM Corporation
* Author: Carsten Otte (cotte@de.ibm.com)
*/
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/buffer_head.h>
#include <linux/ext2_fs_sb.h>
#include <linux/ext2_fs.h>
#include "ext2.h"
#include "xip.h"
static inline int
__inode_direct_access(struct inode *inode, sector_t sector,
unsigned long *data)
{
BUG_ON(!inode->i_sb->s_bdev->bd_disk->fops->direct_access);
return inode->i_sb->s_bdev->bd_disk->fops
->direct_access(inode->i_sb->s_bdev,sector,data);
}
static inline int
__ext2_get_sector(struct inode *inode, sector_t offset, int create,
sector_t *result)
{
struct buffer_head tmp;
int rc;
memset(&tmp, 0, sizeof(struct buffer_head));
rc = ext2_get_block(inode, offset/ (PAGE_SIZE/512), &tmp,
create);
*result = tmp.b_blocknr;
/* did we get a sparse block (hole in the file)? */
if (!tmp.b_blocknr && !rc) {
BUG_ON(create);
rc = -ENODATA;
}
return rc;
}
int
ext2_clear_xip_target(struct inode *inode, int block)
{
sector_t sector = block * (PAGE_SIZE/512);
unsigned long data;
int rc;
rc = __inode_direct_access(inode, sector, &data);
if (!rc)
clear_page((void*)data);
return rc;
}
void ext2_xip_verify_sb(struct super_block *sb)
{
struct ext2_sb_info *sbi = EXT2_SB(sb);
if ((sbi->s_mount_opt & EXT2_MOUNT_XIP) &&
!sb->s_bdev->bd_disk->fops->direct_access) {
sbi->s_mount_opt &= (~EXT2_MOUNT_XIP);
ext2_warning(sb, __FUNCTION__,
"ignoring xip option - not supported by bdev");
}
}
struct page *
ext2_get_xip_page(struct address_space *mapping, sector_t offset,
int create)
{
int rc;
unsigned long data;
sector_t sector;
/* first, retrieve the sector number */
rc = __ext2_get_sector(mapping->host, offset, create, &sector);
if (rc)
goto error;
/* retrieve address of the target data */
rc = __inode_direct_access
(mapping->host, sector * (PAGE_SIZE/512), &data);
if (!rc)
return virt_to_page(data);
error:
return ERR_PTR(rc);
}

25
fs/ext2/xip.h Normal file
View File

@@ -0,0 +1,25 @@
/*
* linux/fs/ext2/xip.h
*
* Copyright (C) 2005 IBM Corporation
* Author: Carsten Otte (cotte@de.ibm.com)
*/
#ifdef CONFIG_EXT2_FS_XIP
extern void ext2_xip_verify_sb (struct super_block *);
extern int ext2_clear_xip_target (struct inode *, int);
static inline int ext2_use_xip (struct super_block *sb)
{
struct ext2_sb_info *sbi = EXT2_SB(sb);
return (sbi->s_mount_opt & EXT2_MOUNT_XIP);
}
struct page* ext2_get_xip_page (struct address_space *, sector_t, int);
#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_page)
#else
#define mapping_is_xip(map) 0
#define ext2_xip_verify_sb(sb) do { } while (0)
#define ext2_use_xip(sb) 0
#define ext2_clear_xip_target(inode, chain) 0
#define ext2_get_xip_page NULL
#endif