Example #1
0
static void*
cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
{
	struct dfs_info3_param *referrals = NULL;
	unsigned int num_referrals = 0;
	struct cifs_sb_info *cifs_sb;
	struct cifsSesInfo *ses;
	char *full_path = NULL;
	int xid, i;
	int rc = 0;
	struct vfsmount *mnt = ERR_PTR(-ENOENT);

	cFYI(1, "in %s", __func__);
	BUG_ON(IS_ROOT(dentry));

	xid = GetXid();

	dput(nd->path.dentry);
	nd->path.dentry = dget(dentry);

	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
	ses = cifs_sb->tcon->ses;

	if (!ses) {
		rc = -EINVAL;
		goto out_err;
	}

	/*
	 * The MSDFS spec states that paths in DFS referral requests and
	 * responses must be prefixed by a single '\' character instead of
	 * the double backslashes usually used in the UNC. This function
	 * gives us the latter, so we must adjust the result.
	 */
	full_path = build_path_from_dentry(dentry);
	if (full_path == NULL) {
		rc = -ENOMEM;
		goto out_err;
	}

	rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls,
		&num_referrals, &referrals,
		cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);

	for (i = 0; i < num_referrals; i++) {
		int len;
		dump_referral(referrals+i);
		/* connect to a node */
		len = strlen(referrals[i].node_name);
		if (len < 2) {
			cERROR(1, "%s: Net Address path too short: %s",
					__func__, referrals[i].node_name);
			rc = -EINVAL;
			goto out_err;
		}
		mnt = cifs_dfs_do_refmount(cifs_sb,
				full_path, referrals + i);
		cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
					referrals[i].node_name, mnt);

		/* complete mount procedure if we accured submount */
		if (!IS_ERR(mnt))
			break;
	}

	/* we need it cause for() above could exit without valid submount */
	rc = PTR_ERR(mnt);
	if (IS_ERR(mnt))
		goto out_err;

	rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);

out:
	FreeXid(xid);
	free_dfs_info_array(referrals, num_referrals);
	kfree(full_path);
	cFYI(1, "leaving %s" , __func__);
	return ERR_PTR(rc);
out_err:
	path_put(&nd->path);
	goto out;
}
Example #2
0
/*
 * Create a vfsmount that we can automount
 */
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
{
	struct dfs_info3_param *referrals = NULL;
	unsigned int num_referrals = 0;
	struct cifs_sb_info *cifs_sb;
	struct cifs_ses *ses;
	char *full_path;
	unsigned int xid;
	int i;
	int rc;
	struct vfsmount *mnt;
	struct tcon_link *tlink;

	cifs_dbg(FYI, "in %s\n", __func__);
	BUG_ON(IS_ROOT(mntpt));

	/*
	 * The MSDFS spec states that paths in DFS referral requests and
	 * responses must be prefixed by a single '\' character instead of
	 * the double backslashes usually used in the UNC. This function
	 * gives us the latter, so we must adjust the result.
	 */
	mnt = ERR_PTR(-ENOMEM);
	full_path = build_path_from_dentry(mntpt);
	if (full_path == NULL)
		goto cdda_exit;

	cifs_sb = CIFS_SB(d_inode(mntpt)->i_sb);
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink)) {
		mnt = ERR_CAST(tlink);
		goto free_full_path;
	}
	ses = tlink_tcon(tlink)->ses;

	xid = get_xid();
	rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
		&num_referrals, &referrals,
		cifs_remap(cifs_sb));
	free_xid(xid);

	cifs_put_tlink(tlink);

	mnt = ERR_PTR(-ENOENT);
	for (i = 0; i < num_referrals; i++) {
		int len;
		dump_referral(referrals + i);
		/* connect to a node */
		len = strlen(referrals[i].node_name);
		if (len < 2) {
			cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
				 __func__, referrals[i].node_name);
			mnt = ERR_PTR(-EINVAL);
			break;
		}
		mnt = cifs_dfs_do_refmount(cifs_sb,
				full_path, referrals + i);
		cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n",
			 __func__, referrals[i].node_name, mnt);
		if (!IS_ERR(mnt))
			goto success;
	}

	/* no valid submounts were found; return error from get_dfs_path() by
	 * preference */
	if (rc != 0)
		mnt = ERR_PTR(rc);

success:
	free_dfs_info_array(referrals, num_referrals);
free_full_path:
	kfree(full_path);
cdda_exit:
	cifs_dbg(FYI, "leaving %s\n" , __func__);
	return mnt;
}
Example #3
0
int
cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen)
{
	struct inode *inode = direntry->d_inode;
	int rc = -EACCES;
	int xid;
	int oplock = FALSE;
	struct cifs_sb_info *cifs_sb;
	struct cifsTconInfo *pTcon;
	char *full_path = NULL;
	char *tmp_path = NULL;
	char *tmpbuffer;
	unsigned char *referrals = NULL;
	unsigned int num_referrals = 0;
	int len;
	__u16 fid;

	xid = GetXid();
	cifs_sb = CIFS_SB(inode->i_sb);
	pTcon = cifs_sb->tcon;

/* BB would it be safe against deadlock to grab this sem
      even though rename itself grabs the sem and calls lookup? */
/*       mutex_lock(&inode->i_sb->s_vfs_rename_mutex);*/
	full_path = build_path_from_dentry(direntry);
/*       mutex_unlock(&inode->i_sb->s_vfs_rename_mutex);*/

	if (full_path == NULL) {
		FreeXid(xid);
		return -ENOMEM;
	}

	cFYI(1,
	     ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d",
	      full_path, inode, pBuffer, buflen));
	if (buflen > PATH_MAX)
		len = PATH_MAX;
	else
		len = buflen;
	tmpbuffer = kmalloc(len, GFP_KERNEL);
	if (tmpbuffer == NULL) {
		kfree(full_path);
		FreeXid(xid);
		return -ENOMEM;
	}

/* BB add read reparse point symlink code and
	Unix extensions symlink code here BB */
/* We could disable this based on pTcon->unix_ext flag instead ... but why? */
	if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
		rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
				tmpbuffer,
				len - 1,
				cifs_sb->local_nls);
	else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
		cERROR(1, ("SFU style symlinks not implemented yet"));
		/* add open and read as in fs/cifs/inode.c */
	} else {
		rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ,
				OPEN_REPARSE_POINT, &fid, &oplock, NULL,
				cifs_sb->local_nls,
				cifs_sb->mnt_cifs_flags &
					CIFS_MOUNT_MAP_SPECIAL_CHR);
		if (!rc) {
			rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path,
				tmpbuffer,
				len - 1,
				fid,
				cifs_sb->local_nls);
			if (CIFSSMBClose(xid, pTcon, fid)) {
				cFYI(1, ("Error closing junction point "
					 "(open for ioctl)"));
			}
			if (rc == -EIO) {
				/* Query if DFS Junction */
				tmp_path =
					kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1,
						GFP_KERNEL);
				if (tmp_path) {
					strncpy(tmp_path, pTcon->treeName,
						MAX_TREE_SIZE);
					strncat(tmp_path, full_path,
						MAX_PATHCONF);
					rc = get_dfs_path(xid, pTcon->ses,
						tmp_path,
						cifs_sb->local_nls,
						&num_referrals, &referrals,
						cifs_sb->mnt_cifs_flags &
						    CIFS_MOUNT_MAP_SPECIAL_CHR);
					cFYI(1, ("Get DFS for %s rc = %d ",
						tmp_path, rc));
					if ((num_referrals == 0) && (rc == 0))
						rc = -EACCES;
					else {
						cFYI(1, ("num referral: %d",
							num_referrals));
						if (referrals) {
							cFYI(1,("referral string: %s", referrals));
							strncpy(tmpbuffer,
								referrals,
								len-1);
						}
					}
					kfree(referrals);
					kfree(tmp_path);
}
				/* BB add code like else decode referrals
				then memcpy to tmpbuffer and free referrals
				string array BB */
			}
		}
	}
	/* BB Anything else to do to handle recursive links? */
	/* BB Should we be using page ops here? */

	/* BB null terminate returned string in pBuffer? BB */
	if (rc == 0) {
		rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer);
		cFYI(1,
		     ("vfs_readlink called from cifs_readlink returned %d",
		      rc));
	}

	kfree(tmpbuffer);
	kfree(full_path);
	FreeXid(xid);
	return rc;
}
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
{
	struct dfs_info3_param *referrals = NULL;
	unsigned int num_referrals = 0;
	struct cifs_sb_info *cifs_sb;
	struct cifs_ses *ses;
	char *full_path;
	int xid, i;
	int rc;
	struct vfsmount *mnt;
	struct tcon_link *tlink;

	cFYI(1, "in %s", __func__);
	BUG_ON(IS_ROOT(mntpt));

	mnt = ERR_PTR(-ENOMEM);
	full_path = build_path_from_dentry(mntpt);
	if (full_path == NULL)
		goto cdda_exit;

	cifs_sb = CIFS_SB(mntpt->d_inode->i_sb);
	tlink = cifs_sb_tlink(cifs_sb);
	if (IS_ERR(tlink)) {
		mnt = ERR_CAST(tlink);
		goto free_full_path;
	}
	ses = tlink_tcon(tlink)->ses;

	xid = GetXid();
	rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
		&num_referrals, &referrals,
		cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	FreeXid(xid);

	cifs_put_tlink(tlink);

	mnt = ERR_PTR(-ENOENT);
	for (i = 0; i < num_referrals; i++) {
		int len;
		dump_referral(referrals + i);
		
		len = strlen(referrals[i].node_name);
		if (len < 2) {
			cERROR(1, "%s: Net Address path too short: %s",
					__func__, referrals[i].node_name);
			mnt = ERR_PTR(-EINVAL);
			break;
		}
		mnt = cifs_dfs_do_refmount(cifs_sb,
				full_path, referrals + i);
		cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
					referrals[i].node_name, mnt);
		if (!IS_ERR(mnt))
			goto success;
	}

	if (rc != 0)
		mnt = ERR_PTR(rc);

success:
	free_dfs_info_array(referrals, num_referrals);
free_full_path:
	kfree(full_path);
cdda_exit:
	cFYI(1, "leaving %s" , __func__);
	return mnt;
}
Example #5
0
static void *cifs_dfs_follow_mountpoint(struct dentry *dentry,
					struct nameidata *nd)
{
	DFS_INFO3_PARAM *referrals = NULL;
	unsigned int num_referrals = 0;
	struct cifs_sb_info *cifs_sb;
	struct cifsSesInfo *ses;
	char *full_path = NULL;
	int xid, i;
	int rc = 0;
	struct vfsmount *mnt = ERR_PTR(-ENOENT);

	cFYI(1, ("in %s", __FUNCTION__ ));
	BUG_ON(IS_ROOT(dentry));

	xid = GetXid();

	dput(nd->dentry);
	nd->dentry = dget(dentry);
	if (d_mountpoint(nd->dentry)) {
		goto out_follow;
	}

	if ( dentry->d_inode == NULL ) {
		rc = -EINVAL;
		goto out_err;
	}

	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
	ses = cifs_sb->tcon->ses;

	if ( !ses ) {
		rc = -EINVAL;
		goto out_err;
	}

	full_path = build_full_dfs_path_from_dentry(dentry);
	if ( full_path == NULL ) {
		rc = -ENOMEM;
		goto out_err;
	}

	rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
			&num_referrals, &referrals,
			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);

	for (i = 0; i < num_referrals; i++) {
		cFYI(1, ("%s: ref path: %s", __FUNCTION__,
			 referrals[i].path_name));
		cFYI(1, ("%s: node path: %s", __FUNCTION__,
			 referrals[i].node_name ));
		cFYI(1, ("%s: fl: %hd, serv_type: %hd, ref_flags: %hd, "
			 "path_consumed: %hd", __FUNCTION__,
			 referrals[i].flags, referrals[i].server_type,
			 referrals[i].ref_flag, referrals[i].PathConsumed));

		/* connect to storage node */
		if (referrals[i].flags & DFSREF_STORAGE_SERVER) {
			int len;
			len = strlen(referrals[i].node_name);
			if (len < 2) {
				cERROR(1, ("%s: Net Address path too short: %s",
					__FUNCTION__, referrals[i].node_name ));
				rc = -EINVAL;
				goto out_err;
			} else {
				mnt = cifs_dfs_do_refmount(nd->mnt,
						nd->dentry,
						referrals[i].node_name);
				cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
					 __FUNCTION__,
					 referrals[i].node_name, mnt));
				if ( !rc ) {
					/* have server so stop here & return */
					break;
				}
			}
		}
	}

	rc = PTR_ERR(mnt);
	if (IS_ERR(mnt))
		goto out_err;

	mntget(mnt);
	rc = do_add_mount(mnt, nd, nd->mnt->mnt_flags,
			  &cifs_dfs_automount_list);
	if (rc < 0) {
		mntput(mnt);
		if (rc == -EBUSY)
			goto out_follow;
		goto out_err;
	}
	mntput(nd->mnt);
	dput(nd->dentry);
	nd->mnt = mnt;
	nd->dentry = dget(mnt->mnt_root);

out:
	FreeXid(xid);
	free_dfs_info_array(referrals, num_referrals);
	cFYI(1, ("leaving %s", __FUNCTION__ ));
	return ERR_PTR(rc);
out_err:
	if ( full_path ) kfree(full_path);
	path_release(nd);
	goto out;
out_follow:
	while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
		;
	rc = 0;
	goto out;
}
Example #6
0
static void*
cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
{
	struct dfs_info3_param *referrals = NULL;
	unsigned int num_referrals = 0;
	struct cifs_sb_info *cifs_sb;
	struct cifsSesInfo *ses;
	char *full_path = NULL;
	int xid, i;
	int rc = 0;
	struct vfsmount *mnt = ERR_PTR(-ENOENT);

	cFYI(1, ("in %s", __func__));
	BUG_ON(IS_ROOT(dentry));

	xid = GetXid();

	dput(nd->path.dentry);
	nd->path.dentry = dget(dentry);

	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
	ses = cifs_sb->tcon->ses;

	if (!ses) {
		rc = -EINVAL;
		goto out_err;
	}

	full_path = build_path_from_dentry(dentry);
	if (full_path == NULL) {
		rc = -ENOMEM;
		goto out_err;
	}

	rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,
		&num_referrals, &referrals,
		cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);

	for (i = 0; i < num_referrals; i++) {
		dump_referral(referrals+i);
		/* connect to a storage node */
		if (referrals[i].flags & DFSREF_STORAGE_SERVER) {
			int len;
			len = strlen(referrals[i].node_name);
			if (len < 2) {
				cERROR(1, ("%s: Net Address path too short: %s",
					__func__, referrals[i].node_name));
				rc = -EINVAL;
				goto out_err;
			}
			mnt = cifs_dfs_do_refmount(nd->path.mnt,
						nd->path.dentry,
						referrals + i);
			cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",
					 __func__,
					referrals[i].node_name, mnt));

			/* complete mount procedure if we accured submount */
			if (!IS_ERR(mnt))
				break;
		}
	}

	/* we need it cause for() above could exit without valid submount */
	rc = PTR_ERR(mnt);
	if (IS_ERR(mnt))
		goto out_err;

	nd->path.mnt->mnt_flags |= MNT_SHRINKABLE;
	rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list);

out:
	FreeXid(xid);
	free_dfs_info_array(referrals, num_referrals);
	kfree(full_path);
	cFYI(1, ("leaving %s" , __func__));
	return ERR_PTR(rc);
out_err:
	path_put(&nd->path);
	goto out;
}