aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/cifs_dfs_ref.c
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2013-05-24 07:40:06 -0400
committerSteve French <sfrench@us.ibm.com>2013-05-24 14:08:31 -0400
commitd9deef0a3f38bcc1c155e8d9a8b522404e5e648c (patch)
tree4a2b632b4ac08d96d29dd847a02f0a2410cdf101 /fs/cifs/cifs_dfs_ref.c
parent9c9c29e1af2ff0459087876e3800078555794b60 (diff)
cifs: fix composing of mount options for DFS referrals
With the change to ignore the unc= and prefixpath= mount options, there is no longer any need to add them to the options string when mounting. By the same token, we now need to build a device name that includes the prefixpath when mounting. To make things neater, the delimiters on the devicename are changed to '/' since that's preferred when mounting anyway. v2: fix some comments and don't bother looking at whether there is a prepath in the ref->node_name when deciding whether to pass a prepath to cifs_build_devname. v3: rebase on top of potential buffer overrun fix for stable Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/cifs_dfs_ref.c')
-rw-r--r--fs/cifs/cifs_dfs_ref.c141
1 files changed, 71 insertions, 70 deletions
diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 8e5a1817496d..58df174deb10 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -49,58 +49,74 @@ void cifs_dfs_release_automount_timer(void)
49} 49}
50 50
51/** 51/**
52 * cifs_get_share_name - extracts share name from UNC 52 * cifs_build_devname - build a devicename from a UNC and optional prepath
53 * @node_name: pointer to UNC string 53 * @nodename: pointer to UNC string
54 * @prepath: pointer to prefixpath (or NULL if there isn't one)
54 * 55 *
55 * Extracts sharename form full UNC. 56 * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
56 * i.e. strips from UNC trailing path that is not part of share 57 * big enough to hold the final thing. Copy the UNC from the nodename, and
57 * name and fixup missing '\' in the beginning of DFS node refferal 58 * concatenate the prepath onto the end of it if there is one.
58 * if necessary. 59 *
59 * Returns pointer to share name on success or ERR_PTR on error. 60 * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
60 * Caller is responsible for freeing returned string. 61 * for freeing the returned string.
61 */ 62 */
62static char *cifs_get_share_name(const char *node_name) 63static char *
64cifs_build_devname(char *nodename, const char *prepath)
63{ 65{
64 int len; 66 size_t pplen;
65 char *UNC; 67 size_t unclen;
66 char *pSep; 68 char *dev;
67 69 char *pos;
68 len = strlen(node_name); 70
69 UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, 71 /* skip over any preceding delimiters */
70 GFP_KERNEL); 72 nodename += strspn(nodename, "\\");
71 if (!UNC) 73 if (!*nodename)
72 return ERR_PTR(-ENOMEM); 74 return ERR_PTR(-EINVAL);
73 75
74 /* get share name and server name */ 76 /* get length of UNC and set pos to last char */
75 if (node_name[1] != '\\') { 77 unclen = strlen(nodename);
76 UNC[0] = '\\'; 78 pos = nodename + unclen - 1;
77 strncpy(UNC+1, node_name, len);
78 len++;
79 UNC[len] = 0;
80 } else {
81 strncpy(UNC, node_name, len);
82 UNC[len] = 0;
83 }
84 79
85 /* find server name end */ 80 /* trim off any trailing delimiters */
86 pSep = memchr(UNC+2, '\\', len-2); 81 while (*pos == '\\') {
87 if (!pSep) { 82 --pos;
88 cifs_dbg(VFS, "%s: no server name end in node name: %s\n", 83 --unclen;
89 __func__, node_name);
90 kfree(UNC);
91 return ERR_PTR(-EINVAL);
92 } 84 }
93 85
94 /* find sharename end */ 86 /* allocate a buffer:
95 pSep++; 87 * +2 for preceding "//"
96 pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); 88 * +1 for delimiter between UNC and prepath
97 if (pSep) { 89 * +1 for trailing NULL
98 /* trim path up to sharename end 90 */
99 * now we have share name in UNC */ 91 pplen = prepath ? strlen(prepath) : 0;
100 *pSep = 0; 92 dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
93 if (!dev)
94 return ERR_PTR(-ENOMEM);
95
96 pos = dev;
97 /* add the initial "//" */
98 *pos = '/';
99 ++pos;
100 *pos = '/';
101 ++pos;
102
103 /* copy in the UNC portion from referral */
104 memcpy(pos, nodename, unclen);
105 pos += unclen;
106
107 /* copy the prefixpath remainder (if there is one) */
108 if (pplen) {
109 *pos = '/';
110 ++pos;
111 memcpy(pos, prepath, pplen);
112 pos += pplen;
101 } 113 }
102 114
103 return UNC; 115 /* NULL terminator */
116 *pos = '\0';
117
118 convert_delimiter(dev, '/');
119 return dev;
104} 120}
105 121
106 122
@@ -124,6 +140,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
124{ 140{
125 int rc; 141 int rc;
126 char *mountdata = NULL; 142 char *mountdata = NULL;
143 const char *prepath = NULL;
127 int md_len; 144 int md_len;
128 char *tkn_e; 145 char *tkn_e;
129 char *srvIP = NULL; 146 char *srvIP = NULL;
@@ -133,7 +150,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
133 if (sb_mountdata == NULL) 150 if (sb_mountdata == NULL)
134 return ERR_PTR(-EINVAL); 151 return ERR_PTR(-EINVAL);
135 152
136 *devname = cifs_get_share_name(ref->node_name); 153 if (strlen(fullpath) - ref->path_consumed)
154 prepath = fullpath + ref->path_consumed;
155
156 *devname = cifs_build_devname(ref->node_name, prepath);
137 if (IS_ERR(*devname)) { 157 if (IS_ERR(*devname)) {
138 rc = PTR_ERR(*devname); 158 rc = PTR_ERR(*devname);
139 *devname = NULL; 159 *devname = NULL;
@@ -147,13 +167,14 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
147 goto compose_mount_options_err; 167 goto compose_mount_options_err;
148 } 168 }
149 169
150 /* md_len = strlen(...) + 12 for 'sep+prefixpath=' 170 /*
151 * assuming that we have 'unc=' and 'ip=' in 171 * In most cases, we'll be building a shorter string than the original,
152 * the original sb_mountdata 172 * but we do have to assume that the address in the ip= option may be
173 * much longer than the original. Add the max length of an address
174 * string to the length of the original string to allow for worst case.
153 */ 175 */
154 md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12 + 176 md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
155 INET6_ADDRSTRLEN; 177 mountdata = kzalloc(md_len + 1, GFP_KERNEL);
156 mountdata = kzalloc(md_len+1, GFP_KERNEL);
157 if (mountdata == NULL) { 178 if (mountdata == NULL) {
158 rc = -ENOMEM; 179 rc = -ENOMEM;
159 goto compose_mount_options_err; 180 goto compose_mount_options_err;
@@ -197,26 +218,6 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
197 strncat(mountdata, &sep, 1); 218 strncat(mountdata, &sep, 1);
198 strcat(mountdata, "ip="); 219 strcat(mountdata, "ip=");
199 strcat(mountdata, srvIP); 220 strcat(mountdata, srvIP);
200 strncat(mountdata, &sep, 1);
201 strcat(mountdata, "unc=");
202 strcat(mountdata, *devname);
203
204 /* find & copy prefixpath */
205 tkn_e = strchr(ref->node_name + 2, '\\');
206 if (tkn_e == NULL) {
207 /* invalid unc, missing share name*/
208 rc = -EINVAL;
209 goto compose_mount_options_err;
210 }
211
212 tkn_e = strchr(tkn_e + 1, '\\');
213 if (tkn_e || (strlen(fullpath) - ref->path_consumed)) {
214 strncat(mountdata, &sep, 1);
215 strcat(mountdata, "prefixpath=");
216 if (tkn_e)
217 strcat(mountdata, tkn_e + 1);
218 strcat(mountdata, fullpath + ref->path_consumed);
219 }
220 221
221 /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/ 222 /*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
222 /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/ 223 /*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/