2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* fs/libfs.c
|
|
|
|
* Library for filesystems writers.
|
|
|
|
*/
|
|
|
|
|
2014-06-05 07:06:27 +08:00
|
|
|
#include <linux/blkdev.h>
|
2011-11-17 12:57:37 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pagemap.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/vfs.h>
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
#include <linux/quotaops.h>
|
2006-03-23 19:00:36 +08:00
|
|
|
#include <linux/mutex.h>
|
2013-09-16 22:30:04 +08:00
|
|
|
#include <linux/namei.h>
|
2007-10-22 07:42:05 +08:00
|
|
|
#include <linux/exportfs.h>
|
2009-06-08 02:56:44 +08:00
|
|
|
#include <linux/writeback.h>
|
2011-09-16 14:31:11 +08:00
|
|
|
#include <linux/buffer_head.h> /* sync_mapping_buffers */
|
2006-03-23 19:00:36 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2011-07-08 03:03:58 +08:00
|
|
|
#include "internal.h"
|
|
|
|
|
2011-01-07 14:49:33 +08:00
|
|
|
static inline int simple_positive(struct dentry *dentry)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
return d_really_is_positive(dentry) && !d_unhashed(dentry);
|
2011-01-07 14:49:33 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int simple_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
|
|
|
struct kstat *stat)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
generic_fillattr(inode, stat);
|
|
|
|
stat->blocks = inode->i_mapping->nrpages << (PAGE_CACHE_SHIFT - 9);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_getattr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-06-23 17:02:58 +08:00
|
|
|
int simple_statfs(struct dentry *dentry, struct kstatfs *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-06-23 17:02:58 +08:00
|
|
|
buf->f_type = dentry->d_sb->s_magic;
|
2005-04-17 06:20:36 +08:00
|
|
|
buf->f_bsize = PAGE_CACHE_SIZE;
|
|
|
|
buf->f_namelen = NAME_MAX;
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_statfs);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Retaining negative dentries for an in-memory filesystem just wastes
|
|
|
|
* memory and lookup time: arrange for them to be deleted immediately.
|
|
|
|
*/
|
2013-10-26 06:47:37 +08:00
|
|
|
int always_delete_dentry(const struct dentry *dentry)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
2013-10-26 06:47:37 +08:00
|
|
|
EXPORT_SYMBOL(always_delete_dentry);
|
|
|
|
|
|
|
|
const struct dentry_operations simple_dentry_operations = {
|
|
|
|
.d_delete = always_delete_dentry,
|
|
|
|
};
|
|
|
|
EXPORT_SYMBOL(simple_dentry_operations);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup the data. This is trivial - if the dentry didn't already
|
|
|
|
* exist, we know it is negative. Set d_op to delete negative dentries.
|
|
|
|
*/
|
2012-06-11 05:13:09 +08:00
|
|
|
struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (dentry->d_name.len > NAME_MAX)
|
|
|
|
return ERR_PTR(-ENAMETOOLONG);
|
2013-07-14 21:43:25 +08:00
|
|
|
if (!dentry->d_sb->s_d_op)
|
|
|
|
d_set_d_op(dentry, &simple_dentry_operations);
|
2005-04-17 06:20:36 +08:00
|
|
|
d_add(dentry, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_lookup);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int dcache_dir_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2012-05-11 04:14:12 +08:00
|
|
|
static struct qstr cursor_name = QSTR_INIT(".", 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-12-08 18:36:35 +08:00
|
|
|
file->private_data = d_alloc(file->f_path.dentry, &cursor_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return file->private_data ? 0 : -ENOMEM;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(dcache_dir_open);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int dcache_dir_close(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
dput(file->private_data);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(dcache_dir_close);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-12-18 07:59:39 +08:00
|
|
|
loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-01-07 14:49:34 +08:00
|
|
|
struct dentry *dentry = file->f_path.dentry;
|
2015-03-18 06:26:15 +08:00
|
|
|
mutex_lock(&d_inode(dentry)->i_mutex);
|
2012-12-18 07:59:39 +08:00
|
|
|
switch (whence) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case 1:
|
|
|
|
offset += file->f_pos;
|
|
|
|
case 0:
|
|
|
|
if (offset >= 0)
|
|
|
|
break;
|
|
|
|
default:
|
2015-03-18 06:26:15 +08:00
|
|
|
mutex_unlock(&d_inode(dentry)->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (offset != file->f_pos) {
|
|
|
|
file->f_pos = offset;
|
|
|
|
if (file->f_pos >= 2) {
|
|
|
|
struct list_head *p;
|
|
|
|
struct dentry *cursor = file->private_data;
|
|
|
|
loff_t n = file->f_pos - 2;
|
|
|
|
|
2011-01-07 14:49:34 +08:00
|
|
|
spin_lock(&dentry->d_lock);
|
|
|
|
/* d_lock not required for cursor */
|
2014-10-27 07:19:16 +08:00
|
|
|
list_del(&cursor->d_child);
|
2011-01-07 14:49:34 +08:00
|
|
|
p = dentry->d_subdirs.next;
|
|
|
|
while (n && p != &dentry->d_subdirs) {
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dentry *next;
|
2014-10-27 07:19:16 +08:00
|
|
|
next = list_entry(p, struct dentry, d_child);
|
2011-01-07 14:49:34 +08:00
|
|
|
spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
|
2011-01-07 14:49:33 +08:00
|
|
|
if (simple_positive(next))
|
2005-04-17 06:20:36 +08:00
|
|
|
n--;
|
2011-01-07 14:49:33 +08:00
|
|
|
spin_unlock(&next->d_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
p = p->next;
|
|
|
|
}
|
2014-10-27 07:19:16 +08:00
|
|
|
list_add_tail(&cursor->d_child, p);
|
2011-01-07 14:49:34 +08:00
|
|
|
spin_unlock(&dentry->d_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2015-03-18 06:26:15 +08:00
|
|
|
mutex_unlock(&d_inode(dentry)->i_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return offset;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(dcache_dir_lseek);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Relationship between i_mode and the DT_xxx types */
|
|
|
|
static inline unsigned char dt_type(struct inode *inode)
|
|
|
|
{
|
|
|
|
return (inode->i_mode >> 12) & 15;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Directory is locked and all positive dentries in it are safe, since
|
|
|
|
* for ramfs-type trees they can't go away without unlink() or rmdir(),
|
|
|
|
* both impossible due to the lock on directory.
|
|
|
|
*/
|
|
|
|
|
2013-05-16 08:23:06 +08:00
|
|
|
int dcache_readdir(struct file *file, struct dir_context *ctx)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-05-16 08:23:06 +08:00
|
|
|
struct dentry *dentry = file->f_path.dentry;
|
|
|
|
struct dentry *cursor = file->private_data;
|
2014-10-27 07:19:16 +08:00
|
|
|
struct list_head *p, *q = &cursor->d_child;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-16 08:23:06 +08:00
|
|
|
if (!dir_emit_dots(file, ctx))
|
|
|
|
return 0;
|
|
|
|
spin_lock(&dentry->d_lock);
|
|
|
|
if (ctx->pos == 2)
|
|
|
|
list_move(q, &dentry->d_subdirs);
|
|
|
|
|
|
|
|
for (p = q->next; p != &dentry->d_subdirs; p = p->next) {
|
2014-10-27 07:19:16 +08:00
|
|
|
struct dentry *next = list_entry(p, struct dentry, d_child);
|
2013-05-16 08:23:06 +08:00
|
|
|
spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
|
|
|
|
if (!simple_positive(next)) {
|
|
|
|
spin_unlock(&next->d_lock);
|
|
|
|
continue;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-16 08:23:06 +08:00
|
|
|
spin_unlock(&next->d_lock);
|
|
|
|
spin_unlock(&dentry->d_lock);
|
|
|
|
if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
|
2015-03-18 06:26:15 +08:00
|
|
|
d_inode(next)->i_ino, dt_type(d_inode(next))))
|
2013-05-16 08:23:06 +08:00
|
|
|
return 0;
|
|
|
|
spin_lock(&dentry->d_lock);
|
|
|
|
spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
|
|
|
|
/* next is still alive */
|
|
|
|
list_move(q, p);
|
|
|
|
spin_unlock(&next->d_lock);
|
|
|
|
p = q;
|
|
|
|
ctx->pos++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-05-16 08:23:06 +08:00
|
|
|
spin_unlock(&dentry->d_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(dcache_readdir);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ssize_t generic_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t *ppos)
|
|
|
|
{
|
|
|
|
return -EISDIR;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(generic_read_dir);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-28 17:56:42 +08:00
|
|
|
const struct file_operations simple_dir_operations = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = dcache_dir_open,
|
|
|
|
.release = dcache_dir_close,
|
|
|
|
.llseek = dcache_dir_lseek,
|
|
|
|
.read = generic_read_dir,
|
2013-05-16 08:23:06 +08:00
|
|
|
.iterate = dcache_readdir,
|
2010-05-26 23:53:41 +08:00
|
|
|
.fsync = noop_fsync,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_dir_operations);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
const struct inode_operations simple_dir_inode_operations = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.lookup = simple_lookup,
|
|
|
|
};
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_dir_inode_operations);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-03-05 16:30:28 +08:00
|
|
|
static const struct super_operations simple_super_operations = {
|
|
|
|
.statfs = simple_statfs,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
|
|
|
|
* will never be mountable)
|
|
|
|
*/
|
2010-07-26 03:47:46 +08:00
|
|
|
struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name,
|
2011-01-13 05:59:34 +08:00
|
|
|
const struct super_operations *ops,
|
|
|
|
const struct dentry_operations *dops, unsigned long magic)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-06-25 19:55:37 +08:00
|
|
|
struct super_block *s;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct dentry *dentry;
|
|
|
|
struct inode *root;
|
2012-05-11 04:14:12 +08:00
|
|
|
struct qstr d_name = QSTR_INIT(name, strlen(name));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-06-25 19:55:37 +08:00
|
|
|
s = sget(fs_type, NULL, set_anon_super, MS_NOUSER, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(s))
|
2010-07-26 03:47:46 +08:00
|
|
|
return ERR_CAST(s);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-08-19 05:11:08 +08:00
|
|
|
s->s_maxbytes = MAX_LFS_FILESIZE;
|
2008-07-30 13:33:03 +08:00
|
|
|
s->s_blocksize = PAGE_SIZE;
|
|
|
|
s->s_blocksize_bits = PAGE_SHIFT;
|
2005-04-17 06:20:36 +08:00
|
|
|
s->s_magic = magic;
|
2007-03-05 16:30:28 +08:00
|
|
|
s->s_op = ops ? ops : &simple_super_operations;
|
2005-04-17 06:20:36 +08:00
|
|
|
s->s_time_gran = 1;
|
|
|
|
root = new_inode(s);
|
|
|
|
if (!root)
|
|
|
|
goto Enomem;
|
2007-05-08 15:32:31 +08:00
|
|
|
/*
|
|
|
|
* since this is the first inode, make it number 1. New inodes created
|
|
|
|
* after this must take care not to collide with it (by passing
|
|
|
|
* max_reserved of 1 to iunique).
|
|
|
|
*/
|
|
|
|
root->i_ino = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
|
|
|
|
root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
|
2011-07-08 03:03:58 +08:00
|
|
|
dentry = __d_alloc(s, &d_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!dentry) {
|
|
|
|
iput(root);
|
|
|
|
goto Enomem;
|
|
|
|
}
|
|
|
|
d_instantiate(dentry, root);
|
|
|
|
s->s_root = dentry;
|
2011-01-13 05:59:34 +08:00
|
|
|
s->s_d_op = dops;
|
2005-04-17 06:20:36 +08:00
|
|
|
s->s_flags |= MS_ACTIVE;
|
2010-07-26 03:47:46 +08:00
|
|
|
return dget(s->s_root);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
Enomem:
|
2009-05-06 13:34:22 +08:00
|
|
|
deactivate_locked_super(s);
|
2010-07-26 03:47:46 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(mount_pseudo);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-04-06 05:25:09 +08:00
|
|
|
int simple_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
if (inode->i_private)
|
|
|
|
file->private_data = inode->i_private;
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_open);
|
2012-04-06 05:25:09 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
struct inode *inode = d_inode(old_dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(inode);
|
2010-10-23 23:11:40 +08:00
|
|
|
ihold(inode);
|
2005-04-17 06:20:36 +08:00
|
|
|
dget(dentry);
|
|
|
|
d_instantiate(dentry, inode);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_link);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int simple_empty(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct dentry *child;
|
|
|
|
int ret = 0;
|
|
|
|
|
2011-01-07 14:49:34 +08:00
|
|
|
spin_lock(&dentry->d_lock);
|
2014-10-27 07:19:16 +08:00
|
|
|
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
2011-01-07 14:49:33 +08:00
|
|
|
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
|
|
|
|
if (simple_positive(child)) {
|
|
|
|
spin_unlock(&child->d_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2011-01-07 14:49:33 +08:00
|
|
|
}
|
|
|
|
spin_unlock(&child->d_lock);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = 1;
|
|
|
|
out:
|
2011-01-07 14:49:34 +08:00
|
|
|
spin_unlock(&dentry->d_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_empty);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int simple_unlink(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
|
2006-10-01 14:29:03 +08:00
|
|
|
drop_nlink(inode);
|
2005-04-17 06:20:36 +08:00
|
|
|
dput(dentry);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_unlink);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int simple_rmdir(struct inode *dir, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
if (!simple_empty(dentry))
|
|
|
|
return -ENOTEMPTY;
|
|
|
|
|
2015-03-18 06:26:15 +08:00
|
|
|
drop_nlink(d_inode(dentry));
|
2005-04-17 06:20:36 +08:00
|
|
|
simple_unlink(dir, dentry);
|
2006-10-01 14:29:03 +08:00
|
|
|
drop_nlink(dir);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_rmdir);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
|
|
struct inode *new_dir, struct dentry *new_dentry)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
struct inode *inode = d_inode(old_dentry);
|
VFS: (Scripted) Convert S_ISLNK/DIR/REG(dentry->d_inode) to d_is_*(dentry)
Convert the following where appropriate:
(1) S_ISLNK(dentry->d_inode) to d_is_symlink(dentry).
(2) S_ISREG(dentry->d_inode) to d_is_reg(dentry).
(3) S_ISDIR(dentry->d_inode) to d_is_dir(dentry). This is actually more
complicated than it appears as some calls should be converted to
d_can_lookup() instead. The difference is whether the directory in
question is a real dir with a ->lookup op or whether it's a fake dir with
a ->d_automount op.
In some circumstances, we can subsume checks for dentry->d_inode not being
NULL into this, provided we the code isn't in a filesystem that expects
d_inode to be NULL if the dirent really *is* negative (ie. if we're going to
use d_inode() rather than d_backing_inode() to get the inode pointer).
Note that the dentry type field may be set to something other than
DCACHE_MISS_TYPE when d_inode is NULL in the case of unionmount, where the VFS
manages the fall-through from a negative dentry to a lower layer. In such a
case, the dentry type of the negative union dentry is set to the same as the
type of the lower dentry.
However, if you know d_inode is not NULL at the call site, then you can use
the d_is_xxx() functions even in a filesystem.
There is one further complication: a 0,0 chardev dentry may be labelled
DCACHE_WHITEOUT_TYPE rather than DCACHE_SPECIAL_TYPE. Strictly, this was
intended for special directory entry types that don't have attached inodes.
The following perl+coccinelle script was used:
use strict;
my @callers;
open($fd, 'git grep -l \'S_IS[A-Z].*->d_inode\' |') ||
die "Can't grep for S_ISDIR and co. callers";
@callers = <$fd>;
close($fd);
unless (@callers) {
print "No matches\n";
exit(0);
}
my @cocci = (
'@@',
'expression E;',
'@@',
'',
'- S_ISLNK(E->d_inode->i_mode)',
'+ d_is_symlink(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISDIR(E->d_inode->i_mode)',
'+ d_is_dir(E)',
'',
'@@',
'expression E;',
'@@',
'',
'- S_ISREG(E->d_inode->i_mode)',
'+ d_is_reg(E)' );
my $coccifile = "tmp.sp.cocci";
open($fd, ">$coccifile") || die $coccifile;
print($fd "$_\n") || die $coccifile foreach (@cocci);
close($fd);
foreach my $file (@callers) {
chomp $file;
print "Processing ", $file, "\n";
system("spatch", "--sp-file", $coccifile, $file, "--in-place", "--no-show-diff") == 0 ||
die "spatch failed";
}
[AV: overlayfs parts skipped]
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2015-01-29 20:02:35 +08:00
|
|
|
int they_are_dirs = d_is_dir(old_dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!simple_empty(new_dentry))
|
|
|
|
return -ENOTEMPTY;
|
|
|
|
|
2015-03-18 06:26:15 +08:00
|
|
|
if (d_really_is_positive(new_dentry)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
simple_unlink(new_dir, new_dentry);
|
2011-07-22 03:49:09 +08:00
|
|
|
if (they_are_dirs) {
|
2015-03-18 06:26:15 +08:00
|
|
|
drop_nlink(d_inode(new_dentry));
|
2006-10-01 14:29:03 +08:00
|
|
|
drop_nlink(old_dir);
|
2011-07-22 03:49:09 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
} else if (they_are_dirs) {
|
2006-10-01 14:29:03 +08:00
|
|
|
drop_nlink(old_dir);
|
2006-10-01 14:29:04 +08:00
|
|
|
inc_nlink(new_dir);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =
|
|
|
|
new_dir->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_rename);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
/**
|
2010-06-04 17:30:01 +08:00
|
|
|
* simple_setattr - setattr for simple filesystem
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
* @dentry: dentry
|
|
|
|
* @iattr: iattr structure
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -error on failure.
|
|
|
|
*
|
2010-06-04 17:30:01 +08:00
|
|
|
* simple_setattr is a simple ->setattr implementation without a proper
|
|
|
|
* implementation of size changes.
|
|
|
|
*
|
|
|
|
* It can either be used for in-memory filesystems or special files
|
|
|
|
* on simple regular filesystems. Anything that needs to change on-disk
|
|
|
|
* or wire state on size changes needs its own setattr method.
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
*/
|
|
|
|
int simple_setattr(struct dentry *dentry, struct iattr *iattr)
|
|
|
|
{
|
2015-03-18 06:26:15 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
int error;
|
|
|
|
|
|
|
|
error = inode_change_ok(inode, iattr);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2010-06-04 17:30:04 +08:00
|
|
|
if (iattr->ia_valid & ATTR_SIZE)
|
|
|
|
truncate_setsize(inode, iattr->ia_size);
|
2010-06-04 17:30:00 +08:00
|
|
|
setattr_copy(inode, iattr);
|
2010-06-04 17:30:01 +08:00
|
|
|
mark_inode_dirty(inode);
|
|
|
|
return 0;
|
fs: introduce new truncate sequence
Introduce a new truncate calling sequence into fs/mm subsystems. Rather than
setattr > vmtruncate > truncate, have filesystems call their truncate sequence
from ->setattr if filesystem specific operations are required. vmtruncate is
deprecated, and truncate_pagecache and inode_newsize_ok helpers introduced
previously should be used.
simple_setattr is introduced for simple in-ram filesystems to implement
the new truncate sequence. Eventually all filesystems should be converted
to implement a setattr, and the default code in notify_change should go
away.
simple_setsize is also introduced to perform just the ATTR_SIZE portion
of simple_setattr (ie. changing i_size and trimming pagecache).
To implement the new truncate sequence:
- filesystem specific manipulations (eg freeing blocks) must be done in
the setattr method rather than ->truncate.
- vmtruncate can not be used by core code to trim blocks past i_size in
the event of write failure after allocation, so this must be performed
in the fs code.
- convert usage of helpers block_write_begin, nobh_write_begin,
cont_write_begin, and *blockdev_direct_IO* to use _newtrunc postfixed
variants. These avoid calling vmtruncate to trim blocks (see previous).
- inode_setattr should not be used. generic_setattr is a new function
to be used to copy simple attributes into the generic inode.
- make use of the better opportunity to handle errors with the new sequence.
Big problem with the previous calling sequence: the filesystem is not called
until i_size has already changed. This means it is not allowed to fail the
call, and also it does not know what the previous i_size was. Also, generic
code calling vmtruncate to truncate allocated blocks in case of error had
no good way to return a meaningful error (or, for example, atomically handle
block deallocation).
Cc: Christoph Hellwig <hch@lst.de>
Acked-by: Jan Kara <jack@suse.cz>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-05-26 23:05:33 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(simple_setattr);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int simple_readpage(struct file *file, struct page *page)
|
|
|
|
{
|
2006-09-29 16:59:09 +08:00
|
|
|
clear_highpage(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
flush_dcache_page(page);
|
|
|
|
SetPageUptodate(page);
|
|
|
|
unlock_page(page);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_readpage);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-16 16:25:01 +08:00
|
|
|
int simple_write_begin(struct file *file, struct address_space *mapping,
|
|
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
|
|
struct page **pagep, void **fsdata)
|
|
|
|
{
|
|
|
|
struct page *page;
|
|
|
|
pgoff_t index;
|
|
|
|
|
|
|
|
index = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
|
fs: symlink write_begin allocation context fix
With the write_begin/write_end aops, page_symlink was broken because it
could no longer pass a GFP_NOFS type mask into the point where the
allocations happened. They are done in write_begin, which would always
assume that the filesystem can be entered from reclaim. This bug could
cause filesystem deadlocks.
The funny thing with having a gfp_t mask there is that it doesn't really
allow the caller to arbitrarily tinker with the context in which it can be
called. It couldn't ever be GFP_ATOMIC, for example, because it needs to
take the page lock. The only thing any callers care about is __GFP_FS
anyway, so turn that into a single flag.
Add a new flag for write_begin, AOP_FLAG_NOFS. Filesystems can now act on
this flag in their write_begin function. Change __grab_cache_page to
accept a nofs argument as well, to honour that flag (while we're there,
change the name to grab_cache_page_write_begin which is more instructive
and does away with random leading underscores).
This is really a more flexible way to go in the end anyway -- if a
filesystem happens to want any extra allocations aside from the pagecache
ones in ints write_begin function, it may now use GFP_KERNEL (rather than
GFP_NOFS) for common case allocations (eg. ocfs2_alloc_write_ctxt, for a
random example).
[kosaki.motohiro@jp.fujitsu.com: fix ubifs]
[kosaki.motohiro@jp.fujitsu.com: fix fuse]
Signed-off-by: Nick Piggin <npiggin@suse.de>
Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: <stable@kernel.org> [2.6.28.x]
Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[ Cleaned up the calling convention: just pass in the AOP flags
untouched to the grab_cache_page_write_begin() function. That
just simplifies everybody, and may even allow future expansion of the
logic. - Linus ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-05 04:00:53 +08:00
|
|
|
page = grab_cache_page_write_begin(mapping, index, flags);
|
2007-10-16 16:25:01 +08:00
|
|
|
if (!page)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*pagep = page;
|
|
|
|
|
2010-01-12 22:18:08 +08:00
|
|
|
if (!PageUptodate(page) && (len != PAGE_CACHE_SIZE)) {
|
|
|
|
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
|
|
|
|
|
|
|
zero_user_segments(page, 0, from, from + len, PAGE_CACHE_SIZE);
|
|
|
|
}
|
|
|
|
return 0;
|
2007-10-16 16:25:01 +08:00
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_write_begin);
|
2007-10-16 16:25:01 +08:00
|
|
|
|
2010-01-12 21:13:47 +08:00
|
|
|
/**
|
|
|
|
* simple_write_end - .write_end helper for non-block-device FSes
|
|
|
|
* @available: See .write_end of address_space_operations
|
|
|
|
* @file: "
|
|
|
|
* @mapping: "
|
|
|
|
* @pos: "
|
|
|
|
* @len: "
|
|
|
|
* @copied: "
|
|
|
|
* @page: "
|
|
|
|
* @fsdata: "
|
|
|
|
*
|
|
|
|
* simple_write_end does the minimum needed for updating a page after writing is
|
|
|
|
* done. It has the same API signature as the .write_end of
|
|
|
|
* address_space_operations vector. So it can just be set onto .write_end for
|
|
|
|
* FSes that don't need any other processing. i_mutex is assumed to be held.
|
|
|
|
* Block based filesystems should use generic_write_end().
|
|
|
|
* NOTE: Even though i_size might get updated by this function, mark_inode_dirty
|
|
|
|
* is not called, so a filesystem that actually does store data in .write_inode
|
|
|
|
* should extend on what's done here with a call to mark_inode_dirty() in the
|
|
|
|
* case that i_size has changed.
|
|
|
|
*/
|
2007-10-16 16:25:01 +08:00
|
|
|
int simple_write_end(struct file *file, struct address_space *mapping,
|
|
|
|
loff_t pos, unsigned len, unsigned copied,
|
|
|
|
struct page *page, void *fsdata)
|
|
|
|
{
|
2010-01-12 21:13:47 +08:00
|
|
|
struct inode *inode = page->mapping->host;
|
|
|
|
loff_t last_pos = pos + copied;
|
2007-10-16 16:25:01 +08:00
|
|
|
|
|
|
|
/* zero the stale part of the page if we did a short copy */
|
|
|
|
if (copied < len) {
|
2010-01-12 21:13:47 +08:00
|
|
|
unsigned from = pos & (PAGE_CACHE_SIZE - 1);
|
|
|
|
|
|
|
|
zero_user(page, from + copied, len - copied);
|
2007-10-16 16:25:01 +08:00
|
|
|
}
|
|
|
|
|
2010-01-12 21:13:47 +08:00
|
|
|
if (!PageUptodate(page))
|
|
|
|
SetPageUptodate(page);
|
|
|
|
/*
|
|
|
|
* No need to use i_size_read() here, the i_size
|
|
|
|
* cannot change under us because we hold the i_mutex.
|
|
|
|
*/
|
|
|
|
if (last_pos > inode->i_size)
|
|
|
|
i_size_write(inode, last_pos);
|
2007-10-16 16:25:01 +08:00
|
|
|
|
2010-01-12 21:13:47 +08:00
|
|
|
set_page_dirty(page);
|
2007-10-16 16:25:01 +08:00
|
|
|
unlock_page(page);
|
|
|
|
page_cache_release(page);
|
|
|
|
|
|
|
|
return copied;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_write_end);
|
2007-10-16 16:25:01 +08:00
|
|
|
|
2007-05-08 15:32:31 +08:00
|
|
|
/*
|
|
|
|
* the inodes created here are not hashed. If you use iunique to generate
|
|
|
|
* unique inode values later for this filesystem, then you must take care
|
|
|
|
* to pass it an appropriate max_reserved value to avoid collisions.
|
|
|
|
*/
|
2010-06-03 17:58:28 +08:00
|
|
|
int simple_fill_super(struct super_block *s, unsigned long magic,
|
|
|
|
struct tree_descr *files)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
struct dentry *root;
|
|
|
|
struct dentry *dentry;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
s->s_blocksize = PAGE_CACHE_SIZE;
|
|
|
|
s->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
|
|
|
s->s_magic = magic;
|
2007-03-05 16:30:28 +08:00
|
|
|
s->s_op = &simple_super_operations;
|
2005-04-17 06:20:36 +08:00
|
|
|
s->s_time_gran = 1;
|
|
|
|
|
|
|
|
inode = new_inode(s);
|
|
|
|
if (!inode)
|
|
|
|
return -ENOMEM;
|
2007-05-08 15:32:31 +08:00
|
|
|
/*
|
|
|
|
* because the root inode is 1, the files array must not contain an
|
|
|
|
* entry at index 1
|
|
|
|
*/
|
|
|
|
inode->i_ino = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
inode->i_mode = S_IFDIR | 0755;
|
|
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
|
|
inode->i_op = &simple_dir_inode_operations;
|
|
|
|
inode->i_fop = &simple_dir_operations;
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(inode, 2);
|
2012-01-09 11:15:13 +08:00
|
|
|
root = d_make_root(inode);
|
|
|
|
if (!root)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; !files->name || files->name[0]; i++, files++) {
|
|
|
|
if (!files->name)
|
|
|
|
continue;
|
2007-05-08 15:32:31 +08:00
|
|
|
|
|
|
|
/* warn if it tries to conflict with the root inode */
|
|
|
|
if (unlikely(i == 1))
|
|
|
|
printk(KERN_WARNING "%s: %s passed in a files array"
|
|
|
|
"with an index of 1!\n", __func__,
|
|
|
|
s->s_type->name);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
dentry = d_alloc_name(root, files->name);
|
|
|
|
if (!dentry)
|
|
|
|
goto out;
|
|
|
|
inode = new_inode(s);
|
2011-11-01 21:12:33 +08:00
|
|
|
if (!inode) {
|
|
|
|
dput(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2011-11-01 21:12:33 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
inode->i_mode = S_IFREG | files->mode;
|
|
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
|
|
inode->i_fop = files->ops;
|
|
|
|
inode->i_ino = i;
|
|
|
|
d_add(dentry, inode);
|
|
|
|
}
|
|
|
|
s->s_root = root;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
d_genocide(root);
|
2012-04-03 07:22:25 +08:00
|
|
|
shrink_dcache_parent(root);
|
2005-04-17 06:20:36 +08:00
|
|
|
dput(root);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_fill_super);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(pin_fs_lock);
|
|
|
|
|
2006-06-09 21:34:16 +08:00
|
|
|
int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct vfsmount *mnt = NULL;
|
|
|
|
spin_lock(&pin_fs_lock);
|
|
|
|
if (unlikely(!*mount)) {
|
|
|
|
spin_unlock(&pin_fs_lock);
|
2012-03-17 14:13:52 +08:00
|
|
|
mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(mnt))
|
|
|
|
return PTR_ERR(mnt);
|
|
|
|
spin_lock(&pin_fs_lock);
|
|
|
|
if (!*mount)
|
|
|
|
*mount = mnt;
|
|
|
|
}
|
|
|
|
mntget(*mount);
|
|
|
|
++*count;
|
|
|
|
spin_unlock(&pin_fs_lock);
|
|
|
|
mntput(mnt);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_pin_fs);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
void simple_release_fs(struct vfsmount **mount, int *count)
|
|
|
|
{
|
|
|
|
struct vfsmount *mnt;
|
|
|
|
spin_lock(&pin_fs_lock);
|
|
|
|
mnt = *mount;
|
|
|
|
if (!--*count)
|
|
|
|
*mount = NULL;
|
|
|
|
spin_unlock(&pin_fs_lock);
|
|
|
|
mntput(mnt);
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_release_fs);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-05 00:59:51 +08:00
|
|
|
/**
|
|
|
|
* simple_read_from_buffer - copy data from the buffer to user space
|
|
|
|
* @to: the user space buffer to read to
|
|
|
|
* @count: the maximum number of bytes to read
|
|
|
|
* @ppos: the current position in the buffer
|
|
|
|
* @from: the buffer to read from
|
|
|
|
* @available: the size of the buffer
|
|
|
|
*
|
|
|
|
* The simple_read_from_buffer() function reads up to @count bytes from the
|
|
|
|
* buffer @from at offset @ppos into the user space address starting at @to.
|
|
|
|
*
|
|
|
|
* On success, the number of bytes read is returned and the offset @ppos is
|
|
|
|
* advanced by this number, or negative value is returned on error.
|
|
|
|
**/
|
2005-04-17 06:20:36 +08:00
|
|
|
ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
|
|
|
|
const void *from, size_t available)
|
|
|
|
{
|
|
|
|
loff_t pos = *ppos;
|
2009-09-19 04:05:42 +08:00
|
|
|
size_t ret;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pos < 0)
|
|
|
|
return -EINVAL;
|
2009-09-19 04:05:42 +08:00
|
|
|
if (pos >= available || !count)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
if (count > available - pos)
|
|
|
|
count = available - pos;
|
2009-09-19 04:05:42 +08:00
|
|
|
ret = copy_to_user(to, from + pos, count);
|
|
|
|
if (ret == count)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
2009-09-19 04:05:42 +08:00
|
|
|
count -= ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
*ppos = pos + count;
|
|
|
|
return count;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_read_from_buffer);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-05-02 05:51:22 +08:00
|
|
|
/**
|
|
|
|
* simple_write_to_buffer - copy data from user space to the buffer
|
|
|
|
* @to: the buffer to write to
|
|
|
|
* @available: the size of the buffer
|
|
|
|
* @ppos: the current position in the buffer
|
|
|
|
* @from: the user space buffer to read from
|
|
|
|
* @count: the maximum number of bytes to read
|
|
|
|
*
|
|
|
|
* The simple_write_to_buffer() function reads up to @count bytes from the user
|
|
|
|
* space address starting at @from into the buffer @to at offset @ppos.
|
|
|
|
*
|
|
|
|
* On success, the number of bytes written is returned and the offset @ppos is
|
|
|
|
* advanced by this number, or negative value is returned on error.
|
|
|
|
**/
|
|
|
|
ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
|
|
|
|
const void __user *from, size_t count)
|
|
|
|
{
|
|
|
|
loff_t pos = *ppos;
|
|
|
|
size_t res;
|
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (pos >= available || !count)
|
|
|
|
return 0;
|
|
|
|
if (count > available - pos)
|
|
|
|
count = available - pos;
|
|
|
|
res = copy_from_user(to + pos, from, count);
|
|
|
|
if (res == count)
|
|
|
|
return -EFAULT;
|
|
|
|
count -= res;
|
|
|
|
*ppos = pos + count;
|
|
|
|
return count;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_write_to_buffer);
|
2010-05-02 05:51:22 +08:00
|
|
|
|
2008-07-05 00:59:51 +08:00
|
|
|
/**
|
|
|
|
* memory_read_from_buffer - copy data from the buffer
|
|
|
|
* @to: the kernel space buffer to read to
|
|
|
|
* @count: the maximum number of bytes to read
|
|
|
|
* @ppos: the current position in the buffer
|
|
|
|
* @from: the buffer to read from
|
|
|
|
* @available: the size of the buffer
|
|
|
|
*
|
|
|
|
* The memory_read_from_buffer() function reads up to @count bytes from the
|
|
|
|
* buffer @from at offset @ppos into the kernel space address starting at @to.
|
|
|
|
*
|
|
|
|
* On success, the number of bytes read is returned and the offset @ppos is
|
|
|
|
* advanced by this number, or negative value is returned on error.
|
|
|
|
**/
|
2008-06-06 13:46:21 +08:00
|
|
|
ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos,
|
|
|
|
const void *from, size_t available)
|
|
|
|
{
|
|
|
|
loff_t pos = *ppos;
|
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (pos >= available)
|
|
|
|
return 0;
|
|
|
|
if (count > available - pos)
|
|
|
|
count = available - pos;
|
|
|
|
memcpy(to, from + pos, count);
|
|
|
|
*ppos = pos + count;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(memory_read_from_buffer);
|
2008-06-06 13:46:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Transaction based IO.
|
|
|
|
* The file expects a single write which triggers the transaction, and then
|
|
|
|
* possibly a read which collects the result - which is stored in a
|
|
|
|
* file-local buffer.
|
|
|
|
*/
|
2009-03-25 23:48:35 +08:00
|
|
|
|
|
|
|
void simple_transaction_set(struct file *file, size_t n)
|
|
|
|
{
|
|
|
|
struct simple_transaction_argresp *ar = file->private_data;
|
|
|
|
|
|
|
|
BUG_ON(n > SIMPLE_TRANSACTION_LIMIT);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The barrier ensures that ar->size will really remain zero until
|
|
|
|
* ar->data is ready for reading.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
|
|
|
ar->size = n;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_transaction_set);
|
2009-03-25 23:48:35 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
char *simple_transaction_get(struct file *file, const char __user *buf, size_t size)
|
|
|
|
{
|
|
|
|
struct simple_transaction_argresp *ar;
|
|
|
|
static DEFINE_SPINLOCK(simple_transaction_lock);
|
|
|
|
|
|
|
|
if (size > SIMPLE_TRANSACTION_LIMIT - 1)
|
|
|
|
return ERR_PTR(-EFBIG);
|
|
|
|
|
|
|
|
ar = (struct simple_transaction_argresp *)get_zeroed_page(GFP_KERNEL);
|
|
|
|
if (!ar)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
spin_lock(&simple_transaction_lock);
|
|
|
|
|
|
|
|
/* only one write allowed per open */
|
|
|
|
if (file->private_data) {
|
|
|
|
spin_unlock(&simple_transaction_lock);
|
|
|
|
free_page((unsigned long)ar);
|
|
|
|
return ERR_PTR(-EBUSY);
|
|
|
|
}
|
|
|
|
|
|
|
|
file->private_data = ar;
|
|
|
|
|
|
|
|
spin_unlock(&simple_transaction_lock);
|
|
|
|
|
|
|
|
if (copy_from_user(ar->data, buf, size))
|
|
|
|
return ERR_PTR(-EFAULT);
|
|
|
|
|
|
|
|
return ar->data;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_transaction_get);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ssize_t simple_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
|
|
|
|
{
|
|
|
|
struct simple_transaction_argresp *ar = file->private_data;
|
|
|
|
|
|
|
|
if (!ar)
|
|
|
|
return 0;
|
|
|
|
return simple_read_from_buffer(buf, size, pos, ar->data, ar->size);
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_transaction_read);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
int simple_transaction_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
free_page((unsigned long)file->private_data);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL(simple_transaction_release);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-05-18 20:40:59 +08:00
|
|
|
/* Simple attribute files */
|
|
|
|
|
|
|
|
struct simple_attr {
|
2008-02-08 20:20:26 +08:00
|
|
|
int (*get)(void *, u64 *);
|
|
|
|
int (*set)(void *, u64);
|
2005-05-18 20:40:59 +08:00
|
|
|
char get_buf[24]; /* enough to store a u64 and "\n\0" */
|
|
|
|
char set_buf[24];
|
|
|
|
void *data;
|
|
|
|
const char *fmt; /* format for read operation */
|
2006-03-23 19:00:36 +08:00
|
|
|
struct mutex mutex; /* protects access to these buffers */
|
2005-05-18 20:40:59 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* simple_attr_open is called by an actual attribute open file operation
|
|
|
|
* to set the attribute specific access operations. */
|
|
|
|
int simple_attr_open(struct inode *inode, struct file *file,
|
2008-02-08 20:20:26 +08:00
|
|
|
int (*get)(void *, u64 *), int (*set)(void *, u64),
|
2005-05-18 20:40:59 +08:00
|
|
|
const char *fmt)
|
|
|
|
{
|
|
|
|
struct simple_attr *attr;
|
|
|
|
|
|
|
|
attr = kmalloc(sizeof(*attr), GFP_KERNEL);
|
|
|
|
if (!attr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
attr->get = get;
|
|
|
|
attr->set = set;
|
2006-09-27 16:50:46 +08:00
|
|
|
attr->data = inode->i_private;
|
2005-05-18 20:40:59 +08:00
|
|
|
attr->fmt = fmt;
|
2006-03-23 19:00:36 +08:00
|
|
|
mutex_init(&attr->mutex);
|
2005-05-18 20:40:59 +08:00
|
|
|
|
|
|
|
file->private_data = attr;
|
|
|
|
|
|
|
|
return nonseekable_open(inode, file);
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL_GPL(simple_attr_open);
|
2005-05-18 20:40:59 +08:00
|
|
|
|
2008-02-08 20:20:28 +08:00
|
|
|
int simple_attr_release(struct inode *inode, struct file *file)
|
2005-05-18 20:40:59 +08:00
|
|
|
{
|
|
|
|
kfree(file->private_data);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL_GPL(simple_attr_release); /* GPL-only? This? Really? */
|
2005-05-18 20:40:59 +08:00
|
|
|
|
|
|
|
/* read from the buffer that is filled with the get function */
|
|
|
|
ssize_t simple_attr_read(struct file *file, char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct simple_attr *attr;
|
|
|
|
size_t size;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
attr = file->private_data;
|
|
|
|
|
|
|
|
if (!attr->get)
|
|
|
|
return -EACCES;
|
|
|
|
|
2008-02-08 20:20:27 +08:00
|
|
|
ret = mutex_lock_interruptible(&attr->mutex);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2008-02-08 20:20:26 +08:00
|
|
|
if (*ppos) { /* continued read */
|
2005-05-18 20:40:59 +08:00
|
|
|
size = strlen(attr->get_buf);
|
2008-02-08 20:20:26 +08:00
|
|
|
} else { /* first read */
|
|
|
|
u64 val;
|
|
|
|
ret = attr->get(attr->data, &val);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2005-05-18 20:40:59 +08:00
|
|
|
size = scnprintf(attr->get_buf, sizeof(attr->get_buf),
|
2008-02-08 20:20:26 +08:00
|
|
|
attr->fmt, (unsigned long long)val);
|
|
|
|
}
|
2005-05-18 20:40:59 +08:00
|
|
|
|
|
|
|
ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size);
|
2008-02-08 20:20:26 +08:00
|
|
|
out:
|
2006-03-23 19:00:36 +08:00
|
|
|
mutex_unlock(&attr->mutex);
|
2005-05-18 20:40:59 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL_GPL(simple_attr_read);
|
2005-05-18 20:40:59 +08:00
|
|
|
|
|
|
|
/* interpret the buffer as a number to call the set function with */
|
|
|
|
ssize_t simple_attr_write(struct file *file, const char __user *buf,
|
|
|
|
size_t len, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct simple_attr *attr;
|
|
|
|
u64 val;
|
|
|
|
size_t size;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
attr = file->private_data;
|
|
|
|
if (!attr->set)
|
|
|
|
return -EACCES;
|
|
|
|
|
2008-02-08 20:20:27 +08:00
|
|
|
ret = mutex_lock_interruptible(&attr->mutex);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2005-05-18 20:40:59 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
size = min(sizeof(attr->set_buf) - 1, len);
|
|
|
|
if (copy_from_user(attr->set_buf, buf, size))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
attr->set_buf[size] = '\0';
|
2011-07-19 23:49:25 +08:00
|
|
|
val = simple_strtoll(attr->set_buf, NULL, 0);
|
2009-09-19 04:06:03 +08:00
|
|
|
ret = attr->set(attr->data, val);
|
|
|
|
if (ret == 0)
|
|
|
|
ret = len; /* on success, claim we got the whole input */
|
2005-05-18 20:40:59 +08:00
|
|
|
out:
|
2006-03-23 19:00:36 +08:00
|
|
|
mutex_unlock(&attr->mutex);
|
2005-05-18 20:40:59 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2013-09-16 09:20:49 +08:00
|
|
|
EXPORT_SYMBOL_GPL(simple_attr_write);
|
2005-05-18 20:40:59 +08:00
|
|
|
|
2007-10-22 07:42:05 +08:00
|
|
|
/**
|
|
|
|
* generic_fh_to_dentry - generic helper for the fh_to_dentry export operation
|
|
|
|
* @sb: filesystem to do the file handle conversion on
|
|
|
|
* @fid: file handle to convert
|
|
|
|
* @fh_len: length of the file handle in bytes
|
|
|
|
* @fh_type: type of file handle
|
|
|
|
* @get_inode: filesystem callback to retrieve inode
|
|
|
|
*
|
|
|
|
* This function decodes @fid as long as it has one of the well-known
|
|
|
|
* Linux filehandle types and calls @get_inode on it to retrieve the
|
|
|
|
* inode for the object specified in the file handle.
|
|
|
|
*/
|
|
|
|
struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,
|
|
|
|
int fh_len, int fh_type, struct inode *(*get_inode)
|
|
|
|
(struct super_block *sb, u64 ino, u32 gen))
|
|
|
|
{
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
|
|
|
|
if (fh_len < 2)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
switch (fh_type) {
|
|
|
|
case FILEID_INO32_GEN:
|
|
|
|
case FILEID_INO32_GEN_PARENT:
|
|
|
|
inode = get_inode(sb, fid->i32.ino, fid->i32.gen);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-08-11 21:48:57 +08:00
|
|
|
return d_obtain_alias(inode);
|
2007-10-22 07:42:05 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(generic_fh_to_dentry);
|
|
|
|
|
|
|
|
/**
|
2012-09-05 16:31:29 +08:00
|
|
|
* generic_fh_to_parent - generic helper for the fh_to_parent export operation
|
2007-10-22 07:42:05 +08:00
|
|
|
* @sb: filesystem to do the file handle conversion on
|
|
|
|
* @fid: file handle to convert
|
|
|
|
* @fh_len: length of the file handle in bytes
|
|
|
|
* @fh_type: type of file handle
|
|
|
|
* @get_inode: filesystem callback to retrieve inode
|
|
|
|
*
|
|
|
|
* This function decodes @fid as long as it has one of the well-known
|
|
|
|
* Linux filehandle types and calls @get_inode on it to retrieve the
|
|
|
|
* inode for the _parent_ object specified in the file handle if it
|
|
|
|
* is specified in the file handle, or NULL otherwise.
|
|
|
|
*/
|
|
|
|
struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,
|
|
|
|
int fh_len, int fh_type, struct inode *(*get_inode)
|
|
|
|
(struct super_block *sb, u64 ino, u32 gen))
|
|
|
|
{
|
|
|
|
struct inode *inode = NULL;
|
|
|
|
|
|
|
|
if (fh_len <= 2)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
switch (fh_type) {
|
|
|
|
case FILEID_INO32_GEN_PARENT:
|
|
|
|
inode = get_inode(sb, fid->i32.parent_ino,
|
|
|
|
(fh_len > 3 ? fid->i32.parent_gen : 0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-08-11 21:48:57 +08:00
|
|
|
return d_obtain_alias(inode);
|
2007-10-22 07:42:05 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(generic_fh_to_parent);
|
|
|
|
|
2010-05-26 23:53:41 +08:00
|
|
|
/**
|
2014-06-05 07:06:27 +08:00
|
|
|
* __generic_file_fsync - generic fsync implementation for simple filesystems
|
|
|
|
*
|
2010-05-26 23:53:41 +08:00
|
|
|
* @file: file to synchronize
|
2014-06-05 07:06:27 +08:00
|
|
|
* @start: start offset in bytes
|
|
|
|
* @end: end offset in bytes (inclusive)
|
2010-05-26 23:53:41 +08:00
|
|
|
* @datasync: only synchronize essential metadata if true
|
|
|
|
*
|
|
|
|
* This is a generic implementation of the fsync method for simple
|
|
|
|
* filesystems which track all non-inode metadata in the buffers list
|
|
|
|
* hanging off the address_space structure.
|
|
|
|
*/
|
2014-06-05 07:06:27 +08:00
|
|
|
int __generic_file_fsync(struct file *file, loff_t start, loff_t end,
|
|
|
|
int datasync)
|
2009-06-08 02:56:44 +08:00
|
|
|
{
|
2010-05-26 23:53:25 +08:00
|
|
|
struct inode *inode = file->f_mapping->host;
|
2009-06-08 02:56:44 +08:00
|
|
|
int err;
|
|
|
|
int ret;
|
|
|
|
|
2011-07-17 08:44:56 +08:00
|
|
|
err = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
mutex_lock(&inode->i_mutex);
|
2009-06-08 02:56:44 +08:00
|
|
|
ret = sync_mapping_buffers(inode->i_mapping);
|
2015-02-02 13:37:00 +08:00
|
|
|
if (!(inode->i_state & I_DIRTY_ALL))
|
2011-07-17 08:44:56 +08:00
|
|
|
goto out;
|
2009-06-08 02:56:44 +08:00
|
|
|
if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
|
2011-07-17 08:44:56 +08:00
|
|
|
goto out;
|
2009-06-08 02:56:44 +08:00
|
|
|
|
2010-10-06 16:48:20 +08:00
|
|
|
err = sync_inode_metadata(inode, 1);
|
2009-06-08 02:56:44 +08:00
|
|
|
if (ret == 0)
|
|
|
|
ret = err;
|
2014-06-05 07:06:27 +08:00
|
|
|
|
2011-07-17 08:44:56 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&inode->i_mutex);
|
2009-06-08 02:56:44 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2014-06-05 07:06:27 +08:00
|
|
|
EXPORT_SYMBOL(__generic_file_fsync);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* generic_file_fsync - generic fsync implementation for simple filesystems
|
|
|
|
* with flush
|
|
|
|
* @file: file to synchronize
|
|
|
|
* @start: start offset in bytes
|
|
|
|
* @end: end offset in bytes (inclusive)
|
|
|
|
* @datasync: only synchronize essential metadata if true
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
int generic_file_fsync(struct file *file, loff_t start, loff_t end,
|
|
|
|
int datasync)
|
|
|
|
{
|
|
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = __generic_file_fsync(file, start, end, datasync);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
|
|
|
|
}
|
2010-05-26 23:53:41 +08:00
|
|
|
EXPORT_SYMBOL(generic_file_fsync);
|
|
|
|
|
2010-07-23 06:03:41 +08:00
|
|
|
/**
|
|
|
|
* generic_check_addressable - Check addressability of file system
|
|
|
|
* @blocksize_bits: log of file system block size
|
|
|
|
* @num_blocks: number of blocks in file system
|
|
|
|
*
|
|
|
|
* Determine whether a file system with @num_blocks blocks (and a
|
|
|
|
* block size of 2**@blocksize_bits) is addressable by the sector_t
|
|
|
|
* and page cache of the system. Return 0 if so and -EFBIG otherwise.
|
|
|
|
*/
|
|
|
|
int generic_check_addressable(unsigned blocksize_bits, u64 num_blocks)
|
|
|
|
{
|
|
|
|
u64 last_fs_block = num_blocks - 1;
|
2010-08-17 03:10:17 +08:00
|
|
|
u64 last_fs_page =
|
|
|
|
last_fs_block >> (PAGE_CACHE_SHIFT - blocksize_bits);
|
2010-07-23 06:03:41 +08:00
|
|
|
|
|
|
|
if (unlikely(num_blocks == 0))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((blocksize_bits < 9) || (blocksize_bits > PAGE_CACHE_SHIFT))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-08-17 03:10:17 +08:00
|
|
|
if ((last_fs_block > (sector_t)(~0ULL) >> (blocksize_bits - 9)) ||
|
|
|
|
(last_fs_page > (pgoff_t)(~0ULL))) {
|
2010-07-23 06:03:41 +08:00
|
|
|
return -EFBIG;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_check_addressable);
|
|
|
|
|
2010-05-26 23:53:41 +08:00
|
|
|
/*
|
|
|
|
* No-op implementation of ->fsync for in-memory filesystems.
|
|
|
|
*/
|
2011-07-17 08:44:56 +08:00
|
|
|
int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
2010-05-26 23:53:41 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(noop_fsync);
|
2013-09-16 22:30:04 +08:00
|
|
|
|
|
|
|
void kfree_put_link(struct dentry *dentry, struct nameidata *nd,
|
|
|
|
void *cookie)
|
|
|
|
{
|
|
|
|
char *s = nd_get_link(nd);
|
|
|
|
if (!IS_ERR(s))
|
|
|
|
kfree(s);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(kfree_put_link);
|
2013-10-03 10:35:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* nop .set_page_dirty method so that people can use .page_mkwrite on
|
|
|
|
* anon inodes.
|
|
|
|
*/
|
|
|
|
static int anon_set_page_dirty(struct page *page)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A single inode exists for all anon_inode files. Contrary to pipes,
|
|
|
|
* anon_inode inodes have no associated per-instance data, so we need
|
|
|
|
* only allocate one of them.
|
|
|
|
*/
|
|
|
|
struct inode *alloc_anon_inode(struct super_block *s)
|
|
|
|
{
|
|
|
|
static const struct address_space_operations anon_aops = {
|
|
|
|
.set_page_dirty = anon_set_page_dirty,
|
|
|
|
};
|
|
|
|
struct inode *inode = new_inode_pseudo(s);
|
|
|
|
|
|
|
|
if (!inode)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
inode->i_ino = get_next_ino();
|
|
|
|
inode->i_mapping->a_ops = &anon_aops;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the inode dirty from the very beginning,
|
|
|
|
* that way it will never be moved to the dirty
|
|
|
|
* list because mark_inode_dirty() will think
|
|
|
|
* that it already _is_ on the dirty list.
|
|
|
|
*/
|
|
|
|
inode->i_state = I_DIRTY;
|
|
|
|
inode->i_mode = S_IRUSR | S_IWUSR;
|
|
|
|
inode->i_uid = current_fsuid();
|
|
|
|
inode->i_gid = current_fsgid();
|
|
|
|
inode->i_flags |= S_PRIVATE;
|
|
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(alloc_anon_inode);
|
2014-08-27 18:49:41 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* simple_nosetlease - generic helper for prohibiting leases
|
|
|
|
* @filp: file pointer
|
|
|
|
* @arg: type of lease to obtain
|
|
|
|
* @flp: new lease supplied for insertion
|
2014-08-22 22:40:25 +08:00
|
|
|
* @priv: private data for lm_setup operation
|
2014-08-27 18:49:41 +08:00
|
|
|
*
|
|
|
|
* Generic helper for filesystems that do not wish to allow leases to be set.
|
|
|
|
* All arguments are ignored and it just returns -EINVAL.
|
|
|
|
*/
|
|
|
|
int
|
2014-08-22 22:40:25 +08:00
|
|
|
simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,
|
|
|
|
void **priv)
|
2014-08-27 18:49:41 +08:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(simple_nosetlease);
|