* Added kerneld support: Jacques Gelinas and Bjorn Ekwall
* Added change_root: Werner Almesberger & Hans Lermen, Feb '96
* Added options to /proc/mounts:
- * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
+ * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
* Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
* Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
*/
#include <linux/idr.h>
#include <linux/kobject.h>
#include <linux/mutex.h>
+#include <linux/file.h>
#include <asm/uaccess.h>
+#include "internal.h"
-void get_filesystem(struct file_system_type *fs);
-void put_filesystem(struct file_system_type *fs);
-struct file_system_type *get_fs_type(const char *name);
-
LIST_HEAD(super_blocks);
DEFINE_SPINLOCK(sb_lock);
}
INIT_LIST_HEAD(&s->s_dirty);
INIT_LIST_HEAD(&s->s_io);
+ INIT_LIST_HEAD(&s->s_more_io);
INIT_LIST_HEAD(&s->s_files);
INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon);
INIT_LIST_HEAD(&s->s_inodes);
+ INIT_LIST_HEAD(&s->s_dentry_lru);
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
lockdep_set_class(&s->s_umount, &type->s_umount_key);
static inline void destroy_super(struct super_block *s)
{
security_sb_free(s);
+ kfree(s->s_subtype);
+ kfree(s->s_options);
kfree(s);
}
* Drop a superblock's refcount. Returns non-zero if the superblock was
* destroyed. The caller must hold sb_lock.
*/
-int __put_super(struct super_block *sb)
+static int __put_super(struct super_block *sb)
{
int ret = 0;
if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
s->s_count -= S_BIAS-1;
spin_unlock(&sb_lock);
- DQUOT_OFF(s);
+ DQUOT_OFF(s, 0);
down_write(&s->s_umount);
fs->kill_sb(s);
put_filesystem(fs);
void *data)
{
struct super_block *s = NULL;
- struct list_head *p;
+ struct super_block *old;
int err;
retry:
spin_lock(&sb_lock);
- if (test) list_for_each(p, &type->fs_supers) {
- struct super_block *old;
- old = list_entry(p, struct super_block, s_instances);
- if (!test(old, data))
- continue;
- if (!grab_super(old))
- goto retry;
- if (s)
- destroy_super(s);
- return old;
+ if (test) {
+ list_for_each_entry(old, &type->fs_supers, s_instances) {
+ if (!test(old, data))
+ continue;
+ if (!grab_super(old))
+ goto retry;
+ if (s)
+ destroy_super(s);
+ return old;
+ }
}
if (!s) {
spin_unlock(&sb_lock);
}
/*
- * Call the ->sync_fs super_op against all filesytems which are r/w and
+ * Call the ->sync_fs super_op against all filesystems which are r/w and
* which implement it.
*
* This operation is careful to avoid the livelock which could easily happen
* is used only here. We set it against all filesystems and then clear it as
* we sync them. So redirtied filesystems are skipped.
*
- * But if process A is currently running sync_filesytems and then process B
+ * But if process A is currently running sync_filesystems and then process B
* calls sync_filesystems as well, process B will set all the s_need_sync_fs
* flags again, which will cause process A to resync everything. Fix that with
* a local mutex.
}
/**
- * mark_files_ro
+ * mark_files_ro - mark all files read-only
* @sb: superblock in question
*
- * All files are marked read/only. We don't care about pending
- * delete files so this should be used in 'force' mode only
+ * All files are marked read-only. We don't care about pending
+ * delete files so this should be used in 'force' mode only.
*/
static void mark_files_ro(struct super_block *sb)
{
struct file *f;
+retry:
file_list_lock();
list_for_each_entry(f, &sb->s_files, f_u.fu_list) {
- if (S_ISREG(f->f_path.dentry->d_inode->i_mode) && file_count(f))
- f->f_mode &= ~FMODE_WRITE;
+ struct vfsmount *mnt;
+ if (!S_ISREG(f->f_path.dentry->d_inode->i_mode))
+ continue;
+ if (!file_count(f))
+ continue;
+ if (!(f->f_mode & FMODE_WRITE))
+ continue;
+ f->f_mode &= ~FMODE_WRITE;
+ if (file_check_writeable(f) != 0)
+ continue;
+ file_release_write(f);
+ mnt = mntget(f->f_path.mnt);
+ file_list_unlock();
+ /*
+ * This can sleep, so we can't hold
+ * the file_list_lock() spinlock.
+ */
+ mnt_drop_write(mnt);
+ mntput(mnt);
+ goto retry;
}
file_list_unlock();
}
int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
{
int retval;
+ int remount_rw;
#ifdef CONFIG_BLOCK
if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
mark_files_ro(sb);
else if (!fs_may_remount_ro(sb))
return -EBUSY;
+ retval = DQUOT_OFF(sb, 1);
+ if (retval < 0 && retval != -ENOSYS)
+ return -EBUSY;
}
+ remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
if (sb->s_op->remount_fs) {
lock_super(sb);
return retval;
}
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
+ if (remount_rw)
+ DQUOT_ON_REMOUNT(sb);
return 0;
}
* filesystems which don't use real block-devices. -- jrs
*/
-static struct idr unnamed_dev_idr;
+static DEFINE_IDA(unnamed_dev_ida);
static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */
int set_anon_super(struct super_block *s, void *data)
int error;
retry:
- if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
+ if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)
return -ENOMEM;
spin_lock(&unnamed_dev_lock);
- error = idr_get_new(&unnamed_dev_idr, NULL, &dev);
+ error = ida_get_new(&unnamed_dev_ida, &dev);
spin_unlock(&unnamed_dev_lock);
if (error == -EAGAIN)
/* We raced and lost with another CPU. */
if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
spin_lock(&unnamed_dev_lock);
- idr_remove(&unnamed_dev_idr, dev);
+ ida_remove(&unnamed_dev_ida, dev);
spin_unlock(&unnamed_dev_lock);
return -EMFILE;
}
generic_shutdown_super(sb);
spin_lock(&unnamed_dev_lock);
- idr_remove(&unnamed_dev_idr, slot);
+ ida_remove(&unnamed_dev_ida, slot);
spin_unlock(&unnamed_dev_lock);
}
EXPORT_SYMBOL(kill_anon_super);
-void __init unnamed_dev_init(void)
-{
- idr_init(&unnamed_dev_idr);
-}
-
void kill_litter_super(struct super_block *sb)
{
if (sb->s_root)
return (void *)s->s_bdev == data;
}
-static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
-{
- if (bdev->bd_disk) {
- if (bdev->bd_part)
- kobject_uevent(&bdev->bd_part->kobj, action);
- else
- kobject_uevent(&bdev->bd_disk->kobj, action);
- }
-}
-
int get_sb_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int),
{
struct block_device *bdev;
struct super_block *s;
+ fmode_t mode = FMODE_READ;
int error = 0;
- bdev = open_bdev_excl(dev_name, flags, fs_type);
+ if (!(flags & MS_RDONLY))
+ mode |= FMODE_WRITE;
+
+ bdev = open_bdev_exclusive(dev_name, mode, fs_type);
if (IS_ERR(bdev))
return PTR_ERR(bdev);
goto error_bdev;
}
- close_bdev_excl(bdev);
+ close_bdev_exclusive(bdev, mode);
} else {
char b[BDEVNAME_SIZE];
s->s_flags = flags;
+ s->s_mode = mode;
strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
sb_set_blocksize(s, block_size(bdev));
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
}
s->s_flags |= MS_ACTIVE;
- bdev_uevent(bdev, KOBJ_MOUNT);
}
return simple_set_mnt(mnt, s);
error_s:
error = PTR_ERR(s);
error_bdev:
- close_bdev_excl(bdev);
+ close_bdev_exclusive(bdev, mode);
error:
return error;
}
void kill_block_super(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
+ fmode_t mode = sb->s_mode;
- bdev_uevent(bdev, KOBJ_UMOUNT);
generic_shutdown_super(sb);
sync_blockdev(bdev);
- close_bdev_excl(bdev);
+ close_bdev_exclusive(bdev, mode);
}
EXPORT_SYMBOL(kill_block_super);
if (!mnt)
goto out;
- if (data) {
+ if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata();
if (!secdata)
goto out_mnt;
- error = security_sb_copy_data(type, data, secdata);
+ error = security_sb_copy_data(data, secdata);
if (error)
goto out_free_secdata;
}
error = type->get_sb(type, flags, name, data, mnt);
if (error < 0)
goto out_free_secdata;
+ BUG_ON(!mnt->mnt_sb);
- error = security_sb_kern_mount(mnt->mnt_sb, secdata);
+ error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
if (error)
goto out_sb;
EXPORT_SYMBOL_GPL(vfs_kern_mount);
+static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
+{
+ int err;
+ const char *subtype = strchr(fstype, '.');
+ if (subtype) {
+ subtype++;
+ err = -EINVAL;
+ if (!subtype[0])
+ goto err;
+ } else
+ subtype = "";
+
+ mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
+ err = -ENOMEM;
+ if (!mnt->mnt_sb->s_subtype)
+ goto err;
+ return mnt;
+
+ err:
+ mntput(mnt);
+ return ERR_PTR(err);
+}
+
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data);
+ if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
+ !mnt->mnt_sb->s_subtype)
+ mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
return mnt;
}
+EXPORT_SYMBOL_GPL(do_kern_mount);
-struct vfsmount *kern_mount(struct file_system_type *type)
+struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
- return vfs_kern_mount(type, 0, type->name, NULL);
+ return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}
-EXPORT_SYMBOL(kern_mount);
+EXPORT_SYMBOL_GPL(kern_mount_data);