mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge with /home/shaggy/git/linus-clean/
Signed-off-by: Dave Kleikamp <[email protected]>
This commit is contained in:
commit
f7f24758ac
2863 changed files with 168538 additions and 88761 deletions
72
fs/Kconfig
72
fs/Kconfig
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
9
fs/aio.c
9
fs/aio.c
|
|
@ -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;
|
||||
|
|
|
|||
33
fs/attr.c
33
fs/attr.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
2
fs/bio.c
2
fs/bio.c
|
|
@ -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
|
||||
|
|
|
|||
29
fs/buffer.c
29
fs/buffer.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
12
fs/compat.c
12
fs/compat.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
12
fs/exec.c
12
fs/exec.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
80
fs/ext2/xip.c
Normal 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
25
fs/ext2/xip.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
135
fs/ext3/balloc.c
135
fs/ext3/balloc.c
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
110
fs/ext3/super.c
110
fs/ext3/super.c
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
* superblocks of the Veritas Filesystem.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include "vxfs_kcompat.h"
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include "vxfs_kcompat.h"
|
||||
#include "vxfs_extern.h"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
47
fs/inode.c
47
fs/inode.c
|
|
@ -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
999
fs/inotify.c
Normal 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
172
fs/ioprio.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
/*
|
||||
|
|
|
|||
114
fs/jffs/intrep.c
114
fs/jffs/intrep.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
164
fs/jffs2/wbuf.c
164
fs/jffs2/wbuf.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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--;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
32
fs/namei.c
32
fs/namei.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
132
fs/namespace.c
132
fs/namespace.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
421
fs/nfsd/nfs4recover.c
Normal 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);
|
||||
}
|
||||
1251
fs/nfsd/nfs4state.c
1251
fs/nfsd/nfs4state.c
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue