Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 804a007f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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: default avatarSteve French <smfrench@gmail.com>
parents 8abf5588 9403c9c5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+65 −91
Original line number Diff line number Diff line
@@ -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;

	cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
	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;

	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 = {
+4 −4
Original line number Diff line number Diff line
@@ -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,
+28 −21
Original line number Diff line number Diff line
@@ -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)