mirror of https://gitee.com/openkylin/linux.git
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: cifs: propagate errors from cifs_get_root() to mount(2) cifs: tidy cifs_do_mount() up a bit cifs: more breakage on mount failures cifs: close sget() races cifs: pull freeing mountdata/dropping nls/freeing cifs_sb into cifs_umount() cifs: move cifs_umount() call into ->kill_sb() cifs: pull cifs_mount() call up sanitize cifs_umount() prototype cifs: initialize ->tlink_tree in cifs_setup_cifs_sb() cifs: allocate mountdata earlier cifs: leak on mount if we share superblock cifs: don't pass superblock to cifs_mount() cifs: don't leak nls on mount failure cifs: double free on mount failure take bdi setup/destruction into cifs_mount/cifs_umount Acked-by: Steve French <smfrench@gmail.com>
This commit is contained in:
commit
804a007f54
|
@ -42,6 +42,7 @@
|
|||
#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
|
||||
#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
|
||||
#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */
|
||||
#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
|
||||
|
||||
struct cifs_sb_info {
|
||||
struct rb_root tlink_tree;
|
||||
|
|
140
fs/cifs/cifsfs.c
140
fs/cifs/cifsfs.c
|
@ -104,8 +104,7 @@ cifs_sb_deactive(struct super_block *sb)
|
|||
}
|
||||
|
||||
static int
|
||||
cifs_read_super(struct super_block *sb, struct smb_vol *volume_info,
|
||||
const char *devname, int silent)
|
||||
cifs_read_super(struct super_block *sb)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
|
@ -113,22 +112,16 @@ cifs_read_super(struct super_block *sb, struct smb_vol *volume_info,
|
|||
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
|
||||
spin_lock_init(&cifs_sb->tlink_tree_lock);
|
||||
cifs_sb->tlink_tree = RB_ROOT;
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
|
||||
rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (cifs_sb_master_tcon(cifs_sb)->ses->capabilities & CAP_LARGE_FILES)
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
else
|
||||
sb->s_maxbytes = MAX_NON_LFS;
|
||||
|
||||
cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
|
||||
|
||||
rc = cifs_mount(sb, cifs_sb, volume_info, devname);
|
||||
|
||||
if (rc) {
|
||||
if (!silent)
|
||||
cERROR(1, "cifs_mount failed w/return code = %d", rc);
|
||||
goto out_mount_failed;
|
||||
}
|
||||
/* BB FIXME fix time_gran to be larger for LANMAN sessions */
|
||||
sb->s_time_gran = 100;
|
||||
|
||||
sb->s_magic = CIFS_MAGIC_NUMBER;
|
||||
sb->s_op = &cifs_super_ops;
|
||||
|
@ -170,37 +163,14 @@ cifs_read_super(struct super_block *sb, struct smb_vol *volume_info,
|
|||
if (inode)
|
||||
iput(inode);
|
||||
|
||||
cifs_umount(sb, cifs_sb);
|
||||
|
||||
out_mount_failed:
|
||||
bdi_destroy(&cifs_sb->bdi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_put_super(struct super_block *sb)
|
||||
static void cifs_kill_sb(struct super_block *sb)
|
||||
{
|
||||
int rc = 0;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
|
||||
cFYI(1, "In cifs_put_super");
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
if (cifs_sb == NULL) {
|
||||
cFYI(1, "Empty cifs superblock info passed to unmount");
|
||||
return;
|
||||
}
|
||||
|
||||
rc = cifs_umount(sb, cifs_sb);
|
||||
if (rc)
|
||||
cERROR(1, "cifs_umount failed with return code %d", rc);
|
||||
if (cifs_sb->mountdata) {
|
||||
kfree(cifs_sb->mountdata);
|
||||
cifs_sb->mountdata = NULL;
|
||||
}
|
||||
|
||||
unload_nls(cifs_sb->local_nls);
|
||||
bdi_destroy(&cifs_sb->bdi);
|
||||
kfree(cifs_sb);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
kill_anon_super(sb);
|
||||
cifs_umount(cifs_sb);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -548,7 +518,6 @@ static int cifs_drop_inode(struct inode *inode)
|
|||
}
|
||||
|
||||
static const struct super_operations cifs_super_ops = {
|
||||
.put_super = cifs_put_super,
|
||||
.statfs = cifs_statfs,
|
||||
.alloc_inode = cifs_alloc_inode,
|
||||
.destroy_inode = cifs_destroy_inode,
|
||||
|
@ -585,7 +554,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
|||
full_path = cifs_build_path_to_root(vol, cifs_sb,
|
||||
cifs_sb_master_tcon(cifs_sb));
|
||||
if (full_path == NULL)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cFYI(1, "Get root dentry for %s", full_path);
|
||||
|
||||
|
@ -614,7 +583,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
|||
dchild = d_alloc(dparent, &name);
|
||||
if (dchild == NULL) {
|
||||
dput(dparent);
|
||||
dparent = NULL;
|
||||
dparent = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +601,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
|||
if (rc) {
|
||||
dput(dchild);
|
||||
dput(dparent);
|
||||
dparent = NULL;
|
||||
dparent = ERR_PTR(rc);
|
||||
goto out;
|
||||
}
|
||||
alias = d_materialise_unique(dchild, inode);
|
||||
|
@ -640,7 +609,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
|||
dput(dchild);
|
||||
if (IS_ERR(alias)) {
|
||||
dput(dparent);
|
||||
dparent = NULL;
|
||||
dparent = ERR_PTR(-EINVAL); /* XXX */
|
||||
goto out;
|
||||
}
|
||||
dchild = alias;
|
||||
|
@ -660,6 +629,13 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
|
|||
return dparent;
|
||||
}
|
||||
|
||||
static int cifs_set_super(struct super_block *sb, void *data)
|
||||
{
|
||||
struct cifs_mnt_data *mnt_data = data;
|
||||
sb->s_fs_info = mnt_data->cifs_sb;
|
||||
return set_anon_super(sb, NULL);
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
cifs_do_mount(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data)
|
||||
|
@ -680,75 +656,73 @@ cifs_do_mount(struct file_system_type *fs_type,
|
|||
cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL);
|
||||
if (cifs_sb == NULL) {
|
||||
root = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
goto out_nls;
|
||||
}
|
||||
|
||||
cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL);
|
||||
if (cifs_sb->mountdata == NULL) {
|
||||
root = ERR_PTR(-ENOMEM);
|
||||
goto out_cifs_sb;
|
||||
}
|
||||
|
||||
cifs_setup_cifs_sb(volume_info, cifs_sb);
|
||||
|
||||
rc = cifs_mount(cifs_sb, volume_info);
|
||||
if (rc) {
|
||||
if (!(flags & MS_SILENT))
|
||||
cERROR(1, "cifs_mount failed w/return code = %d", rc);
|
||||
root = ERR_PTR(rc);
|
||||
goto out_mountdata;
|
||||
}
|
||||
|
||||
mnt_data.vol = volume_info;
|
||||
mnt_data.cifs_sb = cifs_sb;
|
||||
mnt_data.flags = flags;
|
||||
|
||||
sb = sget(fs_type, cifs_match_super, set_anon_super, &mnt_data);
|
||||
sb = sget(fs_type, cifs_match_super, cifs_set_super, &mnt_data);
|
||||
if (IS_ERR(sb)) {
|
||||
root = ERR_CAST(sb);
|
||||
goto out_cifs_sb;
|
||||
cifs_umount(cifs_sb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sb->s_fs_info) {
|
||||
if (sb->s_root) {
|
||||
cFYI(1, "Use existing superblock");
|
||||
goto out_shared;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy mount params for use in submounts. Better to do
|
||||
* the copy here and deal with the error before cleanup gets
|
||||
* complicated post-mount.
|
||||
*/
|
||||
cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL);
|
||||
if (cifs_sb->mountdata == NULL) {
|
||||
root = ERR_PTR(-ENOMEM);
|
||||
goto out_super;
|
||||
}
|
||||
|
||||
cifs_umount(cifs_sb);
|
||||
} else {
|
||||
sb->s_flags = flags;
|
||||
/* BB should we make this contingent on mount parm? */
|
||||
sb->s_flags |= MS_NODIRATIME | MS_NOATIME;
|
||||
sb->s_fs_info = cifs_sb;
|
||||
|
||||
rc = cifs_read_super(sb, volume_info, dev_name,
|
||||
flags & MS_SILENT ? 1 : 0);
|
||||
rc = cifs_read_super(sb);
|
||||
if (rc) {
|
||||
root = ERR_PTR(rc);
|
||||
goto out_super;
|
||||
}
|
||||
|
||||
sb->s_flags |= MS_ACTIVE;
|
||||
}
|
||||
|
||||
root = cifs_get_root(volume_info, sb);
|
||||
if (root == NULL)
|
||||
if (IS_ERR(root))
|
||||
goto out_super;
|
||||
|
||||
cFYI(1, "dentry root is: %p", root);
|
||||
goto out;
|
||||
|
||||
out_shared:
|
||||
root = cifs_get_root(volume_info, sb);
|
||||
if (root)
|
||||
cFYI(1, "dentry root is: %p", root);
|
||||
goto out;
|
||||
|
||||
out_super:
|
||||
kfree(cifs_sb->mountdata);
|
||||
deactivate_locked_super(sb);
|
||||
|
||||
out_cifs_sb:
|
||||
unload_nls(cifs_sb->local_nls);
|
||||
kfree(cifs_sb);
|
||||
|
||||
out:
|
||||
cifs_cleanup_volume_info(&volume_info);
|
||||
return root;
|
||||
|
||||
out_mountdata:
|
||||
kfree(cifs_sb->mountdata);
|
||||
out_cifs_sb:
|
||||
kfree(cifs_sb);
|
||||
out_nls:
|
||||
unload_nls(volume_info->local_nls);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
|
@ -837,7 +811,7 @@ struct file_system_type cifs_fs_type = {
|
|||
.owner = THIS_MODULE,
|
||||
.name = "cifs",
|
||||
.mount = cifs_do_mount,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = cifs_kill_sb,
|
||||
/* .fs_flags */
|
||||
};
|
||||
const struct inode_operations cifs_dir_inode_ops = {
|
||||
|
|
|
@ -157,9 +157,8 @@ extern int cifs_match_super(struct super_block *, void *);
|
|||
extern void cifs_cleanup_volume_info(struct smb_vol **pvolume_info);
|
||||
extern int cifs_setup_volume_info(struct smb_vol **pvolume_info,
|
||||
char *mount_data, const char *devname);
|
||||
extern int cifs_mount(struct super_block *, struct cifs_sb_info *,
|
||||
struct smb_vol *, const char *);
|
||||
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
|
||||
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
|
||||
extern void cifs_umount(struct cifs_sb_info *);
|
||||
extern void cifs_dfs_release_automount_timer(void);
|
||||
void cifs_proc_init(void);
|
||||
void cifs_proc_clean(void);
|
||||
|
@ -218,7 +217,8 @@ extern int get_dfs_path(int xid, struct cifs_ses *pSesInfo,
|
|||
struct dfs_info3_param **preferrals,
|
||||
int remap);
|
||||
extern void reset_cifs_unix_caps(int xid, struct cifs_tcon *tcon,
|
||||
struct super_block *sb, struct smb_vol *vol);
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
struct smb_vol *vol);
|
||||
extern int CIFSSMBQFSInfo(const int xid, struct cifs_tcon *tcon,
|
||||
struct kstatfs *FSData);
|
||||
extern int SMBOldQFSInfo(const int xid, struct cifs_tcon *tcon,
|
||||
|
|
|
@ -2546,7 +2546,7 @@ ip_connect(struct TCP_Server_Info *server)
|
|||
}
|
||||
|
||||
void reset_cifs_unix_caps(int xid, struct cifs_tcon *tcon,
|
||||
struct super_block *sb, struct smb_vol *vol_info)
|
||||
struct cifs_sb_info *cifs_sb, struct smb_vol *vol_info)
|
||||
{
|
||||
/* if we are reconnecting then should we check to see if
|
||||
* any requested capabilities changed locally e.g. via
|
||||
|
@ -2600,22 +2600,23 @@ void reset_cifs_unix_caps(int xid, struct cifs_tcon *tcon,
|
|||
cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
|
||||
else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
|
||||
cFYI(1, "negotiated posix acl support");
|
||||
if (sb)
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
if (cifs_sb)
|
||||
cifs_sb->mnt_cifs_flags |=
|
||||
CIFS_MOUNT_POSIXACL;
|
||||
}
|
||||
|
||||
if (vol_info && vol_info->posix_paths == 0)
|
||||
cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
|
||||
else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
|
||||
cFYI(1, "negotiate posix pathnames");
|
||||
if (sb)
|
||||
CIFS_SB(sb)->mnt_cifs_flags |=
|
||||
if (cifs_sb)
|
||||
cifs_sb->mnt_cifs_flags |=
|
||||
CIFS_MOUNT_POSIX_PATHS;
|
||||
}
|
||||
|
||||
if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
|
||||
if (cifs_sb && (cifs_sb->rsize > 127 * 1024)) {
|
||||
if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
|
||||
CIFS_SB(sb)->rsize = 127 * 1024;
|
||||
cifs_sb->rsize = 127 * 1024;
|
||||
cFYI(DBG2, "larger reads not supported by srv");
|
||||
}
|
||||
}
|
||||
|
@ -2662,6 +2663,9 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
|
|||
{
|
||||
INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
|
||||
|
||||
spin_lock_init(&cifs_sb->tlink_tree_lock);
|
||||
cifs_sb->tlink_tree = RB_ROOT;
|
||||
|
||||
if (pvolume_info->rsize > CIFSMaxBufSize) {
|
||||
cERROR(1, "rsize %d too large, using MaxBufSize",
|
||||
pvolume_info->rsize);
|
||||
|
@ -2982,8 +2986,7 @@ int cifs_setup_volume_info(struct smb_vol **pvolume_info, char *mount_data,
|
|||
}
|
||||
|
||||
int
|
||||
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
||||
struct smb_vol *volume_info, const char *devname)
|
||||
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
|
||||
{
|
||||
int rc = 0;
|
||||
int xid;
|
||||
|
@ -2994,6 +2997,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
struct tcon_link *tlink;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
int referral_walks_count = 0;
|
||||
|
||||
rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
|
||||
|
||||
try_mount_again:
|
||||
/* cleanup activities if we're chasing a referral */
|
||||
if (referral_walks_count) {
|
||||
|
@ -3018,6 +3028,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
srvTcp = cifs_get_tcp_session(volume_info);
|
||||
if (IS_ERR(srvTcp)) {
|
||||
rc = PTR_ERR(srvTcp);
|
||||
bdi_destroy(&cifs_sb->bdi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -3029,14 +3040,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
goto mount_fail_check;
|
||||
}
|
||||
|
||||
if (pSesInfo->capabilities & CAP_LARGE_FILES)
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
else
|
||||
sb->s_maxbytes = MAX_NON_LFS;
|
||||
|
||||
/* BB FIXME fix time_gran to be larger for LANMAN sessions */
|
||||
sb->s_time_gran = 100;
|
||||
|
||||
/* search for existing tcon to this server share */
|
||||
tcon = cifs_get_tcon(pSesInfo, volume_info);
|
||||
if (IS_ERR(tcon)) {
|
||||
|
@ -3049,7 +3052,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
if (tcon->ses->capabilities & CAP_UNIX) {
|
||||
/* reset of caps checks mount to see if unix extensions
|
||||
disabled for just this mount */
|
||||
reset_cifs_unix_caps(xid, tcon, sb, volume_info);
|
||||
reset_cifs_unix_caps(xid, tcon, cifs_sb, volume_info);
|
||||
if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
|
||||
(le64_to_cpu(tcon->fsUnixInfo.Capability) &
|
||||
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
|
||||
|
@ -3172,6 +3175,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
cifs_put_smb_ses(pSesInfo);
|
||||
else
|
||||
cifs_put_tcp_session(srvTcp);
|
||||
bdi_destroy(&cifs_sb->bdi);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -3346,8 +3350,8 @@ CIFSTCon(unsigned int xid, struct cifs_ses *ses,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
||||
void
|
||||
cifs_umount(struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
struct rb_root *root = &cifs_sb->tlink_tree;
|
||||
struct rb_node *node;
|
||||
|
@ -3368,7 +3372,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
|||
}
|
||||
spin_unlock(&cifs_sb->tlink_tree_lock);
|
||||
|
||||
return 0;
|
||||
bdi_destroy(&cifs_sb->bdi);
|
||||
kfree(cifs_sb->mountdata);
|
||||
unload_nls(cifs_sb->local_nls);
|
||||
kfree(cifs_sb);
|
||||
}
|
||||
|
||||
int cifs_negotiate_protocol(unsigned int xid, struct cifs_ses *ses)
|
||||
|
|
Loading…
Reference in New Issue