mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 12:52:30 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
AFS: Use i_generation not i_version for the vnode uniquifier
AFS: Set s_id in the superblock to the volume name
vfs: Fix data corruption after failed write in __block_write_begin()
afs: afs_fill_page reads too much, or wrong data
VFS: Fix vfsmount overput on simultaneous automount
fix wrong iput on d_inode introduced by e6bc45d65d
Delay struct net freeing while there's a sysfs instance refering to it
afs: fix sget() races, close leak on umount
ubifs: fix sget races
ubifs: split allocation of ubifs_info into a separate function
fix leak in proc_set_super()
This commit is contained in:
commit
8dac6bee32
@ -584,11 +584,11 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
success:
|
||||
d_add(dentry, inode);
|
||||
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%llu }",
|
||||
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }",
|
||||
fid.vnode,
|
||||
fid.unique,
|
||||
dentry->d_inode->i_ino,
|
||||
(unsigned long long)dentry->d_inode->i_version);
|
||||
dentry->d_inode->i_generation);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -671,10 +671,10 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
* been deleted and replaced, and the original vnode ID has
|
||||
* been reused */
|
||||
if (fid.unique != vnode->fid.unique) {
|
||||
_debug("%s: file deleted (uq %u -> %u I:%llu)",
|
||||
_debug("%s: file deleted (uq %u -> %u I:%u)",
|
||||
dentry->d_name.name, fid.unique,
|
||||
vnode->fid.unique,
|
||||
(unsigned long long)dentry->d_inode->i_version);
|
||||
dentry->d_inode->i_generation);
|
||||
spin_lock(&vnode->lock);
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
spin_unlock(&vnode->lock);
|
||||
|
@ -89,7 +89,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
|
||||
i_size_write(&vnode->vfs_inode, size);
|
||||
vnode->vfs_inode.i_uid = status->owner;
|
||||
vnode->vfs_inode.i_gid = status->group;
|
||||
vnode->vfs_inode.i_version = vnode->fid.unique;
|
||||
vnode->vfs_inode.i_generation = vnode->fid.unique;
|
||||
vnode->vfs_inode.i_nlink = status->nlink;
|
||||
|
||||
mode = vnode->vfs_inode.i_mode;
|
||||
@ -102,6 +102,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
|
||||
vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server;
|
||||
vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime;
|
||||
vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime;
|
||||
vnode->vfs_inode.i_version = data_version;
|
||||
}
|
||||
|
||||
expected_version = status->data_version;
|
||||
|
@ -75,7 +75,8 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
|
||||
inode->i_ctime.tv_nsec = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_version = vnode->fid.unique;
|
||||
inode->i_generation = vnode->fid.unique;
|
||||
inode->i_version = vnode->status.data_version;
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
|
||||
/* check to see whether a symbolic link is really a mountpoint */
|
||||
@ -100,7 +101,7 @@ static int afs_iget5_test(struct inode *inode, void *opaque)
|
||||
struct afs_iget_data *data = opaque;
|
||||
|
||||
return inode->i_ino == data->fid.vnode &&
|
||||
inode->i_version == data->fid.unique;
|
||||
inode->i_generation == data->fid.unique;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -122,7 +123,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
inode->i_ino = data->fid.vnode;
|
||||
inode->i_version = data->fid.unique;
|
||||
inode->i_generation = data->fid.unique;
|
||||
vnode->fid = data->fid;
|
||||
vnode->volume = data->volume;
|
||||
|
||||
@ -380,8 +381,7 @@ int afs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
|
||||
inode = dentry->d_inode;
|
||||
|
||||
_enter("{ ino=%lu v=%llu }", inode->i_ino,
|
||||
(unsigned long long)inode->i_version);
|
||||
_enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
|
||||
|
||||
generic_fillattr(inode, stat);
|
||||
return 0;
|
||||
|
@ -31,8 +31,8 @@
|
||||
static void afs_i_init_once(void *foo);
|
||||
static struct dentry *afs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data);
|
||||
static void afs_kill_super(struct super_block *sb);
|
||||
static struct inode *afs_alloc_inode(struct super_block *sb);
|
||||
static void afs_put_super(struct super_block *sb);
|
||||
static void afs_destroy_inode(struct inode *inode);
|
||||
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf);
|
||||
|
||||
@ -40,7 +40,7 @@ struct file_system_type afs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "afs",
|
||||
.mount = afs_mount,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = afs_kill_super,
|
||||
.fs_flags = 0,
|
||||
};
|
||||
|
||||
@ -50,7 +50,6 @@ static const struct super_operations afs_super_ops = {
|
||||
.drop_inode = afs_drop_inode,
|
||||
.destroy_inode = afs_destroy_inode,
|
||||
.evict_inode = afs_evict_inode,
|
||||
.put_super = afs_put_super,
|
||||
.show_options = generic_show_options,
|
||||
};
|
||||
|
||||
@ -282,19 +281,25 @@ static int afs_parse_device_name(struct afs_mount_params *params,
|
||||
*/
|
||||
static int afs_test_super(struct super_block *sb, void *data)
|
||||
{
|
||||
struct afs_mount_params *params = data;
|
||||
struct afs_super_info *as1 = data;
|
||||
struct afs_super_info *as = sb->s_fs_info;
|
||||
|
||||
return as->volume == params->volume;
|
||||
return as->volume == as1->volume;
|
||||
}
|
||||
|
||||
static int afs_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
sb->s_fs_info = data;
|
||||
return set_anon_super(sb, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* fill in the superblock
|
||||
*/
|
||||
static int afs_fill_super(struct super_block *sb, void *data)
|
||||
static int afs_fill_super(struct super_block *sb,
|
||||
struct afs_mount_params *params)
|
||||
{
|
||||
struct afs_mount_params *params = data;
|
||||
struct afs_super_info *as = NULL;
|
||||
struct afs_super_info *as = sb->s_fs_info;
|
||||
struct afs_fid fid;
|
||||
struct dentry *root = NULL;
|
||||
struct inode *inode = NULL;
|
||||
@ -302,23 +307,13 @@ static int afs_fill_super(struct super_block *sb, void *data)
|
||||
|
||||
_enter("");
|
||||
|
||||
/* allocate a superblock info record */
|
||||
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
|
||||
if (!as) {
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
afs_get_volume(params->volume);
|
||||
as->volume = params->volume;
|
||||
|
||||
/* fill in the superblock */
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = AFS_FS_MAGIC;
|
||||
sb->s_op = &afs_super_ops;
|
||||
sb->s_fs_info = as;
|
||||
sb->s_bdi = &as->volume->bdi;
|
||||
strlcpy(sb->s_id, as->volume->vlocation->vldb.name, sizeof(sb->s_id));
|
||||
|
||||
/* allocate the root inode and dentry */
|
||||
fid.vid = as->volume->vid;
|
||||
@ -326,7 +321,7 @@ static int afs_fill_super(struct super_block *sb, void *data)
|
||||
fid.unique = 1;
|
||||
inode = afs_iget(sb, params->key, &fid, NULL, NULL);
|
||||
if (IS_ERR(inode))
|
||||
goto error_inode;
|
||||
return PTR_ERR(inode);
|
||||
|
||||
if (params->autocell)
|
||||
set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags);
|
||||
@ -342,16 +337,8 @@ static int afs_fill_super(struct super_block *sb, void *data)
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
error_inode:
|
||||
ret = PTR_ERR(inode);
|
||||
inode = NULL;
|
||||
error:
|
||||
iput(inode);
|
||||
afs_put_volume(as->volume);
|
||||
kfree(as);
|
||||
|
||||
sb->s_fs_info = NULL;
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
@ -367,6 +354,7 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
|
||||
struct afs_volume *vol;
|
||||
struct key *key;
|
||||
char *new_opts = kstrdup(options, GFP_KERNEL);
|
||||
struct afs_super_info *as;
|
||||
int ret;
|
||||
|
||||
_enter(",,%s,%p", dev_name, options);
|
||||
@ -399,12 +387,22 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
|
||||
ret = PTR_ERR(vol);
|
||||
goto error;
|
||||
}
|
||||
params.volume = vol;
|
||||
|
||||
/* allocate a superblock info record */
|
||||
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
|
||||
if (!as) {
|
||||
ret = -ENOMEM;
|
||||
afs_put_volume(vol);
|
||||
goto error;
|
||||
}
|
||||
as->volume = vol;
|
||||
|
||||
/* allocate a deviceless superblock */
|
||||
sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms);
|
||||
sb = sget(fs_type, afs_test_super, afs_set_super, as);
|
||||
if (IS_ERR(sb)) {
|
||||
ret = PTR_ERR(sb);
|
||||
afs_put_volume(vol);
|
||||
kfree(as);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -422,16 +420,16 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
|
||||
} else {
|
||||
_debug("reuse");
|
||||
ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
|
||||
afs_put_volume(vol);
|
||||
kfree(as);
|
||||
}
|
||||
|
||||
afs_put_volume(params.volume);
|
||||
afs_put_cell(params.cell);
|
||||
kfree(new_opts);
|
||||
_leave(" = 0 [%p]", sb);
|
||||
return dget(sb->s_root);
|
||||
|
||||
error:
|
||||
afs_put_volume(params.volume);
|
||||
afs_put_cell(params.cell);
|
||||
key_put(params.key);
|
||||
kfree(new_opts);
|
||||
@ -439,18 +437,12 @@ error:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* finish the unmounting process on the superblock
|
||||
*/
|
||||
static void afs_put_super(struct super_block *sb)
|
||||
static void afs_kill_super(struct super_block *sb)
|
||||
{
|
||||
struct afs_super_info *as = sb->s_fs_info;
|
||||
|
||||
_enter("");
|
||||
|
||||
kill_anon_super(sb);
|
||||
afs_put_volume(as->volume);
|
||||
|
||||
_leave("");
|
||||
kfree(as);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -84,23 +84,21 @@ void afs_put_writeback(struct afs_writeback *wb)
|
||||
* partly or wholly fill a page that's under preparation for writing
|
||||
*/
|
||||
static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
|
||||
loff_t pos, unsigned len, struct page *page)
|
||||
loff_t pos, struct page *page)
|
||||
{
|
||||
loff_t i_size;
|
||||
unsigned eof;
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
_enter(",,%llu,%u", (unsigned long long)pos, len);
|
||||
|
||||
ASSERTCMP(len, <=, PAGE_CACHE_SIZE);
|
||||
_enter(",,%llu", (unsigned long long)pos);
|
||||
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (pos + len > i_size)
|
||||
eof = i_size;
|
||||
if (pos + PAGE_CACHE_SIZE > i_size)
|
||||
len = i_size - pos;
|
||||
else
|
||||
eof = PAGE_CACHE_SIZE;
|
||||
len = PAGE_CACHE_SIZE;
|
||||
|
||||
ret = afs_vnode_fetch_data(vnode, key, 0, eof, page);
|
||||
ret = afs_vnode_fetch_data(vnode, key, pos, len, page);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT) {
|
||||
_debug("got NOENT from server"
|
||||
@ -153,9 +151,8 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
|
||||
*pagep = page;
|
||||
/* page won't leak in error case: it eventually gets cleaned off LRU */
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
_debug("not up to date");
|
||||
ret = afs_fill_page(vnode, key, pos, len, page);
|
||||
if (!PageUptodate(page) && len != PAGE_CACHE_SIZE) {
|
||||
ret = afs_fill_page(vnode, key, index << PAGE_CACHE_SHIFT, page);
|
||||
if (ret < 0) {
|
||||
kfree(candidate);
|
||||
_leave(" = %d [prep]", ret);
|
||||
|
@ -1902,10 +1902,8 @@ int __block_write_begin(struct page *page, loff_t pos, unsigned len,
|
||||
if (!buffer_uptodate(*wait_bh))
|
||||
err = -EIO;
|
||||
}
|
||||
if (unlikely(err)) {
|
||||
if (unlikely(err))
|
||||
page_zero_new_buffers(page, from, to);
|
||||
ClearPageUptodate(page);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(__block_write_begin);
|
||||
|
28
fs/namei.c
28
fs/namei.c
@ -812,6 +812,11 @@ static int follow_automount(struct path *path, unsigned flags,
|
||||
if (!mnt) /* mount collision */
|
||||
return 0;
|
||||
|
||||
if (!*need_mntput) {
|
||||
/* lock_mount() may release path->mnt on error */
|
||||
mntget(path->mnt);
|
||||
*need_mntput = true;
|
||||
}
|
||||
err = finish_automount(mnt, path);
|
||||
|
||||
switch (err) {
|
||||
@ -819,12 +824,9 @@ static int follow_automount(struct path *path, unsigned flags,
|
||||
/* Someone else made a mount here whilst we were busy */
|
||||
return 0;
|
||||
case 0:
|
||||
dput(path->dentry);
|
||||
if (*need_mntput)
|
||||
mntput(path->mnt);
|
||||
path_put(path);
|
||||
path->mnt = mnt;
|
||||
path->dentry = dget(mnt->mnt_root);
|
||||
*need_mntput = true;
|
||||
return 0;
|
||||
default:
|
||||
return err;
|
||||
@ -844,9 +846,10 @@ static int follow_automount(struct path *path, unsigned flags,
|
||||
*/
|
||||
static int follow_managed(struct path *path, unsigned flags)
|
||||
{
|
||||
struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
|
||||
unsigned managed;
|
||||
bool need_mntput = false;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Given that we're not holding a lock here, we retain the value in a
|
||||
* local variable for each dentry as we look at it so that we don't see
|
||||
@ -861,7 +864,7 @@ static int follow_managed(struct path *path, unsigned flags)
|
||||
BUG_ON(!path->dentry->d_op->d_manage);
|
||||
ret = path->dentry->d_op->d_manage(path->dentry, false);
|
||||
if (ret < 0)
|
||||
return ret == -EISDIR ? 0 : ret;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Transit to a mounted filesystem. */
|
||||
@ -887,14 +890,19 @@ static int follow_managed(struct path *path, unsigned flags)
|
||||
if (managed & DCACHE_NEED_AUTOMOUNT) {
|
||||
ret = follow_automount(path, flags, &need_mntput);
|
||||
if (ret < 0)
|
||||
return ret == -EISDIR ? 0 : ret;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We didn't change the current path point */
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
if (need_mntput && path->mnt == mnt)
|
||||
mntput(path->mnt);
|
||||
if (ret == -EISDIR)
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int follow_down_one(struct path *path)
|
||||
@ -2713,8 +2721,10 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
||||
error = PTR_ERR(dentry);
|
||||
if (!IS_ERR(dentry)) {
|
||||
/* Why not before? Because we want correct error value */
|
||||
if (nd.last.name[nd.last.len])
|
||||
goto slashes;
|
||||
inode = dentry->d_inode;
|
||||
if (nd.last.name[nd.last.len] || !inode)
|
||||
if (!inode)
|
||||
goto slashes;
|
||||
ihold(inode);
|
||||
error = mnt_want_write(nd.path.mnt);
|
||||
|
@ -28,11 +28,12 @@ static int proc_test_super(struct super_block *sb, void *data)
|
||||
|
||||
static int proc_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
struct pid_namespace *ns;
|
||||
|
||||
ns = (struct pid_namespace *)data;
|
||||
sb->s_fs_info = get_pid_ns(ns);
|
||||
return set_anon_super(sb, NULL);
|
||||
int err = set_anon_super(sb, NULL);
|
||||
if (!err) {
|
||||
struct pid_namespace *ns = (struct pid_namespace *)data;
|
||||
sb->s_fs_info = get_pid_ns(ns);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *proc_mount(struct file_system_type *fs_type,
|
||||
|
@ -95,6 +95,14 @@ static int sysfs_set_super(struct super_block *sb, void *data)
|
||||
return error;
|
||||
}
|
||||
|
||||
static void free_sysfs_super_info(struct sysfs_super_info *info)
|
||||
{
|
||||
int type;
|
||||
for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
|
||||
kobj_ns_drop(type, info->ns[type]);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
static struct dentry *sysfs_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
{
|
||||
@ -108,11 +116,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
|
||||
info->ns[type] = kobj_ns_current(type);
|
||||
info->ns[type] = kobj_ns_grab_current(type);
|
||||
|
||||
sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
|
||||
if (IS_ERR(sb) || sb->s_fs_info != info)
|
||||
kfree(info);
|
||||
free_sysfs_super_info(info);
|
||||
if (IS_ERR(sb))
|
||||
return ERR_CAST(sb);
|
||||
if (!sb->s_root) {
|
||||
@ -131,12 +139,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
|
||||
static void sysfs_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct sysfs_super_info *info = sysfs_info(sb);
|
||||
|
||||
/* Remove the superblock from fs_supers/s_instances
|
||||
* so we can't find it, before freeing sysfs_super_info.
|
||||
*/
|
||||
kill_anon_super(sb);
|
||||
kfree(info);
|
||||
free_sysfs_super_info(info);
|
||||
}
|
||||
|
||||
static struct file_system_type sysfs_fs_type = {
|
||||
@ -145,28 +152,6 @@ static struct file_system_type sysfs_fs_type = {
|
||||
.kill_sb = sysfs_kill_sb,
|
||||
};
|
||||
|
||||
void sysfs_exit_ns(enum kobj_ns_type type, const void *ns)
|
||||
{
|
||||
struct super_block *sb;
|
||||
|
||||
mutex_lock(&sysfs_mutex);
|
||||
spin_lock(&sb_lock);
|
||||
list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
|
||||
struct sysfs_super_info *info = sysfs_info(sb);
|
||||
/*
|
||||
* If we see a superblock on the fs_supers/s_instances
|
||||
* list the unmount has not completed and sb->s_fs_info
|
||||
* points to a valid struct sysfs_super_info.
|
||||
*/
|
||||
/* Ignore superblocks with the wrong ns */
|
||||
if (info->ns[type] != ns)
|
||||
continue;
|
||||
info->ns[type] = NULL;
|
||||
}
|
||||
spin_unlock(&sb_lock);
|
||||
mutex_unlock(&sysfs_mutex);
|
||||
}
|
||||
|
||||
int __init sysfs_init(void)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
|
@ -136,7 +136,7 @@ struct sysfs_addrm_cxt {
|
||||
* instance).
|
||||
*/
|
||||
struct sysfs_super_info {
|
||||
const void *ns[KOBJ_NS_TYPES];
|
||||
void *ns[KOBJ_NS_TYPES];
|
||||
};
|
||||
#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
|
||||
extern struct sysfs_dirent sysfs_root;
|
||||
|
139
fs/ubifs/super.c
139
fs/ubifs/super.c
@ -1848,7 +1848,6 @@ static void ubifs_put_super(struct super_block *sb)
|
||||
bdi_destroy(&c->bdi);
|
||||
ubi_close_volume(c->ubi);
|
||||
mutex_unlock(&c->umount_mutex);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
|
||||
@ -1971,61 +1970,65 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
|
||||
{
|
||||
struct ubifs_info *c;
|
||||
|
||||
c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
|
||||
if (c) {
|
||||
spin_lock_init(&c->cnt_lock);
|
||||
spin_lock_init(&c->cs_lock);
|
||||
spin_lock_init(&c->buds_lock);
|
||||
spin_lock_init(&c->space_lock);
|
||||
spin_lock_init(&c->orphan_lock);
|
||||
init_rwsem(&c->commit_sem);
|
||||
mutex_init(&c->lp_mutex);
|
||||
mutex_init(&c->tnc_mutex);
|
||||
mutex_init(&c->log_mutex);
|
||||
mutex_init(&c->mst_mutex);
|
||||
mutex_init(&c->umount_mutex);
|
||||
mutex_init(&c->bu_mutex);
|
||||
mutex_init(&c->write_reserve_mutex);
|
||||
init_waitqueue_head(&c->cmt_wq);
|
||||
c->buds = RB_ROOT;
|
||||
c->old_idx = RB_ROOT;
|
||||
c->size_tree = RB_ROOT;
|
||||
c->orph_tree = RB_ROOT;
|
||||
INIT_LIST_HEAD(&c->infos_list);
|
||||
INIT_LIST_HEAD(&c->idx_gc);
|
||||
INIT_LIST_HEAD(&c->replay_list);
|
||||
INIT_LIST_HEAD(&c->replay_buds);
|
||||
INIT_LIST_HEAD(&c->uncat_list);
|
||||
INIT_LIST_HEAD(&c->empty_list);
|
||||
INIT_LIST_HEAD(&c->freeable_list);
|
||||
INIT_LIST_HEAD(&c->frdi_idx_list);
|
||||
INIT_LIST_HEAD(&c->unclean_leb_list);
|
||||
INIT_LIST_HEAD(&c->old_buds);
|
||||
INIT_LIST_HEAD(&c->orph_list);
|
||||
INIT_LIST_HEAD(&c->orph_new);
|
||||
c->no_chk_data_crc = 1;
|
||||
|
||||
c->highest_inum = UBIFS_FIRST_INO;
|
||||
c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
|
||||
|
||||
ubi_get_volume_info(ubi, &c->vi);
|
||||
ubi_get_device_info(c->vi.ubi_num, &c->di);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct ubi_volume_desc *ubi = sb->s_fs_info;
|
||||
struct ubifs_info *c;
|
||||
struct ubifs_info *c = sb->s_fs_info;
|
||||
struct inode *root;
|
||||
int err;
|
||||
|
||||
c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&c->cnt_lock);
|
||||
spin_lock_init(&c->cs_lock);
|
||||
spin_lock_init(&c->buds_lock);
|
||||
spin_lock_init(&c->space_lock);
|
||||
spin_lock_init(&c->orphan_lock);
|
||||
init_rwsem(&c->commit_sem);
|
||||
mutex_init(&c->lp_mutex);
|
||||
mutex_init(&c->tnc_mutex);
|
||||
mutex_init(&c->log_mutex);
|
||||
mutex_init(&c->mst_mutex);
|
||||
mutex_init(&c->umount_mutex);
|
||||
mutex_init(&c->bu_mutex);
|
||||
mutex_init(&c->write_reserve_mutex);
|
||||
init_waitqueue_head(&c->cmt_wq);
|
||||
c->buds = RB_ROOT;
|
||||
c->old_idx = RB_ROOT;
|
||||
c->size_tree = RB_ROOT;
|
||||
c->orph_tree = RB_ROOT;
|
||||
INIT_LIST_HEAD(&c->infos_list);
|
||||
INIT_LIST_HEAD(&c->idx_gc);
|
||||
INIT_LIST_HEAD(&c->replay_list);
|
||||
INIT_LIST_HEAD(&c->replay_buds);
|
||||
INIT_LIST_HEAD(&c->uncat_list);
|
||||
INIT_LIST_HEAD(&c->empty_list);
|
||||
INIT_LIST_HEAD(&c->freeable_list);
|
||||
INIT_LIST_HEAD(&c->frdi_idx_list);
|
||||
INIT_LIST_HEAD(&c->unclean_leb_list);
|
||||
INIT_LIST_HEAD(&c->old_buds);
|
||||
INIT_LIST_HEAD(&c->orph_list);
|
||||
INIT_LIST_HEAD(&c->orph_new);
|
||||
c->no_chk_data_crc = 1;
|
||||
|
||||
c->vfs_sb = sb;
|
||||
c->highest_inum = UBIFS_FIRST_INO;
|
||||
c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
|
||||
|
||||
ubi_get_volume_info(ubi, &c->vi);
|
||||
ubi_get_device_info(c->vi.ubi_num, &c->di);
|
||||
|
||||
/* Re-open the UBI device in read-write mode */
|
||||
c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE);
|
||||
if (IS_ERR(c->ubi)) {
|
||||
err = PTR_ERR(c->ubi);
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2091,24 +2094,29 @@ out_bdi:
|
||||
bdi_destroy(&c->bdi);
|
||||
out_close:
|
||||
ubi_close_volume(c->ubi);
|
||||
out_free:
|
||||
kfree(c);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sb_test(struct super_block *sb, void *data)
|
||||
{
|
||||
dev_t *dev = data;
|
||||
struct ubifs_info *c1 = data;
|
||||
struct ubifs_info *c = sb->s_fs_info;
|
||||
|
||||
return c->vi.cdev == *dev;
|
||||
return c->vi.cdev == c1->vi.cdev;
|
||||
}
|
||||
|
||||
static int sb_set(struct super_block *sb, void *data)
|
||||
{
|
||||
sb->s_fs_info = data;
|
||||
return set_anon_super(sb, NULL);
|
||||
}
|
||||
|
||||
static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
|
||||
const char *name, void *data)
|
||||
{
|
||||
struct ubi_volume_desc *ubi;
|
||||
struct ubi_volume_info vi;
|
||||
struct ubifs_info *c;
|
||||
struct super_block *sb;
|
||||
int err;
|
||||
|
||||
@ -2125,19 +2133,24 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
|
||||
name, (int)PTR_ERR(ubi));
|
||||
return ERR_CAST(ubi);
|
||||
}
|
||||
ubi_get_volume_info(ubi, &vi);
|
||||
|
||||
dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id);
|
||||
c = alloc_ubifs_info(ubi);
|
||||
if (!c) {
|
||||
err = -ENOMEM;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
sb = sget(fs_type, &sb_test, &set_anon_super, &vi.cdev);
|
||||
dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
|
||||
|
||||
sb = sget(fs_type, sb_test, sb_set, c);
|
||||
if (IS_ERR(sb)) {
|
||||
err = PTR_ERR(sb);
|
||||
goto out_close;
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
if (sb->s_root) {
|
||||
struct ubifs_info *c1 = sb->s_fs_info;
|
||||
|
||||
kfree(c);
|
||||
/* A new mount point for already mounted UBIFS */
|
||||
dbg_gen("this ubi volume is already mounted");
|
||||
if (!!(flags & MS_RDONLY) != c1->ro_mount) {
|
||||
@ -2146,11 +2159,6 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
|
||||
}
|
||||
} else {
|
||||
sb->s_flags = flags;
|
||||
/*
|
||||
* Pass 'ubi' to 'fill_super()' in sb->s_fs_info where it is
|
||||
* replaced by 'c'.
|
||||
*/
|
||||
sb->s_fs_info = ubi;
|
||||
err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
||||
if (err)
|
||||
goto out_deact;
|
||||
@ -2170,11 +2178,18 @@ out_close:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void kill_ubifs_super(struct super_block *s)
|
||||
{
|
||||
struct ubifs_info *c = s->s_fs_info;
|
||||
kill_anon_super(s);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static struct file_system_type ubifs_fs_type = {
|
||||
.name = "ubifs",
|
||||
.owner = THIS_MODULE,
|
||||
.mount = ubifs_mount,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = kill_ubifs_super,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -32,15 +32,17 @@ enum kobj_ns_type {
|
||||
|
||||
/*
|
||||
* Callbacks so sysfs can determine namespaces
|
||||
* @current_ns: return calling task's namespace
|
||||
* @grab_current_ns: return a new reference to calling task's namespace
|
||||
* @netlink_ns: return namespace to which a sock belongs (right?)
|
||||
* @initial_ns: return the initial namespace (i.e. init_net_ns)
|
||||
* @drop_ns: drops a reference to namespace
|
||||
*/
|
||||
struct kobj_ns_type_operations {
|
||||
enum kobj_ns_type type;
|
||||
const void *(*current_ns)(void);
|
||||
void *(*grab_current_ns)(void);
|
||||
const void *(*netlink_ns)(struct sock *sk);
|
||||
const void *(*initial_ns)(void);
|
||||
void (*drop_ns)(void *);
|
||||
};
|
||||
|
||||
int kobj_ns_type_register(const struct kobj_ns_type_operations *ops);
|
||||
@ -48,9 +50,9 @@ int kobj_ns_type_registered(enum kobj_ns_type type);
|
||||
const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent);
|
||||
const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj);
|
||||
|
||||
const void *kobj_ns_current(enum kobj_ns_type type);
|
||||
void *kobj_ns_grab_current(enum kobj_ns_type type);
|
||||
const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk);
|
||||
const void *kobj_ns_initial(enum kobj_ns_type type);
|
||||
void kobj_ns_exit(enum kobj_ns_type type, const void *ns);
|
||||
void kobj_ns_drop(enum kobj_ns_type type, void *ns);
|
||||
|
||||
#endif /* _LINUX_KOBJECT_NS_H */
|
||||
|
@ -177,9 +177,6 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
|
||||
struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd);
|
||||
void sysfs_put(struct sysfs_dirent *sd);
|
||||
|
||||
/* Called to clear a ns tag when it is no longer valid */
|
||||
void sysfs_exit_ns(enum kobj_ns_type type, const void *tag);
|
||||
|
||||
int __must_check sysfs_init(void);
|
||||
|
||||
#else /* CONFIG_SYSFS */
|
||||
@ -338,10 +335,6 @@ static inline void sysfs_put(struct sysfs_dirent *sd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void sysfs_exit_ns(int type, const void *tag)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int __must_check sysfs_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
@ -35,8 +35,11 @@ struct netns_ipvs;
|
||||
#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)
|
||||
|
||||
struct net {
|
||||
atomic_t passive; /* To decided when the network
|
||||
* namespace should be freed.
|
||||
*/
|
||||
atomic_t count; /* To decided when the network
|
||||
* namespace should be freed.
|
||||
* namespace should be shut down.
|
||||
*/
|
||||
#ifdef NETNS_REFCNT_DEBUG
|
||||
atomic_t use_count; /* To track references we
|
||||
@ -154,6 +157,9 @@ int net_eq(const struct net *net1, const struct net *net2)
|
||||
{
|
||||
return net1 == net2;
|
||||
}
|
||||
|
||||
extern void net_drop_ns(void *);
|
||||
|
||||
#else
|
||||
|
||||
static inline struct net *get_net(struct net *net)
|
||||
@ -175,6 +181,8 @@ int net_eq(const struct net *net1, const struct net *net2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define net_drop_ns NULL
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -948,14 +948,14 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)
|
||||
}
|
||||
|
||||
|
||||
const void *kobj_ns_current(enum kobj_ns_type type)
|
||||
void *kobj_ns_grab_current(enum kobj_ns_type type)
|
||||
{
|
||||
const void *ns = NULL;
|
||||
void *ns = NULL;
|
||||
|
||||
spin_lock(&kobj_ns_type_lock);
|
||||
if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
|
||||
kobj_ns_ops_tbl[type])
|
||||
ns = kobj_ns_ops_tbl[type]->current_ns();
|
||||
ns = kobj_ns_ops_tbl[type]->grab_current_ns();
|
||||
spin_unlock(&kobj_ns_type_lock);
|
||||
|
||||
return ns;
|
||||
@ -987,23 +987,15 @@ const void *kobj_ns_initial(enum kobj_ns_type type)
|
||||
return ns;
|
||||
}
|
||||
|
||||
/*
|
||||
* kobj_ns_exit - invalidate a namespace tag
|
||||
*
|
||||
* @type: the namespace type (i.e. KOBJ_NS_TYPE_NET)
|
||||
* @ns: the actual namespace being invalidated
|
||||
*
|
||||
* This is called when a tag is no longer valid. For instance,
|
||||
* when a network namespace exits, it uses this helper to
|
||||
* make sure no sb's sysfs_info points to the now-invalidated
|
||||
* netns.
|
||||
*/
|
||||
void kobj_ns_exit(enum kobj_ns_type type, const void *ns)
|
||||
void kobj_ns_drop(enum kobj_ns_type type, void *ns)
|
||||
{
|
||||
sysfs_exit_ns(type, ns);
|
||||
spin_lock(&kobj_ns_type_lock);
|
||||
if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
|
||||
kobj_ns_ops_tbl[type] && kobj_ns_ops_tbl[type]->drop_ns)
|
||||
kobj_ns_ops_tbl[type]->drop_ns(ns);
|
||||
spin_unlock(&kobj_ns_type_lock);
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(kobject_get);
|
||||
EXPORT_SYMBOL(kobject_put);
|
||||
EXPORT_SYMBOL(kobject_del);
|
||||
|
@ -1179,9 +1179,14 @@ static void remove_queue_kobjects(struct net_device *net)
|
||||
#endif
|
||||
}
|
||||
|
||||
static const void *net_current_ns(void)
|
||||
static void *net_grab_current_ns(void)
|
||||
{
|
||||
return current->nsproxy->net_ns;
|
||||
struct net *ns = current->nsproxy->net_ns;
|
||||
#ifdef CONFIG_NET_NS
|
||||
if (ns)
|
||||
atomic_inc(&ns->passive);
|
||||
#endif
|
||||
return ns;
|
||||
}
|
||||
|
||||
static const void *net_initial_ns(void)
|
||||
@ -1196,22 +1201,13 @@ static const void *net_netlink_ns(struct sock *sk)
|
||||
|
||||
struct kobj_ns_type_operations net_ns_type_operations = {
|
||||
.type = KOBJ_NS_TYPE_NET,
|
||||
.current_ns = net_current_ns,
|
||||
.grab_current_ns = net_grab_current_ns,
|
||||
.netlink_ns = net_netlink_ns,
|
||||
.initial_ns = net_initial_ns,
|
||||
.drop_ns = net_drop_ns,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(net_ns_type_operations);
|
||||
|
||||
static void net_kobj_ns_exit(struct net *net)
|
||||
{
|
||||
kobj_ns_exit(KOBJ_NS_TYPE_NET, net);
|
||||
}
|
||||
|
||||
static struct pernet_operations kobj_net_ops = {
|
||||
.exit = net_kobj_ns_exit,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
|
||||
{
|
||||
@ -1339,6 +1335,5 @@ EXPORT_SYMBOL(netdev_class_remove_file);
|
||||
int netdev_kobject_init(void)
|
||||
{
|
||||
kobj_ns_type_register(&net_ns_type_operations);
|
||||
register_pernet_subsys(&kobj_net_ops);
|
||||
return class_register(&net_class);
|
||||
}
|
||||
|
@ -128,6 +128,7 @@ static __net_init int setup_net(struct net *net)
|
||||
LIST_HEAD(net_exit_list);
|
||||
|
||||
atomic_set(&net->count, 1);
|
||||
atomic_set(&net->passive, 1);
|
||||
|
||||
#ifdef NETNS_REFCNT_DEBUG
|
||||
atomic_set(&net->use_count, 0);
|
||||
@ -210,6 +211,13 @@ static void net_free(struct net *net)
|
||||
kmem_cache_free(net_cachep, net);
|
||||
}
|
||||
|
||||
void net_drop_ns(void *p)
|
||||
{
|
||||
struct net *ns = p;
|
||||
if (ns && atomic_dec_and_test(&ns->passive))
|
||||
net_free(ns);
|
||||
}
|
||||
|
||||
struct net *copy_net_ns(unsigned long flags, struct net *old_net)
|
||||
{
|
||||
struct net *net;
|
||||
@ -230,7 +238,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
|
||||
}
|
||||
mutex_unlock(&net_mutex);
|
||||
if (rv < 0) {
|
||||
net_free(net);
|
||||
net_drop_ns(net);
|
||||
return ERR_PTR(rv);
|
||||
}
|
||||
return net;
|
||||
@ -286,7 +294,7 @@ static void cleanup_net(struct work_struct *work)
|
||||
/* Finally it is safe to free my network namespace structure */
|
||||
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
|
||||
list_del_init(&net->exit_list);
|
||||
net_free(net);
|
||||
net_drop_ns(net);
|
||||
}
|
||||
}
|
||||
static DECLARE_WORK(net_cleanup_work, cleanup_net);
|
||||
|
Loading…
Reference in New Issue
Block a user