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; }
/* * 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; }
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; }
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; }
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; }