Commit 054cfaac authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'mnt_devname' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6

* 'mnt_devname' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
  vfs: bury ->get_sb()
  nfs: switch NFS from ->get_sb() to ->mount()
  nfs: stop mangling ->mnt_devname on NFS
  vfs: new superblock methods to override /proc/*/mount{s,info}
  nfs: nfs_do_{ref,sub}mount() superblock argument is redundant
  nfs: make nfs_path() work without vfsmount
  nfs: store devname at disconnected NFS roots
  nfs: propagate devname to nfs{,4}_get_root()
parents dc113c1f 1a102ff9
...@@ -166,13 +166,11 @@ prototypes: ...@@ -166,13 +166,11 @@ prototypes:
void (*kill_sb) (struct super_block *); void (*kill_sb) (struct super_block *);
locking rules: locking rules:
may block may block
get_sb yes
mount yes mount yes
kill_sb yes kill_sb yes
->get_sb() returns error or 0 with locked superblock attached to the vfsmount ->mount() returns ERR_PTR or the root dentry; its superblock should be locked
(exclusive on ->s_umount). on return.
->mount() returns ERR_PTR or the root dentry.
->kill_sb() takes a write-locked superblock, does all shutdown work on it, ->kill_sb() takes a write-locked superblock, does all shutdown work on it,
unlocks and drops the reference. unlocks and drops the reference.
......
...@@ -394,3 +394,10 @@ file) you must return -EOPNOTSUPP if FALLOC_FL_PUNCH_HOLE is set in mode. ...@@ -394,3 +394,10 @@ file) you must return -EOPNOTSUPP if FALLOC_FL_PUNCH_HOLE is set in mode.
Currently you can only have FALLOC_FL_PUNCH_HOLE with FALLOC_FL_KEEP_SIZE set, Currently you can only have FALLOC_FL_PUNCH_HOLE with FALLOC_FL_KEEP_SIZE set,
so the i_size should not change when hole punching, even when puching the end of so the i_size should not change when hole punching, even when puching the end of
a file off. a file off.
--
[mandatory]
->get_sb() is gone. Switch to use of ->mount(). Typically it's just
a matter of switching from calling get_sb_... to mount_... and changing the
function type. If you were doing it manually, just switch from setting ->mnt_root
to some pointer to returning that pointer. On errors return ERR_PTR(...).
...@@ -95,10 +95,11 @@ functions: ...@@ -95,10 +95,11 @@ functions:
extern int unregister_filesystem(struct file_system_type *); extern int unregister_filesystem(struct file_system_type *);
The passed struct file_system_type describes your filesystem. When a The passed struct file_system_type describes your filesystem. When a
request is made to mount a device onto a directory in your filespace, request is made to mount a filesystem onto a directory in your namespace,
the VFS will call the appropriate get_sb() method for the specific the VFS will call the appropriate mount() method for the specific
filesystem. The dentry for the mount point will then be updated to filesystem. New vfsmount refering to the tree returned by ->mount()
point to the root inode for the new filesystem. will be attached to the mountpoint, so that when pathname resolution
reaches the mountpoint it will jump into the root of that vfsmount.
You can see all filesystems that are registered to the kernel in the You can see all filesystems that are registered to the kernel in the
file /proc/filesystems. file /proc/filesystems.
...@@ -107,14 +108,14 @@ file /proc/filesystems. ...@@ -107,14 +108,14 @@ file /proc/filesystems.
struct file_system_type struct file_system_type
----------------------- -----------------------
This describes the filesystem. As of kernel 2.6.22, the following This describes the filesystem. As of kernel 2.6.39, the following
members are defined: members are defined:
struct file_system_type { struct file_system_type {
const char *name; const char *name;
int fs_flags; int fs_flags;
int (*get_sb) (struct file_system_type *, int, struct dentry (*mount) (struct file_system_type *, int,
const char *, void *, struct vfsmount *); const char *, void *);
void (*kill_sb) (struct super_block *); void (*kill_sb) (struct super_block *);
struct module *owner; struct module *owner;
struct file_system_type * next; struct file_system_type * next;
...@@ -128,11 +129,11 @@ struct file_system_type { ...@@ -128,11 +129,11 @@ struct file_system_type {
fs_flags: various flags (i.e. FS_REQUIRES_DEV, FS_NO_DCACHE, etc.) fs_flags: various flags (i.e. FS_REQUIRES_DEV, FS_NO_DCACHE, etc.)
get_sb: the method to call when a new instance of this mount: the method to call when a new instance of this
filesystem should be mounted filesystem should be mounted
kill_sb: the method to call when an instance of this filesystem kill_sb: the method to call when an instance of this filesystem
should be unmounted should be shut down
owner: for internal VFS use: you should initialize this to THIS_MODULE in owner: for internal VFS use: you should initialize this to THIS_MODULE in
most cases. most cases.
...@@ -141,7 +142,7 @@ struct file_system_type { ...@@ -141,7 +142,7 @@ struct file_system_type {
s_lock_key, s_umount_key: lockdep-specific s_lock_key, s_umount_key: lockdep-specific
The get_sb() method has the following arguments: The mount() method has the following arguments:
struct file_system_type *fs_type: describes the filesystem, partly initialized struct file_system_type *fs_type: describes the filesystem, partly initialized
by the specific filesystem code by the specific filesystem code
...@@ -153,32 +154,39 @@ The get_sb() method has the following arguments: ...@@ -153,32 +154,39 @@ The get_sb() method has the following arguments:
void *data: arbitrary mount options, usually comes as an ASCII void *data: arbitrary mount options, usually comes as an ASCII
string (see "Mount Options" section) string (see "Mount Options" section)
struct vfsmount *mnt: a vfs-internal representation of a mount point The mount() method must return the root dentry of the tree requested by
caller. An active reference to its superblock must be grabbed and the
superblock must be locked. On failure it should return ERR_PTR(error).
The get_sb() method must determine if the block device specified The arguments match those of mount(2) and their interpretation
in the dev_name and fs_type contains a filesystem of the type the method depends on filesystem type. E.g. for block filesystems, dev_name is
supports. If it succeeds in opening the named block device, it initializes a interpreted as block device name, that device is opened and if it
struct super_block descriptor for the filesystem contained by the block device. contains a suitable filesystem image the method creates and initializes
On failure it returns an error. struct super_block accordingly, returning its root dentry to caller.
->mount() may choose to return a subtree of existing filesystem - it
doesn't have to create a new one. The main result from the caller's
point of view is a reference to dentry at the root of (sub)tree to
be attached; creation of new superblock is a common side effect.
The most interesting member of the superblock structure that the The most interesting member of the superblock structure that the
get_sb() method fills in is the "s_op" field. This is a pointer to mount() method fills in is the "s_op" field. This is a pointer to
a "struct super_operations" which describes the next level of the a "struct super_operations" which describes the next level of the
filesystem implementation. filesystem implementation.
Usually, a filesystem uses one of the generic get_sb() implementations Usually, a filesystem uses one of the generic mount() implementations
and provides a fill_super() method instead. The generic methods are: and provides a fill_super() callback instead. The generic variants are:
get_sb_bdev: mount a filesystem residing on a block device mount_bdev: mount a filesystem residing on a block device
get_sb_nodev: mount a filesystem that is not backed by a device mount_nodev: mount a filesystem that is not backed by a device
get_sb_single: mount a filesystem which shares the instance between mount_single: mount a filesystem which shares the instance between
all mounts all mounts
A fill_super() method implementation has the following arguments: A fill_super() callback implementation has the following arguments:
struct super_block *sb: the superblock structure. The method fill_super() struct super_block *sb: the superblock structure. The callback
must initialize this properly. must initialize this properly.
void *data: arbitrary mount options, usually comes as an ASCII void *data: arbitrary mount options, usually comes as an ASCII
......
...@@ -978,7 +978,13 @@ static int show_vfsmnt(struct seq_file *m, void *v) ...@@ -978,7 +978,13 @@ static int show_vfsmnt(struct seq_file *m, void *v)
int err = 0; int err = 0;
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
if (mnt->mnt_sb->s_op->show_devname) {
err = mnt->mnt_sb->s_op->show_devname(m, mnt);
if (err)
goto out;
} else {
mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
}
seq_putc(m, ' '); seq_putc(m, ' ');
seq_path(m, &mnt_path, " \t\n\\"); seq_path(m, &mnt_path, " \t\n\\");
seq_putc(m, ' '); seq_putc(m, ' ');
...@@ -1025,7 +1031,12 @@ static int show_mountinfo(struct seq_file *m, void *v) ...@@ -1025,7 +1031,12 @@ static int show_mountinfo(struct seq_file *m, void *v)
seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id,
MAJOR(sb->s_dev), MINOR(sb->s_dev)); MAJOR(sb->s_dev), MINOR(sb->s_dev));
if (sb->s_op->show_path)
err = sb->s_op->show_path(m, mnt);
else
seq_dentry(m, mnt->mnt_root, " \t\n\\"); seq_dentry(m, mnt->mnt_root, " \t\n\\");
if (err)
goto out;
seq_putc(m, ' '); seq_putc(m, ' ');
seq_path_root(m, &mnt_path, &root, " \t\n\\"); seq_path_root(m, &mnt_path, &root, " \t\n\\");
if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) {
...@@ -1060,7 +1071,12 @@ static int show_mountinfo(struct seq_file *m, void *v) ...@@ -1060,7 +1071,12 @@ static int show_mountinfo(struct seq_file *m, void *v)
seq_puts(m, " - "); seq_puts(m, " - ");
show_type(m, sb); show_type(m, sb);
seq_putc(m, ' '); seq_putc(m, ' ');
if (sb->s_op->show_devname)
err = sb->s_op->show_devname(m, mnt);
else
mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
if (err)
goto out;
seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw");
err = show_sb_opts(m, sb); err = show_sb_opts(m, sb);
if (err) if (err)
...@@ -1086,11 +1102,15 @@ static int show_vfsstat(struct seq_file *m, void *v) ...@@ -1086,11 +1102,15 @@ static int show_vfsstat(struct seq_file *m, void *v)
int err = 0; int err = 0;
/* device */ /* device */
if (mnt->mnt_sb->s_op->show_devname) {
err = mnt->mnt_sb->s_op->show_devname(m, mnt);
} else {
if (mnt->mnt_devname) { if (mnt->mnt_devname) {
seq_puts(m, "device "); seq_puts(m, "device ");
mangle(m, mnt->mnt_devname); mangle(m, mnt->mnt_devname);
} else } else
seq_puts(m, "no device"); seq_puts(m, "no device");
}
/* mount point */ /* mount point */
seq_puts(m, " mounted on "); seq_puts(m, " mounted on ");
...@@ -1104,6 +1124,7 @@ static int show_vfsstat(struct seq_file *m, void *v) ...@@ -1104,6 +1124,7 @@ static int show_vfsstat(struct seq_file *m, void *v)
/* optional statistics */ /* optional statistics */
if (mnt->mnt_sb->s_op->show_stats) { if (mnt->mnt_sb->s_op->show_stats) {
seq_putc(m, ' '); seq_putc(m, ' ');
if (!err)
err = mnt->mnt_sb->s_op->show_stats(m, mnt); err = mnt->mnt_sb->s_op->show_stats(m, mnt);
} }
......
...@@ -1169,11 +1169,23 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) ...@@ -1169,11 +1169,23 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
iput(inode); iput(inode);
} }
static void nfs_d_release(struct dentry *dentry)
{
/* free cached devname value, if it survived that far */
if (unlikely(dentry->d_fsdata)) {
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
WARN_ON(1);
else
kfree(dentry->d_fsdata);
}
}
const struct dentry_operations nfs_dentry_operations = { const struct dentry_operations nfs_dentry_operations = {
.d_revalidate = nfs_lookup_revalidate, .d_revalidate = nfs_lookup_revalidate,
.d_delete = nfs_dentry_delete, .d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput, .d_iput = nfs_dentry_iput,
.d_automount = nfs_d_automount, .d_automount = nfs_d_automount,
.d_release = nfs_d_release,
}; };
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
...@@ -1248,6 +1260,7 @@ const struct dentry_operations nfs4_dentry_operations = { ...@@ -1248,6 +1260,7 @@ const struct dentry_operations nfs4_dentry_operations = {
.d_delete = nfs_dentry_delete, .d_delete = nfs_dentry_delete,
.d_iput = nfs_dentry_iput, .d_iput = nfs_dentry_iput,
.d_automount = nfs_d_automount, .d_automount = nfs_d_automount,
.d_release = nfs_d_release,
}; };
/* /*
......
...@@ -75,18 +75,25 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i ...@@ -75,18 +75,25 @@ static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *i
/* /*
* get an NFS2/NFS3 root dentry from the root filehandle * get an NFS2/NFS3 root dentry from the root filehandle
*/ */
struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
const char *devname)
{ {
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
struct dentry *ret; struct dentry *ret;
struct inode *inode; struct inode *inode;
void *name = kstrdup(devname, GFP_KERNEL);
int error; int error;
if (!name)
return ERR_PTR(-ENOMEM);
/* get the actual root for this mount */ /* get the actual root for this mount */
fsinfo.fattr = nfs_alloc_fattr(); fsinfo.fattr = nfs_alloc_fattr();
if (fsinfo.fattr == NULL) if (fsinfo.fattr == NULL) {
kfree(name);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
if (error < 0) { if (error < 0) {
...@@ -119,7 +126,15 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) ...@@ -119,7 +126,15 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
} }
security_d_instantiate(ret, inode); security_d_instantiate(ret, inode);
spin_lock(&ret->d_lock);
if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
ret->d_fsdata = name;
name = NULL;
}
spin_unlock(&ret->d_lock);
out: out:
if (name)
kfree(name);
nfs_free_fattr(fsinfo.fattr); nfs_free_fattr(fsinfo.fattr);
return ret; return ret;
} }
...@@ -169,27 +184,35 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) ...@@ -169,27 +184,35 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
/* /*
* get an NFS4 root dentry from the root filehandle * get an NFS4 root dentry from the root filehandle
*/ */
struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
const char *devname)
{ {
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
struct nfs_fattr *fattr = NULL; struct nfs_fattr *fattr = NULL;
struct dentry *ret; struct dentry *ret;
struct inode *inode; struct inode *inode;
void *name = kstrdup(devname, GFP_KERNEL);
int error; int error;
dprintk("--> nfs4_get_root()\n"); dprintk("--> nfs4_get_root()\n");
if (!name)
return ERR_PTR(-ENOMEM);
/* get the info about the server and filesystem */ /* get the info about the server and filesystem */
error = nfs4_server_capabilities(server, mntfh); error = nfs4_server_capabilities(server, mntfh);
if (error < 0) { if (error < 0) {
dprintk("nfs_get_root: getcaps error = %d\n", dprintk("nfs_get_root: getcaps error = %d\n",
-error); -error);
kfree(name);
return ERR_PTR(error); return ERR_PTR(error);
} }
fattr = nfs_alloc_fattr(); fattr = nfs_alloc_fattr();
if (fattr == NULL) if (fattr == NULL) {
return ERR_PTR(-ENOMEM);; kfree(name);
return ERR_PTR(-ENOMEM);
}
/* get the actual root for this mount */ /* get the actual root for this mount */
error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr);
...@@ -223,8 +246,15 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) ...@@ -223,8 +246,15 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
} }
security_d_instantiate(ret, inode); security_d_instantiate(ret, inode);
spin_lock(&ret->d_lock);
if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
ret->d_fsdata = name;
name = NULL;
}
spin_unlock(&ret->d_lock);
out: out:
if (name)
kfree(name);
nfs_free_fattr(fattr); nfs_free_fattr(fattr);
dprintk("<-- nfs4_get_root()\n"); dprintk("<-- nfs4_get_root()\n");
return ret; return ret;
......
...@@ -163,10 +163,10 @@ static inline void nfs_fs_proc_exit(void) ...@@ -163,10 +163,10 @@ static inline void nfs_fs_proc_exit(void)
/* nfs4namespace.c */ /* nfs4namespace.c */
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry); extern struct vfsmount *nfs_do_refmount(struct dentry *dentry);
#else #else
static inline static inline
struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) struct vfsmount *nfs_do_refmount(struct dentry *dentry)
{ {
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
...@@ -247,16 +247,16 @@ extern void nfs_sb_active(struct super_block *sb); ...@@ -247,16 +247,16 @@ extern void nfs_sb_active(struct super_block *sb);
extern void nfs_sb_deactive(struct super_block *sb); extern void nfs_sb_deactive(struct super_block *sb);
/* namespace.c */ /* namespace.c */
extern char *nfs_path(const char *base, extern char *nfs_path(char **p, struct dentry *dentry,
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen); char *buffer, ssize_t buflen);
extern struct vfsmount *nfs_d_automount(struct path *path); extern struct vfsmount *nfs_d_automount(struct path *path);
/* getroot.c */ /* getroot.c */
extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *); extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
const char *);
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *); extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *,
const char *);
extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh); extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh);
#endif #endif
...@@ -288,12 +288,11 @@ extern int _nfs4_call_sync_session(struct nfs_server *server, ...@@ -288,12 +288,11 @@ extern int _nfs4_call_sync_session(struct nfs_server *server,
/* /*
* Determine the device name as a string * Determine the device name as a string
*/ */
static inline char *nfs_devname(const struct vfsmount *mnt_parent, static inline char *nfs_devname(struct dentry *dentry,
const struct dentry *dentry,
char *buffer, ssize_t buflen) char *buffer, ssize_t buflen)
{ {
return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root, char *dummy;
dentry, buffer, buflen); return nfs_path(&dummy, dentry, buffer, buflen);
} }
/* /*
......
...@@ -25,33 +25,30 @@ static LIST_HEAD(nfs_automount_list); ...@@ -25,33 +25,30 @@ static LIST_HEAD(nfs_automount_list);
static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts);
int nfs_mountpoint_expiry_timeout = 500 * HZ; int nfs_mountpoint_expiry_timeout = 500 * HZ;
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_do_submount(struct dentry *dentry,
const struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fh *fh,
struct nfs_fattr *fattr); struct nfs_fattr *fattr);
/* /*
* nfs_path - reconstruct the path given an arbitrary dentry * nfs_path - reconstruct the path given an arbitrary dentry
* @base - arbitrary string to prepend to the path * @base - used to return pointer to the end of devname part of path
* @droot - pointer to root dentry for mountpoint
* @dentry - pointer to dentry * @dentry - pointer to dentry
* @buffer - result buffer * @buffer - result buffer
* @buflen - length of buffer * @buflen - length of buffer
* *
* Helper function for constructing the path from the * Helper function for constructing the server pathname
* root dentry to an arbitrary hashed dentry. * by arbitrary hashed dentry.
* *
* This is mainly for use in figuring out the path on the * This is mainly for use in figuring out the path on the
* server side when automounting on top of an existing partition. * server side when automounting on top of an existing partition
* and in generating /proc/mounts and friends.
*/ */
char *nfs_path(const char *base, char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen)
const struct dentry *droot,
const struct dentry *dentry,
char *buffer, ssize_t buflen)
{ {
char *end; char *end;
int namelen; int namelen;
unsigned seq; unsigned seq;
const char *base;
rename_retry: rename_retry:
end = buffer+buflen; end = buffer+buflen;
...@@ -60,7 +57,10 @@ char *nfs_path(const char *base, ...@@ -60,7 +57,10 @@ char *nfs_path(const char *base,
seq = read_seqbegin(&rename_lock); seq = read_seqbegin(&rename_lock);
rcu_read_lock(); rcu_read_lock();
while (!IS_ROOT(dentry) && dentry != droot) { while (1) {
spin_lock(&dentry->d_lock);
if (IS_ROOT(dentry))
break;
namelen = dentry->d_name.len; namelen = dentry->d_name.len;
buflen -= namelen + 1; buflen -= namelen + 1;
if (buflen < 0) if (buflen < 0)
...@@ -68,27 +68,47 @@ char *nfs_path(const char *base, ...@@ -68,27 +68,47 @@ char *nfs_path(const char *base,
end -= namelen; end -= namelen;
memcpy(end, dentry->d_name.name, namelen); memcpy(end, dentry->d_name.name, namelen);
*--end = '/'; *--end = '/';
spin_unlock(&dentry->d_lock);
dentry = dentry->d_parent; dentry = dentry->d_parent;
} }
if (read_seqretry(&rename_lock, seq)) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock(); rcu_read_unlock();
if (read_seqretry(&rename_lock, seq))
goto rename_retry; goto rename_retry;
}
if (*end != '/') { if (*end != '/') {
if (--buflen < 0) if (--buflen < 0) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
goto Elong; goto Elong;
}
*--end = '/'; *--end = '/';
} }
*p = end;
base = dentry->d_fsdata;
if (!base) {
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
WARN_ON(1);
return end;
}
namelen = strlen(base); namelen = strlen(base);
/* Strip off excess slashes in base string */ /* Strip off excess slashes in base string */
while (namelen > 0 && base[namelen - 1] == '/') while (namelen > 0 && base[namelen - 1] == '/')
namelen--; namelen--;
buflen -= namelen; buflen -= namelen;
if (buflen < 0) if (buflen < 0) {
spin_lock(&dentry->d_lock);
rcu_read_unlock();
goto Elong; goto Elong;
}
end -= namelen; end -= namelen;
memcpy(end, base, namelen); memcpy(end, base, namelen);
spin_unlock(&dentry->d_lock);
rcu_read_unlock();
return end; return end;
Elong_unlock: Elong_unlock:
spin_lock(&dentry->d_lock);
rcu_read_unlock(); rcu_read_unlock();
if (read_seqretry(&rename_lock, seq)) if (read_seqretry(&rename_lock, seq))
goto rename_retry; goto rename_retry;
...@@ -143,9 +163,9 @@ struct vfsmount *nfs_d_automount(struct path *path) ...@@ -143,9 +163,9 @@ struct vfsmount *nfs_d_automount(struct path *path)
} }
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
mnt = nfs_do_refmount(path->mnt, path->dentry); mnt = nfs_do_refmount(path->dentry);
else else
mnt = nfs_do_submount(path->mnt, path->dentry, fh, fattr); mnt = nfs_do_submount(path->dentry, fh, fattr);
if (IS_ERR(mnt)) if (IS_ERR(mnt))
goto out; goto out;
...@@ -209,19 +229,17 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, ...@@ -209,19 +229,17 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
/** /**
* nfs_do_submount - set up mountpoint when crossing a filesystem boundary * nfs_do_submount - set up mountpoint when crossing a filesystem boundary
* @mnt_parent - mountpoint of parent directory
* @dentry - parent directory * @dentry - parent directory
* @fh - filehandle for new root dentry * @fh - filehandle for new root dentry
* @fattr - attributes for new root inode * @fattr - attributes for new root inode
* *
*/ */
static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_do_submount(struct dentry *dentry,
const struct dentry *dentry,
struct nfs_fh *fh, struct nfs_fh *fh,
struct nfs_fattr *fattr) struct nfs_fattr *fattr)
{ {
struct nfs_clone_mount mountdata = { struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb, .sb = dentry->d_sb,
.dentry = dentry, .dentry = dentry,
.fh = fh, .fh = fh,
.fattr = fattr, .fattr = fattr,
...@@ -237,11 +255,11 @@ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, ...@@ -237,11 +255,11 @@ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent,
dentry->d_name.name); dentry->d_name.name);
if (page == NULL) if (page == NULL)
goto out; goto out;
devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); devname = nfs_devname(dentry, page, PAGE_SIZE);
mnt = (struct vfsmount *)devname; mnt = (struct vfsmount *)devname;
if (IS_ERR(devname)) if (IS_ERR(devname))
goto free_page; goto free_page;
mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
free_page: free_page:
free_page((unsigned long)page); free_page((unsigned long)page);
out: out:
......
...@@ -54,33 +54,29 @@ static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname, ...@@ -54,33 +54,29 @@ static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
/* /*
* Determine the mount path as a string * Determine the mount path as a string
*/ */
static char *nfs4_path(const struct vfsmount *mnt_parent, static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
const struct dentry *dentry,
char *buffer, ssize_t buflen)
{ {
const char *srvpath; char *limit;
char *path = nfs_path(&limit, dentry, buffer, buflen);
srvpath = strchr(mnt_parent->mnt_devname, ':'); if (!IS_ERR(path)) {
if (srvpath) char *colon = strchr(path, ':');
srvpath++; if (colon && colon < limit)
else path = colon + 1;
srvpath = mnt_parent->mnt_devname; }
return path;
return nfs_path(srvpath, mnt_parent->mnt_root, dentry, buffer, buflen);
} }
/* /*
* Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
* believe to be the server path to this dentry * believe to be the server path to this dentry
*/ */
static int nfs4_validate_fspath(const struct vfsmount *mnt_parent, static int nfs4_validate_fspath(struct dentry *dentry,
const struct dentry *dentry,
const struct nfs4_fs_locations *locations, const struct nfs4_fs_locations *locations,
char *page, char *page2) char *page, char *page2)
{ {
const char *path, *fs_path; const char *path, *fs_path;
path = nfs4_path(mnt_parent, dentry, page, PAGE_SIZE); path = nfs4_path(dentry, page, PAGE_SIZE);
if (IS_ERR(path)) if (IS_ERR(path))
return PTR_ERR(path); return PTR_ERR(path);
...@@ -165,20 +161,18 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, ...@@ -165,20 +161,18 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
/** /**
* nfs_follow_referral - set up mountpoint when hitting a referral on moved error * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
* @mnt_parent - mountpoint of parent directory
* @dentry - parent directory * @dentry - parent directory
* @locations - array of NFSv4 server location information * @locations - array of NFSv4 server location information
* *
*/ */
static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
const struct dentry *dentry,
const struct nfs4_fs_locations *locations) const struct nfs4_fs_locations *locations)
{ {
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct nfs_clone_mount mountdata = { struct nfs_clone_mount mountdata = {
.sb = mnt_parent->mnt_sb, .sb = dentry->d_sb,
.dentry = dentry, .dentry = dentry,
.authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
}; };
char *page = NULL, *page2 = NULL; char *page = NULL, *page2 = NULL;
int loc, error; int loc, error;
...@@ -198,7 +192,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, ...@@ -198,7 +192,7 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
goto out; goto out;
/* Ensure fs path is a prefix of current dentry path */ /* Ensure fs path is a prefix of current dentry path */
error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2); error = nfs4_validate_fspath(dentry, locations, page, page2);
if (error < 0) { if (error < 0) {
mnt = ERR_PTR(error); mnt = ERR_PTR(error);
goto out; goto out;
...@@ -225,11 +219,10 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, ...@@ -225,11 +219,10 @@ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent,
/* /*
* nfs_do_refmount - handle crossing a referral on server * nfs_do_refmount - handle crossing a referral on server
* @mnt_parent - mountpoint of referral
* @dentry - dentry of referral * @dentry - dentry of referral
* *
*/ */
struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) struct vfsmount *nfs_do_refmount(struct dentry *dentry)
{ {
struct vfsmount *mnt = ERR_PTR(-ENOMEM); struct vfsmount *mnt = ERR_PTR(-ENOMEM);
struct dentry *parent; struct dentry *parent;
...@@ -262,7 +255,7 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr ...@@ -262,7 +255,7 @@ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentr
fs_locations->fs_path.ncomponents <= 0) fs_locations->fs_path.ncomponents <= 0)
goto out_free; goto out_free;
mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations); mnt = nfs_follow_referral(dentry, fs_locations);
out_free: out_free:
__free_page(page); __free_page(page);
kfree(fs_locations); kfree(fs_locations);
......
...@@ -263,8 +263,11 @@ static match_table_t nfs_local_lock_tokens = { ...@@ -263,8 +263,11 @@ static match_table_t nfs_local_lock_tokens = {
static void nfs_umount_begin(struct super_block *); static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *); static int nfs_statfs(struct dentry *, struct kstatfs *);
static int nfs_show_options(struct seq_file *, struct vfsmount *); static int nfs_show_options(struct seq_file *, struct vfsmount *);
static int nfs_show_devname(struct seq_file *, struct vfsmount *);
static int nfs_show_path(struct seq_file *, struct vfsmount *);
static int nfs_show_stats(struct seq_file *, struct vfsmount *); static int nfs_show_stats(struct seq_file *, struct vfsmount *);
static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *); static struct dentry *nfs_fs_mount(struct file_system_type *,
int, const char *, void *);
static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type, static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static void nfs_put_super(struct super_block *); static void nfs_put_super(struct super_block *);
...@@ -274,7 +277,7 @@ static int nfs_remount(struct super_block *sb, int *flags, char *raw_data); ...@@ -274,7 +277,7 @@ static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
static struct file_system_type nfs_fs_type = { static struct file_system_type nfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "nfs", .name = "nfs",
.get_sb = nfs_get_sb, .mount = nfs_fs_mount,
.kill_sb = nfs_kill_super, .kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
}; };
...@@ -296,6 +299,8 @@ static const struct super_operations nfs_sops = { ...@@ -296,6 +299,8 @@ static const struct super_operations nfs_sops = {
.evict_inode = nfs_evict_inode, .evict_inode = nfs_evict_inode,
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
.show_options = nfs_show_options, .show_options = nfs_show_options,
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats, .show_stats = nfs_show_stats,
.remount_fs = nfs_remount, .remount_fs = nfs_remount,
}; };
...@@ -303,16 +308,16 @@ static const struct super_operations nfs_sops = { ...@@ -303,16 +308,16 @@ static const struct super_operations nfs_sops = {
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
static int nfs4_validate_text_mount_data(void *options, static int nfs4_validate_text_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name); struct nfs_parsed_mount_data *args, const char *dev_name);
static int nfs4_try_mount(int flags, const char *dev_name, static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_parsed_mount_data *data, struct vfsmount *mnt); struct nfs_parsed_mount_data *data);
static int nfs4_get_sb(struct file_system_type *fs_type, static struct dentry *nfs4_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type, static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type, static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static int nfs4_referral_get_sb(struct file_system_type *fs_type, static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type, static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static void nfs4_kill_super(struct super_block *sb); static void nfs4_kill_super(struct super_block *sb);
...@@ -320,7 +325,7 @@ static void nfs4_kill_super(struct super_block *sb); ...@@ -320,7 +325,7 @@ static void nfs4_kill_super(struct super_block *sb);
static struct file_system_type nfs4_fs_type = { static struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "nfs4", .name = "nfs4",
.get_sb = nfs4_get_sb, .mount = nfs4_mount,
.kill_sb = nfs4_kill_super, .kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
}; };
...@@ -352,7 +357,7 @@ static struct file_system_type nfs4_remote_referral_fs_type = { ...@@ -352,7 +357,7 @@ static struct file_system_type nfs4_remote_referral_fs_type = {
struct file_system_type nfs4_referral_fs_type = { struct file_system_type nfs4_referral_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "nfs4", .name = "nfs4",
.get_sb = nfs4_referral_get_sb, .mount = nfs4_referral_mount,
.kill_sb = nfs4_kill_super, .kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
}; };
...@@ -366,6 +371,8 @@ static const struct super_operations nfs4_sops = { ...@@ -366,6 +371,8 @@ static const struct super_operations nfs4_sops = {
.evict_inode = nfs4_evict_inode, .evict_inode = nfs4_evict_inode,
.umount_begin = nfs_umount_begin, .umount_begin = nfs_umount_begin,
.show_options = nfs_show_options, .show_options = nfs_show_options,
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats, .show_stats = nfs_show_stats,
.remount_fs = nfs_remount, .remount_fs = nfs_remount,
}; };
...@@ -726,6 +733,28 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -726,6 +733,28 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
return 0; return 0;
} }
static int nfs_show_devname(struct seq_file *m, struct vfsmount *mnt)
{
char *page = (char *) __get_free_page(GFP_KERNEL);
char *devname, *dummy;
int err = 0;
if (!page)
return -ENOMEM;
devname = nfs_path(&dummy, mnt->mnt_root, page, PAGE_SIZE);
if (IS_ERR(devname))
err = PTR_ERR(devname);
else
seq_escape(m, devname, " \t\n\\");
free_page((unsigned long)page);
return err;
}
static int nfs_show_path(struct seq_file *m, struct vfsmount *mnt)
{
seq_puts(m, "/");
return 0;
}
/* /*
* Present statistical information for this VFS mountpoint * Present statistical information for this VFS mountpoint
*/ */
...@@ -2267,19 +2296,19 @@ static int nfs_bdi_register(struct nfs_server *server) ...@@ -2267,19 +2296,19 @@ static int nfs_bdi_register(struct nfs_server *server)
return bdi_register_dev(&server->backing_dev_info, server->s_dev); return bdi_register_dev(&server->backing_dev_info, server->s_dev);
} }
static int nfs_get_sb(struct file_system_type *fs_type, static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) int flags, const char *dev_name, void *raw_data)
{ {
struct nfs_server *server = NULL; struct nfs_server *server = NULL;
struct super_block *s; struct super_block *s;
struct nfs_parsed_mount_data *data; struct nfs_parsed_mount_data *data;
struct nfs_fh *mntfh; struct nfs_fh *mntfh;
struct dentry *mntroot; struct dentry *mntroot = ERR_PTR(-ENOMEM);
int (*compare_super)(struct super_block *, void *) = nfs_compare_super; int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
struct nfs_sb_mountdata sb_mntdata = { struct nfs_sb_mountdata sb_mntdata = {
.mntflags = flags, .mntflags = flags,
}; };
int error = -ENOMEM; int error;
data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION); data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION);
mntfh = nfs_alloc_fhandle(); mntfh = nfs_alloc_fhandle();
...@@ -2290,12 +2319,14 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2290,12 +2319,14 @@ static int nfs_get_sb(struct file_system_type *fs_type,
/* Validate the mount data */ /* Validate the mount data */
error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name); error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name);
if (error < 0) if (error < 0) {
mntroot = ERR_PTR(error);
goto out; goto out;
}
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
if (data->version == 4) { if (data->version == 4) {
error = nfs4_try_mount(flags, dev_name, data, mnt); mntroot = nfs4_try_mount(flags, dev_name, data);
kfree(data->client_address); kfree(data->client_address);
kfree(data->nfs_server.export_path); kfree(data->nfs_server.export_path);
goto out; goto out;
...@@ -2305,7 +2336,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2305,7 +2336,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
/* Get a volume representation */ /* Get a volume representation */
server = nfs_create_server(data, mntfh); server = nfs_create_server(data, mntfh);
if (IS_ERR(server)) { if (IS_ERR(server)) {
error = PTR_ERR(server); mntroot = ERR_CAST(server);
goto out; goto out;
} }
sb_mntdata.server = server; sb_mntdata.server = server;
...@@ -2316,7 +2347,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2316,7 +2347,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
/* Get a superblock - note that we may end up sharing one that already exists */ /* Get a superblock - note that we may end up sharing one that already exists */
s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata);
if (IS_ERR(s)) { if (IS_ERR(s)) {
error = PTR_ERR(s); mntroot = ERR_CAST(s);
goto out_err_nosb; goto out_err_nosb;
} }
...@@ -2325,9 +2356,11 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2325,9 +2356,11 @@ static int nfs_get_sb(struct file_system_type *fs_type,
server = NULL; server = NULL;
} else { } else {
error = nfs_bdi_register(server); error = nfs_bdi_register(server);
if (error) if (error) {
mntroot = ERR_PTR(error);
goto error_splat_bdi; goto error_splat_bdi;
} }
}
if (!s->s_root) { if (!s->s_root) {
/* initial superblock/root creation */ /* initial superblock/root creation */
...@@ -2336,20 +2369,15 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2336,20 +2369,15 @@ static int nfs_get_sb(struct file_system_type *fs_type,
s, data ? data->fscache_uniq : NULL, NULL); s, data ? data->fscache_uniq : NULL, NULL);
} }
mntroot = nfs_get_root(s, mntfh); mntroot = nfs_get_root(s, mntfh, dev_name);
if (IS_ERR(mntroot)) { if (IS_ERR(mntroot))
error = PTR_ERR(mntroot);
goto error_splat_super; goto error_splat_super;
}
error = security_sb_set_mnt_opts(s, &data->lsm_opts); error = security_sb_set_mnt_opts(s, &data->lsm_opts);
if (error) if (error)
goto error_splat_root; goto error_splat_root;
s->s_flags |= MS_ACTIVE; s->s_flags |= MS_ACTIVE;
mnt->mnt_sb = s;
mnt->mnt_root = mntroot;
error = 0;
out: out:
kfree(data->nfs_server.hostname); kfree(data->nfs_server.hostname);
...@@ -2359,7 +2387,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2359,7 +2387,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
out_free_fh: out_free_fh:
nfs_free_fhandle(mntfh); nfs_free_fhandle(mntfh);
kfree(data); kfree(data);
return error; return mntroot;
out_err_nosb: out_err_nosb:
nfs_free_server(server); nfs_free_server(server);
...@@ -2367,6 +2395,7 @@ static int nfs_get_sb(struct file_system_type *fs_type, ...@@ -2367,6 +2395,7 @@ static int nfs_get_sb(struct file_system_type *fs_type,
error_splat_root: error_splat_root:
dput(mntroot); dput(mntroot);
mntroot = ERR_PTR(error);
error_splat_super: error_splat_super:
if (server && !s->s_root) if (server && !s->s_root)
bdi_unregister(&server->backing_dev_info); bdi_unregister(&server->backing_dev_info);
...@@ -2450,7 +2479,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags, ...@@ -2450,7 +2479,7 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags,
nfs_fscache_get_super_cookie(s, NULL, data); nfs_fscache_get_super_cookie(s, NULL, data);
} }
mntroot = nfs_get_root(s, data->fh); mntroot = nfs_get_root(s, data->fh, dev_name);
if (IS_ERR(mntroot)) { if (IS_ERR(mntroot)) {
error = PTR_ERR(mntroot); error = PTR_ERR(mntroot);
goto error_splat_super; goto error_splat_super;
...@@ -2718,7 +2747,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags, ...@@ -2718,7 +2747,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags,
s, data ? data->fscache_uniq : NULL, NULL); s, data ? data->fscache_uniq : NULL, NULL);
} }
mntroot = nfs4_get_root(s, mntfh); mntroot = nfs4_get_root(s, mntfh, dev_name);
if (IS_ERR(mntroot)) { if (IS_ERR(mntroot)) {
error = PTR_ERR(mntroot); error = PTR_ERR(mntroot);
goto error_splat_super; goto error_splat_super;
...@@ -2771,27 +2800,6 @@ static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, ...@@ -2771,27 +2800,6 @@ static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
return root_mnt; return root_mnt;
} }
static void nfs_fix_devname(const struct path *path, struct vfsmount *mnt)
{
char *page = (char *) __get_free_page(GFP_KERNEL);
char *devname, *tmp;
if (page == NULL)
return;
devname = nfs_path(path->mnt->mnt_devname,
path->mnt->mnt_root, path->dentry,
page, PAGE_SIZE);
if (IS_ERR(devname))
goto out_freepage;
tmp = kstrdup(devname, GFP_KERNEL);
if (tmp == NULL)
goto out_freepage;
kfree(mnt->mnt_devname);
mnt->mnt_devname = tmp;
out_freepage:
free_page((unsigned long)page);
}
struct nfs_referral_count { struct nfs_referral_count {
struct list_head list; struct list_head list;
const struct task_struct *task; const struct task_struct *task;
...@@ -2858,17 +2866,18 @@ static void nfs_referral_loop_unprotect(void) ...@@ -2858,17 +2866,18 @@ static void nfs_referral_loop_unprotect(void)
kfree(p); kfree(p);
} }
static int nfs_follow_remote_path(struct vfsmount *root_mnt, static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
const char *export_path, struct vfsmount *mnt_target) const char *export_path)
{ {
struct nameidata *nd = NULL; struct nameidata *nd = NULL;
struct mnt_namespace *ns_private; struct mnt_namespace *ns_private;
struct super_block *s; struct super_block *s;
struct dentry *dentry;
int ret; int ret;
nd = kmalloc(sizeof(*nd), GFP_KERNEL); nd = kmalloc(sizeof(*nd), GFP_KERNEL);
if (nd == NULL) if (nd == NULL)
return -ENOMEM; return ERR_PTR(-ENOMEM);
ns_private = create_mnt_ns(root_mnt); ns_private = create_mnt_ns(root_mnt);
ret = PTR_ERR(ns_private); ret = PTR_ERR(ns_private);
...@@ -2890,32 +2899,27 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt, ...@@ -2890,32 +2899,27 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt,
s = nd->path.mnt->mnt_sb; s = nd->path.mnt->mnt_sb;
atomic_inc(&s->s_active); atomic_inc(&s->s_active);
mnt_target->mnt_sb = s; dentry = dget(nd->path.dentry);
mnt_target->mnt_root = dget(nd->path.dentry);
/* Correct the device pathname */
nfs_fix_devname(&nd->path, mnt_target);
path_put(&nd->path); path_put(&nd->path);
kfree(nd); kfree(nd);
down_write(&s->s_umount); down_write(&s->s_umount);
return 0; return dentry;
out_put_mnt_ns: out_put_mnt_ns:
put_mnt_ns(ns_private); put_mnt_ns(ns_private);
out_mntput: out_mntput:
mntput(root_mnt); mntput(root_mnt);
out_err: out_err:
kfree(nd); kfree(nd);
return ret; return ERR_PTR(ret);
} }
static int nfs4_try_mount(int flags, const char *dev_name, static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_parsed_mount_data *data, struct nfs_parsed_mount_data *data)
struct vfsmount *mnt)
{ {
char *export_path; char *export_path;
struct vfsmount *root_mnt; struct vfsmount *root_mnt;
int error; struct dentry *res;
dfprintk(MOUNT, "--> nfs4_try_mount()\n"); dfprintk(MOUNT, "--> nfs4_try_mount()\n");
...@@ -2925,26 +2929,25 @@ static int nfs4_try_mount(int flags, const char *dev_name, ...@@ -2925,26 +2929,25 @@ static int nfs4_try_mount(int flags, const char *dev_name,
data->nfs_server.hostname); data->nfs_server.hostname);
data->nfs_server.export_path = export_path; data->nfs_server.export_path = export_path;
error = PTR_ERR(root_mnt); res = ERR_CAST(root_mnt);
if (IS_ERR(root_mnt)) if (!IS_ERR(root_mnt))
goto out; res = nfs_follow_remote_path(root_mnt, export_path);
error = nfs_follow_remote_path(root_mnt, export_path, mnt); dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
IS_ERR(res) ? PTR_ERR(res) : 0,
out: IS_ERR(res) ? " [error]" : "");
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", error, return res;
error != 0 ? " [error]" : "");
return error;
} }
/* /*
* Get the superblock for an NFS4 mountpoint * Get the superblock for an NFS4 mountpoint
*/ */
static int nfs4_get_sb(struct file_system_type *fs_type, static struct dentry *nfs4_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) int flags, const char *dev_name, void *raw_data)
{ {
struct nfs_parsed_mount_data *data; struct nfs_parsed_mount_data *data;
int error = -ENOMEM; int error = -ENOMEM;
struct dentry *res = ERR_PTR(-ENOMEM);
data = nfs_alloc_parsed_mount_data(4); data = nfs_alloc_parsed_mount_data(4);
if (data == NULL) if (data == NULL)
...@@ -2952,10 +2955,14 @@ static int nfs4_get_sb(struct file_system_type *fs_type, ...@@ -2952,10 +2955,14 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
/* Validate the mount data */ /* Validate the mount data */
error = nfs4_validate_mount_data(raw_data, data, dev_name); error = nfs4_validate_mount_data(raw_data, data, dev_name);
if (error < 0) if (error < 0) {
res = ERR_PTR(error);
goto out; goto out;
}
error = nfs4_try_mount(flags, dev_name, data, mnt); res = nfs4_try_mount(flags, dev_name, data);
if (IS_ERR(res))
error = PTR_ERR(res);
out: out:
kfree(data->client_address); kfree(data->client_address);
...@@ -2964,9 +2971,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type, ...@@ -2964,9 +2971,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
kfree(data->fscache_uniq); kfree(data->fscache_uniq);
out_free_data: out_free_data:
kfree(data); kfree(data);
dprintk("<-- nfs4_get_sb() = %d%s\n", error, dprintk("<-- nfs4_mount() = %d%s\n", error,
error != 0 ? " [error]" : ""); error != 0 ? " [error]" : "");
return error; return res;
} }
static void nfs4_kill_super(struct super_block *sb) static void nfs4_kill_super(struct super_block *sb)
...@@ -3033,7 +3040,7 @@ nfs4_xdev_mount(struct file_system_type *fs_type, int flags, ...@@ -3033,7 +3040,7 @@ nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
nfs_fscache_get_super_cookie(s, NULL, data); nfs_fscache_get_super_cookie(s, NULL, data);
} }
mntroot = nfs4_get_root(s, data->fh); mntroot = nfs4_get_root(s, data->fh, dev_name);
if (IS_ERR(mntroot)) { if (IS_ERR(mntroot)) {
error = PTR_ERR(mntroot); error = PTR_ERR(mntroot);
goto error_splat_super; goto error_splat_super;
...@@ -3120,7 +3127,7 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, ...@@ -3120,7 +3127,7 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
nfs_fscache_get_super_cookie(s, NULL, data); nfs_fscache_get_super_cookie(s, NULL, data);
} }
mntroot = nfs4_get_root(s, mntfh); mntroot = nfs4_get_root(s, mntfh, dev_name);
if (IS_ERR(mntroot)) { if (IS_ERR(mntroot)) {
error = PTR_ERR(mntroot); error = PTR_ERR(mntroot);
goto error_splat_super; goto error_splat_super;
...@@ -3160,16 +3167,15 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, ...@@ -3160,16 +3167,15 @@ nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
/* /*
* Create an NFS4 server record on referral traversal * Create an NFS4 server record on referral traversal
*/ */
static int nfs4_referral_get_sb(struct file_system_type *fs_type, static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, int flags, const char *dev_name, void *raw_data)
struct vfsmount *mnt)
{ {
struct nfs_clone_mount *data = raw_data; struct nfs_clone_mount *data = raw_data;
char *export_path; char *export_path;
struct vfsmount *root_mnt; struct vfsmount *root_mnt;
int error; struct dentry *res;
dprintk("--> nfs4_referral_get_sb()\n"); dprintk("--> nfs4_referral_mount()\n");
export_path = data->mnt_path; export_path = data->mnt_path;
data->mnt_path = "/"; data->mnt_path = "/";
...@@ -3178,15 +3184,13 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, ...@@ -3178,15 +3184,13 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type,
flags, data, data->hostname); flags, data, data->hostname);
data->mnt_path = export_path; data->mnt_path = export_path;
error = PTR_ERR(root_mnt); res = ERR_CAST(root_mnt);
if (IS_ERR(root_mnt)) if (!IS_ERR(root_mnt))
goto out; res = nfs_follow_remote_path(root_mnt, export_path);
dprintk("<-- nfs4_referral_mount() = %ld%s\n",
error = nfs_follow_remote_path(root_mnt, export_path, mnt); IS_ERR(res) ? PTR_ERR(res) : 0,
out: IS_ERR(res) ? " [error]" : "");
dprintk("<-- nfs4_referral_get_sb() = %d%s\n", error, return res;
error != 0 ? " [error]" : "");
return error;
} }
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */
...@@ -148,6 +148,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n ...@@ -148,6 +148,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
alias = d_lookup(parent, &data->args.name); alias = d_lookup(parent, &data->args.name);
if (alias != NULL) { if (alias != NULL) {
int ret = 0; int ret = 0;
void *devname_garbage = NULL;
/* /*
* Hey, we raced with lookup... See if we need to transfer * Hey, we raced with lookup... See if we need to transfer
...@@ -157,6 +158,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n ...@@ -157,6 +158,7 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
spin_lock(&alias->d_lock); spin_lock(&alias->d_lock);
if (alias->d_inode != NULL && if (alias->d_inode != NULL &&
!(alias->d_flags & DCACHE_NFSFS_RENAMED)) { !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
devname_garbage = alias->d_fsdata;
alias->d_fsdata = data; alias->d_fsdata = data;
alias->d_flags |= DCACHE_NFSFS_RENAMED; alias->d_flags |= DCACHE_NFSFS_RENAMED;
ret = 1; ret = 1;
...@@ -164,6 +166,13 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n ...@@ -164,6 +166,13 @@ static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct n
spin_unlock(&alias->d_lock); spin_unlock(&alias->d_lock);
nfs_dec_sillycount(dir); nfs_dec_sillycount(dir);
dput(alias); dput(alias);
/*
* If we'd displaced old cached devname, free it. At that
* point dentry is definitely not a root, so we won't need
* that anymore.
*/
if (devname_garbage)
kfree(devname_garbage);
return ret; return ret;
} }
data->dir = igrab(dir); data->dir = igrab(dir);
...@@ -252,6 +261,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) ...@@ -252,6 +261,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
{ {
struct nfs_unlinkdata *data; struct nfs_unlinkdata *data;
int status = -ENOMEM; int status = -ENOMEM;
void *devname_garbage = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL) if (data == NULL)
...@@ -269,8 +279,16 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry) ...@@ -269,8 +279,16 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
goto out_unlock; goto out_unlock;
dentry->d_flags |= DCACHE_NFSFS_RENAMED; dentry->d_flags |= DCACHE_NFSFS_RENAMED;
devname_garbage = dentry->d_fsdata;
dentry->d_fsdata = data; dentry->d_fsdata = data;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
/*
* If we'd displaced old cached devname, free it. At that
* point dentry is definitely not a root, so we won't need
* that anymore.
*/
if (devname_garbage)
kfree(devname_garbage);
return 0; return 0;
out_unlock: out_unlock:
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
...@@ -299,6 +317,7 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode) ...@@ -299,6 +317,7 @@ nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
data = dentry->d_fsdata; data = dentry->d_fsdata;
dentry->d_fsdata = NULL;
} }
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
...@@ -315,6 +334,7 @@ nfs_cancel_async_unlink(struct dentry *dentry) ...@@ -315,6 +334,7 @@ nfs_cancel_async_unlink(struct dentry *dentry)
struct nfs_unlinkdata *data = dentry->d_fsdata; struct nfs_unlinkdata *data = dentry->d_fsdata;
dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
dentry->d_fsdata = NULL;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
nfs_free_unlinkdata(data); nfs_free_unlinkdata(data);
return; return;
......
...@@ -843,23 +843,6 @@ struct dentry *mount_bdev(struct file_system_type *fs_type, ...@@ -843,23 +843,6 @@ struct dentry *mount_bdev(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(mount_bdev); EXPORT_SYMBOL(mount_bdev);
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 vfsmount *mnt)
{
struct dentry *root;
root = mount_bdev(fs_type, flags, dev_name, data, fill_super);
if (IS_ERR(root))
return PTR_ERR(root);
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
return 0;
}
EXPORT_SYMBOL(get_sb_bdev);
void kill_block_super(struct super_block *sb) void kill_block_super(struct super_block *sb)
{ {
struct block_device *bdev = sb->s_bdev; struct block_device *bdev = sb->s_bdev;
...@@ -897,22 +880,6 @@ struct dentry *mount_nodev(struct file_system_type *fs_type, ...@@ -897,22 +880,6 @@ struct dentry *mount_nodev(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(mount_nodev); EXPORT_SYMBOL(mount_nodev);
int get_sb_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt)
{
struct dentry *root;
root = mount_nodev(fs_type, flags, data, fill_super);
if (IS_ERR(root))
return PTR_ERR(root);
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
return 0;
}
EXPORT_SYMBOL(get_sb_nodev);
static int compare_single(struct super_block *s, void *p) static int compare_single(struct super_block *s, void *p)
{ {
return 1; return 1;
...@@ -943,22 +910,6 @@ struct dentry *mount_single(struct file_system_type *fs_type, ...@@ -943,22 +910,6 @@ struct dentry *mount_single(struct file_system_type *fs_type,
} }
EXPORT_SYMBOL(mount_single); EXPORT_SYMBOL(mount_single);
int get_sb_single(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt)
{
struct dentry *root;
root = mount_single(fs_type, flags, data, fill_super);
if (IS_ERR(root))
return PTR_ERR(root);
mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
return 0;
}
EXPORT_SYMBOL(get_sb_single);
struct vfsmount * struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{ {
...@@ -988,7 +939,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void ...@@ -988,7 +939,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
goto out_free_secdata; goto out_free_secdata;
} }
if (type->mount) {
root = type->mount(type, flags, name, data); root = type->mount(type, flags, name, data);
if (IS_ERR(root)) { if (IS_ERR(root)) {
error = PTR_ERR(root); error = PTR_ERR(root);
...@@ -996,11 +946,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void ...@@ -996,11 +946,6 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
} }
mnt->mnt_root = root; mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb; mnt->mnt_sb = root->d_sb;
} else {
error = type->get_sb(type, flags, name, data, mnt);
if (error < 0)
goto out_free_secdata;
}
BUG_ON(!mnt->mnt_sb); BUG_ON(!mnt->mnt_sb);
WARN_ON(!mnt->mnt_sb->s_bdi); WARN_ON(!mnt->mnt_sb->s_bdi);
mnt->mnt_sb->s_flags |= MS_BORN; mnt->mnt_sb->s_flags |= MS_BORN;
......
...@@ -1631,6 +1631,8 @@ struct super_operations { ...@@ -1631,6 +1631,8 @@ struct super_operations {
void (*umount_begin) (struct super_block *); void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *); int (*show_options)(struct seq_file *, struct vfsmount *);
int (*show_devname)(struct seq_file *, struct vfsmount *);
int (*show_path)(struct seq_file *, struct vfsmount *);
int (*show_stats)(struct seq_file *, struct vfsmount *); int (*show_stats)(struct seq_file *, struct vfsmount *);
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
...@@ -1794,8 +1796,6 @@ int sync_inode_metadata(struct inode *inode, int wait); ...@@ -1794,8 +1796,6 @@ int sync_inode_metadata(struct inode *inode, int wait);
struct file_system_type { struct file_system_type {
const char *name; const char *name;
int fs_flags; int fs_flags;
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
struct dentry *(*mount) (struct file_system_type *, int, struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *); const char *, void *);
void (*kill_sb) (struct super_block *); void (*kill_sb) (struct super_block *);
...@@ -1818,24 +1818,12 @@ extern struct dentry *mount_ns(struct file_system_type *fs_type, int flags, ...@@ -1818,24 +1818,12 @@ extern struct dentry *mount_ns(struct file_system_type *fs_type, int flags,
extern struct dentry *mount_bdev(struct file_system_type *fs_type, extern struct dentry *mount_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int)); int (*fill_super)(struct super_block *, void *, int));
extern 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 vfsmount *mnt);
extern struct dentry *mount_single(struct file_system_type *fs_type, extern struct dentry *mount_single(struct file_system_type *fs_type,
int flags, void *data, int flags, void *data,
int (*fill_super)(struct super_block *, void *, int)); int (*fill_super)(struct super_block *, void *, int));
extern int get_sb_single(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt);
extern struct dentry *mount_nodev(struct file_system_type *fs_type, extern struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data, int flags, void *data,
int (*fill_super)(struct super_block *, void *, int)); int (*fill_super)(struct super_block *, void *, int));
extern int get_sb_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int),
struct vfsmount *mnt);
void generic_shutdown_super(struct super_block *sb); void generic_shutdown_super(struct super_block *sb);
void kill_block_super(struct super_block *sb); void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb); void kill_anon_super(struct super_block *sb);
......
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