Merge with /home/shaggy/git/linus-clean/

Signed-off-by: Dave Kleikamp <[email protected]>
This commit is contained in:
Dave Kleikamp 2005-07-13 08:57:38 -05:00
commit f7f24758ac
2863 changed files with 168538 additions and 88761 deletions

View file

@ -29,6 +29,7 @@ config EXT2_FS_XATTR
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.
@ -50,6 +51,23 @@ config EXT2_FS_SECURITY
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
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.
config FS_XIP
# execute in place
bool
depends on EXT2_FS_XIP
default y
config EXT3_FS
tristate "Ext3 journalling file system support"
help
@ -97,6 +115,7 @@ config EXT3_FS_XATTR
config EXT3_FS_POSIX_ACL
bool "Ext3 POSIX Access Control Lists"
depends on EXT3_FS_XATTR
select FS_POSIX_ACL
help
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
@ -224,6 +243,7 @@ config REISERFS_FS_XATTR
config REISERFS_FS_POSIX_ACL
bool "ReiserFS POSIX Access Control Lists"
depends on REISERFS_FS_XATTR
select FS_POSIX_ACL
help
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
@ -257,6 +277,7 @@ config JFS_FS
config JFS_POSIX_ACL
bool "JFS POSIX Access Control Lists"
depends on JFS_FS
select FS_POSIX_ACL
help
Posix Access Control Lists (ACLs) support permissions for users and
groups beyond the owner/group/world scheme.
@ -301,8 +322,7 @@ config FS_POSIX_ACL
# Never use this symbol for ifdefs.
#
bool
depends on EXT2_FS_POSIX_ACL || EXT3_FS_POSIX_ACL || JFS_POSIX_ACL || REISERFS_FS_POSIX_ACL || NFSD_V4
default y
default n
source "fs/xfs/Kconfig"
@ -339,6 +359,19 @@ config ROMFS_FS
If you don't know whether you need it, then you don't need it:
answer N.
config INOTIFY
bool "Inotify file change notification support"
default y
---help---
Say Y here to enable inotify support and the /dev/inotify character
device. Inotify is a file change notification system and a
replacement for dnotify. Inotify fixes numerous shortcomings in
dnotify and introduces several new features. It allows monitoring
of both files and directories via a single open fd. Multiple file
events are supported.
If unsure, say Y.
config QUOTA
bool "Quota support"
help
@ -717,6 +750,12 @@ config PROC_KCORE
bool "/proc/kcore support" if !ARM
depends on PROC_FS && MMU
config PROC_VMCORE
bool "/proc/vmcore support (EXPERIMENTAL)"
depends on PROC_FS && EMBEDDED && EXPERIMENTAL && CRASH_DUMP
help
Exports the dump image of crashed kernel in ELF format.
config SYSFS
bool "sysfs file system support" if EMBEDDED
default y
@ -1013,26 +1052,18 @@ config JFFS2_FS_DEBUG
If reporting bugs, please try to have available a full dump of the
messages at debug level 1 while the misbehaviour was occurring.
config JFFS2_FS_NAND
bool "JFFS2 support for NAND flash"
config JFFS2_FS_WRITEBUFFER
bool "JFFS2 write-buffering support"
depends on JFFS2_FS
default n
default y
help
This enables the support for NAND flash in JFFS2. NAND is a newer
type of flash chip design than the traditional NOR flash, with
higher density but a handful of characteristics which make it more
interesting for the file system to use.
This enables the write-buffering support in JFFS2.
Say 'N' unless you have NAND flash.
config JFFS2_FS_NOR_ECC
bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)"
depends on JFFS2_FS && EXPERIMENTAL
default n
help
This enables the experimental support for NOR flash with transparent
ECC for JFFS2. This type of flash chip is not common, however it is
available from ST Microelectronics.
This functionality is required to support JFFS2 on the following
types of flash devices:
- NAND flash
- NOR flash with transparent ECC
- DataFlash
config JFFS2_COMPRESSION_OPTIONS
bool "Advanced compression options for JFFS2"
@ -1413,6 +1444,9 @@ config NFSD_V4
bool "Provide NFSv4 server support (EXPERIMENTAL)"
depends on NFSD_V3 && EXPERIMENTAL
select NFSD_TCP
select CRYPTO_MD5
select CRYPTO
select FS_POSIX_ACL
help
If you would like to include the NFSv4 server as well as the NFSv2
and NFSv3 servers, say Y here. This feature is experimental, and

View file

@ -10,7 +10,9 @@ obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \
ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \
ioprio.o
obj-$(CONFIG_INOTIFY) += inotify.o
obj-$(CONFIG_EPOLL) += eventpoll.o
obj-$(CONFIG_COMPAT) += compat.o

View file

@ -116,7 +116,7 @@ static int kafsasyncd(void *arg)
remove_wait_queue(&kafsasyncd_sleepq, &myself);
set_current_state(TASK_RUNNING);
try_to_freeze(PF_FREEZE);
try_to_freeze();
/* discard pending signals */
afs_discard_my_signals();

View file

@ -91,7 +91,7 @@ static int kafstimod(void *arg)
complete_and_exit(&kafstimod_dead, 0);
}
try_to_freeze(PF_FREEZE);
try_to_freeze();
/* discard pending signals */
afs_discard_my_signals();

View file

@ -58,6 +58,7 @@ static DEFINE_SPINLOCK(fput_lock);
static LIST_HEAD(fput_head);
static void aio_kick_handler(void *);
static void aio_queue_work(struct kioctx *);
/* aio_setup
* Creates the slab caches used by the aio routines, panic on
@ -747,6 +748,14 @@ out:
* has already been kicked */
if (kiocbIsKicked(iocb)) {
__queue_kicked_iocb(iocb);
/*
* __queue_kicked_iocb will always return 1 here, because
* iocb->ki_run_list is empty at this point so it should
* be safe to unconditionally queue the context into the
* work queue.
*/
aio_queue_work(ctx);
}
}
return ret;

View file

@ -10,7 +10,7 @@
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/smp_lock.h>
#include <linux/dnotify.h>
#include <linux/fsnotify.h>
#include <linux/fcntl.h>
#include <linux/quotaops.h>
#include <linux/security.h>
@ -107,31 +107,8 @@ int inode_setattr(struct inode * inode, struct iattr * attr)
out:
return error;
}
EXPORT_SYMBOL(inode_setattr);
int setattr_mask(unsigned int ia_valid)
{
unsigned long dn_mask = 0;
if (ia_valid & ATTR_UID)
dn_mask |= DN_ATTRIB;
if (ia_valid & ATTR_GID)
dn_mask |= DN_ATTRIB;
if (ia_valid & ATTR_SIZE)
dn_mask |= DN_MODIFY;
/* both times implies a utime(s) call */
if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))
dn_mask |= DN_ATTRIB;
else if (ia_valid & ATTR_ATIME)
dn_mask |= DN_ACCESS;
else if (ia_valid & ATTR_MTIME)
dn_mask |= DN_MODIFY;
if (ia_valid & ATTR_MODE)
dn_mask |= DN_ATTRIB;
return dn_mask;
}
int notify_change(struct dentry * dentry, struct iattr * attr)
{
struct inode *inode = dentry->d_inode;
@ -197,11 +174,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
if (ia_valid & ATTR_SIZE)
up_write(&dentry->d_inode->i_alloc_sem);
if (!error) {
unsigned long dn_mask = setattr_mask(ia_valid);
if (dn_mask)
dnotify_parent(dentry, dn_mask);
}
if (!error)
fsnotify_change(dentry, ia_valid);
return error;
}

View file

@ -231,8 +231,8 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
int type = (notify == NFY_MOUNT ?
autofs_ptype_missing : autofs_ptype_expire_multi);
DPRINTK(("new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify));
DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
/* autofs4_notify_daemon() may block */
autofs4_notify_daemon(sbi, wq, type);

View file

@ -52,7 +52,7 @@ struct biovec_slab {
*/
#define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) }
static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] = {
static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {
BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES),
};
#undef BV

View file

@ -278,7 +278,7 @@ EXPORT_SYMBOL(thaw_bdev);
*/
static void do_sync(unsigned long wait)
{
wakeup_bdflush(0);
wakeup_pdflush(0);
sync_inodes(0); /* All mappings, inodes and their blockdevs */
DQUOT_SYNC(NULL);
sync_supers(); /* Write the superblocks */
@ -497,7 +497,7 @@ static void free_more_memory(void)
struct zone **zones;
pg_data_t *pgdat;
wakeup_bdflush(1024);
wakeup_pdflush(1024);
yield();
for_each_pgdat(pgdat) {
@ -513,8 +513,8 @@ static void free_more_memory(void)
*/
static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
{
static DEFINE_SPINLOCK(page_uptodate_lock);
unsigned long flags;
struct buffer_head *first;
struct buffer_head *tmp;
struct page *page;
int page_uptodate = 1;
@ -536,7 +536,9 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
* two buffer heads end IO at almost the same time and both
* decide that the page is now completely done.
*/
spin_lock_irqsave(&page_uptodate_lock, flags);
first = page_buffers(page);
local_irq_save(flags);
bit_spin_lock(BH_Uptodate_Lock, &first->b_state);
clear_buffer_async_read(bh);
unlock_buffer(bh);
tmp = bh;
@ -549,7 +551,8 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
}
tmp = tmp->b_this_page;
} while (tmp != bh);
spin_unlock_irqrestore(&page_uptodate_lock, flags);
bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
local_irq_restore(flags);
/*
* If none of the buffers had errors and they are all
@ -561,7 +564,8 @@ static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
return;
still_busy:
spin_unlock_irqrestore(&page_uptodate_lock, flags);
bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
local_irq_restore(flags);
return;
}
@ -572,8 +576,8 @@ still_busy:
void end_buffer_async_write(struct buffer_head *bh, int uptodate)
{
char b[BDEVNAME_SIZE];
static DEFINE_SPINLOCK(page_uptodate_lock);
unsigned long flags;
struct buffer_head *first;
struct buffer_head *tmp;
struct page *page;
@ -594,7 +598,10 @@ void end_buffer_async_write(struct buffer_head *bh, int uptodate)
SetPageError(page);
}
spin_lock_irqsave(&page_uptodate_lock, flags);
first = page_buffers(page);
local_irq_save(flags);
bit_spin_lock(BH_Uptodate_Lock, &first->b_state);
clear_buffer_async_write(bh);
unlock_buffer(bh);
tmp = bh->b_this_page;
@ -605,12 +612,14 @@ void end_buffer_async_write(struct buffer_head *bh, int uptodate)
}
tmp = tmp->b_this_page;
}
spin_unlock_irqrestore(&page_uptodate_lock, flags);
bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
local_irq_restore(flags);
end_page_writeback(page);
return;
still_busy:
spin_unlock_irqrestore(&page_uptodate_lock, flags);
bit_spin_unlock(BH_Uptodate_Lock, &first->b_state);
local_irq_restore(flags);
return;
}

View file

@ -150,7 +150,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
struct char_device_struct *cd = NULL, **cp;
int i = major_to_index(major);
up(&chrdevs_lock);
down(&chrdevs_lock);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
@ -277,8 +277,9 @@ static struct kobject *cdev_get(struct cdev *p)
void cdev_put(struct cdev *p)
{
if (p) {
struct module *owner = p->owner;
kobject_put(&p->kobj);
module_put(p->owner);
module_put(owner);
}
}

View file

@ -37,7 +37,7 @@
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/dirent.h>
#include <linux/dnotify.h>
#include <linux/fsnotify.h>
#include <linux/highuid.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
@ -1307,9 +1307,13 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
out:
if (iov != iovstack)
kfree(iov);
if ((ret + (type == READ)) > 0)
dnotify_parent(file->f_dentry,
(type == READ) ? DN_ACCESS : DN_MODIFY);
if ((ret + (type == READ)) > 0) {
struct dentry *dentry = file->f_dentry;
if (type == READ)
fsnotify_access(dentry);
else
fsnotify_modify(dentry);
}
return ret;
}

View file

@ -94,12 +94,10 @@ static struct dcookie_struct * alloc_dcookie(struct dentry * dentry,
if (!dcs)
return NULL;
atomic_inc(&dentry->d_count);
atomic_inc(&vfsmnt->mnt_count);
dentry->d_cookie = dcs;
dcs->dentry = dentry;
dcs->vfsmnt = vfsmnt;
dcs->dentry = dget(dentry);
dcs->vfsmnt = mntget(vfsmnt);
hash_dcookie(dcs);
return dcs;

View file

@ -215,7 +215,7 @@ static struct page *dio_get_page(struct dio *dio)
static void dio_complete(struct dio *dio, loff_t offset, ssize_t bytes)
{
if (dio->end_io && dio->result)
dio->end_io(dio->inode, offset, bytes, dio->map_bh.b_private);
dio->end_io(dio->iocb, offset, bytes, dio->map_bh.b_private);
if (dio->lock_type == DIO_LOCKING)
up_read(&dio->inode->i_alloc_sem);
}

View file

@ -409,13 +409,10 @@ out_dqlock:
* for this sb+type at all. */
static void invalidate_dquots(struct super_block *sb, int type)
{
struct dquot *dquot;
struct list_head *head;
struct dquot *dquot, *tmp;
spin_lock(&dq_list_lock);
for (head = inuse_list.next; head != &inuse_list;) {
dquot = list_entry(head, struct dquot, dq_inuse);
head = head->next;
list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
if (dquot->dq_sb != sb)
continue;
if (dquot->dq_type != type)

View file

@ -642,6 +642,18 @@ static inline int de_thread(struct task_struct *tsk)
count = 2;
if (thread_group_leader(current))
count = 1;
else {
/*
* The SIGALRM timer survives the exec, but needs to point
* at us as the new group leader now. We have a race with
* a timer firing now getting the old leader, so we need to
* synchronize with any firing (by calling del_timer_sync)
* before we can safely let the old group leader die.
*/
sig->real_timer.data = (unsigned long)current;
if (del_timer_sync(&sig->real_timer))
add_timer(&sig->real_timer);
}
while (atomic_read(&sig->count) > count) {
sig->group_exit_task = current;
sig->notify_count = count;

View file

@ -10,3 +10,4 @@ ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.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

View file

@ -1,6 +1,15 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
/*
* ext2 mount options
*/
struct ext2_mount_options {
unsigned long s_mount_opt;
uid_t s_resuid;
gid_t s_resgid;
};
/*
* second extended file system inode data in memory
*/
@ -147,9 +156,11 @@ extern struct file_operations ext2_dir_operations;
/* file.c */
extern struct inode_operations ext2_file_inode_operations;
extern struct file_operations ext2_file_operations;
extern struct file_operations ext2_xip_file_operations;
/* inode.c */
extern struct address_space_operations ext2_aops;
extern struct address_space_operations ext2_aops_xip;
extern struct address_space_operations ext2_nobh_aops;
/* namei.c */

View file

@ -55,6 +55,20 @@ struct file_operations ext2_file_operations = {
.sendfile = generic_file_sendfile,
};
#ifdef CONFIG_EXT2_FS_XIP
struct file_operations ext2_xip_file_operations = {
.llseek = generic_file_llseek,
.read = xip_file_read,
.write = xip_file_write,
.ioctl = ext2_ioctl,
.mmap = xip_file_mmap,
.open = generic_file_open,
.release = ext2_release_file,
.fsync = ext2_sync_file,
.sendfile = xip_file_sendfile,
};
#endif
struct inode_operations ext2_file_inode_operations = {
.truncate = ext2_truncate,
#ifdef CONFIG_EXT2_FS_XATTR

View file

@ -33,6 +33,7 @@
#include <linux/mpage.h>
#include "ext2.h"
#include "acl.h"
#include "xip.h"
MODULE_AUTHOR("Remy Card and others");
MODULE_DESCRIPTION("Second Extended Filesystem");
@ -594,6 +595,16 @@ out:
if (err)
goto cleanup;
if (ext2_use_xip(inode->i_sb)) {
/*
* we need to clear the block
*/
err = ext2_clear_xip_target (inode,
le32_to_cpu(chain[depth-1].key));
if (err)
goto cleanup;
}
if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)
goto changed;
@ -691,6 +702,11 @@ struct address_space_operations ext2_aops = {
.writepages = ext2_writepages,
};
struct address_space_operations ext2_aops_xip = {
.bmap = ext2_bmap,
.get_xip_page = ext2_get_xip_page,
};
struct address_space_operations ext2_nobh_aops = {
.readpage = ext2_readpage,
.readpages = ext2_readpages,
@ -910,7 +926,9 @@ void ext2_truncate (struct inode * inode)
iblock = (inode->i_size + blocksize-1)
>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
if (test_opt(inode->i_sb, NOBH))
if (mapping_is_xip(inode->i_mapping))
xip_truncate_page(inode->i_mapping, inode->i_size);
else if (test_opt(inode->i_sb, NOBH))
nobh_truncate_page(inode->i_mapping, inode->i_size);
else
block_truncate_page(inode->i_mapping,
@ -1110,11 +1128,16 @@ void ext2_read_inode (struct inode * inode)
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
if (test_opt(inode->i_sb, NOBH))
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;
else
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops;
inode->i_fop = &ext2_file_operations;
}
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &ext2_dir_inode_operations;
inode->i_fop = &ext2_dir_operations;

View file

@ -34,6 +34,7 @@
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
#include "xip.h"
/*
* Couple of helper functions - make the code slightly cleaner.
@ -127,11 +128,16 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode, st
int err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
if (test_opt(inode->i_sb, NOBH))
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;
else
inode->i_fop = &ext2_file_operations;
} else {
inode->i_mapping->a_ops = &ext2_aops;
inode->i_fop = &ext2_file_operations;
}
mark_inode_dirty(inode);
err = ext2_add_nondir(dentry, inode);
}

View file

@ -31,6 +31,7 @@
#include "ext2.h"
#include "xattr.h"
#include "acl.h"
#include "xip.h"
static void ext2_sync_super(struct super_block *sb,
struct ext2_super_block *es);
@ -257,7 +258,7 @@ enum {
Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh,
Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip,
Opt_ignore, Opt_err,
};
@ -286,6 +287,7 @@ static match_table_t tokens = {
{Opt_nouser_xattr, "nouser_xattr"},
{Opt_acl, "acl"},
{Opt_noacl, "noacl"},
{Opt_xip, "xip"},
{Opt_ignore, "grpquota"},
{Opt_ignore, "noquota"},
{Opt_ignore, "quota"},
@ -397,6 +399,13 @@ static int parse_options (char * options,
printk("EXT2 (no)acl options not supported\n");
break;
#endif
case Opt_xip:
#ifdef CONFIG_EXT2_FS_XIP
set_opt (sbi->s_mount_opt, XIP);
#else
printk("EXT2 xip option not supported\n");
#endif
break;
case Opt_ignore:
break;
default:
@ -640,6 +649,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
MS_POSIXACL : 0);
ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset
EXT2_MOUNT_XIP if not */
if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
(EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
@ -668,6 +680,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
if ((ext2_use_xip(sb)) && ((blocksize != PAGE_SIZE) ||
(sb->s_blocksize != blocksize))) {
if (!silent)
printk("XIP: Unsupported blocksize\n");
goto failed_mount;
}
/* If the blocksize doesn't match, re-read the thing.. */
if (sb->s_blocksize != blocksize) {
brelse(bh);
@ -916,17 +935,34 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
{
struct ext2_sb_info * sbi = EXT2_SB(sb);
struct ext2_super_block * es;
unsigned long old_mount_opt = sbi->s_mount_opt;
struct ext2_mount_options old_opts;
unsigned long old_sb_flags;
int err;
/* Store the old options */
old_sb_flags = sb->s_flags;
old_opts.s_mount_opt = sbi->s_mount_opt;
old_opts.s_resuid = sbi->s_resuid;
old_opts.s_resgid = sbi->s_resgid;
/*
* Allow the "check" option to be passed as a remount option.
*/
if (!parse_options (data, sbi))
return -EINVAL;
if (!parse_options (data, sbi)) {
err = -EINVAL;
goto restore_opts;
}
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
es = sbi->s_es;
if (((sbi->s_mount_opt & EXT2_MOUNT_XIP) !=
(old_mount_opt & EXT2_MOUNT_XIP)) &&
invalidate_inodes(sb))
ext2_warning(sb, __FUNCTION__, "busy inodes while remounting "\
"xip remain in cache (no functional problem)");
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
if (*flags & MS_RDONLY) {
@ -946,7 +982,8 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
printk("EXT2-fs: %s: couldn't remount RDWR because of "
"unsupported optional features (%x).\n",
sb->s_id, le32_to_cpu(ret));
return -EROFS;
err = -EROFS;
goto restore_opts;
}
/*
* Mounting a RDONLY partition read-write, so reread and
@ -959,6 +996,12 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
}
ext2_sync_super(sb, es);
return 0;
restore_opts:
sbi->s_mount_opt = old_opts.s_mount_opt;
sbi->s_resuid = old_opts.s_resuid;
sbi->s_resgid = old_opts.s_resgid;
sb->s_flags = old_sb_flags;
return err;
}
static int ext2_statfs (struct super_block * sb, struct kstatfs * buf)

80
fs/ext2/xip.c Normal file
View file

@ -0,0 +1,80 @@
/*
* linux/fs/ext2/xip.c
*
* Copyright (C) 2005 IBM Corporation
* Author: Carsten Otte (cotte@de.ibm.com)
*/
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/buffer_head.h>
#include <linux/ext2_fs_sb.h>
#include <linux/ext2_fs.h>
#include "ext2.h"
#include "xip.h"
static inline int
__inode_direct_access(struct inode *inode, sector_t sector, unsigned long *data) {
BUG_ON(!inode->i_sb->s_bdev->bd_disk->fops->direct_access);
return inode->i_sb->s_bdev->bd_disk->fops
->direct_access(inode->i_sb->s_bdev,sector,data);
}
int
ext2_clear_xip_target(struct inode *inode, int block) {
sector_t sector = block*(PAGE_SIZE/512);
unsigned long data;
int rc;
rc = __inode_direct_access(inode, sector, &data);
if (rc)
return rc;
clear_page((void*)data);
return 0;
}
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)) {
if ((sb->s_bdev == NULL) ||
sb->s_bdev->bd_disk == NULL ||
sb->s_bdev->bd_disk->fops == NULL ||
sb->s_bdev->bd_disk->fops->direct_access == NULL) {
sbi->s_mount_opt &= (~EXT2_MOUNT_XIP);
ext2_warning(sb, __FUNCTION__,
"ignoring xip option - not supported by bdev");
}
}
}
struct page*
ext2_get_xip_page(struct address_space *mapping, sector_t blockno,
int create)
{
int rc;
unsigned long data;
struct buffer_head tmp;
tmp.b_state = 0;
tmp.b_blocknr = 0;
rc = ext2_get_block(mapping->host, blockno/(PAGE_SIZE/512) , &tmp,
create);
if (rc)
return ERR_PTR(rc);
if (tmp.b_blocknr == 0) {
/* SPARSE block */
BUG_ON(create);
return ERR_PTR(-ENODATA);
}
rc = __inode_direct_access
(mapping->host,tmp.b_blocknr*(PAGE_SIZE/512) ,&data);
if (rc)
return ERR_PTR(rc);
SetPageUptodate(virt_to_page(data));
return virt_to_page(data);
}

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

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

View file

@ -393,7 +393,8 @@ ext3_acl_chmod(struct inode *inode)
int retries = 0;
retry:
handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS);
handle = ext3_journal_start(inode,
EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
if (IS_ERR(handle)) {
error = PTR_ERR(handle);
ext3_std_error(inode->i_sb, error);
@ -503,7 +504,7 @@ ext3_xattr_set_acl(struct inode *inode, int type, const void *value,
acl = NULL;
retry:
handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS);
handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
error = ext3_set_acl(handle, inode, type, acl);

View file

@ -749,24 +749,24 @@ fail_access:
* to find a free region that is of my size and has not
* been reserved.
*
* on succeed, it returns the reservation window to be appended to.
* failed, return NULL.
*/
static struct ext3_reserve_window_node *find_next_reservable_window(
static int find_next_reservable_window(
struct ext3_reserve_window_node *search_head,
unsigned long size, int *start_block,
struct ext3_reserve_window_node *my_rsv,
struct super_block * sb, int start_block,
int last_block)
{
struct rb_node *next;
struct ext3_reserve_window_node *rsv, *prev;
int cur;
int size = my_rsv->rsv_goal_size;
/* TODO: make the start of the reservation window byte-aligned */
/* cur = *start_block & ~7;*/
cur = *start_block;
cur = start_block;
rsv = search_head;
if (!rsv)
return NULL;
return -1;
while (1) {
if (cur <= rsv->rsv_end)
@ -782,11 +782,11 @@ static struct ext3_reserve_window_node *find_next_reservable_window(
* space with expected-size (or more)...
*/
if (cur > last_block)
return NULL; /* fail */
return -1; /* fail */
prev = rsv;
next = rb_next(&rsv->rsv_node);
rsv = list_entry(next, struct ext3_reserve_window_node, rsv_node);
rsv = list_entry(next,struct ext3_reserve_window_node,rsv_node);
/*
* Reached the last reservation, we can just append to the
@ -813,8 +813,25 @@ static struct ext3_reserve_window_node *find_next_reservable_window(
* return the reservation window that we could append to.
* succeed.
*/
*start_block = cur;
return prev;
if ((prev != my_rsv) && (!rsv_is_empty(&my_rsv->rsv_window)))
rsv_window_remove(sb, my_rsv);
/*
* Let's book the whole avaliable window for now. We will check the
* disk bitmap later and then, if there are free blocks then we adjust
* the window size if it's larger than requested.
* Otherwise, we will remove this node from the tree next time
* call find_next_reservable_window.
*/
my_rsv->rsv_start = cur;
my_rsv->rsv_end = cur + size - 1;
my_rsv->rsv_alloc_hit = 0;
if (prev != my_rsv)
ext3_rsv_window_add(sb, my_rsv);
return 0;
}
/**
@ -852,6 +869,7 @@ static struct ext3_reserve_window_node *find_next_reservable_window(
* @sb: the super block
* @group: the group we are trying to allocate in
* @bitmap_bh: the block group block bitmap
*
*/
static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,
int goal, struct super_block *sb,
@ -860,10 +878,10 @@ static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,
struct ext3_reserve_window_node *search_head;
int group_first_block, group_end_block, start_block;
int first_free_block;
int reservable_space_start;
struct ext3_reserve_window_node *prev_rsv;
struct rb_root *fs_rsv_root = &EXT3_SB(sb)->s_rsv_window_root;
unsigned long size;
int ret;
spinlock_t *rsv_lock = &EXT3_SB(sb)->s_rsv_window_lock;
group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) +
group * EXT3_BLOCKS_PER_GROUP(sb);
@ -875,6 +893,7 @@ static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,
start_block = goal + group_first_block;
size = my_rsv->rsv_goal_size;
if (!rsv_is_empty(&my_rsv->rsv_window)) {
/*
* if the old reservation is cross group boundary
@ -908,6 +927,8 @@ static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,
my_rsv->rsv_goal_size= size;
}
}
spin_lock(rsv_lock);
/*
* shift the search start to the window near the goal block
*/
@ -921,11 +942,16 @@ static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv,
* need to check the bitmap after we found a reservable window.
*/
retry:
prev_rsv = find_next_reservable_window(search_head, size,
&start_block, group_end_block);
if (prev_rsv == NULL)
goto failed;
reservable_space_start = start_block;
ret = find_next_reservable_window(search_head, my_rsv, sb,
start_block, group_end_block);
if (ret == -1) {
if (!rsv_is_empty(&my_rsv->rsv_window))
rsv_window_remove(sb, my_rsv);
spin_unlock(rsv_lock);
return -1;
}
/*
* On success, find_next_reservable_window() returns the
* reservation window where there is a reservable space after it.
@ -937,8 +963,9 @@ retry:
* block. Search start from the start block of the reservable space
* we just found.
*/
spin_unlock(rsv_lock);
first_free_block = bitmap_search_next_usable_block(
reservable_space_start - group_first_block,
my_rsv->rsv_start - group_first_block,
bitmap_bh, group_end_block - group_first_block + 1);
if (first_free_block < 0) {
@ -946,54 +973,29 @@ retry:
* no free block left on the bitmap, no point
* to reserve the space. return failed.
*/
goto failed;
spin_lock(rsv_lock);
if (!rsv_is_empty(&my_rsv->rsv_window))
rsv_window_remove(sb, my_rsv);
spin_unlock(rsv_lock);
return -1; /* failed */
}
start_block = first_free_block + group_first_block;
/*
* check if the first free block is within the
* free space we just found
* free space we just reserved
*/
if ((start_block >= reservable_space_start) &&
(start_block < reservable_space_start + size))
goto found_rsv_window;
if (start_block >= my_rsv->rsv_start && start_block < my_rsv->rsv_end)
return 0; /* success */
/*
* if the first free bit we found is out of the reservable space
* this means there is no free block on the reservable space
* we should continue search for next reservable space,
* continue search for next reservable space,
* start from where the free block is,
* we also shift the list head to where we stopped last time
*/
search_head = prev_rsv;
search_head = my_rsv;
spin_lock(rsv_lock);
goto retry;
found_rsv_window:
/*
* great! the reservable space contains some free blocks.
* if the search returns that we should add the new
* window just next to where the old window, we don't
* need to remove the old window first then add it to the
* same place, just update the new start and new end.
*/
if (my_rsv != prev_rsv) {
if (!rsv_is_empty(&my_rsv->rsv_window))
rsv_window_remove(sb, my_rsv);
}
my_rsv->rsv_start = reservable_space_start;
my_rsv->rsv_end = my_rsv->rsv_start + size - 1;
my_rsv->rsv_alloc_hit = 0;
if (my_rsv != prev_rsv) {
ext3_rsv_window_add(sb, my_rsv);
}
return 0; /* succeed */
failed:
/*
* failed to find a new reservation window in the current
* group, remove the current(stale) reservation window
* if there is any
*/
if (!rsv_is_empty(&my_rsv->rsv_window))
rsv_window_remove(sb, my_rsv);
return -1; /* failed */
}
/*
@ -1023,7 +1025,6 @@ ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
int goal, struct ext3_reserve_window_node * my_rsv,
int *errp)
{
spinlock_t *rsv_lock;
unsigned long group_first_block;
int ret = 0;
int fatal;
@ -1052,7 +1053,6 @@ ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, NULL);
goto out;
}
rsv_lock = &EXT3_SB(sb)->s_rsv_window_lock;
/*
* goal is a group relative block number (if there is a goal)
* 0 < goal < EXT3_BLOCKS_PER_GROUP(sb)
@ -1078,30 +1078,21 @@ ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
* then we could go to allocate from the reservation window directly.
*/
while (1) {
struct ext3_reserve_window rsv_copy;
rsv_copy._rsv_start = my_rsv->rsv_start;
rsv_copy._rsv_end = my_rsv->rsv_end;
if (rsv_is_empty(&rsv_copy) || (ret < 0) ||
!goal_in_my_reservation(&rsv_copy, goal, group, sb)) {
spin_lock(rsv_lock);
if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
!goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb)) {
ret = alloc_new_reservation(my_rsv, goal, sb,
group, bitmap_bh);
rsv_copy._rsv_start = my_rsv->rsv_start;
rsv_copy._rsv_end = my_rsv->rsv_end;
spin_unlock(rsv_lock);
if (ret < 0)
break; /* failed */
if (!goal_in_my_reservation(&rsv_copy, goal, group, sb))
if (!goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb))
goal = -1;
}
if ((rsv_copy._rsv_start >= group_first_block + EXT3_BLOCKS_PER_GROUP(sb))
|| (rsv_copy._rsv_end < group_first_block))
if ((my_rsv->rsv_start >= group_first_block + EXT3_BLOCKS_PER_GROUP(sb))
|| (my_rsv->rsv_end < group_first_block))
BUG();
ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal,
&rsv_copy);
&my_rsv->rsv_window);
if (ret >= 0) {
my_rsv->rsv_alloc_hit++;
break; /* succeed */

View file

@ -36,7 +36,11 @@ static int ext3_release_file (struct inode * inode, struct file * filp)
/* if we are the last writer on the inode, drop the block reservation */
if ((filp->f_mode & FMODE_WRITE) &&
(atomic_read(&inode->i_writecount) == 1))
{
down(&EXT3_I(inode)->truncate_sem);
ext3_discard_reservation(inode);
up(&EXT3_I(inode)->truncate_sem);
}
if (is_dx(inode) && filp->private_data)
ext3_htree_free_dir_info(filp->private_data);

View file

@ -128,7 +128,7 @@ static unsigned long blocks_for_truncate(struct inode *inode)
if (needed > EXT3_MAX_TRANS_DATA)
needed = EXT3_MAX_TRANS_DATA;
return EXT3_DATA_TRANS_BLOCKS + needed;
return EXT3_DATA_TRANS_BLOCKS(inode->i_sb) + needed;
}
/*
@ -2663,7 +2663,7 @@ static int ext3_do_update_inode(handle_t *handle,
} else for (block = 0; block < EXT3_N_BLOCKS; block++)
raw_inode->i_block[block] = ei->i_data[block];
if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
if (ei->i_extra_isize)
raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
@ -2763,7 +2763,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
/* (user+group)*(old+new) structure, inode write (sb,
* inode block, ? - but truncate inode update has it) */
handle = ext3_journal_start(inode, 4*EXT3_QUOTA_INIT_BLOCKS+3);
handle = ext3_journal_start(inode, 2*(EXT3_QUOTA_INIT_BLOCKS(inode->i_sb)+
EXT3_QUOTA_DEL_BLOCKS(inode->i_sb))+3);
if (IS_ERR(handle)) {
error = PTR_ERR(handle);
goto err_out;
@ -2861,7 +2862,7 @@ static int ext3_writepage_trans_blocks(struct inode *inode)
#ifdef CONFIG_QUOTA
/* We know that structure was already allocated during DQUOT_INIT so
* we will be updating only the data blocks + inodes */
ret += 2*EXT3_QUOTA_TRANS_BLOCKS;
ret += 2*EXT3_QUOTA_TRANS_BLOCKS(inode->i_sb);
#endif
return ret;

View file

@ -1645,9 +1645,9 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode,
int err, retries = 0;
retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT3_QUOTA_INIT_BLOCKS);
2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -1679,9 +1679,9 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
return -EINVAL;
retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT3_QUOTA_INIT_BLOCKS);
2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -1715,9 +1715,9 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
return -EMLINK;
retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 +
2*EXT3_QUOTA_INIT_BLOCKS);
2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -2006,7 +2006,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
/* Initialize quotas before so that eventual writes go in
* separate transaction */
DQUOT_INIT(dentry->d_inode);
handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -2065,7 +2065,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
/* Initialize quotas before so that eventual writes go
* in separate transaction */
DQUOT_INIT(dentry->d_inode);
handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -2120,9 +2120,9 @@ static int ext3_symlink (struct inode * dir,
return -ENAMETOOLONG;
retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
2*EXT3_QUOTA_INIT_BLOCKS);
2*EXT3_QUOTA_INIT_BLOCKS(dir->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -2174,7 +2174,7 @@ static int ext3_link (struct dentry * old_dentry,
return -EMLINK;
retry:
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS);
if (IS_ERR(handle))
return PTR_ERR(handle);
@ -2216,7 +2216,8 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
* in separate transaction */
if (new_dentry->d_inode)
DQUOT_INIT(new_dentry->d_inode);
handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS +
handle = ext3_journal_start(old_dir, 2 *
EXT3_DATA_TRANS_BLOCKS(old_dir->i_sb) +
EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2);
if (IS_ERR(handle))
return PTR_ERR(handle);

View file

@ -589,7 +589,7 @@ enum {
Opt_commit, Opt_journal_update, Opt_journal_inum,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota,
Opt_ignore, Opt_barrier, Opt_err, Opt_resize,
};
@ -634,10 +634,10 @@ static match_table_t tokens = {
{Opt_grpjquota, "grpjquota=%s"},
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
{Opt_ignore, "grpquota"},
{Opt_ignore, "noquota"},
{Opt_ignore, "quota"},
{Opt_ignore, "usrquota"},
{Opt_quota, "grpquota"},
{Opt_noquota, "noquota"},
{Opt_quota, "quota"},
{Opt_quota, "usrquota"},
{Opt_barrier, "barrier=%u"},
{Opt_err, NULL},
{Opt_resize, "resize"},
@ -876,6 +876,7 @@ set_qf_name:
sbi->s_qf_names[qtype] = NULL;
return 0;
}
set_opt(sbi->s_mount_opt, QUOTA);
break;
case Opt_offusrjquota:
qtype = USRQUOTA;
@ -889,7 +890,10 @@ clear_qf_name:
"quota turned on.\n");
return 0;
}
kfree(sbi->s_qf_names[qtype]);
/*
* The space will be released later when all options
* are confirmed to be correct
*/
sbi->s_qf_names[qtype] = NULL;
break;
case Opt_jqfmt_vfsold:
@ -898,6 +902,17 @@ clear_qf_name:
case Opt_jqfmt_vfsv0:
sbi->s_jquota_fmt = QFMT_VFS_V0;
break;
case Opt_quota:
set_opt(sbi->s_mount_opt, QUOTA);
break;
case Opt_noquota:
if (sb_any_quota_enabled(sb)) {
printk(KERN_ERR "EXT3-fs: Cannot change quota "
"options when quota turned on.\n");
return 0;
}
clear_opt(sbi->s_mount_opt, QUOTA);
break;
#else
case Opt_usrjquota:
case Opt_grpjquota:
@ -909,6 +924,9 @@ clear_qf_name:
"EXT3-fs: journalled quota options not "
"supported.\n");
break;
case Opt_quota:
case Opt_noquota:
break;
#endif
case Opt_abort:
set_opt(sbi->s_mount_opt, ABORT);
@ -924,12 +942,13 @@ clear_qf_name:
case Opt_ignore:
break;
case Opt_resize:
if (!n_blocks_count) {
if (!is_remount) {
printk("EXT3-fs: resize option only available "
"for remount\n");
return 0;
}
match_int(&args[0], &option);
if (match_int(&args[0], &option) != 0)
return 0;
*n_blocks_count = option;
break;
case Opt_nobh:
@ -2093,14 +2112,33 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
{
struct ext3_super_block * es;
struct ext3_sb_info *sbi = EXT3_SB(sb);
unsigned long tmp;
unsigned long n_blocks_count = 0;
unsigned long old_sb_flags;
struct ext3_mount_options old_opts;
int err;
#ifdef CONFIG_QUOTA
int i;
#endif
/* Store the original options */
old_sb_flags = sb->s_flags;
old_opts.s_mount_opt = sbi->s_mount_opt;
old_opts.s_resuid = sbi->s_resuid;
old_opts.s_resgid = sbi->s_resgid;
old_opts.s_commit_interval = sbi->s_commit_interval;
#ifdef CONFIG_QUOTA
old_opts.s_jquota_fmt = sbi->s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++)
old_opts.s_qf_names[i] = sbi->s_qf_names[i];
#endif
/*
* Allow the "check" option to be passed as a remount option.
*/
if (!parse_options(data, sb, &tmp, &n_blocks_count, 1))
return -EINVAL;
if (!parse_options(data, sb, NULL, &n_blocks_count, 1)) {
err = -EINVAL;
goto restore_opts;
}
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
ext3_abort(sb, __FUNCTION__, "Abort forced by user");
@ -2114,8 +2152,10 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY) ||
n_blocks_count > le32_to_cpu(es->s_blocks_count)) {
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
return -EROFS;
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) {
err = -EROFS;
goto restore_opts;
}
if (*flags & MS_RDONLY) {
/*
@ -2142,7 +2182,8 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
"remount RDWR because of unsupported "
"optional features (%x).\n",
sb->s_id, le32_to_cpu(ret));
return -EROFS;
err = -EROFS;
goto restore_opts;
}
/*
* Mounting a RDONLY partition read-write, so reread
@ -2152,13 +2193,38 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
*/
ext3_clear_journal_err(sb, es);
sbi->s_mount_state = le16_to_cpu(es->s_state);
if ((ret = ext3_group_extend(sb, es, n_blocks_count)))
return ret;
if ((ret = ext3_group_extend(sb, es, n_blocks_count))) {
err = ret;
goto restore_opts;
}
if (!ext3_setup_super (sb, es, 0))
sb->s_flags &= ~MS_RDONLY;
}
}
#ifdef CONFIG_QUOTA
/* Release old quota file names */
for (i = 0; i < MAXQUOTAS; i++)
if (old_opts.s_qf_names[i] &&
old_opts.s_qf_names[i] != sbi->s_qf_names[i])
kfree(old_opts.s_qf_names[i]);
#endif
return 0;
restore_opts:
sb->s_flags = old_sb_flags;
sbi->s_mount_opt = old_opts.s_mount_opt;
sbi->s_resuid = old_opts.s_resuid;
sbi->s_resgid = old_opts.s_resgid;
sbi->s_commit_interval = old_opts.s_commit_interval;
#ifdef CONFIG_QUOTA
sbi->s_jquota_fmt = old_opts.s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++) {
if (sbi->s_qf_names[i] &&
old_opts.s_qf_names[i] != sbi->s_qf_names[i])
kfree(sbi->s_qf_names[i]);
sbi->s_qf_names[i] = old_opts.s_qf_names[i];
}
#endif
return err;
}
static int ext3_statfs (struct super_block * sb, struct kstatfs * buf)
@ -2238,7 +2304,7 @@ static int ext3_dquot_initialize(struct inode *inode, int type)
int ret, err;
/* We may create quota structure so we need to reserve enough blocks */
handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS);
handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS(inode->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
ret = dquot_initialize(inode, type);
@ -2254,7 +2320,7 @@ static int ext3_dquot_drop(struct inode *inode)
int ret, err;
/* We may delete quota structure so we need to reserve enough blocks */
handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS);
handle = ext3_journal_start(inode, 2*EXT3_QUOTA_DEL_BLOCKS(inode->i_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
ret = dquot_drop(inode);
@ -2272,7 +2338,7 @@ static int ext3_write_dquot(struct dquot *dquot)
inode = dquot_to_inode(dquot);
handle = ext3_journal_start(inode,
EXT3_QUOTA_TRANS_BLOCKS);
EXT3_QUOTA_TRANS_BLOCKS(dquot->dq_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
ret = dquot_commit(dquot);
@ -2288,7 +2354,7 @@ static int ext3_acquire_dquot(struct dquot *dquot)
handle_t *handle;
handle = ext3_journal_start(dquot_to_inode(dquot),
EXT3_QUOTA_INIT_BLOCKS);
EXT3_QUOTA_INIT_BLOCKS(dquot->dq_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
ret = dquot_acquire(dquot);
@ -2304,7 +2370,7 @@ static int ext3_release_dquot(struct dquot *dquot)
handle_t *handle;
handle = ext3_journal_start(dquot_to_inode(dquot),
EXT3_QUOTA_INIT_BLOCKS);
EXT3_QUOTA_DEL_BLOCKS(dquot->dq_sb));
if (IS_ERR(handle))
return PTR_ERR(handle);
ret = dquot_release(dquot);
@ -2361,6 +2427,8 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
int err;
struct nameidata nd;
if (!test_opt(sb, QUOTA))
return -EINVAL;
/* Not journalling quota? */
if (!EXT3_SB(sb)->s_qf_names[USRQUOTA] &&
!EXT3_SB(sb)->s_qf_names[GRPQUOTA])

View file

@ -1044,7 +1044,7 @@ ext3_xattr_set(struct inode *inode, int name_index, const char *name,
int error, retries = 0;
retry:
handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS);
handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
if (IS_ERR(handle)) {
error = PTR_ERR(handle);
} else {

View file

@ -56,7 +56,7 @@ int __init fat_cache_init(void)
return 0;
}
void __exit fat_cache_destroy(void)
void fat_cache_destroy(void)
{
if (kmem_cache_destroy(fat_cache_cachep))
printk(KERN_INFO "fat_cache: not all structures were freed\n");

View file

@ -1327,16 +1327,25 @@ out_fail:
EXPORT_SYMBOL(fat_fill_super);
int __init fat_cache_init(void);
void __exit fat_cache_destroy(void);
void fat_cache_destroy(void);
static int __init init_fat_fs(void)
{
int ret;
int err;
ret = fat_cache_init();
if (ret < 0)
return ret;
return fat_init_inodecache();
err = fat_cache_init();
if (err)
return err;
err = fat_init_inodecache();
if (err)
goto failed;
return 0;
failed:
fat_cache_destroy();
return err;
}
static void __exit exit_fat_fs(void)

View file

@ -16,6 +16,7 @@
#include <linux/eventpoll.h>
#include <linux/mount.h>
#include <linux/cdev.h>
#include <linux/fsnotify.h>
/* sysctl tunables... */
struct files_stat_struct files_stat = {
@ -126,6 +127,8 @@ void fastcall __fput(struct file *file)
struct inode *inode = dentry->d_inode;
might_sleep();
fsnotify_close(file);
/*
* The function eventpoll_release() should be the first called
* in the file cleanup chain.

View file

@ -37,7 +37,6 @@
* superblocks of the Veritas Filesystem.
*/
#include <linux/types.h>
#include "vxfs_kcompat.h"
/*

View file

@ -101,7 +101,7 @@ vxfs_bmap_ext4(struct inode *ip, long bn)
return 0;
fail_size:
printk("vxfs: indirect extent to big!\n");
printk("vxfs: indirect extent too big!\n");
fail_buf:
return 0;
}

View file

@ -78,17 +78,18 @@ vxfs_getfsh(struct inode *ip, int which)
struct buffer_head *bp;
bp = vxfs_bread(ip, which);
if (buffer_mapped(bp)) {
if (bp) {
struct vxfs_fsh *fhp;
if (!(fhp = kmalloc(sizeof(*fhp), SLAB_KERNEL)))
return NULL;
if (!(fhp = kmalloc(sizeof(*fhp), GFP_KERNEL)))
goto out;
memcpy(fhp, bp->b_data, sizeof(*fhp));
brelse(bp);
put_bh(bp);
return (fhp);
}
out:
brelse(bp);
return NULL;
}

View file

@ -1,49 +0,0 @@
#ifndef _VXFS_KCOMPAT_H
#define _VXFS_KCOMPAT_H
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
#include <linux/blkdev.h>
typedef long sector_t;
/* From include/linux/fs.h (Linux 2.5.2-pre3) */
static inline struct buffer_head * sb_bread(struct super_block *sb, int block)
{
return bread(sb->s_dev, block, sb->s_blocksize);
}
/* Dito. */
static inline void map_bh(struct buffer_head *bh, struct super_block *sb, int block)
{
bh->b_state |= 1 << BH_Mapped;
bh->b_dev = sb->s_dev;
bh->b_blocknr = block;
}
/* From fs/block_dev.c (Linux 2.5.2-pre2) */
static inline int sb_set_blocksize(struct super_block *sb, int size)
{
int bits;
if (set_blocksize(sb->s_dev, size) < 0)
return 0;
sb->s_blocksize = size;
for (bits = 9, size >>= 9; size >>= 1; bits++)
;
sb->s_blocksize_bits = bits;
return sb->s_blocksize;
}
/* Dito. */
static inline int sb_min_blocksize(struct super_block *sb, int size)
{
int minsize = get_hardsect_size(sb->s_dev);
if (size < minsize)
size = minsize;
return sb_set_blocksize(sb, size);
}
#endif /* Kernel 2.4 */
#endif /* _VXFS_KCOMPAT_H */

View file

@ -61,13 +61,13 @@ struct file_operations vxfs_dir_operations = {
};
static __inline__ u_long
static inline u_long
dir_pages(struct inode *inode)
{
return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
}
static __inline__ u_long
static inline u_long
dir_blocks(struct inode *ip)
{
u_long bsize = ip->i_sb->s_blocksize;
@ -79,7 +79,7 @@ dir_blocks(struct inode *ip)
*
* len <= VXFS_NAMELEN and de != NULL are guaranteed by caller.
*/
static __inline__ int
static inline int
vxfs_match(int len, const char * const name, struct vxfs_direct *de)
{
if (len != de->d_namelen)
@ -89,7 +89,7 @@ vxfs_match(int len, const char * const name, struct vxfs_direct *de)
return !memcmp(name, de->d_name, len);
}
static __inline__ struct vxfs_direct *
static inline struct vxfs_direct *
vxfs_next_entry(struct vxfs_direct *de)
{
return ((struct vxfs_direct *)((char*)de + de->d_reclen));

View file

@ -38,7 +38,7 @@
#include "vxfs_olt.h"
static __inline__ void
static inline void
vxfs_get_fshead(struct vxfs_oltfshead *fshp, struct vxfs_sb_info *infp)
{
if (infp->vsi_fshino)
@ -46,7 +46,7 @@ vxfs_get_fshead(struct vxfs_oltfshead *fshp, struct vxfs_sb_info *infp)
infp->vsi_fshino = fshp->olt_fsino[0];
}
static __inline__ void
static inline void
vxfs_get_ilist(struct vxfs_oltilist *ilistp, struct vxfs_sb_info *infp)
{
if (infp->vsi_iext)
@ -54,7 +54,7 @@ vxfs_get_ilist(struct vxfs_oltilist *ilistp, struct vxfs_sb_info *infp)
infp->vsi_iext = ilistp->olt_iext[0];
}
static __inline__ u_long
static inline u_long
vxfs_oblock(struct super_block *sbp, daddr_t block, u_long bsize)
{
if (sbp->s_blocksize % bsize)
@ -104,8 +104,8 @@ vxfs_read_olt(struct super_block *sbp, u_long bsize)
goto fail;
}
oaddr = (char *)bp->b_data + op->olt_size;
eaddr = (char *)bp->b_data + (infp->vsi_oltsize * sbp->s_blocksize);
oaddr = bp->b_data + op->olt_size;
eaddr = bp->b_data + (infp->vsi_oltsize * sbp->s_blocksize);
while (oaddr < eaddr) {
struct vxfs_oltcommon *ocp =

View file

@ -36,7 +36,6 @@
#include <linux/slab.h>
#include <linux/pagemap.h>
#include "vxfs_kcompat.h"
#include "vxfs_extern.h"

View file

@ -155,12 +155,11 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent)
sbp->s_flags |= MS_RDONLY;
infp = kmalloc(sizeof(*infp), GFP_KERNEL);
infp = kcalloc(1, sizeof(*infp), GFP_KERNEL);
if (!infp) {
printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n");
return -ENOMEM;
}
memset(infp, 0, sizeof(*infp));
bsize = sb_min_blocksize(sbp, BLOCK_SIZE);
if (!bsize) {
@ -196,7 +195,7 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent)
#endif
sbp->s_magic = rsbp->vs_magic;
sbp->s_fs_info = (void *)infp;
sbp->s_fs_info = infp;
infp->vsi_raw = rsbp;
infp->vsi_bp = bp;
@ -263,7 +262,7 @@ vxfs_init(void)
sizeof(struct vxfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, NULL, NULL);
if (vxfs_inode_cachep)
return (register_filesystem(&vxfs_fs_type));
return register_filesystem(&vxfs_fs_type);
return -ENOMEM;
}

View file

@ -4,6 +4,7 @@
*/
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@ -491,7 +492,7 @@ static int hppfs_open(struct inode *inode, struct file *file)
fd = open_host_sock(host_file, &filter);
if(fd > 0){
data->contents = hppfs_get_data(fd, filter,
&data->proc_file,
data->proc_file,
file, &data->len);
if(!IS_ERR(data->contents))
data->host_fd = fd;
@ -543,7 +544,7 @@ static int hppfs_dir_open(struct inode *inode, struct file *file)
static loff_t hppfs_llseek(struct file *file, loff_t off, int where)
{
struct hppfs_private *data = file->private_data;
struct file *proc_file = &data->proc_file;
struct file *proc_file = data->proc_file;
loff_t (*llseek)(struct file *, loff_t, int);
loff_t ret;
@ -586,7 +587,7 @@ static int hppfs_filldir(void *d, const char *name, int size,
static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)
{
struct hppfs_private *data = file->private_data;
struct file *proc_file = &data->proc_file;
struct file *proc_file = data->proc_file;
int (*readdir)(struct file *, void *, filldir_t);
struct hppfs_dirent dirent = ((struct hppfs_dirent)
{ .vfs_dirent = ent,

View file

@ -21,6 +21,7 @@
#include <linux/pagemap.h>
#include <linux/cdev.h>
#include <linux/bootmem.h>
#include <linux/inotify.h>
/*
* This is needed for the following functions:
@ -202,6 +203,10 @@ void inode_init_once(struct inode *inode)
INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear);
spin_lock_init(&inode->i_lock);
i_size_ordered_init(inode);
#ifdef CONFIG_INOTIFY
INIT_LIST_HEAD(&inode->inotify_watches);
sema_init(&inode->inotify_sem, 1);
#endif
}
EXPORT_SYMBOL(inode_init_once);
@ -282,6 +287,13 @@ static void dispose_list(struct list_head *head)
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
clear_inode(inode);
spin_lock(&inode_lock);
hlist_del_init(&inode->i_hash);
list_del_init(&inode->i_sb_list);
spin_unlock(&inode_lock);
wake_up_inode(inode);
destroy_inode(inode);
nr_disposed++;
}
@ -317,8 +329,6 @@ static int invalidate_list(struct list_head *head, struct list_head *dispose)
inode = list_entry(tmp, struct inode, i_sb_list);
invalidate_inode_buffers(inode);
if (!atomic_read(&inode->i_count)) {
hlist_del_init(&inode->i_hash);
list_del(&inode->i_sb_list);
list_move(&inode->i_list, dispose);
inode->i_state |= I_FREEING;
count++;
@ -346,6 +356,7 @@ int invalidate_inodes(struct super_block * sb)
down(&iprune_sem);
spin_lock(&inode_lock);
inotify_unmount_inodes(&sb->s_inodes);
busy = invalidate_list(&sb->s_inodes, &throw_away);
spin_unlock(&inode_lock);
@ -439,8 +450,6 @@ static void prune_icache(int nr_to_scan)
if (!can_unuse(inode))
continue;
}
hlist_del_init(&inode->i_hash);
list_del_init(&inode->i_sb_list);
list_move(&inode->i_list, &freeable);
inode->i_state |= I_FREEING;
nr_pruned++;
@ -1052,7 +1061,7 @@ static void generic_forget_inode(struct inode *inode)
* inode when the usage count drops to zero, and
* i_nlink is zero.
*/
static void generic_drop_inode(struct inode *inode)
void generic_drop_inode(struct inode *inode)
{
if (!inode->i_nlink)
generic_delete_inode(inode);
@ -1060,6 +1069,8 @@ static void generic_drop_inode(struct inode *inode)
generic_forget_inode(inode);
}
EXPORT_SYMBOL_GPL(generic_drop_inode);
/*
* Called when we're dropping the last reference
* to an inode.
@ -1242,29 +1253,21 @@ int inode_wait(void *word)
}
/*
* If we try to find an inode in the inode hash while it is being deleted, we
* have to wait until the filesystem completes its deletion before reporting
* that it isn't found. This is because iget will immediately call
* ->read_inode, and we want to be sure that evidence of the deletion is found
* by ->read_inode.
* If we try to find an inode in the inode hash while it is being
* deleted, we have to wait until the filesystem completes its
* deletion before reporting that it isn't found. This function waits
* until the deletion _might_ have completed. Callers are responsible
* to recheck inode state.
*
* It doesn't matter if I_LOCK is not set initially, a call to
* wake_up_inode() after removing from the hash list will DTRT.
*
* This is called with inode_lock held.
*/
static void __wait_on_freeing_inode(struct inode *inode)
{
wait_queue_head_t *wq;
DEFINE_WAIT_BIT(wait, &inode->i_state, __I_LOCK);
/*
* I_FREEING and I_CLEAR are cleared in process context under
* inode_lock, so we have to give the tasks who would clear them
* a chance to run and acquire inode_lock.
*/
if (!(inode->i_state & I_LOCK)) {
spin_unlock(&inode_lock);
yield();
spin_lock(&inode_lock);
return;
}
wq = bit_waitqueue(&inode->i_state, __I_LOCK);
prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE);
spin_unlock(&inode_lock);

999
fs/inotify.c Normal file
View file

@ -0,0 +1,999 @@
/*
* fs/inotify.c - inode-based file event notifications
*
* Authors:
* John McCutchan <ttb@tentacle.dhs.org>
* Robert Love <rml@novell.com>
*
* Copyright (C) 2005 John McCutchan
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/idr.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/writeback.h>
#include <linux/inotify.h>
#include <asm/ioctls.h>
static atomic_t inotify_cookie;
static kmem_cache_t *watch_cachep;
static kmem_cache_t *event_cachep;
static struct vfsmount *inotify_mnt;
/* These are configurable via /proc/sys/inotify */
int inotify_max_user_devices;
int inotify_max_user_watches;
int inotify_max_queued_events;
/*
* Lock ordering:
*
* dentry->d_lock (used to keep d_move() away from dentry->d_parent)
* iprune_sem (synchronize shrink_icache_memory())
* inode_lock (protects the super_block->s_inodes list)
* inode->inotify_sem (protects inode->inotify_watches and watches->i_list)
* inotify_dev->sem (protects inotify_device and watches->d_list)
*/
/*
* Lifetimes of the three main data structures--inotify_device, inode, and
* inotify_watch--are managed by reference count.
*
* inotify_device: Lifetime is from open until release. Additional references
* can bump the count via get_inotify_dev() and drop the count via
* put_inotify_dev().
*
* inotify_watch: Lifetime is from create_watch() to destory_watch().
* Additional references can bump the count via get_inotify_watch() and drop
* the count via put_inotify_watch().
*
* inode: Pinned so long as the inode is associated with a watch, from
* create_watch() to put_inotify_watch().
*/
/*
* struct inotify_device - represents an open instance of an inotify device
*
* This structure is protected by the semaphore 'sem'.
*/
struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct idr idr; /* idr mapping wd -> watch */
struct semaphore sem; /* protects this bad boy */
struct list_head events; /* list of queued events */
struct list_head watches; /* list of watches */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
};
/*
* struct inotify_kernel_event - An inotify event, originating from a watch and
* queued for user-space. A list of these is attached to each instance of the
* device. In read(), this list is walked and all events that can fit in the
* buffer are returned.
*
* Protected by dev->sem of the device in which we are queued.
*/
struct inotify_kernel_event {
struct inotify_event event; /* the user-space event */
struct list_head list; /* entry in inotify_device's list */
char *name; /* filename, if any */
};
/*
* struct inotify_watch - represents a watch request on a specific inode
*
* d_list is protected by dev->sem of the associated watch->dev.
* i_list and mask are protected by inode->inotify_sem of the associated inode.
* dev, inode, and wd are never written to once the watch is created.
*/
struct inotify_watch {
struct list_head d_list; /* entry in inotify_device's list */
struct list_head i_list; /* entry in inode's list */
atomic_t count; /* reference count */
struct inotify_device *dev; /* associated device */
struct inode *inode; /* associated inode */
s32 wd; /* watch descriptor */
u32 mask; /* event mask for this watch */
};
static inline void get_inotify_dev(struct inotify_device *dev)
{
atomic_inc(&dev->count);
}
static inline void put_inotify_dev(struct inotify_device *dev)
{
if (atomic_dec_and_test(&dev->count)) {
atomic_dec(&dev->user->inotify_devs);
free_uid(dev->user);
kfree(dev);
}
}
static inline void get_inotify_watch(struct inotify_watch *watch)
{
atomic_inc(&watch->count);
}
/*
* put_inotify_watch - decrements the ref count on a given watch. cleans up
* the watch and its references if the count reaches zero.
*/
static inline void put_inotify_watch(struct inotify_watch *watch)
{
if (atomic_dec_and_test(&watch->count)) {
put_inotify_dev(watch->dev);
iput(watch->inode);
kmem_cache_free(watch_cachep, watch);
}
}
/*
* kernel_event - create a new kernel event with the given parameters
*
* This function can sleep.
*/
static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
const char *name)
{
struct inotify_kernel_event *kevent;
kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
if (unlikely(!kevent))
return NULL;
/* we hand this out to user-space, so zero it just in case */
memset(&kevent->event, 0, sizeof(struct inotify_event));
kevent->event.wd = wd;
kevent->event.mask = mask;
kevent->event.cookie = cookie;
INIT_LIST_HEAD(&kevent->list);
if (name) {
size_t len, rem, event_size = sizeof(struct inotify_event);
/*
* We need to pad the filename so as to properly align an
* array of inotify_event structures. Because the structure is
* small and the common case is a small filename, we just round
* up to the next multiple of the structure's sizeof. This is
* simple and safe for all architectures.
*/
len = strlen(name) + 1;
rem = event_size - len;
if (len > event_size) {
rem = event_size - (len % event_size);
if (len % event_size == 0)
rem = 0;
}
kevent->name = kmalloc(len + rem, GFP_KERNEL);
if (unlikely(!kevent->name)) {
kmem_cache_free(event_cachep, kevent);
return NULL;
}
memcpy(kevent->name, name, len);
if (rem)
memset(kevent->name + len, 0, rem);
kevent->event.len = len + rem;
} else {
kevent->event.len = 0;
kevent->name = NULL;
}
return kevent;
}
/*
* inotify_dev_get_event - return the next event in the given dev's queue
*
* Caller must hold dev->sem.
*/
static inline struct inotify_kernel_event *
inotify_dev_get_event(struct inotify_device *dev)
{
return list_entry(dev->events.next, struct inotify_kernel_event, list);
}
/*
* inotify_dev_queue_event - add a new event to the given device
*
* Caller must hold dev->sem. Can sleep (calls kernel_event()).
*/
static void inotify_dev_queue_event(struct inotify_device *dev,
struct inotify_watch *watch, u32 mask,
u32 cookie, const char *name)
{
struct inotify_kernel_event *kevent, *last;
/* coalescing: drop this event if it is a dupe of the previous */
last = inotify_dev_get_event(dev);
if (last && last->event.mask == mask && last->event.wd == watch->wd &&
last->event.cookie == cookie) {
const char *lastname = last->name;
if (!name && !lastname)
return;
if (name && lastname && !strcmp(lastname, name))
return;
}
/* the queue overflowed and we already sent the Q_OVERFLOW event */
if (unlikely(dev->event_count > dev->max_events))
return;
/* if the queue overflows, we need to notify user space */
if (unlikely(dev->event_count == dev->max_events))
kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
else
kevent = kernel_event(watch->wd, mask, cookie, name);
if (unlikely(!kevent))
return;
/* queue the event and wake up anyone waiting */
dev->event_count++;
dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
list_add_tail(&kevent->list, &dev->events);
wake_up_interruptible(&dev->wq);
}
/*
* remove_kevent - cleans up and ultimately frees the given kevent
*
* Caller must hold dev->sem.
*/
static void remove_kevent(struct inotify_device *dev,
struct inotify_kernel_event *kevent)
{
list_del(&kevent->list);
dev->event_count--;
dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
kfree(kevent->name);
kmem_cache_free(event_cachep, kevent);
}
/*
* inotify_dev_event_dequeue - destroy an event on the given device
*
* Caller must hold dev->sem.
*/
static void inotify_dev_event_dequeue(struct inotify_device *dev)
{
if (!list_empty(&dev->events)) {
struct inotify_kernel_event *kevent;
kevent = inotify_dev_get_event(dev);
remove_kevent(dev, kevent);
}
}
/*
* inotify_dev_get_wd - returns the next WD for use by the given dev
*
* Callers must hold dev->sem. This function can sleep.
*/
static int inotify_dev_get_wd(struct inotify_device *dev,
struct inotify_watch *watch)
{
int ret;
do {
if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL)))
return -ENOSPC;
ret = idr_get_new(&dev->idr, watch, &watch->wd);
} while (ret == -EAGAIN);
return ret;
}
/*
* find_inode - resolve a user-given path to a specific inode and return a nd
*/
static int find_inode(const char __user *dirname, struct nameidata *nd)
{
int error;
error = __user_walk(dirname, LOOKUP_FOLLOW, nd);
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
error = permission(nd->dentry->d_inode, MAY_READ, NULL);
if (error)
path_release (nd);
return error;
}
/*
* create_watch - creates a watch on the given device.
*
* Callers must hold dev->sem. Calls inotify_dev_get_wd() so may sleep.
* Both 'dev' and 'inode' (by way of nameidata) need to be pinned.
*/
static struct inotify_watch *create_watch(struct inotify_device *dev,
u32 mask, struct inode *inode)
{
struct inotify_watch *watch;
int ret;
if (atomic_read(&dev->user->inotify_watches) >= inotify_max_user_watches)
return ERR_PTR(-ENOSPC);
watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
if (unlikely(!watch))
return ERR_PTR(-ENOMEM);
ret = inotify_dev_get_wd(dev, watch);
if (unlikely(ret)) {
kmem_cache_free(watch_cachep, watch);
return ERR_PTR(ret);
}
watch->mask = mask;
atomic_set(&watch->count, 0);
INIT_LIST_HEAD(&watch->d_list);
INIT_LIST_HEAD(&watch->i_list);
/* save a reference to device and bump the count to make it official */
get_inotify_dev(dev);
watch->dev = dev;
/*
* Save a reference to the inode and bump the ref count to make it
* official. We hold a reference to nameidata, which makes this safe.
*/
watch->inode = igrab(inode);
/* bump our own count, corresponding to our entry in dev->watches */
get_inotify_watch(watch);
atomic_inc(&dev->user->inotify_watches);
return watch;
}
/*
* inotify_find_dev - find the watch associated with the given inode and dev
*
* Callers must hold inode->inotify_sem.
*/
static struct inotify_watch *inode_find_dev(struct inode *inode,
struct inotify_device *dev)
{
struct inotify_watch *watch;
list_for_each_entry(watch, &inode->inotify_watches, i_list) {
if (watch->dev == dev)
return watch;
}
return NULL;
}
/*
* remove_watch_no_event - remove_watch() without the IN_IGNORED event.
*/
static void remove_watch_no_event(struct inotify_watch *watch,
struct inotify_device *dev)
{
list_del(&watch->i_list);
list_del(&watch->d_list);
atomic_dec(&dev->user->inotify_watches);
idr_remove(&dev->idr, watch->wd);
put_inotify_watch(watch);
}
/*
* remove_watch - Remove a watch from both the device and the inode. Sends
* the IN_IGNORED event to the given device signifying that the inode is no
* longer watched.
*
* Callers must hold both inode->inotify_sem and dev->sem. We drop a
* reference to the inode before returning.
*
* The inode is not iput() so as to remain atomic. If the inode needs to be
* iput(), the call returns one. Otherwise, it returns zero.
*/
static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev)
{
inotify_dev_queue_event(dev, watch, IN_IGNORED, 0, NULL);
remove_watch_no_event(watch, dev);
}
/*
* inotify_inode_watched - returns nonzero if there are watches on this inode
* and zero otherwise. We call this lockless, we do not care if we race.
*/
static inline int inotify_inode_watched(struct inode *inode)
{
return !list_empty(&inode->inotify_watches);
}
/* Kernel API */
/**
* inotify_inode_queue_event - queue an event to all watches on this inode
* @inode: inode event is originating from
* @mask: event mask describing this event
* @cookie: cookie for synchronization, or zero
* @name: filename, if any
*/
void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
const char *name)
{
struct inotify_watch *watch, *next;
if (!inotify_inode_watched(inode))
return;
down(&inode->inotify_sem);
list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
u32 watch_mask = watch->mask;
if (watch_mask & mask) {
struct inotify_device *dev = watch->dev;
get_inotify_watch(watch);
down(&dev->sem);
inotify_dev_queue_event(dev, watch, mask, cookie, name);
if (watch_mask & IN_ONESHOT)
remove_watch_no_event(watch, dev);
up(&dev->sem);
put_inotify_watch(watch);
}
}
up(&inode->inotify_sem);
}
EXPORT_SYMBOL_GPL(inotify_inode_queue_event);
/**
* inotify_dentry_parent_queue_event - queue an event to a dentry's parent
* @dentry: the dentry in question, we queue against this dentry's parent
* @mask: event mask describing this event
* @cookie: cookie for synchronization, or zero
* @name: filename, if any
*/
void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
u32 cookie, const char *name)
{
struct dentry *parent;
struct inode *inode;
spin_lock(&dentry->d_lock);
parent = dentry->d_parent;
inode = parent->d_inode;
if (inotify_inode_watched(inode)) {
dget(parent);
spin_unlock(&dentry->d_lock);
inotify_inode_queue_event(inode, mask, cookie, name);
dput(parent);
} else
spin_unlock(&dentry->d_lock);
}
EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event);
/**
* inotify_get_cookie - return a unique cookie for use in synchronizing events.
*/
u32 inotify_get_cookie(void)
{
return atomic_inc_return(&inotify_cookie);
}
EXPORT_SYMBOL_GPL(inotify_get_cookie);
/**
* inotify_unmount_inodes - an sb is unmounting. handle any watched inodes.
* @list: list of inodes being unmounted (sb->s_inodes)
*
* Called with inode_lock held, protecting the unmounting super block's list
* of inodes, and with iprune_sem held, keeping shrink_icache_memory() at bay.
* We temporarily drop inode_lock, however, and CAN block.
*/
void inotify_unmount_inodes(struct list_head *list)
{
struct inode *inode, *next_i, *need_iput = NULL;
list_for_each_entry_safe(inode, next_i, list, i_sb_list) {
struct inotify_watch *watch, *next_w;
struct inode *need_iput_tmp;
struct list_head *watches;
/*
* If i_count is zero, the inode cannot have any watches and
* doing an __iget/iput with MS_ACTIVE clear would actually
* evict all inodes with zero i_count from icache which is
* unnecessarily violent and may in fact be illegal to do.
*/
if (!atomic_read(&inode->i_count))
continue;
/*
* We cannot __iget() an inode in state I_CLEAR, I_FREEING, or
* I_WILL_FREE which is fine because by that point the inode
* cannot have any associated watches.
*/
if (inode->i_state & (I_CLEAR | I_FREEING | I_WILL_FREE))
continue;
need_iput_tmp = need_iput;
need_iput = NULL;
/* In case the remove_watch() drops a reference. */
if (inode != need_iput_tmp)
__iget(inode);
else
need_iput_tmp = NULL;
/* In case the dropping of a reference would nuke next_i. */
if ((&next_i->i_sb_list != list) &&
atomic_read(&next_i->i_count) &&
!(next_i->i_state & (I_CLEAR | I_FREEING |
I_WILL_FREE))) {
__iget(next_i);
need_iput = next_i;
}
/*
* We can safely drop inode_lock here because we hold
* references on both inode and next_i. Also no new inodes
* will be added since the umount has begun. Finally,
* iprune_sem keeps shrink_icache_memory() away.
*/
spin_unlock(&inode_lock);
if (need_iput_tmp)
iput(need_iput_tmp);
/* for each watch, send IN_UNMOUNT and then remove it */
down(&inode->inotify_sem);
watches = &inode->inotify_watches;
list_for_each_entry_safe(watch, next_w, watches, i_list) {
struct inotify_device *dev = watch->dev;
down(&dev->sem);
inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL);
remove_watch(watch, dev);
up(&dev->sem);
}
up(&inode->inotify_sem);
iput(inode);
spin_lock(&inode_lock);
}
}
EXPORT_SYMBOL_GPL(inotify_unmount_inodes);
/**
* inotify_inode_is_dead - an inode has been deleted, cleanup any watches
* @inode: inode that is about to be removed
*/
void inotify_inode_is_dead(struct inode *inode)
{
struct inotify_watch *watch, *next;
down(&inode->inotify_sem);
list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
struct inotify_device *dev = watch->dev;
down(&dev->sem);
remove_watch(watch, dev);
up(&dev->sem);
}
up(&inode->inotify_sem);
}
EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
/* Device Interface */
static unsigned int inotify_poll(struct file *file, poll_table *wait)
{
struct inotify_device *dev = file->private_data;
int ret = 0;
poll_wait(file, &dev->wq, wait);
down(&dev->sem);
if (!list_empty(&dev->events))
ret = POLLIN | POLLRDNORM;
up(&dev->sem);
return ret;
}
static ssize_t inotify_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
size_t event_size = sizeof (struct inotify_event);
struct inotify_device *dev;
char __user *start;
int ret;
DEFINE_WAIT(wait);
start = buf;
dev = file->private_data;
while (1) {
int events;
prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
down(&dev->sem);
events = !list_empty(&dev->events);
up(&dev->sem);
if (events) {
ret = 0;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -EINTR;
break;
}
schedule();
}
finish_wait(&dev->wq, &wait);
if (ret)
return ret;
down(&dev->sem);
while (1) {
struct inotify_kernel_event *kevent;
ret = buf - start;
if (list_empty(&dev->events))
break;
kevent = inotify_dev_get_event(dev);
if (event_size + kevent->event.len > count)
break;
if (copy_to_user(buf, &kevent->event, event_size)) {
ret = -EFAULT;
break;
}
buf += event_size;
count -= event_size;
if (kevent->name) {
if (copy_to_user(buf, kevent->name, kevent->event.len)){
ret = -EFAULT;
break;
}
buf += kevent->event.len;
count -= kevent->event.len;
}
remove_kevent(dev, kevent);
}
up(&dev->sem);
return ret;
}
static int inotify_release(struct inode *ignored, struct file *file)
{
struct inotify_device *dev = file->private_data;
/*
* Destroy all of the watches on this device. Unfortunately, not very
* pretty. We cannot do a simple iteration over the list, because we
* do not know the inode until we iterate to the watch. But we need to
* hold inode->inotify_sem before dev->sem. The following works.
*/
while (1) {
struct inotify_watch *watch;
struct list_head *watches;
struct inode *inode;
down(&dev->sem);
watches = &dev->watches;
if (list_empty(watches)) {
up(&dev->sem);
break;
}
watch = list_entry(watches->next, struct inotify_watch, d_list);
get_inotify_watch(watch);
up(&dev->sem);
inode = watch->inode;
down(&inode->inotify_sem);
down(&dev->sem);
remove_watch_no_event(watch, dev);
up(&dev->sem);
up(&inode->inotify_sem);
put_inotify_watch(watch);
}
/* destroy all of the events on this device */
down(&dev->sem);
while (!list_empty(&dev->events))
inotify_dev_event_dequeue(dev);
up(&dev->sem);
/* free this device: the put matching the get in inotify_open() */
put_inotify_dev(dev);
return 0;
}
/*
* inotify_ignore - handle the INOTIFY_IGNORE ioctl, asking that a given wd be
* removed from the device.
*
* Can sleep.
*/
static int inotify_ignore(struct inotify_device *dev, s32 wd)
{
struct inotify_watch *watch;
struct inode *inode;
down(&dev->sem);
watch = idr_find(&dev->idr, wd);
if (unlikely(!watch)) {
up(&dev->sem);
return -EINVAL;
}
get_inotify_watch(watch);
inode = watch->inode;
up(&dev->sem);
down(&inode->inotify_sem);
down(&dev->sem);
/* make sure that we did not race */
watch = idr_find(&dev->idr, wd);
if (likely(watch))
remove_watch(watch, dev);
up(&dev->sem);
up(&inode->inotify_sem);
put_inotify_watch(watch);
return 0;
}
static long inotify_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct inotify_device *dev;
void __user *p;
int ret = -ENOTTY;
dev = file->private_data;
p = (void __user *) arg;
switch (cmd) {
case FIONREAD:
ret = put_user(dev->queue_size, (int __user *) p);
break;
}
return ret;
}
static struct file_operations inotify_fops = {
.poll = inotify_poll,
.read = inotify_read,
.release = inotify_release,
.unlocked_ioctl = inotify_ioctl,
.compat_ioctl = inotify_ioctl,
};
asmlinkage long sys_inotify_init(void)
{
struct inotify_device *dev;
struct user_struct *user;
int ret = -ENOTTY;
int fd;
struct file *filp;
fd = get_unused_fd();
if (fd < 0) {
ret = fd;
goto out;
}
filp = get_empty_filp();
if (!filp) {
put_unused_fd(fd);
ret = -ENFILE;
goto out;
}
filp->f_op = &inotify_fops;
filp->f_vfsmnt = mntget(inotify_mnt);
filp->f_dentry = dget(inotify_mnt->mnt_root);
filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
filp->f_mode = FMODE_READ;
filp->f_flags = O_RDONLY;
user = get_uid(current->user);
if (unlikely(atomic_read(&user->inotify_devs) >= inotify_max_user_devices)) {
ret = -EMFILE;
goto out_err;
}
dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
if (unlikely(!dev)) {
ret = -ENOMEM;
goto out_err;
}
idr_init(&dev->idr);
INIT_LIST_HEAD(&dev->events);
INIT_LIST_HEAD(&dev->watches);
init_waitqueue_head(&dev->wq);
sema_init(&dev->sem, 1);
dev->event_count = 0;
dev->queue_size = 0;
dev->max_events = inotify_max_queued_events;
dev->user = user;
atomic_set(&dev->count, 0);
get_inotify_dev(dev);
atomic_inc(&user->inotify_devs);
filp->private_data = dev;
fd_install (fd, filp);
return fd;
out_err:
put_unused_fd (fd);
put_filp (filp);
free_uid(user);
out:
return ret;
}
asmlinkage long sys_inotify_add_watch(int fd, const char *path, u32 mask)
{
struct inotify_watch *watch, *old;
struct inode *inode;
struct inotify_device *dev;
struct nameidata nd;
struct file *filp;
int ret;
filp = fget(fd);
if (!filp)
return -EBADF;
dev = filp->private_data;
ret = find_inode ((const char __user*)path, &nd);
if (ret)
goto fput_and_out;
/* Held in place by reference in nd */
inode = nd.dentry->d_inode;
down(&inode->inotify_sem);
down(&dev->sem);
/* don't let user-space set invalid bits: we don't want flags set */
mask &= IN_ALL_EVENTS;
if (!mask) {
ret = -EINVAL;
goto out;
}
/*
* Handle the case of re-adding a watch on an (inode,dev) pair that we
* are already watching. We just update the mask and return its wd.
*/
old = inode_find_dev(inode, dev);
if (unlikely(old)) {
old->mask = mask;
ret = old->wd;
goto out;
}
watch = create_watch(dev, mask, inode);
if (unlikely(IS_ERR(watch))) {
ret = PTR_ERR(watch);
goto out;
}
/* Add the watch to the device's and the inode's list */
list_add(&watch->d_list, &dev->watches);
list_add(&watch->i_list, &inode->inotify_watches);
ret = watch->wd;
out:
path_release (&nd);
up(&dev->sem);
up(&inode->inotify_sem);
fput_and_out:
fput(filp);
return ret;
}
asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
{
struct file *filp;
struct inotify_device *dev;
int ret;
filp = fget(fd);
if (!filp)
return -EBADF;
dev = filp->private_data;
ret = inotify_ignore (dev, wd);
fput(filp);
return ret;
}
static struct super_block *
inotify_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
{
return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
}
static struct file_system_type inotify_fs_type = {
.name = "inotifyfs",
.get_sb = inotify_get_sb,
.kill_sb = kill_anon_super,
};
/*
* inotify_init - Our initialization function. Note that we cannnot return
* error because we have compiled-in VFS hooks. So an (unlikely) failure here
* must result in panic().
*/
static int __init inotify_init(void)
{
register_filesystem(&inotify_fs_type);
inotify_mnt = kern_mount(&inotify_fs_type);
inotify_max_queued_events = 8192;
inotify_max_user_devices = 128;
inotify_max_user_watches = 8192;
atomic_set(&inotify_cookie, 0);
watch_cachep = kmem_cache_create("inotify_watch_cache",
sizeof(struct inotify_watch),
0, SLAB_PANIC, NULL, NULL);
event_cachep = kmem_cache_create("inotify_event_cache",
sizeof(struct inotify_kernel_event),
0, SLAB_PANIC, NULL, NULL);
printk(KERN_INFO "inotify syscall\n");
return 0;
}
module_init(inotify_init);

172
fs/ioprio.c Normal file
View file

@ -0,0 +1,172 @@
/*
* fs/ioprio.c
*
* Copyright (C) 2004 Jens Axboe <axboe@suse.de>
*
* Helper functions for setting/querying io priorities of processes. The
* system calls closely mimmick getpriority/setpriority, see the man page for
* those. The prio argument is a composite of prio class and prio data, where
* the data argument has meaning within that class. The standard scheduling
* classes have 8 distinct prio levels, with 0 being the highest prio and 7
* being the lowest.
*
* IOW, setting BE scheduling class with prio 2 is done ala:
*
* unsigned int prio = (IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT) | 2;
*
* ioprio_set(PRIO_PROCESS, pid, prio);
*
* See also Documentation/block/ioprio.txt
*
*/
#include <linux/kernel.h>
#include <linux/ioprio.h>
#include <linux/blkdev.h>
static int set_task_ioprio(struct task_struct *task, int ioprio)
{
struct io_context *ioc;
if (task->uid != current->euid &&
task->uid != current->uid && !capable(CAP_SYS_NICE))
return -EPERM;
task_lock(task);
task->ioprio = ioprio;
ioc = task->io_context;
if (ioc && ioc->set_ioprio)
ioc->set_ioprio(ioc, ioprio);
task_unlock(task);
return 0;
}
asmlinkage long sys_ioprio_set(int which, int who, int ioprio)
{
int class = IOPRIO_PRIO_CLASS(ioprio);
int data = IOPRIO_PRIO_DATA(ioprio);
struct task_struct *p, *g;
struct user_struct *user;
int ret;
switch (class) {
case IOPRIO_CLASS_RT:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* fall through, rt has prio field too */
case IOPRIO_CLASS_BE:
if (data >= IOPRIO_BE_NR || data < 0)
return -EINVAL;
break;
case IOPRIO_CLASS_IDLE:
break;
default:
return -EINVAL;
}
ret = -ESRCH;
read_lock_irq(&tasklist_lock);
switch (which) {
case IOPRIO_WHO_PROCESS:
if (!who)
p = current;
else
p = find_task_by_pid(who);
if (p)
ret = set_task_ioprio(p, ioprio);
break;
case IOPRIO_WHO_PGRP:
if (!who)
who = process_group(current);
do_each_task_pid(who, PIDTYPE_PGID, p) {
ret = set_task_ioprio(p, ioprio);
if (ret)
break;
} while_each_task_pid(who, PIDTYPE_PGID, p);
break;
case IOPRIO_WHO_USER:
if (!who)
user = current->user;
else
user = find_user(who);
if (!user)
break;
do_each_thread(g, p) {
if (p->uid != who)
continue;
ret = set_task_ioprio(p, ioprio);
if (ret)
break;
} while_each_thread(g, p);
if (who)
free_uid(user);
break;
default:
ret = -EINVAL;
}
read_unlock_irq(&tasklist_lock);
return ret;
}
asmlinkage long sys_ioprio_get(int which, int who)
{
struct task_struct *g, *p;
struct user_struct *user;
int ret = -ESRCH;
read_lock_irq(&tasklist_lock);
switch (which) {
case IOPRIO_WHO_PROCESS:
if (!who)
p = current;
else
p = find_task_by_pid(who);
if (p)
ret = p->ioprio;
break;
case IOPRIO_WHO_PGRP:
if (!who)
who = process_group(current);
do_each_task_pid(who, PIDTYPE_PGID, p) {
if (ret == -ESRCH)
ret = p->ioprio;
else
ret = ioprio_best(ret, p->ioprio);
} while_each_task_pid(who, PIDTYPE_PGID, p);
break;
case IOPRIO_WHO_USER:
if (!who)
user = current->user;
else
user = find_user(who);
if (!user)
break;
do_each_thread(g, p) {
if (p->uid != user->uid)
continue;
if (ret == -ESRCH)
ret = p->ioprio;
else
ret = ioprio_best(ret, p->ioprio);
} while_each_thread(g, p);
if (who)
free_uid(user);
break;
default:
ret = -EINVAL;
}
read_unlock_irq(&tasklist_lock);
return ret;
}

View file

@ -167,7 +167,7 @@ loop:
}
wake_up(&journal->j_wait_done_commit);
if (current->flags & PF_FREEZE) {
if (freezing(current)) {
/*
* The simpler the better. Flushing journal isn't a
* good idea, because that depends on threads that may
@ -175,7 +175,7 @@ loop:
*/
jbd_debug(1, "Now suspending kjournald\n");
spin_unlock(&journal->j_state_lock);
refrigerator(PF_FREEZE);
refrigerator();
spin_lock(&journal->j_state_lock);
} else {
/*

View file

@ -175,8 +175,64 @@ jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size)
}
}
/* Print the contents of a node. */
static void
jffs_print_node(struct jffs_node *n)
{
D(printk("jffs_node: 0x%p\n", n));
D(printk("{\n"));
D(printk(" 0x%08x, /* version */\n", n->version));
D(printk(" 0x%08x, /* data_offset */\n", n->data_offset));
D(printk(" 0x%08x, /* data_size */\n", n->data_size));
D(printk(" 0x%08x, /* removed_size */\n", n->removed_size));
D(printk(" 0x%08x, /* fm_offset */\n", n->fm_offset));
D(printk(" 0x%02x, /* name_size */\n", n->name_size));
D(printk(" 0x%p, /* fm, fm->offset: %u */\n",
n->fm, (n->fm ? n->fm->offset : 0)));
D(printk(" 0x%p, /* version_prev */\n", n->version_prev));
D(printk(" 0x%p, /* version_next */\n", n->version_next));
D(printk(" 0x%p, /* range_prev */\n", n->range_prev));
D(printk(" 0x%p, /* range_next */\n", n->range_next));
D(printk("}\n"));
}
#endif
/* Print the contents of a raw inode. */
static void
jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
{
D(printk("jffs_raw_inode: inode number: %u\n", raw_inode->ino));
D(printk("{\n"));
D(printk(" 0x%08x, /* magic */\n", raw_inode->magic));
D(printk(" 0x%08x, /* ino */\n", raw_inode->ino));
D(printk(" 0x%08x, /* pino */\n", raw_inode->pino));
D(printk(" 0x%08x, /* version */\n", raw_inode->version));
D(printk(" 0x%08x, /* mode */\n", raw_inode->mode));
D(printk(" 0x%04x, /* uid */\n", raw_inode->uid));
D(printk(" 0x%04x, /* gid */\n", raw_inode->gid));
D(printk(" 0x%08x, /* atime */\n", raw_inode->atime));
D(printk(" 0x%08x, /* mtime */\n", raw_inode->mtime));
D(printk(" 0x%08x, /* ctime */\n", raw_inode->ctime));
D(printk(" 0x%08x, /* offset */\n", raw_inode->offset));
D(printk(" 0x%08x, /* dsize */\n", raw_inode->dsize));
D(printk(" 0x%08x, /* rsize */\n", raw_inode->rsize));
D(printk(" 0x%02x, /* nsize */\n", raw_inode->nsize));
D(printk(" 0x%02x, /* nlink */\n", raw_inode->nlink));
D(printk(" 0x%02x, /* spare */\n",
raw_inode->spare));
D(printk(" %u, /* rename */\n",
raw_inode->rename));
D(printk(" %u, /* deleted */\n",
raw_inode->deleted));
D(printk(" 0x%02x, /* accurate */\n",
raw_inode->accurate));
D(printk(" 0x%08x, /* dchksum */\n", raw_inode->dchksum));
D(printk(" 0x%04x, /* nchksum */\n", raw_inode->nchksum));
D(printk(" 0x%04x, /* chksum */\n", raw_inode->chksum));
D(printk("}\n"));
}
#define flash_safe_acquire(arg)
#define flash_safe_release(arg)
@ -2507,64 +2563,6 @@ jffs_update_file(struct jffs_file *f, struct jffs_node *node)
return 0;
}
/* Print the contents of a node. */
void
jffs_print_node(struct jffs_node *n)
{
D(printk("jffs_node: 0x%p\n", n));
D(printk("{\n"));
D(printk(" 0x%08x, /* version */\n", n->version));
D(printk(" 0x%08x, /* data_offset */\n", n->data_offset));
D(printk(" 0x%08x, /* data_size */\n", n->data_size));
D(printk(" 0x%08x, /* removed_size */\n", n->removed_size));
D(printk(" 0x%08x, /* fm_offset */\n", n->fm_offset));
D(printk(" 0x%02x, /* name_size */\n", n->name_size));
D(printk(" 0x%p, /* fm, fm->offset: %u */\n",
n->fm, (n->fm ? n->fm->offset : 0)));
D(printk(" 0x%p, /* version_prev */\n", n->version_prev));
D(printk(" 0x%p, /* version_next */\n", n->version_next));
D(printk(" 0x%p, /* range_prev */\n", n->range_prev));
D(printk(" 0x%p, /* range_next */\n", n->range_next));
D(printk("}\n"));
}
/* Print the contents of a raw inode. */
void
jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
{
D(printk("jffs_raw_inode: inode number: %u\n", raw_inode->ino));
D(printk("{\n"));
D(printk(" 0x%08x, /* magic */\n", raw_inode->magic));
D(printk(" 0x%08x, /* ino */\n", raw_inode->ino));
D(printk(" 0x%08x, /* pino */\n", raw_inode->pino));
D(printk(" 0x%08x, /* version */\n", raw_inode->version));
D(printk(" 0x%08x, /* mode */\n", raw_inode->mode));
D(printk(" 0x%04x, /* uid */\n", raw_inode->uid));
D(printk(" 0x%04x, /* gid */\n", raw_inode->gid));
D(printk(" 0x%08x, /* atime */\n", raw_inode->atime));
D(printk(" 0x%08x, /* mtime */\n", raw_inode->mtime));
D(printk(" 0x%08x, /* ctime */\n", raw_inode->ctime));
D(printk(" 0x%08x, /* offset */\n", raw_inode->offset));
D(printk(" 0x%08x, /* dsize */\n", raw_inode->dsize));
D(printk(" 0x%08x, /* rsize */\n", raw_inode->rsize));
D(printk(" 0x%02x, /* nsize */\n", raw_inode->nsize));
D(printk(" 0x%02x, /* nlink */\n", raw_inode->nlink));
D(printk(" 0x%02x, /* spare */\n",
raw_inode->spare));
D(printk(" %u, /* rename */\n",
raw_inode->rename));
D(printk(" %u, /* deleted */\n",
raw_inode->deleted));
D(printk(" 0x%02x, /* accurate */\n",
raw_inode->accurate));
D(printk(" 0x%08x, /* dchksum */\n", raw_inode->dchksum));
D(printk(" 0x%04x, /* nchksum */\n", raw_inode->nchksum));
D(printk(" 0x%04x, /* chksum */\n", raw_inode->chksum));
D(printk("}\n"));
}
/* Print the contents of a file. */
#if 0
int

View file

@ -49,8 +49,6 @@ int jffs_garbage_collect_thread(void *c);
void jffs_garbage_collect_trigger(struct jffs_control *c);
/* For debugging purposes. */
void jffs_print_node(struct jffs_node *n);
void jffs_print_raw_inode(struct jffs_raw_inode *raw_inode);
#if 0
int jffs_print_file(struct jffs_file *f);
#endif /* 0 */

View file

@ -31,6 +31,60 @@ static void jffs_free_fm(struct jffs_fm *n);
extern kmem_cache_t *fm_cache;
extern kmem_cache_t *node_cache;
#if CONFIG_JFFS_FS_VERBOSE > 0
void
jffs_print_fmcontrol(struct jffs_fmcontrol *fmc)
{
D(printk("struct jffs_fmcontrol: 0x%p\n", fmc));
D(printk("{\n"));
D(printk(" %u, /* flash_size */\n", fmc->flash_size));
D(printk(" %u, /* used_size */\n", fmc->used_size));
D(printk(" %u, /* dirty_size */\n", fmc->dirty_size));
D(printk(" %u, /* free_size */\n", fmc->free_size));
D(printk(" %u, /* sector_size */\n", fmc->sector_size));
D(printk(" %u, /* min_free_size */\n", fmc->min_free_size));
D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size));
D(printk(" 0x%p, /* mtd */\n", fmc->mtd));
D(printk(" 0x%p, /* head */ "
"(head->offset = 0x%08x)\n",
fmc->head, (fmc->head ? fmc->head->offset : 0)));
D(printk(" 0x%p, /* tail */ "
"(tail->offset + tail->size = 0x%08x)\n",
fmc->tail,
(fmc->tail ? fmc->tail->offset + fmc->tail->size : 0)));
D(printk(" 0x%p, /* head_extra */\n", fmc->head_extra));
D(printk(" 0x%p, /* tail_extra */\n", fmc->tail_extra));
D(printk("}\n"));
}
#endif /* CONFIG_JFFS_FS_VERBOSE > 0 */
#if CONFIG_JFFS_FS_VERBOSE > 2
static void
jffs_print_fm(struct jffs_fm *fm)
{
D(printk("struct jffs_fm: 0x%p\n", fm));
D(printk("{\n"));
D(printk(" 0x%08x, /* offset */\n", fm->offset));
D(printk(" %u, /* size */\n", fm->size));
D(printk(" 0x%p, /* prev */\n", fm->prev));
D(printk(" 0x%p, /* next */\n", fm->next));
D(printk(" 0x%p, /* nodes */\n", fm->nodes));
D(printk("}\n"));
}
#endif /* CONFIG_JFFS_FS_VERBOSE > 2 */
#if 0
void
jffs_print_node_ref(struct jffs_node_ref *ref)
{
D(printk("struct jffs_node_ref: 0x%p\n", ref));
D(printk("{\n"));
D(printk(" 0x%p, /* node */\n", ref->node));
D(printk(" 0x%p, /* next */\n", ref->next));
D(printk("}\n"));
}
#endif /* 0 */
/* This function creates a new shiny flash memory control structure. */
struct jffs_fmcontrol *
jffs_build_begin(struct jffs_control *c, int unit)
@ -742,54 +796,3 @@ int jffs_get_node_inuse(void)
{
return no_jffs_node;
}
void
jffs_print_fmcontrol(struct jffs_fmcontrol *fmc)
{
D(printk("struct jffs_fmcontrol: 0x%p\n", fmc));
D(printk("{\n"));
D(printk(" %u, /* flash_size */\n", fmc->flash_size));
D(printk(" %u, /* used_size */\n", fmc->used_size));
D(printk(" %u, /* dirty_size */\n", fmc->dirty_size));
D(printk(" %u, /* free_size */\n", fmc->free_size));
D(printk(" %u, /* sector_size */\n", fmc->sector_size));
D(printk(" %u, /* min_free_size */\n", fmc->min_free_size));
D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size));
D(printk(" 0x%p, /* mtd */\n", fmc->mtd));
D(printk(" 0x%p, /* head */ "
"(head->offset = 0x%08x)\n",
fmc->head, (fmc->head ? fmc->head->offset : 0)));
D(printk(" 0x%p, /* tail */ "
"(tail->offset + tail->size = 0x%08x)\n",
fmc->tail,
(fmc->tail ? fmc->tail->offset + fmc->tail->size : 0)));
D(printk(" 0x%p, /* head_extra */\n", fmc->head_extra));
D(printk(" 0x%p, /* tail_extra */\n", fmc->tail_extra));
D(printk("}\n"));
}
void
jffs_print_fm(struct jffs_fm *fm)
{
D(printk("struct jffs_fm: 0x%p\n", fm));
D(printk("{\n"));
D(printk(" 0x%08x, /* offset */\n", fm->offset));
D(printk(" %u, /* size */\n", fm->size));
D(printk(" 0x%p, /* prev */\n", fm->prev));
D(printk(" 0x%p, /* next */\n", fm->next));
D(printk(" 0x%p, /* nodes */\n", fm->nodes));
D(printk("}\n"));
}
#if 0
void
jffs_print_node_ref(struct jffs_node_ref *ref)
{
D(printk("struct jffs_node_ref: 0x%p\n", ref));
D(printk("{\n"));
D(printk(" 0x%p, /* node */\n", ref->node));
D(printk(" 0x%p, /* next */\n", ref->next));
D(printk("}\n"));
}
#endif /* 0 */

View file

@ -139,8 +139,9 @@ int jffs_add_node(struct jffs_node *node);
void jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm,
__u32 size);
#if CONFIG_JFFS_FS_VERBOSE > 0
void jffs_print_fmcontrol(struct jffs_fmcontrol *fmc);
void jffs_print_fm(struct jffs_fm *fm);
#endif
#if 0
void jffs_print_node_ref(struct jffs_node_ref *ref);
#endif /* 0 */

View file

@ -1,7 +1,7 @@
#
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
# $Id: Makefile.common,v 1.9 2005/02/09 09:23:53 pavlov Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
@ -11,8 +11,7 @@ jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o
jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o

View file

@ -1,4 +1,4 @@
$Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $
$Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $
JFFS2 LOCKING DOCUMENTATION
---------------------------
@ -108,6 +108,10 @@ in-core jffs2_inode_cache objects (each inode in JFFS2 has the
correspondent jffs2_inode_cache object). So, the inocache_lock
has to be locked while walking the c->inocache_list hash buckets.
This spinlock also covers allocation of new inode numbers, which is
currently just '++->highest_ino++', but might one day get more complicated
if we need to deal with wrapping after 4 milliard inode numbers are used.
Note, the f->sem guarantees that the correspondent jffs2_inode_cache
will not be removed. So, it is allowed to access it without locking
the inocache_lock spinlock.

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
* $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $
*
*/
@ -37,7 +37,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
if (c->gc_task)
BUG();
init_MUTEX_LOCKED(&c->gc_thread_start);
init_completion(&c->gc_thread_start);
init_completion(&c->gc_thread_exit);
pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
@ -48,7 +48,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
} else {
/* Wait for it... */
D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
down(&c->gc_thread_start);
wait_for_completion(&c->gc_thread_start);
}
return ret;
@ -56,13 +56,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
{
int wait = 0;
spin_lock(&c->erase_completion_lock);
if (c->gc_task) {
D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
send_sig(SIGKILL, c->gc_task, 1);
wait = 1;
}
spin_unlock(&c->erase_completion_lock);
wait_for_completion(&c->gc_thread_exit);
if (wait)
wait_for_completion(&c->gc_thread_exit);
}
static int jffs2_garbage_collect_thread(void *_c)
@ -75,7 +78,7 @@ static int jffs2_garbage_collect_thread(void *_c)
allow_signal(SIGCONT);
c->gc_task = current;
up(&c->gc_thread_start);
complete(&c->gc_thread_start);
set_user_nice(current, 10);
@ -92,7 +95,7 @@ static int jffs2_garbage_collect_thread(void *_c)
schedule();
}
if (try_to_freeze(0))
if (try_to_freeze())
continue;
cond_resched();

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $
* $Id: build.c,v 1.70 2005/02/28 08:21:05 dedekind Exp $
*
*/
@ -97,14 +97,16 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
c->flags |= JFFS2_SB_FLAG_MOUNTING;
c->flags |= JFFS2_SB_FLAG_SCANNING;
ret = jffs2_scan_medium(c);
c->flags &= ~JFFS2_SB_FLAG_SCANNING;
if (ret)
goto exit;
D1(printk(KERN_DEBUG "Scanned flash completely\n"));
D2(jffs2_dump_block_lists(c));
c->flags |= JFFS2_SB_FLAG_BUILDING;
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
@ -116,7 +118,6 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
cond_resched();
}
}
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
@ -164,6 +165,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
ic->scan_dents = NULL;
cond_resched();
}
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
D2(jffs2_dump_block_lists(c));

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
* $Id: compr_zlib.c,v 1.31 2005/05/20 19:30:06 gleixner Exp $
*
*/
@ -17,10 +17,10 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/zutil.h>
#include <asm/semaphore.h>
#include "nodelist.h"
#include "compr.h"

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
* $Id: dir.c,v 1.86 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
@ -22,16 +22,6 @@
#include <linux/time.h>
#include "nodelist.h"
/* Urgh. Please tell me there's a nicer way of doing these. */
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
typedef int mknod_arg_t;
#define NAMEI_COMPAT(x) ((void *)x)
#else
typedef dev_t mknod_arg_t;
#define NAMEI_COMPAT(x) (x)
#endif
static int jffs2_readdir (struct file *, void *, filldir_t);
static int jffs2_create (struct inode *,struct dentry *,int,
@ -43,7 +33,7 @@ static int jffs2_unlink (struct inode *,struct dentry *);
static int jffs2_symlink (struct inode *,struct dentry *,const char *);
static int jffs2_mkdir (struct inode *,struct dentry *,int);
static int jffs2_rmdir (struct inode *,struct dentry *);
static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
@ -58,8 +48,8 @@ struct file_operations jffs2_dir_operations =
struct inode_operations jffs2_dir_inode_operations =
{
.create = NAMEI_COMPAT(jffs2_create),
.lookup = NAMEI_COMPAT(jffs2_lookup),
.create = jffs2_create,
.lookup = jffs2_lookup,
.link = jffs2_link,
.unlink = jffs2_unlink,
.symlink = jffs2_symlink,
@ -296,11 +286,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen, phys_ofs;
int ret;
int ret, targetlen = strlen(target);
/* FIXME: If you care. We'd need to use frags for the target
if it grows much more than this */
if (strlen(target) > 254)
if (targetlen > 254)
return -EINVAL;
ri = jffs2_alloc_raw_inode();
@ -314,7 +304,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
* Just the node will do for now, though
*/
namelen = dentry->d_name.len;
ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_inode(ri);
@ -333,16 +323,16 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
f = JFFS2_INODE_INFO(inode);
inode->i_size = strlen(target);
inode->i_size = targetlen;
ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
ri->compr = JFFS2_COMPR_NONE;
ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_inode(ri);
@ -353,6 +343,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
jffs2_clear_inode(inode);
return PTR_ERR(fn);
}
/* We use f->dents field to store the target path. */
f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
if (!f->dents) {
printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
return -ENOMEM;
}
memcpy(f->dents, target, targetlen + 1);
D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
@ -564,7 +568,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
return ret;
}
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev)
{
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $
* $Id: erase.c,v 1.76 2005/05/03 15:11:40 dedekind Exp $
*
*/
@ -48,6 +48,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
#else /* Linux */
struct erase_info *instr;
D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
@ -233,7 +234,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
continue;
}
if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
/* It's in the block we're erasing */
struct jffs2_raw_node_ref *this;
@ -277,11 +278,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
printk("\n");
});
if (ic->nodes == (void *)ic) {
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
if (ic->nodes == (void *)ic && ic->nlink == 0)
jffs2_del_ino_cache(c, ic);
jffs2_free_inode_cache(ic);
}
}
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@ -310,7 +308,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
int ret;
uint32_t bad_offset;
if (!jffs2_cleanmarker_oob(c)) {
if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) {
marker_ref = jffs2_alloc_raw_node_ref();
if (!marker_ref) {
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
@ -335,7 +333,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
bad_offset = ofs;
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
goto bad;
@ -351,7 +350,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
bad_offset += i;
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
bad:
if (!jffs2_cleanmarker_oob(c))
if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
jffs2_free_raw_node_ref(marker_ref);
kfree(ebuf);
bad2:
@ -383,6 +382,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jeb->first_node = jeb->last_node = NULL;
jeb->free_size = c->sector_size;
jeb->used_size = 0;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
} else if (c->cleanmarker_size == 0) {
jeb->first_node = jeb->last_node = NULL;
jeb->free_size = c->sector_size;
jeb->used_size = 0;
jeb->dirty_size = 0;

View file

@ -7,11 +7,10 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
* $Id: file.c,v 1.102 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
@ -51,9 +50,7 @@ struct file_operations jffs2_file_operations =
.ioctl = jffs2_ioctl,
.mmap = generic_file_readonly_mmap,
.fsync = jffs2_fsync,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
.sendfile = generic_file_sendfile
#endif
};
/* jffs2_file_inode_operations */

View file

@ -7,11 +7,10 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs.c,v 1.51 2004/11/28 12:19:37 dedekind Exp $
* $Id: fs.c,v 1.56 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@ -450,11 +449,15 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
c = JFFS2_SB_INFO(sb);
#ifndef CONFIG_JFFS2_FS_NAND
#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
if (c->mtd->type == MTD_NANDFLASH) {
printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
return -EINVAL;
}
if (c->mtd->type == MTD_DATAFLASH) {
printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
return -EINVAL;
}
#endif
c->flash_size = c->mtd->size;
@ -522,9 +525,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
if (!sb->s_root)
goto out_root_i;
#if LINUX_VERSION_CODE >= 0x20403
sb->s_maxbytes = 0xFFFFFFFF;
#endif
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
@ -661,6 +662,14 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
if (ret)
return ret;
}
/* and Dataflash */
if (jffs2_dataflash(c)) {
ret = jffs2_dataflash_setup(c);
if (ret)
return ret;
}
return ret;
}
@ -674,4 +683,9 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
if (jffs2_nor_ecc(c)) {
jffs2_nor_ecc_flash_cleanup(c);
}
/* and DataFlash */
if (jffs2_dataflash(c)) {
jffs2_dataflash_cleanup(c);
}
}

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $
* $Id: gc.c,v 1.148 2005/04/09 10:47:00 dedekind Exp $
*
*/
@ -50,6 +50,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
put the clever wear-levelling algorithms. Eventually. */
/* We possibly want to favour the dirtier blocks more when the
number of free blocks is low. */
again:
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
nextlist = &c->bad_used_list;
@ -79,6 +80,13 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
nextlist = &c->erasable_list;
} else if (!list_empty(&c->erasable_pending_wbuf_list)) {
/* There are blocks are wating for the wbuf sync */
D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n"));
spin_unlock(&c->erase_completion_lock);
jffs2_flush_wbuf_pad(c);
spin_lock(&c->erase_completion_lock);
goto again;
} else {
/* Eep. All were empty */
D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
@ -661,9 +669,10 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
struct jffs2_node_frag *last_frag;
jint16_t dev;
char *mdata = NULL, mdatalen = 0;
uint32_t alloclen, phys_ofs;
uint32_t alloclen, phys_ofs, ilen;
int ret;
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
@ -699,6 +708,14 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
goto out;
}
last_frag = frag_last(&f->fragtree);
if (last_frag)
/* Fetch the inode length from the fragtree rather then
* from i_size since i_size may have not been updated yet */
ilen = last_frag->ofs + last_frag->size;
else
ilen = JFFS2_F_I_SIZE(f);
memset(&ri, 0, sizeof(ri));
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
@ -710,7 +727,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.isize = cpu_to_je32(ilen);
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
@ -816,8 +833,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
if ((raw->flash_offset & ~(c->sector_size-1)) ==
(fd->raw->flash_offset & ~(c->sector_size-1)))
if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
continue;
D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
@ -891,7 +907,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
struct jffs2_raw_inode ri;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *new_fn;
uint32_t alloclen, phys_ofs;
uint32_t alloclen, phys_ofs, ilen;
int ret;
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
@ -951,10 +967,19 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
}
frag = frag_last(&f->fragtree);
if (frag)
/* Fetch the inode length from the fragtree rather then
* from i_size since i_size may have not been updated yet */
ilen = frag->ofs + frag->size;
else
ilen = JFFS2_F_I_SIZE(f);
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
ri.isize = cpu_to_je32(ilen);
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
@ -1161,7 +1186,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
orig_start, orig_end, start, end));
BUG_ON(end > JFFS2_F_I_SIZE(f));
D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size));
BUG_ON(end < orig_end);
BUG_ON(start > orig_start);
}

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.c,v 1.90 2004/12/08 17:59:20 dwmw2 Exp $
* $Id: nodelist.c,v 1.97 2005/07/06 15:18:41 dwmw2 Exp $
*
*/
@ -58,27 +58,60 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new
/* Put a new tmp_dnode_info into the list, keeping the list in
order of increasing version
*/
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
{
struct jffs2_tmp_dnode_info **prev = list;
while ((*prev) && (*prev)->version < tn->version) {
prev = &((*prev)->next);
}
tn->next = (*prev);
*prev = tn;
struct rb_node **p = &list->rb_node;
struct rb_node * parent = NULL;
struct jffs2_tmp_dnode_info *this;
while (*p) {
parent = *p;
this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
/* There may actually be a collision here, but it doesn't
actually matter. As long as the two nodes with the same
version are together, it's all fine. */
if (tn->version < this->version)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&tn->rb, parent, p);
rb_insert_color(&tn->rb, list);
}
static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
{
struct jffs2_tmp_dnode_info *next;
struct rb_node *this;
struct jffs2_tmp_dnode_info *tn;
while (tn) {
next = tn;
tn = tn->next;
jffs2_free_full_dnode(next->fn);
jffs2_free_tmp_dnode_info(next);
this = list->rb_node;
/* Now at bottom of tree */
while (this) {
if (this->rb_left)
this = this->rb_left;
else if (this->rb_right)
this = this->rb_right;
else {
tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
jffs2_free_full_dnode(tn->fn);
jffs2_free_tmp_dnode_info(tn);
this = this->rb_parent;
if (!this)
break;
if (this->rb_left == &tn->rb)
this->rb_left = NULL;
else if (this->rb_right == &tn->rb)
this->rb_right = NULL;
else BUG();
}
}
list->rb_node = NULL;
}
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
@ -108,12 +141,13 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
with this ino, returning the former in order of version */
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
struct rb_root *tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver)
{
struct jffs2_raw_node_ref *ref, *valid_ref;
struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
struct jffs2_tmp_dnode_info *tn;
struct rb_root ret_tn = RB_ROOT;
struct jffs2_full_dirent *fd, *ret_fd = NULL;
union jffs2_node_union node;
size_t retlen;
@ -127,7 +161,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
valid_ref = jffs2_first_valid_node(f->inocache->nodes);
if (!valid_ref)
if (!valid_ref && (f->inocache->ino != 1))
printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
while (valid_ref) {
@ -450,7 +484,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
return 0;
free_out:
jffs2_free_tmp_dnode_info_list(ret_tn);
jffs2_free_tmp_dnode_info_list(&ret_tn);
jffs2_free_full_dirent_list(ret_fd);
return err;
}
@ -489,9 +523,13 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
{
struct jffs2_inode_cache **prev;
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
spin_lock(&c->inocache_lock);
if (!new->ino)
new->ino = ++c->highest_ino;
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
while ((*prev) && (*prev)->ino < new->ino) {
@ -506,7 +544,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
{
struct jffs2_inode_cache **prev;
D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
spin_lock(&c->inocache_lock);
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
@ -518,6 +556,14 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
*prev = old->next;
}
/* Free it now unless it's in READING or CLEARING state, which
are the transitions upon read_inode() and clear_inode(). The
rest of the time we know nobody else is looking at it, and
if it's held by read_inode() or clear_inode() they'll free it
for themselves. */
if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
jffs2_free_inode_cache(old);
spin_unlock(&c->inocache_lock);
}
@ -530,7 +576,6 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
this = c->inocache_list[i];
while (this) {
next = this->next;
D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
jffs2_free_inode_cache(this);
this = next;
}

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $
* $Id: nodelist.h,v 1.131 2005/07/05 21:03:07 dwmw2 Exp $
*
*/
@ -135,6 +135,7 @@ struct jffs2_inode_cache {
#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
#define INO_STATE_GC 4 /* GCing a 'pristine' node */
#define INO_STATE_READING 5 /* In read_inode() */
#define INO_STATE_CLEARING 6 /* In clear_inode() */
#define INOCACHE_HASHSIZE 128
@ -160,7 +161,7 @@ struct jffs2_full_dnode
*/
struct jffs2_tmp_dnode_info
{
struct jffs2_tmp_dnode_info *next;
struct rb_node rb;
struct jffs2_full_dnode *fn;
uint32_t version;
};
@ -362,6 +363,18 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
node = node->rb_left;
return rb_entry(node, struct jffs2_node_frag, rb);
}
static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
{
struct rb_node *node = root->rb_node;
if (!node)
return NULL;
while(node->rb_right)
node = node->rb_right;
return rb_entry(node, struct jffs2_node_frag, rb);
}
#define rb_parent(rb) ((rb)->rb_parent)
#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
@ -374,7 +387,7 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
struct rb_root *tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver);
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
@ -462,7 +475,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c);
/* erase.c */
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
#ifdef CONFIG_JFFS2_FS_NAND
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
/* wbuf.c */
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $
* $Id: nodemgmt.c,v 1.122 2005/05/06 09:30:27 dedekind Exp $
*
*/
@ -75,7 +75,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
if (dirty < c->nospc_dirty_size) {
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"));
break;
}
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
@ -98,7 +98,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
if ( (avail / c->sector_size) <= blocksneeded) {
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"));
break;
}
@ -308,7 +308,10 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
/* we could get some obsolete nodes after nextblock was refiled
in wbuf.c */
if ((c->nextblock || !ref_obsolete(new))
&&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
printk(KERN_WARNING "argh. node added in wrong place\n");
jffs2_free_raw_node_ref(new);
return -EINVAL;
@ -332,7 +335,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
c->used_size += len;
}
if (!jeb->free_size && !jeb->dirty_size) {
if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
@ -400,7 +403,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
jeb = &c->blocks[blocknr];
if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
!(c->flags & JFFS2_SB_FLAG_MOUNTING)) {
!(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
/* Hm. This may confuse static lock analysis. If any of the above
three conditions is false, we're going to return from this
function without actually obliterating any nodes or freeing
@ -434,7 +437,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
// Take care, that wasted size is taken into concern
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
D1(printk(KERN_DEBUG "Dirtying\n"));
addedsize = ref_totlen(c, jeb, ref);
jeb->dirty_size += ref_totlen(c, jeb, ref);
c->dirty_size += ref_totlen(c, jeb, ref);
@ -456,7 +459,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
}
}
} else {
D1(printk("Wasting\n"));
D1(printk(KERN_DEBUG "Wasting\n"));
addedsize = 0;
jeb->wasted_size += ref_totlen(c, jeb, ref);
c->wasted_size += ref_totlen(c, jeb, ref);
@ -467,8 +470,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
D1(ACCT_PARANOIA_CHECK(jeb));
if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
/* Mount in progress. Don't muck about with the block
if (c->flags & JFFS2_SB_FLAG_SCANNING) {
/* Flash scanning is in progress. Don't muck about with the block
lists because they're not ready yet, and don't actually
obliterate nodes that look obsolete. If they weren't
marked obsolete on the flash at the time they _became_
@ -527,7 +530,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_unlock(&c->erase_completion_lock);
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) {
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
(c->flags & JFFS2_SB_FLAG_BUILDING)) {
/* We didn't lock the erase_free_sem */
return;
}
@ -590,11 +594,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
*p = ref->next_in_ino;
ref->next_in_ino = NULL;
if (ic->nodes == (void *)ic) {
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
if (ic->nodes == (void *)ic && ic->nlink == 0)
jffs2_del_ino_cache(c, ic);
jffs2_free_inode_cache(ic);
}
spin_unlock(&c->erase_completion_lock);
}

View file

@ -7,41 +7,24 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: os-linux.h,v 1.51 2004/11/16 20:36:11 dwmw2 Exp $
* $Id: os-linux.h,v 1.57 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
#ifndef __JFFS2_OS_LINUX_H__
#define __JFFS2_OS_LINUX_H__
#include <linux/version.h>
/* JFFS2 uses Linux mode bits natively -- no need for conversion */
#define os_to_jffs2_mode(x) (x)
#define jffs2_to_os_mode(x) (x)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
#define kstatfs statfs
#endif
struct kstatfs;
struct kvec;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
#elif defined(JFFS2_OUT_OF_KERNEL)
#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#else
#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
#endif
#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
@ -49,28 +32,14 @@ struct kvec;
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
#else
#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
#endif
/* Urgh. The things we do to keep the 2.4 build working */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
#define ITIME(sec) ((struct timespec){sec, 0})
#define I_SEC(tv) ((tv).tv_sec)
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
#else
#define ITIME(x) (x)
#define I_SEC(x) (x)
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
#endif
#define sleep_on_spinunlock(wq, s) \
do { \
@ -84,23 +53,21 @@ struct kvec;
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
f->highest_version = 0;
f->fragtree = RB_ROOT;
f->metadata = NULL;
f->dents = NULL;
f->flags = 0;
f->usercompr = 0;
#else
memset(f, 0, sizeof(*f));
init_MUTEX_LOCKED(&f->sem);
#endif
}
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC)
#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
#define jffs2_can_mark_obsolete(c) (1)
#define jffs2_is_writebuffered(c) (0)
#define jffs2_cleanmarker_oob(c) (0)
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
@ -116,11 +83,14 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_wbuf_timeout NULL
#define jffs2_wbuf_process NULL
#define jffs2_nor_ecc(c) (0)
#define jffs2_dataflash(c) (0)
#define jffs2_nor_ecc_flash_setup(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#else /* NAND and/or ECC'd NOR support present */
#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
@ -142,16 +112,16 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
#ifdef CONFIG_JFFS2_FS_NOR_ECC
#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
#else
#define jffs2_nor_ecc(c) (0)
#define jffs2_nor_ecc_flash_setup(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#endif /* NOR ECC */
#endif /* NAND */
#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
int jffs2_dataflash_setup(struct jffs2_sb_info *c);
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
#endif /* WRITEBUFFER */
/* erase.c */
static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $
* $Id: read.c,v 1.39 2005/03/01 10:34:03 dedekind Exp $
*
*/
@ -214,33 +214,3 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
return 0;
}
/* Core function to read symlink target. */
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
{
char *buf;
int ret;
down(&f->sem);
if (!f->metadata) {
printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
up(&f->sem);
return ERR_PTR(-EINVAL);
}
buf = kmalloc(f->metadata->size+1, GFP_USER);
if (!buf) {
up(&f->sem);
return ERR_PTR(-ENOMEM);
}
buf[f->metadata->size]=0;
ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
up(&f->sem);
if (ret) {
kfree(buf);
return ERR_PTR(ret);
}
return buf;
}

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $
* $Id: readinode.c,v 1.120 2005/07/05 21:03:07 dwmw2 Exp $
*
*/
@ -500,7 +500,9 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
struct jffs2_inode_info *f,
struct jffs2_raw_inode *latest_node)
{
struct jffs2_tmp_dnode_info *tn_list, *tn;
struct jffs2_tmp_dnode_info *tn = NULL;
struct rb_root tn_list;
struct rb_node *rb, *repl_rb;
struct jffs2_full_dirent *fd_list;
struct jffs2_full_dnode *fn = NULL;
uint32_t crc;
@ -522,9 +524,10 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
}
f->dents = fd_list;
while (tn_list) {
tn = tn_list;
rb = rb_first(&tn_list);
while (rb) {
tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb);
fn = tn->fn;
if (f->metadata) {
@ -556,7 +559,30 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
mdata_ver = tn->version;
}
next_tn:
tn_list = tn->next;
BUG_ON(rb->rb_left);
repl_rb = NULL;
if (rb->rb_parent && rb->rb_parent->rb_left == rb) {
/* We were then left-hand child of our parent. We need
to move our own right-hand child into our place. */
repl_rb = rb->rb_right;
if (repl_rb)
repl_rb->rb_parent = rb->rb_parent;
} else
repl_rb = NULL;
rb = rb_next(rb);
/* Remove the spent tn from the tree; don't bother rebalancing
but put our right-hand child in our own place. */
if (tn->rb.rb_parent) {
if (tn->rb.rb_parent->rb_left == &tn->rb)
tn->rb.rb_parent->rb_left = repl_rb;
else if (tn->rb.rb_parent->rb_right == &tn->rb)
tn->rb.rb_parent->rb_right = repl_rb;
else BUG();
} else if (tn->rb.rb_right)
tn->rb.rb_right->rb_parent = NULL;
jffs2_free_tmp_dnode_info(tn);
}
D1(jffs2_sanitycheck_fragtree(f));
@ -623,6 +649,40 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
case. */
if (!je32_to_cpu(latest_node->isize))
latest_node->isize = latest_node->dsize;
if (f->inocache->state != INO_STATE_CHECKING) {
/* Symlink's inode data is the target path. Read it and
* keep in RAM to facilitate quick follow symlink operation.
* We use f->dents field to store the target path, which
* is somewhat ugly. */
f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
if (!f->dents) {
printk(KERN_WARNING "Can't allocate %d bytes of memory "
"for the symlink target path cache\n",
je32_to_cpu(latest_node->csize));
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -ENOMEM;
}
ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
if (ret || retlen != je32_to_cpu(latest_node->csize)) {
if (retlen != je32_to_cpu(latest_node->csize))
ret = -EIO;
kfree(f->dents);
f->dents = NULL;
up(&f->sem);
jffs2_do_clear_inode(c, f);
return -ret;
}
((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
(char *)f->dents));
}
/* fall through... */
case S_IFBLK:
@ -672,6 +732,9 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
down(&f->sem);
deleted = f->inocache && !f->inocache->nlink;
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
if (f->metadata) {
if (deleted)
jffs2_mark_node_obsolete(c, f->metadata->raw);
@ -680,16 +743,27 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
fds = f->dents;
/* For symlink inodes we us f->dents to store the target path name */
if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
if (f->dents) {
kfree(f->dents);
f->dents = NULL;
}
} else {
fds = f->dents;
while(fds) {
fd = fds;
fds = fd->next;
jffs2_free_full_dirent(fd);
while(fds) {
fd = fds;
fds = fd->next;
jffs2_free_full_dirent(fd);
}
}
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
if (f->inocache->nodes == (void *)f->inocache)
jffs2_del_ino_cache(c, f->inocache);
}
up(&f->sem);
}

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $
* $Id: scan.c,v 1.119 2005/02/17 17:51:13 dedekind Exp $
*
*/
#include <linux/kernel.h>
@ -19,7 +19,7 @@
#include <linux/compiler.h>
#include "nodelist.h"
#define EMPTY_SCAN_SIZE 1024
#define DEFAULT_EMPTY_SCAN_SIZE 1024
#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
c->free_size -= _x; c->dirty_size += _x; \
@ -68,13 +68,21 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
static inline int min_free(struct jffs2_sb_info *c)
{
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
return c->wbuf_pagesize;
#endif
return min;
}
static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
return sector_size;
else
return DEFAULT_EMPTY_SCAN_SIZE;
}
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
int i, ret;
@ -220,7 +228,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
c->dirty_size -= c->nextblock->dirty_size;
c->nextblock->dirty_size = 0;
}
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
/* If we're going to start writing into a block which already
contains data, and the end of the data isn't page-aligned,
@ -286,7 +294,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
uint32_t hdr_crc, buf_ofs, buf_len;
int err;
int noise = 0;
#ifdef CONFIG_JFFS2_FS_NAND
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
int cleanmarkerfound = 0;
#endif
@ -295,7 +303,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
#ifdef CONFIG_JFFS2_FS_NAND
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) {
int ret = jffs2_check_nand_cleanmarker(c, jeb);
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
@ -316,7 +324,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
if (!buf_size) {
buf_len = c->sector_size;
} else {
buf_len = EMPTY_SCAN_SIZE;
buf_len = EMPTY_SCAN_SIZE(c->sector_size);
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
if (err)
return err;
@ -326,11 +334,11 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
ofs = 0;
/* Scan only 4KiB of 0xFF before declaring it's empty */
while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
ofs += 4;
if (ofs == EMPTY_SCAN_SIZE) {
#ifdef CONFIG_JFFS2_FS_NAND
if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) {
/* scan oob, take care of cleanmarker */
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
@ -343,7 +351,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
}
#endif
D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
if (c->cleanmarker_size == 0)
return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */
else
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
}
if (ofs) {
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
@ -422,8 +433,8 @@ scan_more:
/* If we're only checking the beginning of a block with a cleanmarker,
bail now */
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
return BLK_STATE_CLEANMARKER;
}
@ -618,7 +629,7 @@ scan_more:
}
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
&& (!jeb->first_node || !jeb->first_node->next_phys) )
return BLK_STATE_CLEANMARKER;
/* move blocks with max 4 byte dirty space to cleanlist */

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $
* $Id: super.c,v 1.106 2005/05/18 11:37:25 dedekind Exp $
*
*/
@ -270,8 +270,6 @@ static void jffs2_put_super (struct super_block *sb)
D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
down(&c->alloc_sem);
jffs2_flush_wbuf_pad(c);
up(&c->alloc_sem);
@ -292,6 +290,8 @@ static void jffs2_put_super (struct super_block *sb)
static void jffs2_kill_sb(struct super_block *sb)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
if (!(sb->s_flags & MS_RDONLY))
jffs2_stop_garbage_collect_thread(c);
generic_shutdown_super(sb);
put_mtd_device(c->mtd);
kfree(c);
@ -309,7 +309,7 @@ static int __init init_jffs2_fs(void)
int ret;
printk(KERN_INFO "JFFS2 version 2.2."
#ifdef CONFIG_JFFS2_FS_NAND
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
" (NAND)"
#endif
" (C) 2001-2003 Red Hat, Inc.\n");

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $
* $Id: symlink.c,v 1.16 2005/03/01 10:50:48 dedekind Exp $
*
*/
@ -19,27 +19,45 @@
#include "nodelist.h"
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd);
struct inode_operations jffs2_symlink_inode_operations =
{
.readlink = generic_readlink,
.follow_link = jffs2_follow_link,
.put_link = jffs2_put_link,
.setattr = jffs2_setattr
};
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
unsigned char *buf;
buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
nd_set_link(nd, buf);
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
/*
* We don't acquire the f->sem mutex here since the only data we
* use is f->dents which in case of the symlink inode points to the
* symlink's target path.
*
* 1. If we are here the inode has already built and f->dents has
* to point to the target path.
* 2. Nobody uses f->dents (if the inode is symlink's inode). The
* exception is inode freeing function which frees f->dents. But
* it can't be called while we are here and before VFS has
* stopped using our f->dents string which we provide by means of
* nd_set_link() call.
*/
if (!f->dents) {
printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
return -EIO;
}
D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
nd_set_link(nd, (char *)f->dents);
/*
* We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
* since the only way that may cause f->dents to be changed is iput() operation.
* But VFS will not use f->dents after iput() has been called.
*/
return 0;
}
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd)
{
char *s = nd_get_link(nd);
if (!IS_ERR(s))
kfree(s);
}

View file

@ -9,7 +9,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: wbuf.c,v 1.82 2004/11/20 22:08:31 dwmw2 Exp $
* $Id: wbuf.c,v 1.92 2005/04/05 12:51:54 dedekind Exp $
*
*/
@ -83,7 +83,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
struct jffs2_inodirty *new;
/* Mark the superblock dirty so that kupdated will flush... */
OFNI_BS_2SFFJ(c)->s_dirt = 1;
jffs2_erase_pending_trigger(c);
if (jffs2_wbuf_pending_for_ino(c, ino))
return;
@ -130,7 +130,10 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
}
}
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
#define REFILE_NOTEMPTY 0
#define REFILE_ANYWAY 1
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
{
D1(printk("About to refile bad block at %08x\n", jeb->offset));
@ -144,7 +147,7 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock
D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
list_add(&jeb->list, &c->bad_used_list);
} else {
BUG();
BUG_ON(allow_empty == REFILE_NOTEMPTY);
/* It has to have had some nodes or we couldn't be here */
D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
list_add(&jeb->list, &c->erase_pending_list);
@ -179,7 +182,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
jffs2_block_refile(c, jeb);
jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
/* Find the first node to be recovered, by skipping over every
node which ends before the wbuf starts, or which is obsolete. */
@ -264,17 +267,16 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
if (ret) {
printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
if (buf)
kfree(buf);
kfree(buf);
return;
}
if (end-start >= c->wbuf_pagesize) {
/* Need to do another write immediately. This, btw,
means that we'll be writing from 'buf' and not from
the wbuf. Since if we're writing from the wbuf there
won't be more than a wbuf full of data, now will
there? :) */
/* Need to do another write immediately, but it's possible
that this is just because the wbuf itself is completely
full, and there's nothing earlier read back from the
flash. Hence 'buf' isn't necessarily what we're writing
from. */
unsigned char *rewrite_buf = buf?:c->wbuf;
uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
@ -292,9 +294,9 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
#endif
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
buf, NULL, c->oobinfo);
rewrite_buf, NULL, c->oobinfo);
else
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf);
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
if (ret || retlen != towrite) {
/* Argh. We tried. Really we did. */
@ -321,10 +323,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
c->wbuf_len = (end - start) - towrite;
c->wbuf_ofs = ofs + towrite;
memcpy(c->wbuf, buf + towrite, c->wbuf_len);
memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
kfree(buf);
if (buf)
kfree(buf);
} else {
/* OK, now we're left with the dregs in whichever buffer we're using */
if (buf) {
@ -413,9 +415,9 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
int ret;
size_t retlen;
/* Nothing to do if not NAND flash. In particular, we shouldn't
/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
del_timer() the timer we never initialised. */
if (jffs2_can_mark_obsolete(c))
if (!jffs2_is_writebuffered(c))
return 0;
if (!down_trylock(&c->alloc_sem)) {
@ -424,7 +426,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
BUG();
}
if(!c->wbuf || !c->wbuf_len)
if (!c->wbuf_len) /* already checked c->wbuf above */
return 0;
/* claim remaining space on the page
@ -433,7 +435,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
if we have a switch to next page, we will not have
enough remaining space for this.
*/
if (pad) {
if (pad && !jffs2_dataflash(c)) {
c->wbuf_len = PAD(c->wbuf_len);
/* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
@ -484,7 +486,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
spin_lock(&c->erase_completion_lock);
/* Adjust free size of the block if we padded. */
if (pad) {
if (pad && !jffs2_dataflash(c)) {
struct jffs2_eraseblock *jeb;
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
@ -532,6 +534,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
if (!c->wbuf)
return 0;
down(&c->alloc_sem);
if (!jffs2_wbuf_pending_for_ino(c, ino)) {
D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
@ -547,6 +552,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
/* retry flushing wbuf in case jffs2_wbuf_recover
left some data in the wbuf */
if (ret)
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
up_write(&c->wbuf_sem);
} else while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
@ -561,6 +570,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
down(&c->alloc_sem);
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
/* retry flushing wbuf in case jffs2_wbuf_recover
left some data in the wbuf */
if (ret)
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
up_write(&c->wbuf_sem);
break;
}
@ -578,15 +591,27 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
{
int ret;
if (!c->wbuf)
return 0;
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
/* retry - maybe wbuf recover left some data in wbuf. */
if (ret)
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
up_write(&c->wbuf_sem);
return ret;
}
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
#else
#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
#endif
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
{
struct kvec outvecs[3];
@ -601,7 +626,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
uint32_t outvec_to = to;
/* If not NAND flash, don't bother */
if (!c->wbuf)
if (!jffs2_is_writebuffered(c))
return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
down_write(&c->wbuf_sem);
@ -630,7 +655,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
erase block. Anything else, and you die.
New block starts at xxx000c (0-b = block header)
*/
if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
/* It's a write to a new block */
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
@ -762,9 +787,18 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
/* At this point we have no problem,
c->wbuf is empty.
c->wbuf is empty. However refile nextblock to avoid
writing again to same address.
*/
*retlen = donelen;
struct jffs2_eraseblock *jeb;
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[outvec_to / c->sector_size];
jffs2_block_refile(c, jeb, REFILE_ANYWAY);
*retlen = 0;
spin_unlock(&c->erase_completion_lock);
goto exit;
}
@ -819,7 +853,7 @@ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *r
{
struct kvec vecs[1];
if (jffs2_can_mark_obsolete(c))
if (!jffs2_is_writebuffered(c))
return c->mtd->write(c->mtd, ofs, len, retlen, buf);
vecs[0].iov_base = (unsigned char *) buf;
@ -835,39 +869,38 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
loff_t orbf = 0, owbf = 0, lwbf = 0;
int ret;
/* Read flash */
if (!jffs2_can_mark_obsolete(c)) {
down_read(&c->wbuf_sem);
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
else
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
if ( (ret == -EBADMSG) && (*retlen == len) ) {
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
len, ofs);
/*
* We have the raw data without ECC correction in the buffer, maybe
* we are lucky and all data or parts are correct. We check the node.
* If data are corrupted node check will sort it out.
* We keep this block, it will fail on write or erase and the we
* mark it bad. Or should we do that now? But we should give him a chance.
* Maybe we had a system crash or power loss before the ecc write or
* a erase was completed.
* So we return success. :)
*/
ret = 0;
}
} else
if (!jffs2_is_writebuffered(c))
return c->mtd->read(c->mtd, ofs, len, retlen, buf);
/* Read flash */
down_read(&c->wbuf_sem);
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
else
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
if ( (ret == -EBADMSG) && (*retlen == len) ) {
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
len, ofs);
/*
* We have the raw data without ECC correction in the buffer, maybe
* we are lucky and all data or parts are correct. We check the node.
* If data are corrupted node check will sort it out.
* We keep this block, it will fail on write or erase and the we
* mark it bad. Or should we do that now? But we should give him a chance.
* Maybe we had a system crash or power loss before the ecc write or
* a erase was completed.
* So we return success. :)
*/
ret = 0;
}
/* if no writebuffer available or write buffer empty, return */
if (!c->wbuf_pagesize || !c->wbuf_len)
goto exit;
/* if we read in a different block, return */
if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
goto exit;
if (ofs >= c->wbuf_ofs) {
@ -1161,7 +1194,27 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
kfree(c->wbuf);
}
#ifdef CONFIG_JFFS2_FS_NOR_ECC
int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
c->cleanmarker_size = 0; /* No cleanmarkers needed */
/* Initialize write buffer */
init_rwsem(&c->wbuf_sem);
c->wbuf_pagesize = c->sector_size;
c->wbuf_ofs = 0xFFFFFFFF;
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
return 0;
}
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
kfree(c->wbuf);
}
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
/* Cleanmarker is actually larger on the flashes */
c->cleanmarker_size = 16;
@ -1181,4 +1234,3 @@ int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
kfree(c->wbuf);
}
#endif

View file

@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $
* $Id: write.c,v 1.92 2005/04/13 13:22:35 dwmw2 Exp $
*
*/
@ -35,13 +35,12 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
f->inocache = ic;
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->ino = ++c->highest_ino;
f->inocache->state = INO_STATE_PRESENT;
ri->ino = cpu_to_je32(f->inocache->ino);
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
jffs2_add_ino_cache(c, f->inocache);
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
ri->ino = cpu_to_je32(f->inocache->ino);
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
@ -136,6 +135,15 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
raw->__totlen = PAD(sizeof(*ri)+datalen);
raw->next_phys = NULL;
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
BUG_ON(!retried);
D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
"highest version %d -> updating dnode\n",
je32_to_cpu(ri->version), f->highest_version));
ri->version = cpu_to_je32(++f->highest_version);
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
}
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
(alloc_mode==ALLOC_GC)?0:f->inocache->ino);
@ -280,6 +288,16 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
raw->__totlen = PAD(sizeof(*rd)+namelen);
raw->next_phys = NULL;
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
BUG_ON(!retried);
D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
"highest version %d -> updating dirent\n",
je32_to_cpu(rd->version), f->highest_version));
rd->version = cpu_to_je32(++f->highest_version);
fd->version = je32_to_cpu(rd->version);
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
}
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
(alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
if (ret || (retlen != sizeof(*rd) + namelen)) {
@ -625,20 +643,23 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
down(&dead_f->sem);
while (dead_f->dents) {
/* There can be only deleted ones */
fd = dead_f->dents;
dead_f->dents = fd->next;
if (fd->ino) {
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
dead_f->inocache->ino, fd->name, fd->ino);
} else {
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
while (dead_f->dents) {
/* There can be only deleted ones */
fd = dead_f->dents;
dead_f->dents = fd->next;
if (fd->ino) {
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
dead_f->inocache->ino, fd->name, fd->ino);
} else {
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
fd->name, dead_f->inocache->ino));
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
}
jffs2_mark_node_obsolete(c, fd->raw);
jffs2_free_full_dirent(fd);
}
dead_f->inocache->nlink--;

View file

@ -2359,9 +2359,9 @@ int jfsIOWait(void *arg)
lbmStartIO(bp);
spin_lock_irq(&log_redrive_lock);
}
if (current->flags & PF_FREEZE) {
if (freezing(current)) {
spin_unlock_irq(&log_redrive_lock);
refrigerator(PF_FREEZE);
refrigerator();
} else {
add_wait_queue(&jfs_IO_thread_wait, &wq);
set_current_state(TASK_INTERRUPTIBLE);

View file

@ -2788,9 +2788,9 @@ int jfs_lazycommit(void *arg)
/* In case a wakeup came while all threads were active */
jfs_commit_thread_waking = 0;
if (current->flags & PF_FREEZE) {
if (freezing(current)) {
LAZY_UNLOCK(flags);
refrigerator(PF_FREEZE);
refrigerator();
} else {
DECLARE_WAITQUEUE(wq, current);
@ -2987,9 +2987,9 @@ int jfs_sync(void *arg)
/* Add anon_list2 back to anon_list */
list_splice_init(&TxAnchor.anon_list2, &TxAnchor.anon_list);
if (current->flags & PF_FREEZE) {
if (freezing(current)) {
TXN_UNLOCK();
refrigerator(PF_FREEZE);
refrigerator();
} else {
DECLARE_WAITQUEUE(wq, current);

View file

@ -183,6 +183,7 @@ struct file_operations simple_dir_operations = {
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
.readdir = dcache_readdir,
.fsync = simple_sync_file,
};
struct inode_operations simple_dir_inode_operations = {

View file

@ -313,7 +313,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
prepare_to_wait(queue, &wait, TASK_INTERRUPTIBLE);
if (!signalled ()) {
schedule_timeout(NLMCLNT_GRACE_WAIT);
try_to_freeze(PF_FREEZE);
try_to_freeze();
if (!signalled ())
status = 0;
}

View file

@ -191,7 +191,9 @@ lockd(struct svc_rqst *rqstp)
printk(KERN_DEBUG
"lockd: new process, skipping host shutdown\n");
wake_up(&lockd_exit);
flush_signals(current);
/* Exit the RPC thread */
svc_exit_thread(rqstp);

View file

@ -1276,7 +1276,7 @@ int fcntl_getlease(struct file *filp)
*/
static int __setlease(struct file *filp, long arg, struct file_lock **flp)
{
struct file_lock *fl, **before, **my_before = NULL, *lease = *flp;
struct file_lock *fl, **before, **my_before = NULL, *lease;
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
int error, rdlease_count = 0, wrlease_count = 0;
@ -1287,6 +1287,8 @@ static int __setlease(struct file *filp, long arg, struct file_lock **flp)
if (!flp || !(*flp) || !(*flp)->fl_lmops || !(*flp)->fl_lmops->fl_break)
goto out;
lease = *flp;
error = -EAGAIN;
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
goto out;

View file

@ -21,7 +21,7 @@
#include <linux/namei.h>
#include <linux/quotaops.h>
#include <linux/pagemap.h>
#include <linux/dnotify.h>
#include <linux/fsnotify.h>
#include <linux/smp_lock.h>
#include <linux/personality.h>
#include <linux/security.h>
@ -314,7 +314,7 @@ void path_release(struct nameidata *nd)
void path_release_on_umount(struct nameidata *nd)
{
dput(nd->dentry);
_mntput(nd->mnt);
mntput_no_expire(nd->mnt);
}
/*
@ -1312,7 +1312,7 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode, nd);
if (!error) {
inode_dir_notify(dir, DN_CREATE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_create(dir, dentry, mode);
}
return error;
@ -1637,7 +1637,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
DQUOT_INIT(dir);
error = dir->i_op->mknod(dir, dentry, mode, dev);
if (!error) {
inode_dir_notify(dir, DN_CREATE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_mknod(dir, dentry, mode, dev);
}
return error;
@ -1710,7 +1710,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);
if (!error) {
inode_dir_notify(dir, DN_CREATE);
fsnotify_mkdir(dir, dentry->d_name.name);
security_inode_post_mkdir(dir,dentry, mode);
}
return error;
@ -1801,7 +1801,7 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
}
up(&dentry->d_inode->i_sem);
if (!error) {
inode_dir_notify(dir, DN_DELETE);
fsnotify_rmdir(dentry, dentry->d_inode, dir);
d_delete(dentry);
}
dput(dentry);
@ -1874,9 +1874,10 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
/* We don't d_delete() NFS sillyrenamed files--they still exist. */
if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
fsnotify_unlink(dentry, dir);
d_delete(dentry);
inode_dir_notify(dir, DN_DELETE);
}
return error;
}
@ -1950,7 +1951,7 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, i
DQUOT_INIT(dir);
error = dir->i_op->symlink(dir, dentry, oldname);
if (!error) {
inode_dir_notify(dir, DN_CREATE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_symlink(dir, dentry, oldname);
}
return error;
@ -2023,7 +2024,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
error = dir->i_op->link(old_dentry, dir, new_dentry);
up(&old_dentry->d_inode->i_sem);
if (!error) {
inode_dir_notify(dir, DN_CREATE);
fsnotify_create(dir, new_dentry->d_name.name);
security_inode_post_link(old_dentry, dir, new_dentry);
}
return error;
@ -2187,6 +2188,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
{
int error;
int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
const char *old_name;
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
@ -2208,18 +2210,18 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
DQUOT_INIT(old_dir);
DQUOT_INIT(new_dir);
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
if (is_dir)
error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
else
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
if (!error) {
if (old_dir == new_dir)
inode_dir_notify(old_dir, DN_RENAME);
else {
inode_dir_notify(old_dir, DN_DELETE);
inode_dir_notify(new_dir, DN_CREATE);
}
const char *new_name = old_dentry->d_name.name;
fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir);
}
fsnotify_oldname_free(old_name);
return error;
}

View file

@ -61,7 +61,7 @@ struct vfsmount *alloc_vfsmnt(const char *name)
INIT_LIST_HEAD(&mnt->mnt_child);
INIT_LIST_HEAD(&mnt->mnt_mounts);
INIT_LIST_HEAD(&mnt->mnt_list);
INIT_LIST_HEAD(&mnt->mnt_fslink);
INIT_LIST_HEAD(&mnt->mnt_expire);
if (name) {
int size = strlen(name)+1;
char *newname = kmalloc(size, GFP_KERNEL);
@ -165,8 +165,8 @@ clone_mnt(struct vfsmount *old, struct dentry *root)
/* stick the duplicate mount on the same expiry list
* as the original if that was on one */
spin_lock(&vfsmount_lock);
if (!list_empty(&old->mnt_fslink))
list_add(&mnt->mnt_fslink, &old->mnt_fslink);
if (!list_empty(&old->mnt_expire))
list_add(&mnt->mnt_expire, &old->mnt_expire);
spin_unlock(&vfsmount_lock);
}
return mnt;
@ -337,7 +337,7 @@ int may_umount(struct vfsmount *mnt)
EXPORT_SYMBOL(may_umount);
void umount_tree(struct vfsmount *mnt)
static void umount_tree(struct vfsmount *mnt)
{
struct vfsmount *p;
LIST_HEAD(kill);
@ -345,12 +345,13 @@ void umount_tree(struct vfsmount *mnt)
for (p = mnt; p; p = next_mnt(p, mnt)) {
list_del(&p->mnt_list);
list_add(&p->mnt_list, &kill);
p->mnt_namespace = NULL;
}
while (!list_empty(&kill)) {
mnt = list_entry(kill.next, struct vfsmount, mnt_list);
list_del_init(&mnt->mnt_list);
list_del_init(&mnt->mnt_fslink);
list_del_init(&mnt->mnt_expire);
if (mnt->mnt_parent == mnt) {
spin_unlock(&vfsmount_lock);
} else {
@ -644,7 +645,7 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
if (mnt) {
/* stop bind mounts from expiring */
spin_lock(&vfsmount_lock);
list_del_init(&mnt->mnt_fslink);
list_del_init(&mnt->mnt_expire);
spin_unlock(&vfsmount_lock);
err = graft_tree(mnt, nd);
@ -743,7 +744,7 @@ static int do_move_mount(struct nameidata *nd, char *old_name)
/* if the mount is moved, it should no longer be expire
* automatically */
list_del_init(&old_nd.mnt->mnt_fslink);
list_del_init(&old_nd.mnt->mnt_expire);
out2:
spin_unlock(&vfsmount_lock);
out1:
@ -807,12 +808,13 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd,
goto unlock;
newmnt->mnt_flags = mnt_flags;
newmnt->mnt_namespace = current->namespace;
err = graft_tree(newmnt, nd);
if (err == 0 && fslist) {
/* add to the specified expiration list */
spin_lock(&vfsmount_lock);
list_add_tail(&newmnt->mnt_fslink, fslist);
list_add_tail(&newmnt->mnt_expire, fslist);
spin_unlock(&vfsmount_lock);
}
@ -824,6 +826,54 @@ unlock:
EXPORT_SYMBOL_GPL(do_add_mount);
static void expire_mount(struct vfsmount *mnt, struct list_head *mounts)
{
spin_lock(&vfsmount_lock);
/*
* Check if mount is still attached, if not, let whoever holds it deal
* with the sucker
*/
if (mnt->mnt_parent == mnt) {
spin_unlock(&vfsmount_lock);
return;
}
/*
* Check that it is still dead: the count should now be 2 - as
* contributed by the vfsmount parent and the mntget above
*/
if (atomic_read(&mnt->mnt_count) == 2) {
struct nameidata old_nd;
/* delete from the namespace */
list_del_init(&mnt->mnt_list);
mnt->mnt_namespace = NULL;
detach_mnt(mnt, &old_nd);
spin_unlock(&vfsmount_lock);
path_release(&old_nd);
/*
* Now lay it to rest if this was the last ref on the superblock
*/
if (atomic_read(&mnt->mnt_sb->s_active) == 1) {
/* last instance - try to be smart */
lock_kernel();
DQUOT_OFF(mnt->mnt_sb);
acct_auto_close(mnt->mnt_sb);
unlock_kernel();
}
mntput(mnt);
} else {
/*
* Someone brought it back to life whilst we didn't have any
* locks held so return it to the expiration list
*/
list_add_tail(&mnt->mnt_expire, mounts);
spin_unlock(&vfsmount_lock);
}
}
/*
* process a list of expirable mountpoints with the intent of discarding any
* mountpoints that aren't in use and haven't been touched since last we came
@ -846,13 +896,13 @@ void mark_mounts_for_expiry(struct list_head *mounts)
* - still marked for expiry (marked on the last call here; marks are
* cleared by mntput())
*/
list_for_each_entry_safe(mnt, next, mounts, mnt_fslink) {
list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
if (!xchg(&mnt->mnt_expiry_mark, 1) ||
atomic_read(&mnt->mnt_count) != 1)
continue;
mntget(mnt);
list_move(&mnt->mnt_fslink, &graveyard);
list_move(&mnt->mnt_expire, &graveyard);
}
/*
@ -862,61 +912,19 @@ void mark_mounts_for_expiry(struct list_head *mounts)
* - dispose of the corpse
*/
while (!list_empty(&graveyard)) {
mnt = list_entry(graveyard.next, struct vfsmount, mnt_fslink);
list_del_init(&mnt->mnt_fslink);
mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
list_del_init(&mnt->mnt_expire);
/* don't do anything if the namespace is dead - all the
* vfsmounts from it are going away anyway */
namespace = mnt->mnt_namespace;
if (!namespace || atomic_read(&namespace->count) <= 0)
if (!namespace || !namespace->root)
continue;
get_namespace(namespace);
spin_unlock(&vfsmount_lock);
down_write(&namespace->sem);
spin_lock(&vfsmount_lock);
/* check that it is still dead: the count should now be 2 - as
* contributed by the vfsmount parent and the mntget above */
if (atomic_read(&mnt->mnt_count) == 2) {
struct vfsmount *xdmnt;
struct dentry *xdentry;
/* delete from the namespace */
list_del_init(&mnt->mnt_list);
list_del_init(&mnt->mnt_child);
list_del_init(&mnt->mnt_hash);
mnt->mnt_mountpoint->d_mounted--;
xdentry = mnt->mnt_mountpoint;
mnt->mnt_mountpoint = mnt->mnt_root;
xdmnt = mnt->mnt_parent;
mnt->mnt_parent = mnt;
spin_unlock(&vfsmount_lock);
mntput(xdmnt);
dput(xdentry);
/* now lay it to rest if this was the last ref on the
* superblock */
if (atomic_read(&mnt->mnt_sb->s_active) == 1) {
/* last instance - try to be smart */
lock_kernel();
DQUOT_OFF(mnt->mnt_sb);
acct_auto_close(mnt->mnt_sb);
unlock_kernel();
}
mntput(mnt);
} else {
/* someone brought it back to life whilst we didn't
* have any locks held so return it to the expiration
* list */
list_add_tail(&mnt->mnt_fslink, mounts);
spin_unlock(&vfsmount_lock);
}
expire_mount(mnt, mounts);
up_write(&namespace->sem);
mntput(mnt);
@ -1449,16 +1457,12 @@ void __init mnt_init(unsigned long mempages)
void __put_namespace(struct namespace *namespace)
{
struct vfsmount *mnt;
struct vfsmount *root = namespace->root;
namespace->root = NULL;
spin_unlock(&vfsmount_lock);
down_write(&namespace->sem);
spin_lock(&vfsmount_lock);
list_for_each_entry(mnt, &namespace->list, mnt_list) {
mnt->mnt_namespace = NULL;
}
umount_tree(namespace->root);
umount_tree(root);
spin_unlock(&vfsmount_lock);
up_write(&namespace->sem);
kfree(namespace);

View file

@ -705,18 +705,6 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
DPRINTK("ncp_do_readdir: init failed, err=%d\n", err);
return;
}
#ifdef USE_OLD_SLOW_DIRECTORY_LISTING
for (;;) {
err = ncp_search_for_file_or_subdir(server, &seq, &entry.i);
if (err) {
DPRINTK("ncp_do_readdir: search failed, err=%d\n", err);
break;
}
entry.volume = entry.i.volNumber;
if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry))
break;
}
#else
/* We MUST NOT use server->buffer_size handshaked with server if we are
using UDP, as for UDP server uses max. buffer size determined by
MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes).
@ -754,7 +742,6 @@ ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
}
} while (more);
vfree(buf);
#endif
return;
}

View file

@ -845,46 +845,6 @@ out:
return result;
}
/* Search for everything */
int ncp_search_for_file_or_subdir(struct ncp_server *server,
struct nw_search_sequence *seq,
struct nw_info_struct *target)
{
int result;
ncp_init_request(server);
ncp_add_byte(server, 3); /* subfunction */
ncp_add_byte(server, server->name_space[seq->volNumber]);
ncp_add_byte(server, 0); /* data stream (???) */
ncp_add_word(server, cpu_to_le16(0x8006)); /* Search attribs */
ncp_add_dword(server, RIM_ALL); /* return info mask */
ncp_add_mem(server, seq, 9);
#ifdef CONFIG_NCPFS_NFS_NS
if (server->name_space[seq->volNumber] == NW_NS_NFS) {
ncp_add_byte(server, 0); /* 0 byte pattern */
} else
#endif
{
ncp_add_byte(server, 2); /* 2 byte pattern */
ncp_add_byte(server, 0xff); /* following is a wildcard */
ncp_add_byte(server, '*');
}
if ((result = ncp_request(server, 87)) != 0)
goto out;
memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
ncp_extract_file_info(ncp_reply_data(server, 10), target);
ncp_unlock_server(server);
result = ncp_obtain_nfs_info(server, target);
return result;
out:
ncp_unlock_server(server);
return result;
}
int ncp_search_for_fileset(struct ncp_server *server,
struct nw_search_sequence *seq,
int* more,

View file

@ -87,9 +87,6 @@ int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
int ncp_initialize_search(struct ncp_server *, struct inode *,
struct nw_search_sequence *target);
int ncp_search_for_file_or_subdir(struct ncp_server *server,
struct nw_search_sequence *seq,
struct nw_info_struct *target);
int ncp_search_for_fileset(struct ncp_server *server,
struct nw_search_sequence *seq,
int* more, int* cnt,

View file

@ -2,7 +2,7 @@
#include <linux/nfs.h>
#include <linux/nfs3.h>
#include <linux/nfs_fs.h>
#include <linux/xattr_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/nfsacl.h>
#define NFSDBG_FACILITY NFSDBG_PROC
@ -53,9 +53,9 @@ ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
struct posix_acl *acl;
int type, error = 0;
if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;
@ -82,9 +82,9 @@ int nfs3_setxattr(struct dentry *dentry, const char *name,
struct posix_acl *acl;
int type, error;
if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;
@ -103,9 +103,9 @@ int nfs3_removexattr(struct dentry *dentry, const char *name)
struct inode *inode = dentry->d_inode;
int type;
if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0)
if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
type = ACL_TYPE_ACCESS;
else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0)
else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
type = ACL_TYPE_DEFAULT;
else
return -EOPNOTSUPP;

View file

@ -10,5 +10,5 @@ nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
nfs4acl.o nfs4callback.o
nfs4acl.o nfs4callback.o nfs4recover.o
nfsd-objs := $(nfsd-y)

View file

@ -125,7 +125,7 @@ static short ace2type(struct nfs4_ace *);
static int _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *, unsigned int);
static struct posix_acl *_nfsv4_to_posix_one(struct nfs4_acl *, unsigned int);
int nfs4_acl_add_ace(struct nfs4_acl *, u32, u32, u32, int, uid_t);
int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *);
static int nfs4_acl_split(struct nfs4_acl *, struct nfs4_acl *);
struct nfs4_acl *
nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl,
@ -775,7 +775,7 @@ out_err:
return pacl;
}
int
static int
nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl)
{
struct list_head *h, *n;

View file

@ -54,7 +54,6 @@
/* declarations */
static void nfs4_cb_null(struct rpc_task *task);
extern spinlock_t recall_lock;
/* Index of predefined Linux callback client operations */
@ -329,12 +328,12 @@ out:
.p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \
}
struct rpc_procinfo nfs4_cb_procedures[] = {
static struct rpc_procinfo nfs4_cb_procedures[] = {
PROC(CB_NULL, NULL, enc_cb_null, dec_cb_null),
PROC(CB_RECALL, COMPOUND, enc_cb_recall, dec_cb_recall),
};
struct rpc_version nfs_cb_version4 = {
static struct rpc_version nfs_cb_version4 = {
.number = 1,
.nrprocs = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]),
.procs = nfs4_cb_procedures
@ -348,7 +347,7 @@ static struct rpc_version * nfs_cb_version[] = {
/*
* Use the SETCLIENTID credential
*/
struct rpc_cred *
static struct rpc_cred *
nfsd4_lookupcred(struct nfs4_client *clp, int taskflags)
{
struct auth_cred acred;
@ -387,9 +386,7 @@ nfsd4_probe_callback(struct nfs4_client *clp)
char hostname[32];
int status;
dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d\n",
cb->cb_parsed, atomic_read(&cb->cb_set));
if (!cb->cb_parsed || atomic_read(&cb->cb_set))
if (atomic_read(&cb->cb_set))
return;
/* Initialize address */
@ -427,7 +424,7 @@ nfsd4_probe_callback(struct nfs4_client *clp)
* XXX AUTH_UNIX only - need AUTH_GSS....
*/
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX);
clnt = rpc_new_client(xprt, hostname, program, 1, RPC_AUTH_UNIX);
if (IS_ERR(clnt)) {
dprintk("NFSD: couldn't create callback client\n");
goto out_err;

View file

@ -104,7 +104,7 @@ ent_update(struct ent *new, struct ent *itm)
ent_init(new, itm);
}
void
static void
ent_put(struct cache_head *ch, struct cache_detail *cd)
{
if (cache_put(ch, cd)) {
@ -186,7 +186,7 @@ warn_no_idmapd(struct cache_detail *detail)
static int idtoname_parse(struct cache_detail *, char *, int);
static struct ent *idtoname_lookup(struct ent *, int);
struct cache_detail idtoname_cache = {
static struct cache_detail idtoname_cache = {
.hash_size = ENT_HASHMAX,
.hash_table = idtoname_table,
.name = "nfs4.idtoname",
@ -277,7 +277,7 @@ nametoid_hash(struct ent *ent)
return hash_str(ent->name, ENT_HASHBITS);
}
void
static void
nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
int *blen)
{
@ -317,9 +317,9 @@ nametoid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
}
static struct ent *nametoid_lookup(struct ent *, int);
int nametoid_parse(struct cache_detail *, char *, int);
static int nametoid_parse(struct cache_detail *, char *, int);
struct cache_detail nametoid_cache = {
static struct cache_detail nametoid_cache = {
.hash_size = ENT_HASHMAX,
.hash_table = nametoid_table,
.name = "nfs4.nametoid",
@ -330,7 +330,7 @@ struct cache_detail nametoid_cache = {
.warn_no_listener = warn_no_idmapd,
};
int
static int
nametoid_parse(struct cache_detail *cd, char *buf, int buflen)
{
struct ent ent, *res;

View file

@ -45,6 +45,7 @@
#include <linux/param.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
@ -168,12 +169,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
(int)open->op_fname.len, open->op_fname.data,
open->op_stateowner);
if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
return nfserr_grace;
if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
return nfserr_no_grace;
/* This check required by spec. */
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
return nfserr_inval;
@ -198,6 +193,11 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
if (status)
goto out;
switch (open->op_claim_type) {
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
status = nfserr_inval;
if (open->op_create)
goto out;
/* fall through */
case NFS4_OPEN_CLAIM_NULL:
/*
* (1) set CURRENT_FH to the file being opened,
@ -220,7 +220,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
if (status)
goto out;
break;
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
case NFS4_OPEN_CLAIM_DELEGATE_PREV:
printk("NFSD: unsupported OPEN claim type %d\n",
open->op_claim_type);
@ -473,26 +472,27 @@ static inline int
nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read)
{
int status;
struct file *filp = NULL;
/* no need to check permission - this will be done in nfsd_read() */
read->rd_filp = NULL;
if (read->rd_offset >= OFFSET_MAX)
return nfserr_inval;
nfs4_lock_state();
/* check stateid */
if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid,
CHECK_FH | RD_STATE, &filp))) {
CHECK_FH | RD_STATE, &read->rd_filp))) {
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out;
}
if (read->rd_filp)
get_file(read->rd_filp);
status = nfs_ok;
out:
nfs4_unlock_state();
read->rd_rqstp = rqstp;
read->rd_fhp = current_fh;
read->rd_filp = filp;
return status;
}
@ -532,6 +532,8 @@ nfsd4_remove(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_rem
{
int status;
if (nfs4_in_grace())
return nfserr_grace;
status = nfsd_unlink(rqstp, current_fh, 0, remove->rm_name, remove->rm_namelen);
if (status == nfserr_symlink)
return nfserr_notdir;
@ -550,6 +552,9 @@ nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh,
if (!save_fh->fh_dentry)
return status;
if (nfs4_in_grace() && !(save_fh->fh_export->ex_flags
& NFSEXP_NOSUBTREECHECK))
return nfserr_grace;
status = nfsd_rename(rqstp, save_fh, rename->rn_sname,
rename->rn_snamelen, current_fh,
rename->rn_tname, rename->rn_tnamelen);
@ -624,6 +629,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
goto out;
}
if (filp)
get_file(filp);
nfs4_unlock_state();
write->wr_bytes_written = write->wr_buflen;
@ -635,6 +642,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
status = nfsd_write(rqstp, current_fh, filp, write->wr_offset,
write->wr_vec, write->wr_vlen, write->wr_buflen,
&write->wr_how_written);
if (filp)
fput(filp);
if (status == nfserr_symlink)
status = nfserr_inval;
@ -923,6 +932,9 @@ encode_op:
nfs4_put_stateowner(replay_owner);
replay_owner = NULL;
}
/* XXX Ugh, we need to get rid of this kind of special case: */
if (op->opnum == OP_READ && op->u.read.rd_filp)
fput(op->u.read.rd_filp);
}
out:

421
fs/nfsd/nfs4recover.c Normal file
View file

@ -0,0 +1,421 @@
/*
* linux/fs/nfsd/nfs4recover.c
*
* Copyright (c) 2004 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@citi.umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
#include <linux/param.h>
#include <linux/file.h>
#include <linux/namei.h>
#include <asm/uaccess.h>
#include <asm/scatterlist.h>
#include <linux/crypto.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
/* Globals */
static struct nameidata rec_dir;
static int rec_dir_init = 0;
static void
nfs4_save_user(uid_t *saveuid, gid_t *savegid)
{
*saveuid = current->fsuid;
*savegid = current->fsgid;
current->fsuid = 0;
current->fsgid = 0;
}
static void
nfs4_reset_user(uid_t saveuid, gid_t savegid)
{
current->fsuid = saveuid;
current->fsgid = savegid;
}
static void
md5_to_hex(char *out, char *md5)
{
int i;
for (i=0; i<16; i++) {
unsigned char c = md5[i];
*out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
*out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
}
*out = '\0';
}
int
nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname)
{
struct xdr_netobj cksum;
struct crypto_tfm *tfm;
struct scatterlist sg[1];
int status = nfserr_resource;
dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n",
clname->len, clname->data);
tfm = crypto_alloc_tfm("md5", 0);
if (tfm == NULL)
goto out;
cksum.len = crypto_tfm_alg_digestsize(tfm);
cksum.data = kmalloc(cksum.len, GFP_KERNEL);
if (cksum.data == NULL)
goto out;
crypto_digest_init(tfm);
sg[0].page = virt_to_page(clname->data);
sg[0].offset = offset_in_page(clname->data);
sg[0].length = clname->len;
crypto_digest_update(tfm, sg, 1);
crypto_digest_final(tfm, cksum.data);
md5_to_hex(dname, cksum.data);
kfree(cksum.data);
status = nfs_ok;
out:
if (tfm)
crypto_free_tfm(tfm);
return status;
}
static void
nfsd4_sync_rec_dir(void)
{
down(&rec_dir.dentry->d_inode->i_sem);
nfsd_sync_dir(rec_dir.dentry);
up(&rec_dir.dentry->d_inode->i_sem);
}
int
nfsd4_create_clid_dir(struct nfs4_client *clp)
{
char *dname = clp->cl_recdir;
struct dentry *dentry;
uid_t uid;
gid_t gid;
int status;
dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname);
if (!rec_dir_init || clp->cl_firststate)
return 0;
nfs4_save_user(&uid, &gid);
/* lock the parent */
down(&rec_dir.dentry->d_inode->i_sem);
dentry = lookup_one_len(dname, rec_dir.dentry, HEXDIR_LEN-1);
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
goto out_unlock;
}
status = -EEXIST;
if (dentry->d_inode) {
dprintk("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n");
goto out_put;
}
status = vfs_mkdir(rec_dir.dentry->d_inode, dentry, S_IRWXU);
out_put:
dput(dentry);
out_unlock:
up(&rec_dir.dentry->d_inode->i_sem);
if (status == 0) {
clp->cl_firststate = 1;
nfsd4_sync_rec_dir();
}
nfs4_reset_user(uid, gid);
dprintk("NFSD: nfsd4_create_clid_dir returns %d\n", status);
return status;
}
typedef int (recdir_func)(struct dentry *, struct dentry *);
struct dentry_list {
struct dentry *dentry;
struct list_head list;
};
struct dentry_list_arg {
struct list_head dentries;
struct dentry *parent;
};
static int
nfsd4_build_dentrylist(void *arg, const char *name, int namlen,
loff_t offset, ino_t ino, unsigned int d_type)
{
struct dentry_list_arg *dla = arg;
struct list_head *dentries = &dla->dentries;
struct dentry *parent = dla->parent;
struct dentry *dentry;
struct dentry_list *child;
if (name && isdotent(name, namlen))
return nfs_ok;
dentry = lookup_one_len(name, parent, namlen);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
child = kmalloc(sizeof(*child), GFP_KERNEL);
if (child == NULL)
return -ENOMEM;
child->dentry = dentry;
list_add(&child->list, dentries);
return 0;
}
static int
nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
{
struct file *filp;
struct dentry_list_arg dla = {
.parent = dir,
};
struct list_head *dentries = &dla.dentries;
struct dentry_list *child;
uid_t uid;
gid_t gid;
int status;
if (!rec_dir_init)
return 0;
nfs4_save_user(&uid, &gid);
filp = dentry_open(dget(dir), mntget(rec_dir.mnt),
O_RDWR);
status = PTR_ERR(filp);
if (IS_ERR(filp))
goto out;
INIT_LIST_HEAD(dentries);
status = vfs_readdir(filp, nfsd4_build_dentrylist, &dla);
fput(filp);
while (!list_empty(dentries)) {
child = list_entry(dentries->next, struct dentry_list, list);
status = f(dir, child->dentry);
if (status)
goto out;
list_del(&child->list);
dput(child->dentry);
kfree(child);
}
out:
while (!list_empty(dentries)) {
child = list_entry(dentries->next, struct dentry_list, list);
list_del(&child->list);
dput(child->dentry);
kfree(child);
}
nfs4_reset_user(uid, gid);
return status;
}
static int
nfsd4_remove_clid_file(struct dentry *dir, struct dentry *dentry)
{
int status;
if (!S_ISREG(dir->d_inode->i_mode)) {
printk("nfsd4: non-file found in client recovery directory\n");
return -EINVAL;
}
down(&dir->d_inode->i_sem);
status = vfs_unlink(dir->d_inode, dentry);
up(&dir->d_inode->i_sem);
return status;
}
static int
nfsd4_clear_clid_dir(struct dentry *dir, struct dentry *dentry)
{
int status;
/* For now this directory should already be empty, but we empty it of
* any regular files anyway, just in case the directory was created by
* a kernel from the future.... */
nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file);
down(&dir->d_inode->i_sem);
status = vfs_rmdir(dir->d_inode, dentry);
up(&dir->d_inode->i_sem);
return status;
}
static int
nfsd4_unlink_clid_dir(char *name, int namlen)
{
struct dentry *dentry;
int status;
dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name);
down(&rec_dir.dentry->d_inode->i_sem);
dentry = lookup_one_len(name, rec_dir.dentry, namlen);
up(&rec_dir.dentry->d_inode->i_sem);
if (IS_ERR(dentry)) {
status = PTR_ERR(dentry);
return status;
}
status = -ENOENT;
if (!dentry->d_inode)
goto out;
status = nfsd4_clear_clid_dir(rec_dir.dentry, dentry);
out:
dput(dentry);
return status;
}
void
nfsd4_remove_clid_dir(struct nfs4_client *clp)
{
uid_t uid;
gid_t gid;
int status;
if (!rec_dir_init || !clp->cl_firststate)
return;
clp->cl_firststate = 0;
nfs4_save_user(&uid, &gid);
status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1);
nfs4_reset_user(uid, gid);
if (status == 0)
nfsd4_sync_rec_dir();
if (status)
printk("NFSD: Failed to remove expired client state directory"
" %.*s\n", HEXDIR_LEN, clp->cl_recdir);
return;
}
static int
purge_old(struct dentry *parent, struct dentry *child)
{
int status;
if (nfs4_has_reclaimed_state(child->d_name.name))
return nfs_ok;
status = nfsd4_clear_clid_dir(parent, child);
if (status)
printk("failed to remove client recovery directory %s\n",
child->d_name.name);
/* Keep trying, success or failure: */
return nfs_ok;
}
void
nfsd4_recdir_purge_old(void) {
int status;
if (!rec_dir_init)
return;
status = nfsd4_list_rec_dir(rec_dir.dentry, purge_old);
if (status == 0)
nfsd4_sync_rec_dir();
if (status)
printk("nfsd4: failed to purge old clients from recovery"
" directory %s\n", rec_dir.dentry->d_name.name);
return;
}
static int
load_recdir(struct dentry *parent, struct dentry *child)
{
if (child->d_name.len != HEXDIR_LEN - 1) {
printk("nfsd4: illegal name %s in recovery directory\n",
child->d_name.name);
/* Keep trying; maybe the others are OK: */
return nfs_ok;
}
nfs4_client_to_reclaim(child->d_name.name);
return nfs_ok;
}
int
nfsd4_recdir_load(void) {
int status;
status = nfsd4_list_rec_dir(rec_dir.dentry, load_recdir);
if (status)
printk("nfsd4: failed loading clients from recovery"
" directory %s\n", rec_dir.dentry->d_name.name);
return status;
}
/*
* Hold reference to the recovery directory.
*/
void
nfsd4_init_recdir(char *rec_dirname)
{
uid_t uid = 0;
gid_t gid = 0;
int status;
printk("NFSD: Using %s as the NFSv4 state recovery directory\n",
rec_dirname);
BUG_ON(rec_dir_init);
nfs4_save_user(&uid, &gid);
status = path_lookup(rec_dirname, LOOKUP_FOLLOW, &rec_dir);
if (status == -ENOENT)
printk("NFSD: recovery directory %s doesn't exist\n",
rec_dirname);
if (!status)
rec_dir_init = 1;
nfs4_reset_user(uid, gid);
}
void
nfsd4_shutdown_recdir(void)
{
if (!rec_dir_init)
return;
rec_dir_init = 0;
path_release(&rec_dir);
}

File diff suppressed because it is too large Load diff

View file

@ -136,7 +136,7 @@ xdr_error: \
} \
} while (0)
u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
static u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
{
/* We want more bytes than seem to be available.
* Maybe we need a new page, maybe we have just run out
@ -190,7 +190,7 @@ defer_free(struct nfsd4_compoundargs *argp,
return 0;
}
char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
{
void *new = NULL;
if (p == argp->tmp) {
@ -1210,16 +1210,15 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
save = resp->p;
/*
* Routine for encoding the result of a
* "seqid-mutating" NFSv4 operation. This is
* where seqids are incremented, and the
* replay cache is filled.
* Routine for encoding the result of a "seqid-mutating" NFSv4 operation. This
* is where sequence id's are incremented, and the replay cache is filled.
* Note that we increment sequence id's here, at the last moment, so we're sure
* we know whether the error to be returned is a sequence id mutating error.
*/
#define ENCODE_SEQID_OP_TAIL(stateowner) do { \
if (seqid_mutating_err(nfserr) && stateowner) { \
if (stateowner->so_confirmed) \
stateowner->so_seqid++; \
stateowner->so_seqid++; \
stateowner->so_replay.rp_status = nfserr; \
stateowner->so_replay.rp_buflen = \
(((char *)(resp)->p - (char *)save)); \
@ -1366,7 +1365,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
if ((buflen -= 4) < 0)
goto out_resource;
WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME );
if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
WRITE32(NFS4_FH_PERSISTENT);
else
WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
}
if (bmval0 & FATTR4_WORD0_CHANGE) {
/*
@ -1969,7 +1971,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
case NFS4_OPEN_DELEGATE_READ:
RESERVE_SPACE(20 + sizeof(stateid_t));
WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
WRITE32(0);
WRITE32(open->op_recall);
/*
* TODO: ACE's in delegations

View file

@ -51,6 +51,7 @@ enum {
NFSD_Fh,
NFSD_Threads,
NFSD_Leasetime,
NFSD_RecoveryDir,
};
/*
@ -66,6 +67,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size);
static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
static ssize_t write_threads(struct file *file, char *buf, size_t size);
static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[NFSD_Svc] = write_svc,
@ -78,6 +80,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
[NFSD_Fh] = write_filehandle,
[NFSD_Threads] = write_threads,
[NFSD_Leasetime] = write_leasetime,
[NFSD_RecoveryDir] = write_recoverydir,
};
static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
@ -349,6 +352,25 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
return strlen(buf);
}
static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
char *recdir;
int len, status;
if (size > PATH_MAX || buf[size-1] != '\n')
return -EINVAL;
buf[size-1] = 0;
recdir = mesg;
len = qword_get(&mesg, recdir, size);
if (len <= 0)
return -EINVAL;
status = nfs4_reset_recoverydir(recdir);
return strlen(buf);
}
/*----------------------------------------------------------------------------*/
/*
* populating the filesystem.
@ -369,6 +391,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
#ifdef CONFIG_NFSD_V4
[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
[NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
#endif
/* last one */ {""}
};
@ -397,9 +420,8 @@ static int __init init_nfsd(void)
nfsd_cache_init(); /* RPC reply cache */
nfsd_export_init(); /* Exports table */
nfsd_lockd_init(); /* lockd->nfsd callbacks */
#ifdef CONFIG_NFSD_V4
nfs4_state_init(); /* NFSv4 locking state */
nfsd_idmap_init(); /* Name to ID mapping */
#endif /* CONFIG_NFSD_V4 */
if (proc_mkdir("fs/nfs", NULL)) {
struct proc_dir_entry *entry;
entry = create_proc_entry("fs/nfs/exports", 0, NULL);
@ -426,9 +448,7 @@ static void __exit exit_nfsd(void)
remove_proc_entry("fs/nfs", NULL);
nfsd_stat_shutdown();
nfsd_lockd_shutdown();
#ifdef CONFIG_NFSD_V4
nfsd_idmap_shutdown();
#endif /* CONFIG_NFSD_V4 */
unregister_filesystem(&nfsd_fs_type);
}

View file

@ -95,7 +95,7 @@ nfsd_svc(unsigned short port, int nrservs)
error = nfsd_racache_init(2*nrservs);
if (error<0)
goto out;
error = nfs4_state_init();
error = nfs4_state_start();
if (error<0)
goto out;
if (!nfsd_serv) {

View file

@ -45,11 +45,10 @@
#endif /* CONFIG_NFSD_V3 */
#include <linux/nfsd/nfsfh.h>
#include <linux/quotaops.h>
#include <linux/dnotify.h>
#include <linux/xattr_acl.h>
#include <linux/fsnotify.h>
#include <linux/posix_acl.h>
#ifdef CONFIG_NFSD_V4
#include <linux/posix_acl_xattr.h>
#ifdef CONFIG_NFSD_V4
#include <linux/xattr.h>
#include <linux/nfs4.h>
#include <linux/nfs4_acl.h>
@ -734,7 +733,7 @@ nfsd_sync(struct file *filp)
up(&inode->i_sem);
}
static void
void
nfsd_sync_dir(struct dentry *dp)
{
nfsd_dosync(NULL, dp, dp->d_inode->i_fop);
@ -861,7 +860,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
nfsdstats.io_read += err;
*count = err;
err = 0;
dnotify_parent(file->f_dentry, DN_ACCESS);
fsnotify_access(file->f_dentry);
} else
err = nfserrno(err);
out:
@ -917,7 +916,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
set_fs(oldfs);
if (err >= 0) {
nfsdstats.io_write += cnt;
dnotify_parent(file->f_dentry, DN_MODIFY);
fsnotify_modify(file->f_dentry);
}
/* clear setuid/setgid flag after write */
@ -1872,10 +1871,10 @@ nfsd_get_posix_acl(struct svc_fh *fhp, int type)
return ERR_PTR(-EOPNOTSUPP);
switch(type) {
case ACL_TYPE_ACCESS:
name = XATTR_NAME_ACL_ACCESS;
name = POSIX_ACL_XATTR_ACCESS;
break;
case ACL_TYPE_DEFAULT:
name = XATTR_NAME_ACL_DEFAULT;
name = POSIX_ACL_XATTR_DEFAULT;
break;
default:
return ERR_PTR(-EOPNOTSUPP);
@ -1919,17 +1918,17 @@ nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
return -EOPNOTSUPP;
switch(type) {
case ACL_TYPE_ACCESS:
name = XATTR_NAME_ACL_ACCESS;
name = POSIX_ACL_XATTR_ACCESS;
break;
case ACL_TYPE_DEFAULT:
name = XATTR_NAME_ACL_DEFAULT;
name = POSIX_ACL_XATTR_DEFAULT;
break;
default:
return -EOPNOTSUPP;
}
if (acl && acl->a_count) {
size = xattr_acl_size(acl->a_count);
size = posix_acl_xattr_size(acl->a_count);
value = kmalloc(size, GFP_KERNEL);
if (!value)
return -ENOMEM;

View file

@ -10,7 +10,7 @@
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
#include <linux/dnotify.h>
#include <linux/fsnotify.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tty.h>
@ -808,7 +808,9 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
/* NB: we're sure to have correct a_ops only after f_op->open */
if (f->f_flags & O_DIRECT) {
if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO) {
if (!f->f_mapping->a_ops ||
((!f->f_mapping->a_ops->direct_IO) &&
(!f->f_mapping->a_ops->get_xip_page))) {
fput(f);
f = ERR_PTR(-EINVAL);
}
@ -949,6 +951,7 @@ asmlinkage long sys_open(const char __user * filename, int flags, int mode)
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_dentry);
fd_install(fd, f);
}
}

View file

@ -17,4 +17,3 @@ obj-$(CONFIG_SUN_PARTITION) += sun.o
obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o
obj-$(CONFIG_IBM_PARTITION) += ibm.o
obj-$(CONFIG_EFI_PARTITION) += efi.o
obj-$(CONFIG_NEC98_PARTITION) += nec98.o msdos.o

View file

@ -79,9 +79,6 @@ static int (*check_part[])(struct parsed_partitions *, struct block_device *) =
#ifdef CONFIG_LDM_PARTITION
ldm_partition, /* this must come before msdos */
#endif
#ifdef CONFIG_NEC98_PARTITION
nec98_partition, /* must be come before `msdos_partition' */
#endif
#ifdef CONFIG_MSDOS_PARTITION
msdos_partition,
#endif

View file

@ -30,7 +30,3 @@ put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size)
extern int warn_no_part;
extern void parse_bsd(struct parsed_partitions *state,
struct block_device *bdev, u32 offset, u32 size,
int origin, char *flavour, int max_partitions);

View file

@ -202,12 +202,12 @@ parse_solaris_x86(struct parsed_partitions *state, struct block_device *bdev,
#endif
}
#if defined(CONFIG_BSD_DISKLABEL) || defined(CONFIG_NEC98_PARTITION)
#if defined(CONFIG_BSD_DISKLABEL)
/*
* Create devices for BSD partitions listed in a disklabel, under a
* dos-like partition. See parse_extended() for more information.
*/
void
static void
parse_bsd(struct parsed_partitions *state, struct block_device *bdev,
u32 offset, u32 size, int origin, char *flavour,
int max_partitions)

View file

@ -11,4 +11,5 @@ proc-y += inode.o root.o base.o generic.o array.o \
kmsg.o proc_tty.o proc_misc.o
proc-$(CONFIG_PROC_KCORE) += kcore.o
proc-$(CONFIG_PROC_VMCORE) += vmcore.o
proc-$(CONFIG_PROC_DEVICETREE) += proc_devtree.o

Some files were not shown because too many files have changed in this diff Show more