mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
55
fs/ext2/Kconfig
Normal file
55
fs/ext2/Kconfig
Normal file
|
@ -0,0 +1,55 @@
|
|||
config EXT2_FS
|
||||
tristate "Second extended fs support"
|
||||
help
|
||||
Ext2 is a standard Linux file system for hard disks.
|
||||
|
||||
To compile this file system support as a module, choose M here: the
|
||||
module will be called ext2.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config EXT2_FS_XATTR
|
||||
bool "Ext2 extended attributes"
|
||||
depends on EXT2_FS
|
||||
help
|
||||
Extended attributes are name:value pairs associated with inodes by
|
||||
the kernel or by users (see the attr(5) manual page, or visit
|
||||
<http://acl.bestbits.at/> for details).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config EXT2_FS_POSIX_ACL
|
||||
bool "Ext2 POSIX Access Control Lists"
|
||||
depends on EXT2_FS_XATTR
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
Posix Access Control Lists (ACLs) support permissions for users and
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the Posix ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
||||
If you don't know what Access Control Lists are, say N
|
||||
|
||||
config EXT2_FS_SECURITY
|
||||
bool "Ext2 Security Labels"
|
||||
depends on EXT2_FS_XATTR
|
||||
help
|
||||
Security labels support alternative access control models
|
||||
implemented by security modules like SELinux. This option
|
||||
enables an extended attribute handler for file security
|
||||
labels in the ext2 filesystem.
|
||||
|
||||
If you are not using a security module that requires using
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config EXT2_FS_XIP
|
||||
bool "Ext2 execute in place support"
|
||||
depends on EXT2_FS && MMU
|
||||
help
|
||||
Execute in place can be used on memory-backed block devices. If you
|
||||
enable this option, you can select to mount block devices which are
|
||||
capable of this feature without using the page cache.
|
||||
|
||||
If you do not use a block device that is capable of using this,
|
||||
or if unsure, say N.
|
13
fs/ext2/Makefile
Normal file
13
fs/ext2/Makefile
Normal 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 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
|
257
fs/ext2/acl.c
Normal file
257
fs/ext2/acl.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* linux/fs/ext2/acl.c
|
||||
*
|
||||
* Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
|
||||
*/
|
||||
|
||||
#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);
|
||||
break;
|
||||
|
||||
case ACL_USER:
|
||||
value = (char *)value + sizeof(ext2_acl_entry);
|
||||
if ((char *)value > end)
|
||||
goto fail;
|
||||
acl->a_entries[n].e_uid =
|
||||
make_kuid(&init_user_ns,
|
||||
le32_to_cpu(entry->e_id));
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
value = (char *)value + sizeof(ext2_acl_entry);
|
||||
if ((char *)value > end)
|
||||
goto fail;
|
||||
acl->a_entries[n].e_gid =
|
||||
make_kgid(&init_user_ns,
|
||||
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++) {
|
||||
const struct posix_acl_entry *acl_e = &acl->a_entries[n];
|
||||
ext2_acl_entry *entry = (ext2_acl_entry *)e;
|
||||
entry->e_tag = cpu_to_le16(acl_e->e_tag);
|
||||
entry->e_perm = cpu_to_le16(acl_e->e_perm);
|
||||
switch(acl_e->e_tag) {
|
||||
case ACL_USER:
|
||||
entry->e_id = cpu_to_le32(
|
||||
from_kuid(&init_user_ns, acl_e->e_uid));
|
||||
e += sizeof(ext2_acl_entry);
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
entry->e_id = cpu_to_le32(
|
||||
from_kgid(&init_user_ns, acl_e->e_gid));
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* inode->i_mutex: don't care
|
||||
*/
|
||||
struct posix_acl *
|
||||
ext2_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
int name_index;
|
||||
char *value = NULL;
|
||||
struct posix_acl *acl;
|
||||
int retval;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
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))
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
return acl;
|
||||
}
|
||||
|
||||
/*
|
||||
* inode->i_mutex: down
|
||||
*/
|
||||
int
|
||||
ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
||||
{
|
||||
int name_index;
|
||||
void *value = NULL;
|
||||
size_t size = 0;
|
||||
int error;
|
||||
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
|
||||
if (acl) {
|
||||
error = posix_acl_equiv_mode(acl, &inode->i_mode);
|
||||
if (error < 0)
|
||||
return error;
|
||||
else {
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
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)
|
||||
set_cached_acl(inode, type, acl);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *default_acl, *acl;
|
||||
int error;
|
||||
|
||||
error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (default_acl) {
|
||||
error = ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
|
||||
posix_acl_release(default_acl);
|
||||
}
|
||||
if (acl) {
|
||||
if (!error)
|
||||
error = ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
return error;
|
||||
}
|
71
fs/ext2/acl.h
Normal file
71
fs/ext2/acl.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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
|
||||
|
||||
/* acl.c */
|
||||
extern struct posix_acl *ext2_get_acl(struct inode *inode, int type);
|
||||
extern int ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
|
||||
extern int ext2_init_acl (struct inode *, struct inode *);
|
||||
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#define ext2_get_acl NULL
|
||||
#define ext2_set_acl NULL
|
||||
|
||||
static inline int ext2_init_acl (struct inode *inode, struct inode *dir)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
1536
fs/ext2/balloc.c
Normal file
1536
fs/ext2/balloc.c
Normal file
File diff suppressed because it is too large
Load diff
730
fs/ext2/dir.c
Normal file
730
fs/ext2/dir.c
Normal file
|
@ -0,0 +1,730 @@
|
|||
/*
|
||||
* 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/buffer_head.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/swap.h>
|
||||
|
||||
typedef struct ext2_dir_entry_2 ext2_dirent;
|
||||
|
||||
/*
|
||||
* Tests against MAX_REC_LEN etc were put in place for 64k block
|
||||
* sizes; if that is not possible on this arch, we can skip
|
||||
* those tests and speed things up.
|
||||
*/
|
||||
static inline unsigned ext2_rec_len_from_disk(__le16 dlen)
|
||||
{
|
||||
unsigned len = le16_to_cpu(dlen);
|
||||
|
||||
#if (PAGE_CACHE_SIZE >= 65536)
|
||||
if (len == EXT2_MAX_REC_LEN)
|
||||
return 1 << 16;
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline __le16 ext2_rec_len_to_disk(unsigned len)
|
||||
{
|
||||
#if (PAGE_CACHE_SIZE >= 65536)
|
||||
if (len == (1 << 16))
|
||||
return cpu_to_le16(EXT2_MAX_REC_LEN);
|
||||
else
|
||||
BUG_ON(len > (1 << 16));
|
||||
#endif
|
||||
return cpu_to_le16(len);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, loff_t pos, unsigned len)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *dir = mapping->host;
|
||||
int err = 0;
|
||||
|
||||
dir->i_version++;
|
||||
block_write_end(NULL, mapping, pos, len, len, page, NULL);
|
||||
|
||||
if (pos+len > dir->i_size) {
|
||||
i_size_write(dir, pos+len);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
|
||||
if (IS_DIRSYNC(dir)) {
|
||||
err = write_one_page(page, 1);
|
||||
if (!err)
|
||||
err = sync_inode_metadata(dir, 1);
|
||||
} else {
|
||||
unlock_page(page);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ext2_check_page(struct page *page, int quiet)
|
||||
{
|
||||
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 = ext2_rec_len_from_disk(p->rec_len);
|
||||
|
||||
if (unlikely(rec_len < EXT2_DIR_REC_LEN(1)))
|
||||
goto Eshort;
|
||||
if (unlikely(rec_len & 3))
|
||||
goto Ealign;
|
||||
if (unlikely(rec_len < EXT2_DIR_REC_LEN(p->name_len)))
|
||||
goto Enamelen;
|
||||
if (unlikely(((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)))
|
||||
goto Espan;
|
||||
if (unlikely(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:
|
||||
if (!quiet)
|
||||
ext2_error(sb, __func__,
|
||||
"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:
|
||||
if (!quiet)
|
||||
ext2_error(sb, __func__, "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:
|
||||
if (!quiet) {
|
||||
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,
|
||||
int quiet)
|
||||
{
|
||||
struct address_space *mapping = dir->i_mapping;
|
||||
struct page *page = read_mapping_page(mapping, n, NULL);
|
||||
if (!IS_ERR(page)) {
|
||||
kmap(page);
|
||||
if (!PageChecked(page))
|
||||
ext2_check_page(page, quiet);
|
||||
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 +
|
||||
ext2_rec_len_from_disk(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)
|
||||
{
|
||||
umode_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 *file, struct dir_context *ctx)
|
||||
{
|
||||
loff_t pos = ctx->pos;
|
||||
struct inode *inode = file_inode(file);
|
||||
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 = file->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, 0);
|
||||
|
||||
if (IS_ERR(page)) {
|
||||
ext2_error(sb, __func__,
|
||||
"bad page in #%lu",
|
||||
inode->i_ino);
|
||||
ctx->pos += PAGE_CACHE_SIZE - offset;
|
||||
return PTR_ERR(page);
|
||||
}
|
||||
kaddr = page_address(page);
|
||||
if (unlikely(need_revalidate)) {
|
||||
if (offset) {
|
||||
offset = ext2_validate_entry(kaddr, offset, chunk_mask);
|
||||
ctx->pos = (n<<PAGE_CACHE_SHIFT) + offset;
|
||||
}
|
||||
file->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, __func__,
|
||||
"zero-length directory entry");
|
||||
ext2_put_page(page);
|
||||
return -EIO;
|
||||
}
|
||||
if (de->inode) {
|
||||
unsigned char d_type = DT_UNKNOWN;
|
||||
|
||||
if (types && de->file_type < EXT2_FT_MAX)
|
||||
d_type = types[de->file_type];
|
||||
|
||||
if (!dir_emit(ctx, de->name, de->name_len,
|
||||
le32_to_cpu(de->inode),
|
||||
d_type)) {
|
||||
ext2_put_page(page);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ctx->pos += ext2_rec_len_from_disk(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 (as a parameter - res_page),
|
||||
* and the entry itself. Page is returned mapped and unlocked.
|
||||
* Entry is guaranteed to be valid.
|
||||
*/
|
||||
struct ext2_dir_entry_2 *ext2_find_entry (struct inode * dir,
|
||||
struct qstr *child, struct page ** res_page)
|
||||
{
|
||||
const char *name = child->name;
|
||||
int namelen = child->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;
|
||||
int dir_has_error = 0;
|
||||
|
||||
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, dir_has_error);
|
||||
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, __func__,
|
||||
"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);
|
||||
} else
|
||||
dir_has_error = 1;
|
||||
|
||||
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, __func__,
|
||||
"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, 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 qstr *child)
|
||||
{
|
||||
ino_t res = 0;
|
||||
struct ext2_dir_entry_2 *de;
|
||||
struct page *page;
|
||||
|
||||
de = ext2_find_entry (dir, child, &page);
|
||||
if (de) {
|
||||
res = le32_to_cpu(de->inode);
|
||||
ext2_put_page(page);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
{
|
||||
return __block_write_begin(page, pos, len, ext2_get_block);
|
||||
}
|
||||
|
||||
/* Releases the page */
|
||||
void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
|
||||
struct page *page, struct inode *inode, int update_times)
|
||||
{
|
||||
loff_t pos = page_offset(page) +
|
||||
(char *) de - (char *) page_address(page);
|
||||
unsigned len = ext2_rec_len_from_disk(de->rec_len);
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
err = ext2_prepare_chunk(page, pos, len);
|
||||
BUG_ON(err);
|
||||
de->inode = cpu_to_le32(inode->i_ino);
|
||||
ext2_set_de_type(de, inode);
|
||||
err = ext2_commit_chunk(page, pos, len);
|
||||
ext2_put_page(page);
|
||||
if (update_times)
|
||||
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;
|
||||
loff_t pos;
|
||||
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, 0);
|
||||
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 = ext2_rec_len_to_disk(chunk_size);
|
||||
de->inode = 0;
|
||||
goto got_it;
|
||||
}
|
||||
if (de->rec_len == 0) {
|
||||
ext2_error(dir->i_sb, __func__,
|
||||
"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 = ext2_rec_len_from_disk(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:
|
||||
pos = page_offset(page) +
|
||||
(char*)de - (char*)page_address(page);
|
||||
err = ext2_prepare_chunk(page, pos, rec_len);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
if (de->inode) {
|
||||
ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
|
||||
de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len);
|
||||
de->rec_len = ext2_rec_len_to_disk(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, pos, rec_len);
|
||||
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 inode *inode = page->mapping->host;
|
||||
char *kaddr = page_address(page);
|
||||
unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
|
||||
unsigned to = ((char *)dir - kaddr) +
|
||||
ext2_rec_len_from_disk(dir->rec_len);
|
||||
loff_t pos;
|
||||
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, __func__,
|
||||
"zero-length directory entry");
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
pde = de;
|
||||
de = ext2_next_entry(de);
|
||||
}
|
||||
if (pde)
|
||||
from = (char*)pde - (char*)page_address(page);
|
||||
pos = page_offset(page) + from;
|
||||
lock_page(page);
|
||||
err = ext2_prepare_chunk(page, pos, to - from);
|
||||
BUG_ON(err);
|
||||
if (pde)
|
||||
pde->rec_len = ext2_rec_len_to_disk(to - from);
|
||||
dir->inode = 0;
|
||||
err = ext2_commit_chunk(page, pos, to - from);
|
||||
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 page *page = grab_cache_page(inode->i_mapping, 0);
|
||||
unsigned chunk_size = ext2_chunk_size(inode);
|
||||
struct ext2_dir_entry_2 * de;
|
||||
int err;
|
||||
void *kaddr;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ext2_prepare_chunk(page, 0, chunk_size);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
goto fail;
|
||||
}
|
||||
kaddr = kmap_atomic(page);
|
||||
memset(kaddr, 0, chunk_size);
|
||||
de = (struct ext2_dir_entry_2 *)kaddr;
|
||||
de->name_len = 1;
|
||||
de->rec_len = ext2_rec_len_to_disk(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 = ext2_rec_len_to_disk(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);
|
||||
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);
|
||||
int dir_has_error = 0;
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
char *kaddr;
|
||||
ext2_dirent * de;
|
||||
page = ext2_get_page(inode, i, dir_has_error);
|
||||
|
||||
if (IS_ERR(page)) {
|
||||
dir_has_error = 1;
|
||||
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, __func__,
|
||||
"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,
|
||||
.iterate = ext2_readdir,
|
||||
.unlocked_ioctl = ext2_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ext2_compat_ioctl,
|
||||
#endif
|
||||
.fsync = ext2_fsync,
|
||||
};
|
814
fs/ext2/ext2.h
Normal file
814
fs/ext2/ext2.h
Normal file
|
@ -0,0 +1,814 @@
|
|||
/*
|
||||
* 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/include/linux/minix_fs.h
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ext2_fs.h>
|
||||
#include <linux/blockgroup_lock.h>
|
||||
#include <linux/percpu_counter.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
/* XXX Here for now... not interested in restructing headers JUST now */
|
||||
|
||||
/* data type for block offset of block group */
|
||||
typedef int ext2_grpblk_t;
|
||||
|
||||
/* data type for filesystem-wide blocks number */
|
||||
typedef unsigned long ext2_fsblk_t;
|
||||
|
||||
#define E2FSBLK "%lu"
|
||||
|
||||
struct ext2_reserve_window {
|
||||
ext2_fsblk_t _rsv_start; /* First byte reserved */
|
||||
ext2_fsblk_t _rsv_end; /* Last byte reserved or 0 */
|
||||
};
|
||||
|
||||
struct ext2_reserve_window_node {
|
||||
struct rb_node rsv_node;
|
||||
__u32 rsv_goal_size;
|
||||
__u32 rsv_alloc_hit;
|
||||
struct ext2_reserve_window rsv_window;
|
||||
};
|
||||
|
||||
struct ext2_block_alloc_info {
|
||||
/* information about reservation window */
|
||||
struct ext2_reserve_window_node rsv_window_node;
|
||||
/*
|
||||
* was i_next_alloc_block in ext2_inode_info
|
||||
* is the logical (file-relative) number of the
|
||||
* most-recently-allocated block in this file.
|
||||
* We use this for detecting linearly ascending allocation requests.
|
||||
*/
|
||||
__u32 last_alloc_logical_block;
|
||||
/*
|
||||
* Was i_next_alloc_goal in ext2_inode_info
|
||||
* is the *physical* companion to i_next_alloc_block.
|
||||
* it the the physical block number of the block which was most-recentl
|
||||
* allocated to this file. This give us the goal (target) for the next
|
||||
* allocation when we detect linearly ascending requests.
|
||||
*/
|
||||
ext2_fsblk_t last_alloc_physical_block;
|
||||
};
|
||||
|
||||
#define rsv_start rsv_window._rsv_start
|
||||
#define rsv_end rsv_window._rsv_end
|
||||
|
||||
/*
|
||||
* second extended-fs super-block data in memory
|
||||
*/
|
||||
struct ext2_sb_info {
|
||||
unsigned long s_frag_size; /* Size of a fragment in bytes */
|
||||
unsigned long s_frags_per_block;/* Number of fragments per block */
|
||||
unsigned long s_inodes_per_block;/* Number of inodes per block */
|
||||
unsigned long s_frags_per_group;/* Number of fragments in a group */
|
||||
unsigned long s_blocks_per_group;/* Number of blocks in a group */
|
||||
unsigned long s_inodes_per_group;/* Number of inodes in a group */
|
||||
unsigned long s_itb_per_group; /* Number of inode table blocks per group */
|
||||
unsigned long s_gdb_count; /* Number of group descriptor blocks */
|
||||
unsigned long s_desc_per_block; /* Number of group descriptors per block */
|
||||
unsigned long s_groups_count; /* Number of groups in the fs */
|
||||
unsigned long s_overhead_last; /* Last calculated overhead */
|
||||
unsigned long s_blocks_last; /* Last seen block count */
|
||||
struct buffer_head * s_sbh; /* Buffer containing the super block */
|
||||
struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
|
||||
struct buffer_head ** s_group_desc;
|
||||
unsigned long s_mount_opt;
|
||||
unsigned long s_sb_block;
|
||||
kuid_t s_resuid;
|
||||
kgid_t s_resgid;
|
||||
unsigned short s_mount_state;
|
||||
unsigned short s_pad;
|
||||
int s_addr_per_block_bits;
|
||||
int s_desc_per_block_bits;
|
||||
int s_inode_size;
|
||||
int s_first_ino;
|
||||
spinlock_t s_next_gen_lock;
|
||||
u32 s_next_generation;
|
||||
unsigned long s_dir_count;
|
||||
u8 *s_debts;
|
||||
struct percpu_counter s_freeblocks_counter;
|
||||
struct percpu_counter s_freeinodes_counter;
|
||||
struct percpu_counter s_dirs_counter;
|
||||
struct blockgroup_lock *s_blockgroup_lock;
|
||||
/* root of the per fs reservation window tree */
|
||||
spinlock_t s_rsv_window_lock;
|
||||
struct rb_root s_rsv_window_root;
|
||||
struct ext2_reserve_window_node s_rsv_window_head;
|
||||
/*
|
||||
* s_lock protects against concurrent modifications of s_mount_state,
|
||||
* s_blocks_last, s_overhead_last and the content of superblock's
|
||||
* buffer pointed to by sbi->s_es.
|
||||
*
|
||||
* Note: It is used in ext2_show_options() to provide a consistent view
|
||||
* of the mount options.
|
||||
*/
|
||||
spinlock_t s_lock;
|
||||
};
|
||||
|
||||
static inline spinlock_t *
|
||||
sb_bgl_lock(struct ext2_sb_info *sbi, unsigned int block_group)
|
||||
{
|
||||
return bgl_lock_ptr(sbi->s_blockgroup_lock, block_group);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define EXT2FS_DEBUG to produce debug messages
|
||||
*/
|
||||
#undef EXT2FS_DEBUG
|
||||
|
||||
/*
|
||||
* Define EXT2_RESERVATION to reserve data blocks for expanding files
|
||||
*/
|
||||
#define EXT2_DEFAULT_RESERVE_BLOCKS 8
|
||||
/*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */
|
||||
#define EXT2_MAX_RESERVE_BLOCKS 1027
|
||||
#define EXT2_RESERVE_WINDOW_NOT_ALLOCATED 0
|
||||
/*
|
||||
* The second extended file system version
|
||||
*/
|
||||
#define EXT2FS_DATE "95/08/09"
|
||||
#define EXT2FS_VERSION "0.5b"
|
||||
|
||||
/*
|
||||
* Debug code
|
||||
*/
|
||||
#ifdef EXT2FS_DEBUG
|
||||
# define ext2_debug(f, a...) { \
|
||||
printk ("EXT2-fs DEBUG (%s, %d): %s:", \
|
||||
__FILE__, __LINE__, __func__); \
|
||||
printk (f, ## a); \
|
||||
}
|
||||
#else
|
||||
# define ext2_debug(f, a...) /**/
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Special inode numbers
|
||||
*/
|
||||
#define EXT2_BAD_INO 1 /* Bad blocks inode */
|
||||
#define EXT2_ROOT_INO 2 /* Root inode */
|
||||
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
|
||||
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
|
||||
|
||||
/* First non-reserved inode for old ext2 filesystems */
|
||||
#define EXT2_GOOD_OLD_FIRST_INO 11
|
||||
|
||||
static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)
|
||||
{
|
||||
return sb->s_fs_info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage several block sizes
|
||||
*/
|
||||
#define EXT2_MIN_BLOCK_SIZE 1024
|
||||
#define EXT2_MAX_BLOCK_SIZE 4096
|
||||
#define EXT2_MIN_BLOCK_LOG_SIZE 10
|
||||
#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
|
||||
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
|
||||
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
|
||||
#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_addr_per_block_bits)
|
||||
#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size)
|
||||
#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino)
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage fragments
|
||||
*/
|
||||
#define EXT2_MIN_FRAG_SIZE 1024
|
||||
#define EXT2_MAX_FRAG_SIZE 4096
|
||||
#define EXT2_MIN_FRAG_LOG_SIZE 10
|
||||
#define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size)
|
||||
#define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block)
|
||||
|
||||
/*
|
||||
* Structure of a blocks group descriptor
|
||||
*/
|
||||
struct ext2_group_desc
|
||||
{
|
||||
__le32 bg_block_bitmap; /* Blocks bitmap block */
|
||||
__le32 bg_inode_bitmap; /* Inodes bitmap block */
|
||||
__le32 bg_inode_table; /* Inodes table block */
|
||||
__le16 bg_free_blocks_count; /* Free blocks count */
|
||||
__le16 bg_free_inodes_count; /* Free inodes count */
|
||||
__le16 bg_used_dirs_count; /* Directories count */
|
||||
__le16 bg_pad;
|
||||
__le32 bg_reserved[3];
|
||||
};
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage group descriptors
|
||||
*/
|
||||
#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
|
||||
#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
|
||||
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
|
||||
#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
|
||||
|
||||
/*
|
||||
* Constants relative to the data blocks
|
||||
*/
|
||||
#define EXT2_NDIR_BLOCKS 12
|
||||
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
|
||||
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
|
||||
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
|
||||
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
|
||||
|
||||
/*
|
||||
* Inode flags (GETFLAGS/SETFLAGS)
|
||||
*/
|
||||
#define EXT2_SECRM_FL FS_SECRM_FL /* Secure deletion */
|
||||
#define EXT2_UNRM_FL FS_UNRM_FL /* Undelete */
|
||||
#define EXT2_COMPR_FL FS_COMPR_FL /* Compress file */
|
||||
#define EXT2_SYNC_FL FS_SYNC_FL /* Synchronous updates */
|
||||
#define EXT2_IMMUTABLE_FL FS_IMMUTABLE_FL /* Immutable file */
|
||||
#define EXT2_APPEND_FL FS_APPEND_FL /* writes to file may only append */
|
||||
#define EXT2_NODUMP_FL FS_NODUMP_FL /* do not dump file */
|
||||
#define EXT2_NOATIME_FL FS_NOATIME_FL /* do not update atime */
|
||||
/* Reserved for compression usage... */
|
||||
#define EXT2_DIRTY_FL FS_DIRTY_FL
|
||||
#define EXT2_COMPRBLK_FL FS_COMPRBLK_FL /* One or more compressed clusters */
|
||||
#define EXT2_NOCOMP_FL FS_NOCOMP_FL /* Don't compress */
|
||||
#define EXT2_ECOMPR_FL FS_ECOMPR_FL /* Compression error */
|
||||
/* End compression flags --- maybe not all used */
|
||||
#define EXT2_BTREE_FL FS_BTREE_FL /* btree format dir */
|
||||
#define EXT2_INDEX_FL FS_INDEX_FL /* hash-indexed directory */
|
||||
#define EXT2_IMAGIC_FL FS_IMAGIC_FL /* AFS directory */
|
||||
#define EXT2_JOURNAL_DATA_FL FS_JOURNAL_DATA_FL /* Reserved for ext3 */
|
||||
#define EXT2_NOTAIL_FL FS_NOTAIL_FL /* file tail should not be merged */
|
||||
#define EXT2_DIRSYNC_FL FS_DIRSYNC_FL /* dirsync behaviour (directories only) */
|
||||
#define EXT2_TOPDIR_FL FS_TOPDIR_FL /* Top of directory hierarchies*/
|
||||
#define EXT2_RESERVED_FL FS_RESERVED_FL /* reserved for ext2 lib */
|
||||
|
||||
#define EXT2_FL_USER_VISIBLE FS_FL_USER_VISIBLE /* User visible flags */
|
||||
#define EXT2_FL_USER_MODIFIABLE FS_FL_USER_MODIFIABLE /* User modifiable flags */
|
||||
|
||||
/* Flags that should be inherited by new inodes from their parent. */
|
||||
#define EXT2_FL_INHERITED (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL |\
|
||||
EXT2_SYNC_FL | EXT2_NODUMP_FL |\
|
||||
EXT2_NOATIME_FL | EXT2_COMPRBLK_FL |\
|
||||
EXT2_NOCOMP_FL | EXT2_JOURNAL_DATA_FL |\
|
||||
EXT2_NOTAIL_FL | EXT2_DIRSYNC_FL)
|
||||
|
||||
/* Flags that are appropriate for regular files (all but dir-specific ones). */
|
||||
#define EXT2_REG_FLMASK (~(EXT2_DIRSYNC_FL | EXT2_TOPDIR_FL))
|
||||
|
||||
/* Flags that are appropriate for non-directories/regular files. */
|
||||
#define EXT2_OTHER_FLMASK (EXT2_NODUMP_FL | EXT2_NOATIME_FL)
|
||||
|
||||
/* Mask out flags that are inappropriate for the given type of inode. */
|
||||
static inline __u32 ext2_mask_flags(umode_t mode, __u32 flags)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return flags;
|
||||
else if (S_ISREG(mode))
|
||||
return flags & EXT2_REG_FLMASK;
|
||||
else
|
||||
return flags & EXT2_OTHER_FLMASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl commands
|
||||
*/
|
||||
#define EXT2_IOC_GETFLAGS FS_IOC_GETFLAGS
|
||||
#define EXT2_IOC_SETFLAGS FS_IOC_SETFLAGS
|
||||
#define EXT2_IOC_GETVERSION FS_IOC_GETVERSION
|
||||
#define EXT2_IOC_SETVERSION FS_IOC_SETVERSION
|
||||
#define EXT2_IOC_GETRSVSZ _IOR('f', 5, long)
|
||||
#define EXT2_IOC_SETRSVSZ _IOW('f', 6, long)
|
||||
|
||||
/*
|
||||
* ioctl commands in 32 bit emulation
|
||||
*/
|
||||
#define EXT2_IOC32_GETFLAGS FS_IOC32_GETFLAGS
|
||||
#define EXT2_IOC32_SETFLAGS FS_IOC32_SETFLAGS
|
||||
#define EXT2_IOC32_GETVERSION FS_IOC32_GETVERSION
|
||||
#define EXT2_IOC32_SETVERSION FS_IOC32_SETVERSION
|
||||
|
||||
/*
|
||||
* Structure of an inode on the disk
|
||||
*/
|
||||
struct ext2_inode {
|
||||
__le16 i_mode; /* File mode */
|
||||
__le16 i_uid; /* Low 16 bits of Owner Uid */
|
||||
__le32 i_size; /* Size in bytes */
|
||||
__le32 i_atime; /* Access time */
|
||||
__le32 i_ctime; /* Creation time */
|
||||
__le32 i_mtime; /* Modification time */
|
||||
__le32 i_dtime; /* Deletion Time */
|
||||
__le16 i_gid; /* Low 16 bits of Group Id */
|
||||
__le16 i_links_count; /* Links count */
|
||||
__le32 i_blocks; /* Blocks count */
|
||||
__le32 i_flags; /* File flags */
|
||||
union {
|
||||
struct {
|
||||
__le32 l_i_reserved1;
|
||||
} linux1;
|
||||
struct {
|
||||
__le32 h_i_translator;
|
||||
} hurd1;
|
||||
struct {
|
||||
__le32 m_i_reserved1;
|
||||
} masix1;
|
||||
} osd1; /* OS dependent 1 */
|
||||
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
|
||||
__le32 i_generation; /* File version (for NFS) */
|
||||
__le32 i_file_acl; /* File ACL */
|
||||
__le32 i_dir_acl; /* Directory ACL */
|
||||
__le32 i_faddr; /* Fragment address */
|
||||
union {
|
||||
struct {
|
||||
__u8 l_i_frag; /* Fragment number */
|
||||
__u8 l_i_fsize; /* Fragment size */
|
||||
__u16 i_pad1;
|
||||
__le16 l_i_uid_high; /* these 2 fields */
|
||||
__le16 l_i_gid_high; /* were reserved2[0] */
|
||||
__u32 l_i_reserved2;
|
||||
} linux2;
|
||||
struct {
|
||||
__u8 h_i_frag; /* Fragment number */
|
||||
__u8 h_i_fsize; /* Fragment size */
|
||||
__le16 h_i_mode_high;
|
||||
__le16 h_i_uid_high;
|
||||
__le16 h_i_gid_high;
|
||||
__le32 h_i_author;
|
||||
} hurd2;
|
||||
struct {
|
||||
__u8 m_i_frag; /* Fragment number */
|
||||
__u8 m_i_fsize; /* Fragment size */
|
||||
__u16 m_pad1;
|
||||
__u32 m_i_reserved2[2];
|
||||
} masix2;
|
||||
} osd2; /* OS dependent 2 */
|
||||
};
|
||||
|
||||
#define i_size_high i_dir_acl
|
||||
|
||||
#define i_reserved1 osd1.linux1.l_i_reserved1
|
||||
#define i_frag osd2.linux2.l_i_frag
|
||||
#define i_fsize osd2.linux2.l_i_fsize
|
||||
#define i_uid_low i_uid
|
||||
#define i_gid_low i_gid
|
||||
#define i_uid_high osd2.linux2.l_i_uid_high
|
||||
#define i_gid_high osd2.linux2.l_i_gid_high
|
||||
#define i_reserved2 osd2.linux2.l_i_reserved2
|
||||
|
||||
/*
|
||||
* File system states
|
||||
*/
|
||||
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
|
||||
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
|
||||
|
||||
/*
|
||||
* Mount flags
|
||||
*/
|
||||
#define EXT2_MOUNT_CHECK 0x000001 /* Do mount-time checks */
|
||||
#define EXT2_MOUNT_OLDALLOC 0x000002 /* Don't use the new Orlov allocator */
|
||||
#define EXT2_MOUNT_GRPID 0x000004 /* Create files with directory's group */
|
||||
#define EXT2_MOUNT_DEBUG 0x000008 /* Some debugging messages */
|
||||
#define EXT2_MOUNT_ERRORS_CONT 0x000010 /* Continue on errors */
|
||||
#define EXT2_MOUNT_ERRORS_RO 0x000020 /* Remount fs ro on errors */
|
||||
#define EXT2_MOUNT_ERRORS_PANIC 0x000040 /* Panic on errors */
|
||||
#define EXT2_MOUNT_MINIX_DF 0x000080 /* Mimics the Minix statfs */
|
||||
#define EXT2_MOUNT_NOBH 0x000100 /* No buffer_heads */
|
||||
#define EXT2_MOUNT_NO_UID32 0x000200 /* Disable 32-bit UIDs */
|
||||
#define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */
|
||||
#define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */
|
||||
#define EXT2_MOUNT_XIP 0x010000 /* Execute in place */
|
||||
#define EXT2_MOUNT_USRQUOTA 0x020000 /* user quota */
|
||||
#define EXT2_MOUNT_GRPQUOTA 0x040000 /* group quota */
|
||||
#define EXT2_MOUNT_RESERVATION 0x080000 /* Preallocation */
|
||||
|
||||
|
||||
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
|
||||
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
|
||||
#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
|
||||
EXT2_MOUNT_##opt)
|
||||
/*
|
||||
* Maximal mount counts between two filesystem checks
|
||||
*/
|
||||
#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
|
||||
#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
|
||||
|
||||
/*
|
||||
* Behaviour when detecting errors
|
||||
*/
|
||||
#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
|
||||
#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
|
||||
#define EXT2_ERRORS_PANIC 3 /* Panic */
|
||||
#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
|
||||
|
||||
/*
|
||||
* Structure of the super block
|
||||
*/
|
||||
struct ext2_super_block {
|
||||
__le32 s_inodes_count; /* Inodes count */
|
||||
__le32 s_blocks_count; /* Blocks count */
|
||||
__le32 s_r_blocks_count; /* Reserved blocks count */
|
||||
__le32 s_free_blocks_count; /* Free blocks count */
|
||||
__le32 s_free_inodes_count; /* Free inodes count */
|
||||
__le32 s_first_data_block; /* First Data Block */
|
||||
__le32 s_log_block_size; /* Block size */
|
||||
__le32 s_log_frag_size; /* Fragment size */
|
||||
__le32 s_blocks_per_group; /* # Blocks per group */
|
||||
__le32 s_frags_per_group; /* # Fragments per group */
|
||||
__le32 s_inodes_per_group; /* # Inodes per group */
|
||||
__le32 s_mtime; /* Mount time */
|
||||
__le32 s_wtime; /* Write time */
|
||||
__le16 s_mnt_count; /* Mount count */
|
||||
__le16 s_max_mnt_count; /* Maximal mount count */
|
||||
__le16 s_magic; /* Magic signature */
|
||||
__le16 s_state; /* File system state */
|
||||
__le16 s_errors; /* Behaviour when detecting errors */
|
||||
__le16 s_minor_rev_level; /* minor revision level */
|
||||
__le32 s_lastcheck; /* time of last check */
|
||||
__le32 s_checkinterval; /* max. time between checks */
|
||||
__le32 s_creator_os; /* OS */
|
||||
__le32 s_rev_level; /* Revision level */
|
||||
__le16 s_def_resuid; /* Default uid for reserved blocks */
|
||||
__le16 s_def_resgid; /* Default gid for reserved blocks */
|
||||
/*
|
||||
* These fields are for EXT2_DYNAMIC_REV superblocks only.
|
||||
*
|
||||
* Note: the difference between the compatible feature set and
|
||||
* the incompatible feature set is that if there is a bit set
|
||||
* in the incompatible feature set that the kernel doesn't
|
||||
* know about, it should refuse to mount the filesystem.
|
||||
*
|
||||
* e2fsck's requirements are more strict; if it doesn't know
|
||||
* about a feature in either the compatible or incompatible
|
||||
* feature set, it must abort and not try to meddle with
|
||||
* things it doesn't understand...
|
||||
*/
|
||||
__le32 s_first_ino; /* First non-reserved inode */
|
||||
__le16 s_inode_size; /* size of inode structure */
|
||||
__le16 s_block_group_nr; /* block group # of this superblock */
|
||||
__le32 s_feature_compat; /* compatible feature set */
|
||||
__le32 s_feature_incompat; /* incompatible feature set */
|
||||
__le32 s_feature_ro_compat; /* readonly-compatible feature set */
|
||||
__u8 s_uuid[16]; /* 128-bit uuid for volume */
|
||||
char s_volume_name[16]; /* volume name */
|
||||
char s_last_mounted[64]; /* directory where last mounted */
|
||||
__le32 s_algorithm_usage_bitmap; /* For compression */
|
||||
/*
|
||||
* Performance hints. Directory preallocation should only
|
||||
* happen if the EXT2_COMPAT_PREALLOC flag is on.
|
||||
*/
|
||||
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
|
||||
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
|
||||
__u16 s_padding1;
|
||||
/*
|
||||
* Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set.
|
||||
*/
|
||||
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
|
||||
__u32 s_journal_inum; /* inode number of journal file */
|
||||
__u32 s_journal_dev; /* device number of journal file */
|
||||
__u32 s_last_orphan; /* start of list of inodes to delete */
|
||||
__u32 s_hash_seed[4]; /* HTREE hash seed */
|
||||
__u8 s_def_hash_version; /* Default hash version to use */
|
||||
__u8 s_reserved_char_pad;
|
||||
__u16 s_reserved_word_pad;
|
||||
__le32 s_default_mount_opts;
|
||||
__le32 s_first_meta_bg; /* First metablock block group */
|
||||
__u32 s_reserved[190]; /* Padding to the end of the block */
|
||||
};
|
||||
|
||||
/*
|
||||
* Codes for operating systems
|
||||
*/
|
||||
#define EXT2_OS_LINUX 0
|
||||
#define EXT2_OS_HURD 1
|
||||
#define EXT2_OS_MASIX 2
|
||||
#define EXT2_OS_FREEBSD 3
|
||||
#define EXT2_OS_LITES 4
|
||||
|
||||
/*
|
||||
* Revision levels
|
||||
*/
|
||||
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
|
||||
#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
|
||||
|
||||
#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
|
||||
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
|
||||
|
||||
#define EXT2_GOOD_OLD_INODE_SIZE 128
|
||||
|
||||
/*
|
||||
* Feature set definitions
|
||||
*/
|
||||
|
||||
#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) )
|
||||
#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) )
|
||||
#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) )
|
||||
#define EXT2_SET_COMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask)
|
||||
#define EXT2_SET_RO_COMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask)
|
||||
#define EXT2_SET_INCOMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask)
|
||||
#define EXT2_CLEAR_COMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask)
|
||||
#define EXT2_CLEAR_RO_COMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask)
|
||||
#define EXT2_CLEAR_INCOMPAT_FEATURE(sb,mask) \
|
||||
EXT2_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask)
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
|
||||
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
|
||||
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
|
||||
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
|
||||
#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010
|
||||
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
|
||||
#define EXT2_FEATURE_COMPAT_ANY 0xffffffff
|
||||
|
||||
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
|
||||
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
|
||||
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
|
||||
#define EXT2_FEATURE_RO_COMPAT_ANY 0xffffffff
|
||||
|
||||
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
|
||||
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
|
||||
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
|
||||
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
|
||||
#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
|
||||
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
|
||||
EXT2_FEATURE_INCOMPAT_META_BG)
|
||||
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
|
||||
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
|
||||
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
|
||||
#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP
|
||||
#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP
|
||||
|
||||
/*
|
||||
* Default values for user and/or group using reserved blocks
|
||||
*/
|
||||
#define EXT2_DEF_RESUID 0
|
||||
#define EXT2_DEF_RESGID 0
|
||||
|
||||
/*
|
||||
* Default mount options
|
||||
*/
|
||||
#define EXT2_DEFM_DEBUG 0x0001
|
||||
#define EXT2_DEFM_BSDGROUPS 0x0002
|
||||
#define EXT2_DEFM_XATTR_USER 0x0004
|
||||
#define EXT2_DEFM_ACL 0x0008
|
||||
#define EXT2_DEFM_UID16 0x0010
|
||||
/* Not used by ext2, but reserved for use by ext3 */
|
||||
#define EXT3_DEFM_JMODE 0x0060
|
||||
#define EXT3_DEFM_JMODE_DATA 0x0020
|
||||
#define EXT3_DEFM_JMODE_ORDERED 0x0040
|
||||
#define EXT3_DEFM_JMODE_WBACK 0x0060
|
||||
|
||||
/*
|
||||
* Structure of a directory entry
|
||||
*/
|
||||
|
||||
struct ext2_dir_entry {
|
||||
__le32 inode; /* Inode number */
|
||||
__le16 rec_len; /* Directory entry length */
|
||||
__le16 name_len; /* Name length */
|
||||
char name[]; /* File name, up to EXT2_NAME_LEN */
|
||||
};
|
||||
|
||||
/*
|
||||
* The new version of the directory entry. Since EXT2 structures are
|
||||
* stored in intel byte order, and the name_len field could never be
|
||||
* bigger than 255 chars, it's safe to reclaim the extra byte for the
|
||||
* file_type field.
|
||||
*/
|
||||
struct ext2_dir_entry_2 {
|
||||
__le32 inode; /* Inode number */
|
||||
__le16 rec_len; /* Directory entry length */
|
||||
__u8 name_len; /* Name length */
|
||||
__u8 file_type;
|
||||
char name[]; /* File name, up to EXT2_NAME_LEN */
|
||||
};
|
||||
|
||||
/*
|
||||
* Ext2 directory file types. Only the low 3 bits are used. The
|
||||
* other bits are reserved for now.
|
||||
*/
|
||||
enum {
|
||||
EXT2_FT_UNKNOWN = 0,
|
||||
EXT2_FT_REG_FILE = 1,
|
||||
EXT2_FT_DIR = 2,
|
||||
EXT2_FT_CHRDEV = 3,
|
||||
EXT2_FT_BLKDEV = 4,
|
||||
EXT2_FT_FIFO = 5,
|
||||
EXT2_FT_SOCK = 6,
|
||||
EXT2_FT_SYMLINK = 7,
|
||||
EXT2_FT_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* EXT2_DIR_PAD defines the directory entries boundaries
|
||||
*
|
||||
* NOTE: It must be a multiple of 4
|
||||
*/
|
||||
#define EXT2_DIR_PAD 4
|
||||
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
|
||||
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
|
||||
~EXT2_DIR_ROUND)
|
||||
#define EXT2_MAX_REC_LEN ((1<<16)-1)
|
||||
|
||||
static inline void verify_offsets(void)
|
||||
{
|
||||
#define A(x,y) BUILD_BUG_ON(x != offsetof(struct ext2_super_block, y));
|
||||
A(EXT2_SB_MAGIC_OFFSET, s_magic);
|
||||
A(EXT2_SB_BLOCKS_OFFSET, s_blocks_count);
|
||||
A(EXT2_SB_BSIZE_OFFSET, s_log_block_size);
|
||||
#undef A
|
||||
}
|
||||
|
||||
/*
|
||||
* ext2 mount options
|
||||
*/
|
||||
struct ext2_mount_options {
|
||||
unsigned long s_mount_opt;
|
||||
kuid_t s_resuid;
|
||||
kgid_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 used 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;
|
||||
|
||||
/* block reservation info */
|
||||
struct ext2_block_alloc_info *i_block_alloc_info;
|
||||
|
||||
__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
|
||||
rwlock_t i_meta_lock;
|
||||
|
||||
/*
|
||||
* truncate_mutex is for serialising ext2_truncate() against
|
||||
* ext2_getblock(). It also protects the internals of the inode's
|
||||
* reservation data structures: ext2_reserve_window and
|
||||
* ext2_reserve_window_node.
|
||||
*/
|
||||
struct mutex truncate_mutex;
|
||||
struct inode vfs_inode;
|
||||
struct list_head i_orphan; /* unlinked but open inodes */
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);
|
||||
extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,
|
||||
unsigned long *, 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);
|
||||
extern void ext2_discard_reservation (struct inode *);
|
||||
extern int ext2_should_retry_alloc(struct super_block *sb, int *retries);
|
||||
extern void ext2_init_block_alloc_info(struct inode *);
|
||||
extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv);
|
||||
|
||||
/* dir.c */
|
||||
extern int ext2_add_link (struct dentry *, struct inode *);
|
||||
extern ino_t ext2_inode_by_name(struct inode *, struct qstr *);
|
||||
extern int ext2_make_empty(struct inode *, struct inode *);
|
||||
extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct qstr *, 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 *, int);
|
||||
|
||||
/* ialloc.c */
|
||||
extern struct inode * ext2_new_inode (struct inode *, umode_t, const struct qstr *);
|
||||
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 struct inode *ext2_iget (struct super_block *, unsigned long);
|
||||
extern int ext2_write_inode (struct inode *, struct writeback_control *);
|
||||
extern void ext2_evict_inode(struct inode *);
|
||||
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
|
||||
extern int ext2_setattr (struct dentry *, struct iattr *);
|
||||
extern void ext2_set_inode_flags(struct inode *inode);
|
||||
extern void ext2_get_inode_flags(struct ext2_inode_info *);
|
||||
extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len);
|
||||
|
||||
/* ioctl.c */
|
||||
extern long ext2_ioctl(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 __printf(3, 4)
|
||||
void ext2_error(struct super_block *, const char *, const char *, ...);
|
||||
extern __printf(3, 4)
|
||||
void ext2_msg(struct super_block *, const char *, const char *, ...);
|
||||
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 int ext2_fsync(struct file *file, loff_t start, loff_t end,
|
||||
int datasync);
|
||||
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;
|
||||
|
||||
static inline ext2_fsblk_t
|
||||
ext2_group_first_block_no(struct super_block *sb, unsigned long group_no)
|
||||
{
|
||||
return group_no * (ext2_fsblk_t)EXT2_BLOCKS_PER_GROUP(sb) +
|
||||
le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block);
|
||||
}
|
||||
|
||||
#define ext2_set_bit __test_and_set_bit_le
|
||||
#define ext2_clear_bit __test_and_clear_bit_le
|
||||
#define ext2_test_bit test_bit_le
|
||||
#define ext2_find_first_zero_bit find_first_zero_bit_le
|
||||
#define ext2_find_next_zero_bit find_next_zero_bit_le
|
108
fs/ext2/file.c
Normal file
108
fs/ext2/file.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 <linux/pagemap.h>
|
||||
#include <linux/quotaops.h>
|
||||
#include "ext2.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
/*
|
||||
* Called when filp is released. This happens when all file descriptors
|
||||
* for a single struct file are closed. Note that different open() calls
|
||||
* for the same file yield different struct file structures.
|
||||
*/
|
||||
static int ext2_release_file (struct inode * inode, struct file * filp)
|
||||
{
|
||||
if (filp->f_mode & FMODE_WRITE) {
|
||||
mutex_lock(&EXT2_I(inode)->truncate_mutex);
|
||||
ext2_discard_reservation(inode);
|
||||
mutex_unlock(&EXT2_I(inode)->truncate_mutex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
int ret;
|
||||
struct super_block *sb = file->f_mapping->host->i_sb;
|
||||
struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
|
||||
|
||||
ret = generic_file_fsync(file, start, end, datasync);
|
||||
if (ret == -EIO || test_and_clear_bit(AS_EIO, &mapping->flags)) {
|
||||
/* We don't really know where the IO error happened... */
|
||||
ext2_error(sb, __func__,
|
||||
"detected IO error when writing metadata buffers");
|
||||
ret = -EIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = new_sync_read,
|
||||
.write = new_sync_write,
|
||||
.read_iter = generic_file_read_iter,
|
||||
.write_iter = generic_file_write_iter,
|
||||
.unlocked_ioctl = ext2_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ext2_compat_ioctl,
|
||||
#endif
|
||||
.mmap = generic_file_mmap,
|
||||
.open = dquot_file_open,
|
||||
.release = ext2_release_file,
|
||||
.fsync = ext2_fsync,
|
||||
.splice_read = generic_file_splice_read,
|
||||
.splice_write = iter_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,
|
||||
.unlocked_ioctl = ext2_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = ext2_compat_ioctl,
|
||||
#endif
|
||||
.mmap = xip_file_mmap,
|
||||
.open = dquot_file_open,
|
||||
.release = ext2_release_file,
|
||||
.fsync = ext2_fsync,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct inode_operations ext2_file_inode_operations = {
|
||||
#ifdef CONFIG_EXT2_FS_XATTR
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.listxattr = ext2_listxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
#endif
|
||||
.setattr = ext2_setattr,
|
||||
.get_acl = ext2_get_acl,
|
||||
.set_acl = ext2_set_acl,
|
||||
.fiemap = ext2_fiemap,
|
||||
};
|
675
fs/ext2/ialloc.c
Normal file
675
fs/ext2/ialloc.c
Normal file
|
@ -0,0 +1,675 @@
|
|||
/*
|
||||
* 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));
|
||||
le16_add_cpu(&desc->bg_free_inodes_count, 1);
|
||||
if (dir)
|
||||
le16_add_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);
|
||||
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;
|
||||
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.
|
||||
*/
|
||||
/* Quota is already initialized in iput() */
|
||||
dquot_free_inode(inode);
|
||||
dquot_drop(inode);
|
||||
|
||||
es = EXT2_SB(sb)->s_es;
|
||||
is_directory = S_ISDIR(inode->i_mode);
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
|
||||
bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
|
||||
bitmap_bh = read_inode_bitmap(sb, block_group);
|
||||
if (!bitmap_bh)
|
||||
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);
|
||||
|
||||
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 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, NULL);
|
||||
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;
|
||||
int group, best_group = -1;
|
||||
|
||||
for (group = 0; group < ngroups; group++) {
|
||||
desc = ext2_get_group_desc (sb, group, NULL);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 preferred, 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;
|
||||
|
||||
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;
|
||||
int best_ndir = inodes_per_group;
|
||||
int best_group = -1;
|
||||
|
||||
group = prandom_u32();
|
||||
parent_group = (unsigned)group % ngroups;
|
||||
for (i = 0; i < ngroups; i++) {
|
||||
group = (parent_group + i) % ngroups;
|
||||
desc = ext2_get_group_desc (sb, group, NULL);
|
||||
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;
|
||||
}
|
||||
if (best_group >= 0) {
|
||||
desc = best_desc;
|
||||
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, NULL);
|
||||
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, NULL);
|
||||
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;
|
||||
int group, i;
|
||||
|
||||
/*
|
||||
* Try to place the inode in its parent directory
|
||||
*/
|
||||
group = parent_group;
|
||||
desc = ext2_get_group_desc (sb, group, NULL);
|
||||
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, NULL);
|
||||
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, NULL);
|
||||
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, umode_t mode,
|
||||
const struct qstr *qstr)
|
||||
{
|
||||
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_add(&sbi->s_freeinodes_counter, -1);
|
||||
if (S_ISDIR(mode))
|
||||
percpu_counter_inc(&sbi->s_dirs_counter);
|
||||
|
||||
spin_lock(sb_bgl_lock(sbi, group));
|
||||
le16_add_cpu(&gdp->bg_free_inodes_count, -1);
|
||||
if (S_ISDIR(mode)) {
|
||||
if (sbi->s_debts[group] < 255)
|
||||
sbi->s_debts[group]++;
|
||||
le16_add_cpu(&gdp->bg_used_dirs_count, 1);
|
||||
} else {
|
||||
if (sbi->s_debts[group])
|
||||
sbi->s_debts[group]--;
|
||||
}
|
||||
spin_unlock(sb_bgl_lock(sbi, group));
|
||||
|
||||
mark_buffer_dirty(bh2);
|
||||
if (test_opt(sb, GRPID)) {
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current_fsuid();
|
||||
inode->i_gid = dir->i_gid;
|
||||
} else
|
||||
inode_init_owner(inode, dir, 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_mask_flags(mode, EXT2_I(dir)->i_flags & EXT2_FL_INHERITED);
|
||||
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_alloc_info = NULL;
|
||||
ei->i_block_group = group;
|
||||
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);
|
||||
if (insert_inode_locked(inode) < 0) {
|
||||
ext2_error(sb, "ext2_new_inode",
|
||||
"inode number already in use - inode=%lu",
|
||||
(unsigned long) ino);
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dquot_initialize(inode);
|
||||
err = dquot_alloc_inode(inode);
|
||||
if (err)
|
||||
goto fail_drop;
|
||||
|
||||
err = ext2_init_acl(inode, dir);
|
||||
if (err)
|
||||
goto fail_free_drop;
|
||||
|
||||
err = ext2_init_security(inode, dir, qstr);
|
||||
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;
|
||||
clear_nlink(inode);
|
||||
unlock_new_inode(inode);
|
||||
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",
|
||||
(unsigned long)
|
||||
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;
|
||||
}
|
||||
|
1573
fs/ext2/inode.c
Normal file
1573
fs/ext2/inode.c
Normal file
File diff suppressed because it is too large
Load diff
188
fs/ext2/ioctl.c
Normal file
188
fs/ext2/ioctl.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* 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/mount.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
||||
long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct ext2_inode_info *ei = EXT2_I(inode);
|
||||
unsigned int flags;
|
||||
unsigned short rsv_window_size;
|
||||
int ret;
|
||||
|
||||
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
|
||||
|
||||
switch (cmd) {
|
||||
case EXT2_IOC_GETFLAGS:
|
||||
ext2_get_inode_flags(ei);
|
||||
flags = ei->i_flags & EXT2_FL_USER_VISIBLE;
|
||||
return put_user(flags, (int __user *) arg);
|
||||
case EXT2_IOC_SETFLAGS: {
|
||||
unsigned int oldflags;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!inode_owner_or_capable(inode)) {
|
||||
ret = -EACCES;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
if (get_user(flags, (int __user *) arg)) {
|
||||
ret = -EFAULT;
|
||||
goto setflags_out;
|
||||
}
|
||||
|
||||
flags = ext2_mask_flags(inode->i_mode, flags);
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
/* Is it quota file? Do not allow user to mess with it */
|
||||
if (IS_NOQUOTA(inode)) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
ret = -EPERM;
|
||||
goto setflags_out;
|
||||
}
|
||||
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);
|
||||
ret = -EPERM;
|
||||
goto setflags_out;
|
||||
}
|
||||
}
|
||||
|
||||
flags = flags & EXT2_FL_USER_MODIFIABLE;
|
||||
flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
|
||||
ei->i_flags = flags;
|
||||
|
||||
ext2_set_inode_flags(inode);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
mark_inode_dirty(inode);
|
||||
setflags_out:
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
case EXT2_IOC_GETVERSION:
|
||||
return put_user(inode->i_generation, (int __user *) arg);
|
||||
case EXT2_IOC_SETVERSION: {
|
||||
__u32 generation;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EPERM;
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (get_user(generation, (int __user *) arg)) {
|
||||
ret = -EFAULT;
|
||||
goto setversion_out;
|
||||
}
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
inode->i_generation = generation;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
mark_inode_dirty(inode);
|
||||
setversion_out:
|
||||
mnt_drop_write_file(filp);
|
||||
return ret;
|
||||
}
|
||||
case EXT2_IOC_GETRSVSZ:
|
||||
if (test_opt(inode->i_sb, RESERVATION)
|
||||
&& S_ISREG(inode->i_mode)
|
||||
&& ei->i_block_alloc_info) {
|
||||
rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
|
||||
return put_user(rsv_window_size, (int __user *)arg);
|
||||
}
|
||||
return -ENOTTY;
|
||||
case EXT2_IOC_SETRSVSZ: {
|
||||
|
||||
if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
|
||||
return -ENOTTY;
|
||||
|
||||
if (!inode_owner_or_capable(inode))
|
||||
return -EACCES;
|
||||
|
||||
if (get_user(rsv_window_size, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
ret = mnt_want_write_file(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS)
|
||||
rsv_window_size = EXT2_MAX_RESERVE_BLOCKS;
|
||||
|
||||
/*
|
||||
* need to allocate reservation structure for this inode
|
||||
* before set the window size
|
||||
*/
|
||||
/*
|
||||
* XXX What lock should protect the rsv_goal_size?
|
||||
* Accessed in ext2_get_block only. ext3 uses i_truncate.
|
||||
*/
|
||||
mutex_lock(&ei->truncate_mutex);
|
||||
if (!ei->i_block_alloc_info)
|
||||
ext2_init_block_alloc_info(inode);
|
||||
|
||||
if (ei->i_block_alloc_info){
|
||||
struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
|
||||
rsv->rsv_goal_size = rsv_window_size;
|
||||
}
|
||||
mutex_unlock(&ei->truncate_mutex);
|
||||
mnt_drop_write_file(filp);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
return ext2_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
|
||||
}
|
||||
#endif
|
438
fs/ext2/namei.c
Normal file
438
fs/ext2/namei.c
Normal file
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* 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 <linux/quotaops.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) {
|
||||
unlock_new_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
return 0;
|
||||
}
|
||||
inode_dec_link_count(inode);
|
||||
unlock_new_inode(inode);
|
||||
iput(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Methods themselves.
|
||||
*/
|
||||
|
||||
static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
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->d_name);
|
||||
inode = NULL;
|
||||
if (ino) {
|
||||
inode = ext2_iget(dir->i_sb, ino);
|
||||
if (inode == ERR_PTR(-ESTALE)) {
|
||||
ext2_error(dir->i_sb, __func__,
|
||||
"deleted inode referenced: %lu",
|
||||
(unsigned long) ino);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
}
|
||||
return d_splice_alias(inode, dentry);
|
||||
}
|
||||
|
||||
struct dentry *ext2_get_parent(struct dentry *child)
|
||||
{
|
||||
struct qstr dotdot = QSTR_INIT("..", 2);
|
||||
unsigned long ino = ext2_inode_by_name(child->d_inode, &dotdot);
|
||||
if (!ino)
|
||||
return ERR_PTR(-ENOENT);
|
||||
return d_obtain_alias(ext2_iget(child->d_inode->i_sb, ino));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, umode_t mode, bool excl)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
inode = ext2_new_inode(dir, mode, &dentry->d_name);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_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);
|
||||
return ext2_add_nondir(dentry, inode);
|
||||
}
|
||||
|
||||
static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode *inode = ext2_new_inode(dir, mode, NULL);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_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);
|
||||
d_tmpfile(dentry, inode);
|
||||
unlock_new_inode(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev)
|
||||
{
|
||||
struct inode * inode;
|
||||
int err;
|
||||
|
||||
if (!new_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
inode = ext2_new_inode (dir, mode, &dentry->d_name);
|
||||
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;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO, &dentry->d_name);
|
||||
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);
|
||||
unlock_new_inode(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;
|
||||
int err;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
inode_inc_link_count(inode);
|
||||
ihold(inode);
|
||||
|
||||
err = ext2_add_link(dentry, inode);
|
||||
if (!err) {
|
||||
d_instantiate(dentry, inode);
|
||||
return 0;
|
||||
}
|
||||
inode_dec_link_count(inode);
|
||||
iput(inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
|
||||
{
|
||||
struct inode * inode;
|
||||
int err;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
inode_inc_link_count(dir);
|
||||
|
||||
inode = ext2_new_inode(dir, S_IFDIR | mode, &dentry->d_name);
|
||||
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;
|
||||
|
||||
unlock_new_inode(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
out:
|
||||
return err;
|
||||
|
||||
out_fail:
|
||||
inode_dec_link_count(inode);
|
||||
inode_dec_link_count(inode);
|
||||
unlock_new_inode(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;
|
||||
|
||||
dquot_initialize(dir);
|
||||
|
||||
de = ext2_find_entry (dir, &dentry->d_name, &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;
|
||||
|
||||
dquot_initialize(old_dir);
|
||||
dquot_initialize(new_dir);
|
||||
|
||||
old_de = ext2_find_entry (old_dir, &old_dentry->d_name, &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->d_name, &new_page);
|
||||
if (!new_de)
|
||||
goto out_dir;
|
||||
ext2_set_link(new_dir, new_de, new_page, old_inode, 1);
|
||||
new_inode->i_ctime = CURRENT_TIME_SEC;
|
||||
if (dir_de)
|
||||
drop_nlink(new_inode);
|
||||
inode_dec_link_count(new_inode);
|
||||
} else {
|
||||
err = ext2_add_link(new_dentry, old_inode);
|
||||
if (err)
|
||||
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.
|
||||
*/
|
||||
old_inode->i_ctime = CURRENT_TIME_SEC;
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
ext2_delete_entry (old_de, old_page);
|
||||
|
||||
if (dir_de) {
|
||||
if (old_dir != new_dir)
|
||||
ext2_set_link(old_inode, dir_de, dir_page, new_dir, 0);
|
||||
else {
|
||||
kunmap(dir_page);
|
||||
page_cache_release(dir_page);
|
||||
}
|
||||
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,
|
||||
.get_acl = ext2_get_acl,
|
||||
.set_acl = ext2_set_acl,
|
||||
.tmpfile = ext2_tmpfile,
|
||||
};
|
||||
|
||||
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,
|
||||
.get_acl = ext2_get_acl,
|
||||
.set_acl = ext2_set_acl,
|
||||
};
|
1573
fs/ext2/super.c
Normal file
1573
fs/ext2/super.c
Normal file
File diff suppressed because it is too large
Load diff
54
fs/ext2/symlink.c
Normal file
54
fs/ext2/symlink.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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,
|
||||
.setattr = ext2_setattr,
|
||||
#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,
|
||||
.setattr = ext2_setattr,
|
||||
#ifdef CONFIG_EXT2_FS_XATTR
|
||||
.setxattr = generic_setxattr,
|
||||
.getxattr = generic_getxattr,
|
||||
.listxattr = ext2_listxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
#endif
|
||||
};
|
1029
fs/ext2/xattr.c
Normal file
1029
fs/ext2/xattr.c
Normal file
File diff suppressed because it is too large
Load diff
125
fs/ext2/xattr.h
Normal file
125
fs/ext2/xattr.h
Normal 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 const struct xattr_handler ext2_xattr_user_handler;
|
||||
extern const struct xattr_handler ext2_xattr_trusted_handler;
|
||||
extern const 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 const 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,
|
||||
const struct qstr *qstr);
|
||||
#else
|
||||
static inline int ext2_init_security(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
74
fs/ext2/xattr_security.c
Normal file
74
fs/ext2/xattr_security.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* linux/fs/ext2/xattr_security.c
|
||||
* Handler for storing security labels as extended attributes.
|
||||
*/
|
||||
|
||||
#include "ext2.h"
|
||||
#include <linux/security.h>
|
||||
#include "xattr.h"
|
||||
|
||||
static size_t
|
||||
ext2_xattr_security_list(struct dentry *dentry, char *list, size_t list_size,
|
||||
const char *name, size_t name_len, int type)
|
||||
{
|
||||
const int prefix_len = XATTR_SECURITY_PREFIX_LEN;
|
||||
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 dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return ext2_xattr_get(dentry->d_inode, EXT2_XATTR_INDEX_SECURITY, name,
|
||||
buffer, size);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_xattr_security_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return ext2_xattr_set(dentry->d_inode, EXT2_XATTR_INDEX_SECURITY, name,
|
||||
value, size, flags);
|
||||
}
|
||||
|
||||
static int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
|
||||
void *fs_info)
|
||||
{
|
||||
const struct xattr *xattr;
|
||||
int err = 0;
|
||||
|
||||
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
|
||||
err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
|
||||
xattr->name, xattr->value,
|
||||
xattr->value_len, 0);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
ext2_init_security(struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr)
|
||||
{
|
||||
return security_inode_init_security(inode, dir, qstr,
|
||||
&ext2_initxattrs, NULL);
|
||||
}
|
||||
|
||||
const 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,
|
||||
};
|
54
fs/ext2/xattr_trusted.c
Normal file
54
fs/ext2/xattr_trusted.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* linux/fs/ext2/xattr_trusted.c
|
||||
* Handler for trusted extended attributes.
|
||||
*
|
||||
* Copyright (C) 2003 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
|
||||
*/
|
||||
|
||||
#include "ext2.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static size_t
|
||||
ext2_xattr_trusted_list(struct dentry *dentry, char *list, size_t list_size,
|
||||
const char *name, size_t name_len, int type)
|
||||
{
|
||||
const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;
|
||||
const size_t total_len = prefix_len + name_len + 1;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return 0;
|
||||
|
||||
if (list && 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 dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return ext2_xattr_get(dentry->d_inode, EXT2_XATTR_INDEX_TRUSTED, name,
|
||||
buffer, size);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_xattr_trusted_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
return ext2_xattr_set(dentry->d_inode, EXT2_XATTR_INDEX_TRUSTED, name,
|
||||
value, size, flags);
|
||||
}
|
||||
|
||||
const 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,
|
||||
};
|
61
fs/ext2/xattr_user.c
Normal file
61
fs/ext2/xattr_user.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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/string.h>
|
||||
#include "ext2.h"
|
||||
#include "xattr.h"
|
||||
|
||||
static size_t
|
||||
ext2_xattr_user_list(struct dentry *dentry, char *list, size_t list_size,
|
||||
const char *name, size_t name_len, int type)
|
||||
{
|
||||
const size_t prefix_len = XATTR_USER_PREFIX_LEN;
|
||||
const size_t total_len = prefix_len + name_len + 1;
|
||||
|
||||
if (!test_opt(dentry->d_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 dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
if (!test_opt(dentry->d_sb, XATTR_USER))
|
||||
return -EOPNOTSUPP;
|
||||
return ext2_xattr_get(dentry->d_inode, EXT2_XATTR_INDEX_USER,
|
||||
name, buffer, size);
|
||||
}
|
||||
|
||||
static int
|
||||
ext2_xattr_user_set(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags, int type)
|
||||
{
|
||||
if (strcmp(name, "") == 0)
|
||||
return -EINVAL;
|
||||
if (!test_opt(dentry->d_sb, XATTR_USER))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ext2_xattr_set(dentry->d_inode, EXT2_XATTR_INDEX_USER,
|
||||
name, value, size, flags);
|
||||
}
|
||||
|
||||
const 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,
|
||||
};
|
91
fs/ext2/xip.c
Normal file
91
fs/ext2/xip.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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/blkdev.h>
|
||||
#include "ext2.h"
|
||||
#include "xip.h"
|
||||
|
||||
static inline int
|
||||
__inode_direct_access(struct inode *inode, sector_t block,
|
||||
void **kaddr, unsigned long *pfn)
|
||||
{
|
||||
struct block_device *bdev = inode->i_sb->s_bdev;
|
||||
const struct block_device_operations *ops = bdev->bd_disk->fops;
|
||||
sector_t sector;
|
||||
|
||||
sector = block * (PAGE_SIZE / 512); /* ext2 block to bdev sector */
|
||||
|
||||
BUG_ON(!ops->direct_access);
|
||||
return ops->direct_access(bdev, sector, kaddr, pfn);
|
||||
}
|
||||
|
||||
static inline int
|
||||
__ext2_get_block(struct inode *inode, pgoff_t pgoff, int create,
|
||||
sector_t *result)
|
||||
{
|
||||
struct buffer_head tmp;
|
||||
int rc;
|
||||
|
||||
memset(&tmp, 0, sizeof(struct buffer_head));
|
||||
tmp.b_size = 1 << inode->i_blkbits;
|
||||
rc = ext2_get_block(inode, pgoff, &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, sector_t block)
|
||||
{
|
||||
void *kaddr;
|
||||
unsigned long pfn;
|
||||
int rc;
|
||||
|
||||
rc = __inode_direct_access(inode, block, &kaddr, &pfn);
|
||||
if (!rc)
|
||||
clear_page(kaddr);
|
||||
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_msg(sb, KERN_WARNING,
|
||||
"warning: ignoring xip option - "
|
||||
"not supported by bdev");
|
||||
}
|
||||
}
|
||||
|
||||
int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create,
|
||||
void **kmem, unsigned long *pfn)
|
||||
{
|
||||
int rc;
|
||||
sector_t block;
|
||||
|
||||
/* first, retrieve the sector number */
|
||||
rc = __ext2_get_block(mapping->host, pgoff, create, &block);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* retrieve address of the target data */
|
||||
rc = __inode_direct_access(mapping->host, block, kmem, pfn);
|
||||
return rc;
|
||||
}
|
26
fs/ext2/xip.h
Normal file
26
fs/ext2/xip.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* linux/fs/ext2/xip.h
|
||||
*
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
* Author: Carsten Otte (cotte@de.ibm.com)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_EXT2_FS_XIP
|
||||
extern void ext2_xip_verify_sb (struct super_block *);
|
||||
extern int ext2_clear_xip_target (struct inode *, sector_t);
|
||||
|
||||
static inline int ext2_use_xip (struct super_block *sb)
|
||||
{
|
||||
struct ext2_sb_info *sbi = EXT2_SB(sb);
|
||||
return (sbi->s_mount_opt & EXT2_MOUNT_XIP);
|
||||
}
|
||||
int ext2_get_xip_mem(struct address_space *, pgoff_t, int,
|
||||
void **, unsigned long *);
|
||||
#define mapping_is_xip(map) unlikely(map->a_ops->get_xip_mem)
|
||||
#else
|
||||
#define mapping_is_xip(map) 0
|
||||
#define ext2_xip_verify_sb(sb) do { } while (0)
|
||||
#define ext2_use_xip(sb) 0
|
||||
#define ext2_clear_xip_target(inode, chain) 0
|
||||
#define ext2_get_xip_mem NULL
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue