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
@ -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)
|
|||||||
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)
|
|||||||
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(¤t->pending.signal, SIGPIPE);
|
sigpipe = sigismember(¤t->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,
|
|||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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 @@
|
|||||||
#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;
|
|||||||
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)
|
|||||||
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,
|
|||||||
- 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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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 {
|
|||||||
#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 = {
|
|||||||
/* 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
|
|||||||
|
|
||||||
/* 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)
|
|||||||
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",
|
||||||
|
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)
|
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 @@ relock:
|
|||||||
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 @@ resume:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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_move_list(dentry, dispose);
|
|
||||||
found++;
|
|
||||||
} else {
|
|
||||||
dentry_lru_del(dentry);
|
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
|
* 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)
|
|||||||
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 {
|
|||||||
|
|
||||||
#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
|
||||||
|
193
security/inode.c
193
security/inode.c
@ -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 = {
|
|||||||
.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,
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = create_by_name(name, mode, parent, &dentry);
|
if (!parent)
|
||||||
if (error) {
|
parent = mount->mnt_root;
|
||||||
dentry = ERR_PTR(error);
|
|
||||||
simple_release_fs(&mount, &mount_count);
|
dir = parent->d_inode;
|
||||||
goto exit;
|
|
||||||
}
|
mutex_lock(&dir->i_mutex);
|
||||||
|
dentry = lookup_one_len(name, parent, strlen(name));
|
||||||
|
if (IS_ERR(dentry))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (dentry->d_inode) {
|
if (dentry->d_inode) {
|
||||||
if (fops)
|
error = -EEXIST;
|
||||||
dentry->d_inode->i_fop = fops;
|
goto out1;
|
||||||
if (data)
|
|
||||||
dentry->d_inode->i_private = data;
|
|
||||||
}
|
}
|
||||||
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;
|
return dentry;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(securityfs_create_file);
|
EXPORT_SYMBOL_GPL(securityfs_create_file);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user