Commit 5cd9599b authored by Linus Torvalds's avatar Linus Torvalds

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
parents e343a895 d668dc56
...@@ -116,6 +116,7 @@ struct autofs_sb_info { ...@@ -116,6 +116,7 @@ struct autofs_sb_info {
int needs_reghost; int needs_reghost;
struct super_block *sb; struct super_block *sb;
struct mutex wq_mutex; struct mutex wq_mutex;
struct mutex pipe_mutex;
spinlock_t fs_lock; spinlock_t fs_lock;
struct autofs_wait_queue *queues; /* Wait queue pointer */ struct autofs_wait_queue *queues; /* Wait queue pointer */
spinlock_t lookup_lock; spinlock_t lookup_lock;
......
...@@ -225,6 +225,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) ...@@ -225,6 +225,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
sbi->min_proto = 0; sbi->min_proto = 0;
sbi->max_proto = 0; sbi->max_proto = 0;
mutex_init(&sbi->wq_mutex); mutex_init(&sbi->wq_mutex);
mutex_init(&sbi->pipe_mutex);
spin_lock_init(&sbi->fs_lock); spin_lock_init(&sbi->fs_lock);
sbi->queues = NULL; sbi->queues = NULL;
spin_lock_init(&sbi->lookup_lock); spin_lock_init(&sbi->lookup_lock);
......
...@@ -56,26 +56,27 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi) ...@@ -56,26 +56,27 @@ void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
mutex_unlock(&sbi->wq_mutex); 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; unsigned long sigpipe, flags;
mm_segment_t fs; mm_segment_t fs;
const char *data = (const char *)addr; const char *data = (const char *)addr;
ssize_t wr = 0; ssize_t wr = 0;
/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
sigpipe = sigismember(&current->pending.signal, SIGPIPE); sigpipe = sigismember(&current->pending.signal, SIGPIPE);
/* Save pointer to user space and point back to kernel space */ /* Save pointer to user space and point back to kernel space */
fs = get_fs(); fs = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
mutex_lock(&sbi->pipe_mutex);
while (bytes && while (bytes &&
(wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) { (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
data += wr; data += wr;
bytes -= wr; bytes -= wr;
} }
mutex_lock(&sbi->pipe_mutex);
set_fs(fs); set_fs(fs);
...@@ -110,6 +111,13 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, ...@@ -110,6 +111,13 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
pkt.hdr.proto_version = sbi->version; pkt.hdr.proto_version = sbi->version;
pkt.hdr.type = type; 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) { switch (type) {
/* Kernel protocol v4 missing and expire packets */ /* Kernel protocol v4 missing and expire packets */
case autofs_ptype_missing: case autofs_ptype_missing:
...@@ -163,22 +171,18 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi, ...@@ -163,22 +171,18 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
} }
default: default:
printk("autofs4_notify_daemon: bad type %d!\n", type); printk("autofs4_notify_daemon: bad type %d!\n", type);
mutex_unlock(&sbi->wq_mutex);
return; return;
} }
/* Check if we have become catatonic */ pipe = sbi->pipe;
mutex_lock(&sbi->wq_mutex); get_file(pipe);
if (!sbi->catatonic) {
pipe = sbi->pipe;
get_file(pipe);
}
mutex_unlock(&sbi->wq_mutex); mutex_unlock(&sbi->wq_mutex);
if (pipe) { if (autofs4_write(sbi, pipe, &pkt, pktsz))
if (autofs4_write(pipe, &pkt, pktsz)) autofs4_catatonic_mode(sbi);
autofs4_catatonic_mode(sbi); fput(pipe);
fput(pipe);
}
} }
static int autofs4_getpath(struct autofs_sb_info *sbi, static int autofs4_getpath(struct autofs_sb_info *sbi,
...@@ -257,6 +261,9 @@ static int validate_request(struct autofs_wait_queue **wait, ...@@ -257,6 +261,9 @@ static int validate_request(struct autofs_wait_queue **wait,
struct autofs_wait_queue *wq; struct autofs_wait_queue *wq;
struct autofs_info *ino; struct autofs_info *ino;
if (sbi->catatonic)
return -ENOENT;
/* Wait in progress, continue; */ /* Wait in progress, continue; */
wq = autofs4_find_wait(sbi, qstr); wq = autofs4_find_wait(sbi, qstr);
if (wq) { if (wq) {
...@@ -289,6 +296,9 @@ static int validate_request(struct autofs_wait_queue **wait, ...@@ -289,6 +296,9 @@ static int validate_request(struct autofs_wait_queue **wait,
if (mutex_lock_interruptible(&sbi->wq_mutex)) if (mutex_lock_interruptible(&sbi->wq_mutex))
return -EINTR; return -EINTR;
if (sbi->catatonic)
return -ENOENT;
wq = autofs4_find_wait(sbi, qstr); wq = autofs4_find_wait(sbi, qstr);
if (wq) { if (wq) {
*wait = wq; *wait = wq;
...@@ -389,7 +399,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, ...@@ -389,7 +399,7 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
ret = validate_request(&wq, sbi, &qstr, dentry, notify); ret = validate_request(&wq, sbi, &qstr, dentry, notify);
if (ret <= 0) { if (ret <= 0) {
if (ret == 0) if (ret != -EINTR)
mutex_unlock(&sbi->wq_mutex); mutex_unlock(&sbi->wq_mutex);
kfree(qstr.name); kfree(qstr.name);
return ret; return ret;
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/kmemleak.h>
#include <linux/cleancache.h> #include <linux/cleancache.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "internal.h" #include "internal.h"
...@@ -521,7 +520,7 @@ static struct super_block *blockdev_superblock __read_mostly; ...@@ -521,7 +520,7 @@ static struct super_block *blockdev_superblock __read_mostly;
void __init bdev_cache_init(void) void __init bdev_cache_init(void)
{ {
int err; int err;
struct vfsmount *bd_mnt; static struct vfsmount *bd_mnt;
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode), bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| 0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
...@@ -533,12 +532,7 @@ void __init bdev_cache_init(void) ...@@ -533,12 +532,7 @@ void __init bdev_cache_init(void)
bd_mnt = kern_mount(&bd_type); bd_mnt = kern_mount(&bd_type);
if (IS_ERR(bd_mnt)) if (IS_ERR(bd_mnt))
panic("Cannot create bdev pseudo-fs"); panic("Cannot create bdev pseudo-fs");
/* blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
* 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 */
} }
/* /*
......
...@@ -88,24 +88,21 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, ...@@ -88,24 +88,21 @@ struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
- link the two up if this is needed - link the two up if this is needed
- fill in the attributes - 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 coda_vattr attr;
struct inode *inode;
int error; int error;
/* We get inode numbers from Venus -- see venus source */ /* We get inode numbers from Venus -- see venus source */
error = venus_getattr(sb, fid, &attr); error = venus_getattr(sb, fid, &attr);
if ( error ) { if (error)
*inode = NULL; return ERR_PTR(error);
return error;
}
*inode = coda_iget(sb, fid, &attr); inode = coda_iget(sb, fid, &attr);
if ( IS_ERR(*inode) ) { if (IS_ERR(inode))
printk("coda_cnode_make: coda_iget failed\n"); printk("coda_cnode_make: coda_iget failed\n");
return PTR_ERR(*inode); return inode;
}
return 0;
} }
...@@ -156,19 +153,16 @@ struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) ...@@ -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 */ /* 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; struct inode *inode = new_inode(sb);
if (inode) {
*inode = new_inode(sb); inode->i_ino = CTL_INO;
if (*inode) { inode->i_op = &coda_ioctl_inode_operations;
(*inode)->i_ino = CTL_INO; inode->i_fop = &coda_ioctl_operations;
(*inode)->i_op = &coda_ioctl_inode_operations; inode->i_mode = 0444;
(*inode)->i_fop = &coda_ioctl_operations; return inode;
(*inode)->i_mode = 0444;
error = 0;
} }
return ERR_PTR(-ENOMEM);
return error;
} }
...@@ -49,9 +49,9 @@ struct coda_file_info { ...@@ -49,9 +49,9 @@ struct coda_file_info {
#define C_DYING 0x4 /* from venus (which died) */ #define C_DYING 0x4 /* from venus (which died) */
#define C_PURGE 0x8 #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); 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); struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb);
void coda_replace_fid(struct inode *, struct CodaFid *, struct CodaFid *); void coda_replace_fid(struct inode *, struct CodaFid *, struct CodaFid *);
......
...@@ -96,12 +96,11 @@ const struct file_operations coda_dir_operations = { ...@@ -96,12 +96,11 @@ const struct file_operations coda_dir_operations = {
/* access routines: lookup, readlink, permission */ /* access routines: lookup, readlink, permission */
static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd) static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd)
{ {
struct inode *inode = NULL; struct super_block *sb = dir->i_sb;
struct CodaFid resfid = { { 0, } };
int type = 0;
int error = 0;
const char *name = entry->d_name.name; const char *name = entry->d_name.name;
size_t length = entry->d_name.len; size_t length = entry->d_name.len;
struct inode *inode;
int type = 0;
if (length > CODA_MAXNAMLEN) { if (length > CODA_MAXNAMLEN) {
printk(KERN_ERR "name too long: lookup, %s (%*s)\n", 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 ...@@ -111,23 +110,21 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struc
/* control object, create inode on the fly */ /* control object, create inode on the fly */
if (coda_isroot(dir) && coda_iscontrol(name, length)) { if (coda_isroot(dir) && coda_iscontrol(name, length)) {
error = coda_cnode_makectl(&inode, dir->i_sb); inode = coda_cnode_makectl(sb);
type = CODA_NOCACHE; 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, if (!IS_ERR(inode) && (type & CODA_NOCACHE))
&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))
coda_flag_inode(inode, C_VATTR | C_PURGE); coda_flag_inode(inode, C_VATTR | C_PURGE);
if (inode == ERR_PTR(-ENOENT))
inode = NULL;
return d_splice_alias(inode, entry); return d_splice_alias(inode, entry);
} }
......
...@@ -204,10 +204,12 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) ...@@ -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)); printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
/* make root inode */ /* make root inode */
error = coda_cnode_make(&root, &fid, sb); root = coda_cnode_make(&fid, sb);
if ( error || !root ) { if (IS_ERR(root)) {
printk("Failure of coda_cnode_make for root: error %d\n", error); error = PTR_ERR(root);
goto error; 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", printk("coda_read_super: rootinode is %ld dev %s\n",
......
...@@ -243,6 +243,7 @@ static void dentry_lru_add(struct dentry *dentry) ...@@ -243,6 +243,7 @@ static void dentry_lru_add(struct dentry *dentry)
static void __dentry_lru_del(struct dentry *dentry) static void __dentry_lru_del(struct dentry *dentry)
{ {
list_del_init(&dentry->d_lru); list_del_init(&dentry->d_lru);
dentry->d_flags &= ~DCACHE_SHRINK_LIST;
dentry->d_sb->s_nr_dentry_unused--; dentry->d_sb->s_nr_dentry_unused--;
dentry_stat.nr_unused--; dentry_stat.nr_unused--;
} }
...@@ -806,6 +807,7 @@ void prune_dcache_sb(struct super_block *sb, int count) ...@@ -806,6 +807,7 @@ void prune_dcache_sb(struct super_block *sb, int count)
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
} else { } else {
list_move_tail(&dentry->d_lru, &tmp); list_move_tail(&dentry->d_lru, &tmp);
dentry->d_flags |= DCACHE_SHRINK_LIST;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
if (!--count) if (!--count)
break; break;
...@@ -1097,14 +1099,19 @@ static int select_parent(struct dentry *parent, struct list_head *dispose) ...@@ -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. * 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) { if (dentry->d_count) {
dentry_lru_del(dentry);
} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
dentry_lru_move_list(dentry, dispose); dentry_lru_move_list(dentry, dispose);
dentry->d_flags |= DCACHE_SHRINK_LIST;
found++; found++;
} else {
dentry_lru_del(dentry);
} }
/* /*
* We can return to the caller if we have found some (this * We can return to the caller if we have found some (this
* ensures forward progress). We'll be coming back to find * 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) ...@@ -499,9 +499,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
if (!sbi->hidden_dir) { if (!sbi->hidden_dir) {
mutex_lock(&sbi->vh_mutex); mutex_lock(&sbi->vh_mutex);
sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str, if (!sbi->hidden_dir) {
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); mutex_unlock(&sbi->vh_mutex);
if (err)
goto out_put_hidden_dir;
hfsplus_mark_inode_dirty(sbi->hidden_dir, hfsplus_mark_inode_dirty(sbi->hidden_dir,
HFSPLUS_I_CAT_DIRTY); HFSPLUS_I_CAT_DIRTY);
......
...@@ -203,6 +203,7 @@ struct dentry_operations { ...@@ -203,6 +203,7 @@ struct dentry_operations {
#define DCACHE_CANT_MOUNT 0x0100 #define DCACHE_CANT_MOUNT 0x0100
#define DCACHE_GENOCIDE 0x0200 #define DCACHE_GENOCIDE 0x0200
#define DCACHE_SHRINK_LIST 0x0400
#define DCACHE_NFSFS_RENAMED 0x1000 #define DCACHE_NFSFS_RENAMED 0x1000
/* this dentry has been "silly renamed" and has to be deleted on the last /* this dentry has been "silly renamed" and has to be deleted on the last
......
...@@ -25,100 +25,6 @@ ...@@ -25,100 +25,6 @@
static struct vfsmount *mount; static struct vfsmount *mount;
static int mount_count; 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) static inline int positive(struct dentry *dentry)
{ {
return dentry->d_inode && !d_unhashed(dentry); return dentry->d_inode && !d_unhashed(dentry);
...@@ -145,38 +51,6 @@ static struct file_system_type fs_type = { ...@@ -145,38 +51,6 @@ static struct file_system_type fs_type = {
.kill_sb = kill_litter_super, .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 * 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, ...@@ -209,31 +83,66 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data, struct dentry *parent, void *data,
const struct file_operations *fops) const struct file_operations *fops)
{ {
struct dentry *dentry = NULL; struct dentry *dentry;
int is_dir = S_ISDIR(mode);
struct inode *dir, *inode;
int error; int error;
if (!is_dir) {
BUG_ON(!fops);
mode = (mode & S_IALLUGO) | S_IFREG;
}
pr_debug("securityfs: creating file '%s'\n",name); pr_debug("securityfs: creating file '%s'\n",name);
error = simple_pin_fs(&fs_type, &mount, &mount_count); error = simple_pin_fs(&fs_type, &mount, &mount_count);
if (error) { if (error)
dentry = ERR_PTR(error); return ERR_PTR(error);
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) {
error = -EEXIST;
goto out1;
} }
error = create_by_name(name, mode, parent, &dentry); inode = new_inode(dir->i_sb);
if (error) { if (!inode) {
dentry = ERR_PTR(error); error = -ENOMEM;
simple_release_fs(&mount, &mount_count); goto out1;
goto exit;
} }
if (dentry->d_inode) { inode->i_ino = get_next_ino();
if (fops) inode->i_mode = mode;
dentry->d_inode->i_fop = fops; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
if (data) inode->i_private = data;
dentry->d_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;
} }
exit: 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; return dentry;
} }
EXPORT_SYMBOL_GPL(securityfs_create_file); EXPORT_SYMBOL_GPL(securityfs_create_file);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment