Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: autofs4: deal with autofs4_write/autofs4_write races autofs4: catatonic_mode vs. notify_daemon race autofs4: autofs4_wait() vs. autofs4_catatonic_mode() race hfsplus: creation of hidden dir on mount can fail block_dev: Suppress bdev_cache_init() kmemleak warninig fix shrink_dcache_parent() livelock coda: switch coda_cnode_make() to sane API as well, clean coda_lookup() coda: deal correctly with allocation failure from coda_cnode_makectl() securityfs: fix object creation races
This commit is contained in:
commit
5cd9599bba
12 changed files with 139 additions and 216 deletions
|
@ -116,6 +116,7 @@ struct autofs_sb_info {
|
|||
int needs_reghost;
|
||||
struct super_block *sb;
|
||||
struct mutex wq_mutex;
|
||||
struct mutex pipe_mutex;
|
||||
spinlock_t fs_lock;
|
||||
struct autofs_wait_queue *queues; /* Wait queue pointer */
|
||||
spinlock_t lookup_lock;
|
||||
|
|
|
@ -225,6 +225,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
|
|||
sbi->min_proto = 0;
|
||||
sbi->max_proto = 0;
|
||||
mutex_init(&sbi->wq_mutex);
|
||||
mutex_init(&sbi->pipe_mutex);
|
||||
spin_lock_init(&sbi->fs_lock);
|
||||
sbi->queues = NULL;
|
||||
spin_lock_init(&sbi->lookup_lock);
|
||||
|
|
|
@ -56,26 +56,27 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
|
|||
mutex_unlock(&sbi->wq_mutex);
|
||||
}
|
||||
|
||||
static int autofs4_write(struct file *file, const void *addr, int bytes)
|
||||
static int autofs4_write(struct autofs_sb_info *sbi,
|
||||
struct file *file, const void *addr, int bytes)
|
||||
{
|
||||
unsigned long sigpipe, flags;
|
||||
mm_segment_t fs;
|
||||
const char *data = (const char *)addr;
|
||||
ssize_t wr = 0;
|
||||
|
||||
/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
|
||||
|
||||
sigpipe = sigismember(¤t->pending.signal, SIGPIPE);
|
||||
|
||||
/* Save pointer to user space and point back to kernel space */
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
mutex_lock(&sbi->pipe_mutex);
|
||||
while (bytes &&
|
||||
(wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
|
||||
data += wr;
|
||||
bytes -= wr;
|
||||
}
|
||||
mutex_lock(&sbi->pipe_mutex);
|
||||
|
||||
set_fs(fs);
|
||||
|
||||
|
@ -110,6 +111,13 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
|
|||
|
||||
pkt.hdr.proto_version = sbi->version;
|
||||
pkt.hdr.type = type;
|
||||
mutex_lock(&sbi->wq_mutex);
|
||||
|
||||
/* Check if we have become catatonic */
|
||||
if (sbi->catatonic) {
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
/* Kernel protocol v4 missing and expire packets */
|
||||
case autofs_ptype_missing:
|
||||
|
@ -163,22 +171,18 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
|
|||
}
|
||||
default:
|
||||
printk("autofs4_notify_daemon: bad type %d!\n", type);
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we have become catatonic */
|
||||
mutex_lock(&sbi->wq_mutex);
|
||||
if (!sbi->catatonic) {
|
||||
pipe = sbi->pipe;
|
||||
get_file(pipe);
|
||||
}
|
||||
pipe = sbi->pipe;
|
||||
get_file(pipe);
|
||||
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
|
||||
if (pipe) {
|
||||
if (autofs4_write(pipe, &pkt, pktsz))
|
||||
autofs4_catatonic_mode(sbi);
|
||||
fput(pipe);
|
||||
}
|
||||
if (autofs4_write(sbi, pipe, &pkt, pktsz))
|
||||
autofs4_catatonic_mode(sbi);
|
||||
fput(pipe);
|
||||
}
|
||||
|
||||
static int autofs4_getpath(struct autofs_sb_info *sbi,
|
||||
|
@ -257,6 +261,9 @@ static int validate_request(struct autofs_wait_queue **wait,
|
|||
struct autofs_wait_queue *wq;
|
||||
struct autofs_info *ino;
|
||||
|
||||
if (sbi->catatonic)
|
||||
return -ENOENT;
|
||||
|
||||
/* Wait in progress, continue; */
|
||||
wq = autofs4_find_wait(sbi, qstr);
|
||||
if (wq) {
|
||||
|
@ -289,6 +296,9 @@ static int validate_request(struct autofs_wait_queue **wait,
|
|||
if (mutex_lock_interruptible(&sbi->wq_mutex))
|
||||
return -EINTR;
|
||||
|
||||
if (sbi->catatonic)
|
||||
return -ENOENT;
|
||||
|
||||
wq = autofs4_find_wait(sbi, qstr);
|
||||
if (wq) {
|
||||
*wait = wq;
|
||||
|
@ -389,7 +399,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
|
|||
|
||||
ret = validate_request(&wq, sbi, &qstr, dentry, notify);
|
||||
if (ret <= 0) {
|
||||
if (ret == 0)
|
||||
if (ret != -EINTR)
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
kfree(qstr.name);
|
||||
return ret;
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <linux/uio.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/cleancache.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
@ -521,7 +520,7 @@ static struct super_block *blockdev_superblock __read_mostly;
|
|||
void __init bdev_cache_init(void)
|
||||
{
|
||||
int err;
|
||||
struct vfsmount *bd_mnt;
|
||||
static struct vfsmount *bd_mnt;
|
||||
|
||||
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
|
||||
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
|
||||
|
@ -533,12 +532,7 @@ void __init bdev_cache_init(void)
|
|||
bd_mnt = kern_mount(&bd_type);
|
||||
if (IS_ERR(bd_mnt))
|
||||
panic("Cannot create bdev pseudo-fs");
|
||||
/*
|
||||
* This vfsmount structure is only used to obtain the
|
||||
* blockdev_superblock, so tell kmemleak not to report it.
|
||||
*/
|
||||
kmemleak_not_leak(bd_mnt);
|
||||
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
|
||||
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -88,24 +88,21 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
|
|||
- link the two up if this is needed
|
||||
- fill in the attributes
|
||||
*/
|
||||
int coda_cnode_make(struct inode **inode, struct CodaFid *fid, struct super_block *sb)
|
||||
struct inode *coda_cnode_make(struct CodaFid *fid, struct super_block *sb)
|
||||
{
|
||||
struct coda_vattr attr;
|
||||
struct inode *inode;
|
||||
int error;
|
||||
|
||||
/* We get inode numbers from Venus -- see venus source */
|
||||
error = venus_getattr(sb, fid, &attr);
|
||||
if ( error ) {
|
||||
*inode = NULL;
|
||||
return error;
|
||||
}
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
*inode = coda_iget(sb, fid, &attr);
|
||||
if ( IS_ERR(*inode) ) {
|
||||
inode = coda_iget(sb, fid, &attr);
|
||||
if (IS_ERR(inode))
|
||||
printk("coda_cnode_make: coda_iget failed\n");
|
||||
return PTR_ERR(*inode);
|
||||
}
|
||||
return 0;
|
||||
return inode;
|
||||
}
|
||||
|
||||
|
||||
|
@ -156,19 +153,16 @@ struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb)
|
|||
}
|
||||
|
||||
/* the CONTROL inode is made without asking attributes from Venus */
|
||||
int coda_cnode_makectl(struct inode **inode, struct super_block *sb)
|
||||
struct inode *coda_cnode_makectl(struct super_block *sb)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
|
||||
*inode = new_inode(sb);
|
||||
if (*inode) {
|
||||
(*inode)->i_ino = CTL_INO;
|
||||
(*inode)->i_op = &coda_ioctl_inode_operations;
|
||||
(*inode)->i_fop = &coda_ioctl_operations;
|
||||
(*inode)->i_mode = 0444;
|
||||
error = 0;
|
||||
struct inode *inode = new_inode(sb);
|
||||
if (inode) {
|
||||
inode->i_ino = CTL_INO;
|
||||
inode->i_op = &coda_ioctl_inode_operations;
|
||||
inode->i_fop = &coda_ioctl_operations;
|
||||
inode->i_mode = 0444;
|
||||
return inode;
|
||||
}
|
||||
|
||||
return error;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,9 +49,9 @@ struct coda_file_info {
|
|||
#define C_DYING 0x4 /* from venus (which died) */
|
||||
#define C_PURGE 0x8
|
||||
|
||||
int coda_cnode_make(struct inode **, struct CodaFid *, struct super_block *);
|
||||
struct inode *coda_cnode_make(struct CodaFid *, struct super_block *);
|
||||
struct inode *coda_iget(struct super_block *sb, struct CodaFid *fid, struct coda_vattr *attr);
|
||||
int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
|
||||
struct inode *coda_cnode_makectl(struct super_block *sb);
|
||||
struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb);
|
||||
void coda_replace_fid(struct inode *, struct CodaFid *, struct CodaFid *);
|
||||
|
||||
|
|
|
@ -96,12 +96,11 @@ const struct file_operations coda_dir_operations = {
|
|||
/* access routines: lookup, readlink, permission */
|
||||
static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd)
|
||||
{
|
||||
struct inode *inode = NULL;
|
||||
struct CodaFid resfid = { { 0, } };
|
||||
int type = 0;
|
||||
int error = 0;
|
||||
struct super_block *sb = dir->i_sb;
|
||||
const char *name = entry->d_name.name;
|
||||
size_t length = entry->d_name.len;
|
||||
struct inode *inode;
|
||||
int type = 0;
|
||||
|
||||
if (length > CODA_MAXNAMLEN) {
|
||||
printk(KERN_ERR "name too long: lookup, %s (%*s)\n",
|
||||
|
@ -111,23 +110,21 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc
|
|||
|
||||
/* control object, create inode on the fly */
|
||||
if (coda_isroot(dir) && coda_iscontrol(name, length)) {
|
||||
error = coda_cnode_makectl(&inode, dir->i_sb);
|
||||
inode = coda_cnode_makectl(sb);
|
||||
type = CODA_NOCACHE;
|
||||
goto exit;
|
||||
} else {
|
||||
struct CodaFid fid = { { 0, } };
|
||||
int error = venus_lookup(sb, coda_i2f(dir), name, length,
|
||||
&type, &fid);
|
||||
inode = !error ? coda_cnode_make(&fid, sb) : ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = venus_lookup(dir->i_sb, coda_i2f(dir), name, length,
|
||||
&type, &resfid);
|
||||
if (!error)
|
||||
error = coda_cnode_make(&inode, &resfid, dir->i_sb);
|
||||
|
||||
if (error && error != -ENOENT)
|
||||
return ERR_PTR(error);
|
||||
|
||||
exit:
|
||||
if (inode && (type & CODA_NOCACHE))
|
||||
if (!IS_ERR(inode) && (type & CODA_NOCACHE))
|
||||
coda_flag_inode(inode, C_VATTR | C_PURGE);
|
||||
|
||||
if (inode == ERR_PTR(-ENOENT))
|
||||
inode = NULL;
|
||||
|
||||
return d_splice_alias(inode, entry);
|
||||
}
|
||||
|
||||
|
|
|
@ -204,10 +204,12 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent)
|
|||
printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
|
||||
|
||||
/* make root inode */
|
||||
error = coda_cnode_make(&root, &fid, sb);
|
||||
if ( error || !root ) {
|
||||
printk("Failure of coda_cnode_make for root: error %d\n", error);
|
||||
goto error;
|
||||
root = coda_cnode_make(&fid, sb);
|
||||
if (IS_ERR(root)) {
|
||||
error = PTR_ERR(root);
|
||||
printk("Failure of coda_cnode_make for root: error %d\n", error);
|
||||
root = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
printk("coda_read_super: rootinode is %ld dev %s\n",
|
||||
|
|
17
fs/dcache.c
17
fs/dcache.c
|
@ -243,6 +243,7 @@ static void dentry_lru_add(struct dentry *dentry)
|
|||
static void __dentry_lru_del(struct dentry *dentry)
|
||||
{
|
||||
list_del_init(&dentry->d_lru);
|
||||
dentry->d_flags &= ~DCACHE_SHRINK_LIST;
|
||||
dentry->d_sb->s_nr_dentry_unused--;
|
||||
dentry_stat.nr_unused--;
|
||||
}
|
||||
|
@ -806,6 +807,7 @@ void prune_dcache_sb(struct super_block *sb, int count)
|
|||
spin_unlock(&dentry->d_lock);
|
||||
} else {
|
||||
list_move_tail(&dentry->d_lru, &tmp);
|
||||
dentry->d_flags |= DCACHE_SHRINK_LIST;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
if (!--count)
|
||||
break;
|
||||
|
@ -1097,14 +1099,19 @@ static int select_parent(struct dentry *parent, struct list_head *dispose)
|
|||
|
||||
/*
|
||||
* move only zero ref count dentries to the dispose list.
|
||||
*
|
||||
* Those which are presently on the shrink list, being processed
|
||||
* by shrink_dentry_list(), shouldn't be moved. Otherwise the
|
||||
* loop in shrink_dcache_parent() might not make any progress
|
||||
* and loop forever.
|
||||
*/
|
||||
if (!dentry->d_count) {
|
||||
dentry_lru_move_list(dentry, dispose);
|
||||
found++;
|
||||
} else {
|
||||
if (dentry->d_count) {
|
||||
dentry_lru_del(dentry);
|
||||
} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
|
||||
dentry_lru_move_list(dentry, dispose);
|
||||
dentry->d_flags |= DCACHE_SHRINK_LIST;
|
||||
found++;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can return to the caller if we have found some (this
|
||||
* ensures forward progress). We'll be coming back to find
|
||||
|
|
|
@ -499,9 +499,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
|
|||
if (!sbi->hidden_dir) {
|
||||
mutex_lock(&sbi->vh_mutex);
|
||||
sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
|
||||
hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str,
|
||||
sbi->hidden_dir);
|
||||
if (!sbi->hidden_dir) {
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
err = -ENOMEM;
|
||||
goto out_put_root;
|
||||
}
|
||||
err = hfsplus_create_cat(sbi->hidden_dir->i_ino, root,
|
||||
&str, sbi->hidden_dir);
|
||||
mutex_unlock(&sbi->vh_mutex);
|
||||
if (err)
|
||||
goto out_put_hidden_dir;
|
||||
|
||||
hfsplus_mark_inode_dirty(sbi->hidden_dir,
|
||||
HFSPLUS_I_CAT_DIRTY);
|
||||
|
|
|
@ -203,6 +203,7 @@ struct dentry_operations {
|
|||
|
||||
#define DCACHE_CANT_MOUNT 0x0100
|
||||
#define DCACHE_GENOCIDE 0x0200
|
||||
#define DCACHE_SHRINK_LIST 0x0400
|
||||
|
||||
#define DCACHE_NFSFS_RENAMED 0x1000
|
||||
/* this dentry has been "silly renamed" and has to be deleted on the last
|
||||
|
|
193
security/inode.c
193
security/inode.c
|
@ -25,100 +25,6 @@
|
|||
static struct vfsmount *mount;
|
||||
static int mount_count;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* I think I can get rid of these default_file_ops, but not quite sure...
|
||||
*/
|
||||
static ssize_t default_read_file(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t default_write_file(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
static int default_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (inode->i_private)
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations default_file_ops = {
|
||||
.read = default_read_file,
|
||||
.write = default_write_file,
|
||||
.open = default_open,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct inode *get_inode(struct super_block *sb, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct inode *inode = new_inode(sb);
|
||||
|
||||
if (inode) {
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_mode = mode;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
switch (mode & S_IFMT) {
|
||||
default:
|
||||
init_special_inode(inode, mode, dev);
|
||||
break;
|
||||
case S_IFREG:
|
||||
inode->i_fop = &default_file_ops;
|
||||
break;
|
||||
case S_IFDIR:
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
|
||||
/* directory inodes start off with i_nlink == 2 (for "." entry) */
|
||||
inc_nlink(inode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* SMP-safe */
|
||||
static int mknod(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, dev_t dev)
|
||||
{
|
||||
struct inode *inode;
|
||||
int error = -ENOMEM;
|
||||
|
||||
if (dentry->d_inode)
|
||||
return -EEXIST;
|
||||
|
||||
inode = get_inode(dir->i_sb, mode, dev);
|
||||
if (inode) {
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
error = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
int res;
|
||||
|
||||
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
|
||||
res = mknod(dir, dentry, mode, 0);
|
||||
if (!res)
|
||||
inc_nlink(dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int create(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
mode = (mode & S_IALLUGO) | S_IFREG;
|
||||
return mknod(dir, dentry, mode, 0);
|
||||
}
|
||||
|
||||
static inline int positive(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_inode && !d_unhashed(dentry);
|
||||
|
@ -145,38 +51,6 @@ static struct file_system_type fs_type = {
|
|||
.kill_sb = kill_litter_super,
|
||||
};
|
||||
|
||||
static int create_by_name(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
struct dentry **dentry)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
*dentry = NULL;
|
||||
|
||||
/* If the parent is not specified, we create it in the root.
|
||||
* We need the root dentry to do this, which is in the super
|
||||
* block. A pointer to that is in the struct vfsmount that we
|
||||
* have around.
|
||||
*/
|
||||
if (!parent)
|
||||
parent = mount->mnt_root;
|
||||
|
||||
mutex_lock(&parent->d_inode->i_mutex);
|
||||
*dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (!IS_ERR(*dentry)) {
|
||||
if (S_ISDIR(mode))
|
||||
error = mkdir(parent->d_inode, *dentry, mode);
|
||||
else
|
||||
error = create(parent->d_inode, *dentry, mode);
|
||||
if (error)
|
||||
dput(*dentry);
|
||||
} else
|
||||
error = PTR_ERR(*dentry);
|
||||
mutex_unlock(&parent->d_inode->i_mutex);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* securityfs_create_file - create a file in the securityfs filesystem
|
||||
*
|
||||
|
@ -209,31 +83,66 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
|
|||
struct dentry *parent, void *data,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct dentry *dentry = NULL;
|
||||
struct dentry *dentry;
|
||||
int is_dir = S_ISDIR(mode);
|
||||
struct inode *dir, *inode;
|
||||
int error;
|
||||
|
||||
if (!is_dir) {
|
||||
BUG_ON(!fops);
|
||||
mode = (mode & S_IALLUGO) | S_IFREG;
|
||||
}
|
||||
|
||||
pr_debug("securityfs: creating file '%s'\n",name);
|
||||
|
||||
error = simple_pin_fs(&fs_type, &mount, &mount_count);
|
||||
if (error) {
|
||||
dentry = ERR_PTR(error);
|
||||
goto exit;
|
||||
}
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
error = create_by_name(name, mode, parent, &dentry);
|
||||
if (error) {
|
||||
dentry = ERR_PTR(error);
|
||||
simple_release_fs(&mount, &mount_count);
|
||||
goto exit;
|
||||
}
|
||||
if (!parent)
|
||||
parent = mount->mnt_root;
|
||||
|
||||
dir = parent->d_inode;
|
||||
|
||||
mutex_lock(&dir->i_mutex);
|
||||
dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (IS_ERR(dentry))
|
||||
goto out;
|
||||
|
||||
if (dentry->d_inode) {
|
||||
if (fops)
|
||||
dentry->d_inode->i_fop = fops;
|
||||
if (data)
|
||||
dentry->d_inode->i_private = data;
|
||||
error = -EEXIST;
|
||||
goto out1;
|
||||
}
|
||||
exit:
|
||||
|
||||
inode = new_inode(dir->i_sb);
|
||||
if (!inode) {
|
||||
error = -ENOMEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_mode = mode;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_private = data;
|
||||
if (is_dir) {
|
||||
inode->i_op = &simple_dir_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
inc_nlink(inode);
|
||||
inc_nlink(dir);
|
||||
} else {
|
||||
inode->i_fop = fops;
|
||||
}
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
mutex_unlock(&dir->i_mutex);
|
||||
return dentry;
|
||||
|
||||
out1:
|
||||
dput(dentry);
|
||||
dentry = ERR_PTR(error);
|
||||
out:
|
||||
mutex_unlock(&dir->i_mutex);
|
||||
simple_release_fs(&mount, &mount_count);
|
||||
return dentry;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(securityfs_create_file);
|
||||
|
|
Loading…
Reference in a new issue