diff options
Diffstat (limited to 'fs/cifs/link.c')
-rw-r--r-- | fs/cifs/link.c | 431 |
1 files changed, 401 insertions, 30 deletions
diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 473ca8033656..556b1a0b54de 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c | |||
@@ -29,6 +29,347 @@ | |||
29 | #include "cifs_debug.h" | 29 | #include "cifs_debug.h" |
30 | #include "cifs_fs_sb.h" | 30 | #include "cifs_fs_sb.h" |
31 | 31 | ||
32 | #define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) | ||
33 | #define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) | ||
34 | #define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) | ||
35 | #define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) | ||
36 | #define CIFS_MF_SYMLINK_FILE_SIZE \ | ||
37 | (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) | ||
38 | |||
39 | #define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" | ||
40 | #define CIFS_MF_SYMLINK_MD5_FORMAT \ | ||
41 | "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n" | ||
42 | #define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \ | ||
43 | md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \ | ||
44 | md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \ | ||
45 | md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\ | ||
46 | md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15] | ||
47 | |||
48 | static int | ||
49 | symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash) | ||
50 | { | ||
51 | int rc; | ||
52 | unsigned int size; | ||
53 | struct crypto_shash *md5; | ||
54 | struct sdesc *sdescmd5; | ||
55 | |||
56 | md5 = crypto_alloc_shash("md5", 0, 0); | ||
57 | if (IS_ERR(md5)) { | ||
58 | rc = PTR_ERR(md5); | ||
59 | cERROR(1, "%s: Crypto md5 allocation error %d\n", __func__, rc); | ||
60 | return rc; | ||
61 | } | ||
62 | size = sizeof(struct shash_desc) + crypto_shash_descsize(md5); | ||
63 | sdescmd5 = kmalloc(size, GFP_KERNEL); | ||
64 | if (!sdescmd5) { | ||
65 | rc = -ENOMEM; | ||
66 | cERROR(1, "%s: Memory allocation failure\n", __func__); | ||
67 | goto symlink_hash_err; | ||
68 | } | ||
69 | sdescmd5->shash.tfm = md5; | ||
70 | sdescmd5->shash.flags = 0x0; | ||
71 | |||
72 | rc = crypto_shash_init(&sdescmd5->shash); | ||
73 | if (rc) { | ||
74 | cERROR(1, "%s: Could not init md5 shash\n", __func__); | ||
75 | goto symlink_hash_err; | ||
76 | } | ||
77 | crypto_shash_update(&sdescmd5->shash, link_str, link_len); | ||
78 | rc = crypto_shash_final(&sdescmd5->shash, md5_hash); | ||
79 | |||
80 | symlink_hash_err: | ||
81 | crypto_free_shash(md5); | ||
82 | kfree(sdescmd5); | ||
83 | |||
84 | return rc; | ||
85 | } | ||
86 | |||
87 | static int | ||
88 | CIFSParseMFSymlink(const u8 *buf, | ||
89 | unsigned int buf_len, | ||
90 | unsigned int *_link_len, | ||
91 | char **_link_str) | ||
92 | { | ||
93 | int rc; | ||
94 | unsigned int link_len; | ||
95 | const char *md5_str1; | ||
96 | const char *link_str; | ||
97 | u8 md5_hash[16]; | ||
98 | char md5_str2[34]; | ||
99 | |||
100 | if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) | ||
101 | return -EINVAL; | ||
102 | |||
103 | md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; | ||
104 | link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; | ||
105 | |||
106 | rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); | ||
107 | if (rc != 1) | ||
108 | return -EINVAL; | ||
109 | |||
110 | rc = symlink_hash(link_len, link_str, md5_hash); | ||
111 | if (rc) { | ||
112 | cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc); | ||
113 | return rc; | ||
114 | } | ||
115 | |||
116 | snprintf(md5_str2, sizeof(md5_str2), | ||
117 | CIFS_MF_SYMLINK_MD5_FORMAT, | ||
118 | CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); | ||
119 | |||
120 | if (strncmp(md5_str1, md5_str2, 17) != 0) | ||
121 | return -EINVAL; | ||
122 | |||
123 | if (_link_str) { | ||
124 | *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); | ||
125 | if (!*_link_str) | ||
126 | return -ENOMEM; | ||
127 | } | ||
128 | |||
129 | *_link_len = link_len; | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int | ||
134 | CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) | ||
135 | { | ||
136 | int rc; | ||
137 | unsigned int link_len; | ||
138 | unsigned int ofs; | ||
139 | u8 md5_hash[16]; | ||
140 | |||
141 | if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) | ||
142 | return -EINVAL; | ||
143 | |||
144 | link_len = strlen(link_str); | ||
145 | |||
146 | if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) | ||
147 | return -ENAMETOOLONG; | ||
148 | |||
149 | rc = symlink_hash(link_len, link_str, md5_hash); | ||
150 | if (rc) { | ||
151 | cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc); | ||
152 | return rc; | ||
153 | } | ||
154 | |||
155 | snprintf(buf, buf_len, | ||
156 | CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, | ||
157 | link_len, | ||
158 | CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); | ||
159 | |||
160 | ofs = CIFS_MF_SYMLINK_LINK_OFFSET; | ||
161 | memcpy(buf + ofs, link_str, link_len); | ||
162 | |||
163 | ofs += link_len; | ||
164 | if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
165 | buf[ofs] = '\n'; | ||
166 | ofs++; | ||
167 | } | ||
168 | |||
169 | while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
170 | buf[ofs] = ' '; | ||
171 | ofs++; | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon, | ||
179 | const char *fromName, const char *toName, | ||
180 | const struct nls_table *nls_codepage, int remap) | ||
181 | { | ||
182 | int rc; | ||
183 | int oplock = 0; | ||
184 | __u16 netfid = 0; | ||
185 | u8 *buf; | ||
186 | unsigned int bytes_written = 0; | ||
187 | struct cifs_io_parms io_parms; | ||
188 | |||
189 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
190 | if (!buf) | ||
191 | return -ENOMEM; | ||
192 | |||
193 | rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); | ||
194 | if (rc != 0) { | ||
195 | kfree(buf); | ||
196 | return rc; | ||
197 | } | ||
198 | |||
199 | rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, | ||
200 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | ||
201 | nls_codepage, remap); | ||
202 | if (rc != 0) { | ||
203 | kfree(buf); | ||
204 | return rc; | ||
205 | } | ||
206 | |||
207 | io_parms.netfid = netfid; | ||
208 | io_parms.pid = current->tgid; | ||
209 | io_parms.tcon = tcon; | ||
210 | io_parms.offset = 0; | ||
211 | io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; | ||
212 | |||
213 | rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, buf, NULL, 0); | ||
214 | CIFSSMBClose(xid, tcon, netfid); | ||
215 | kfree(buf); | ||
216 | if (rc != 0) | ||
217 | return rc; | ||
218 | |||
219 | if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) | ||
220 | return -EIO; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int | ||
226 | CIFSQueryMFSymLink(const int xid, struct cifs_tcon *tcon, | ||
227 | const unsigned char *searchName, char **symlinkinfo, | ||
228 | const struct nls_table *nls_codepage, int remap) | ||
229 | { | ||
230 | int rc; | ||
231 | int oplock = 0; | ||
232 | __u16 netfid = 0; | ||
233 | u8 *buf; | ||
234 | char *pbuf; | ||
235 | unsigned int bytes_read = 0; | ||
236 | int buf_type = CIFS_NO_BUFFER; | ||
237 | unsigned int link_len = 0; | ||
238 | struct cifs_io_parms io_parms; | ||
239 | FILE_ALL_INFO file_info; | ||
240 | |||
241 | rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, | ||
242 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
243 | nls_codepage, remap); | ||
244 | if (rc != 0) | ||
245 | return rc; | ||
246 | |||
247 | if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { | ||
248 | CIFSSMBClose(xid, tcon, netfid); | ||
249 | /* it's not a symlink */ | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
254 | if (!buf) | ||
255 | return -ENOMEM; | ||
256 | pbuf = buf; | ||
257 | io_parms.netfid = netfid; | ||
258 | io_parms.pid = current->tgid; | ||
259 | io_parms.tcon = tcon; | ||
260 | io_parms.offset = 0; | ||
261 | io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; | ||
262 | |||
263 | rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); | ||
264 | CIFSSMBClose(xid, tcon, netfid); | ||
265 | if (rc != 0) { | ||
266 | kfree(buf); | ||
267 | return rc; | ||
268 | } | ||
269 | |||
270 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); | ||
271 | kfree(buf); | ||
272 | if (rc != 0) | ||
273 | return rc; | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | bool | ||
279 | CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) | ||
280 | { | ||
281 | if (!(fattr->cf_mode & S_IFREG)) | ||
282 | /* it's not a symlink */ | ||
283 | return false; | ||
284 | |||
285 | if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) | ||
286 | /* it's not a symlink */ | ||
287 | return false; | ||
288 | |||
289 | return true; | ||
290 | } | ||
291 | |||
292 | int | ||
293 | CIFSCheckMFSymlink(struct cifs_fattr *fattr, | ||
294 | const unsigned char *path, | ||
295 | struct cifs_sb_info *cifs_sb, int xid) | ||
296 | { | ||
297 | int rc; | ||
298 | int oplock = 0; | ||
299 | __u16 netfid = 0; | ||
300 | struct tcon_link *tlink; | ||
301 | struct cifs_tcon *pTcon; | ||
302 | struct cifs_io_parms io_parms; | ||
303 | u8 *buf; | ||
304 | char *pbuf; | ||
305 | unsigned int bytes_read = 0; | ||
306 | int buf_type = CIFS_NO_BUFFER; | ||
307 | unsigned int link_len = 0; | ||
308 | FILE_ALL_INFO file_info; | ||
309 | |||
310 | if (!CIFSCouldBeMFSymlink(fattr)) | ||
311 | /* it's not a symlink */ | ||
312 | return 0; | ||
313 | |||
314 | tlink = cifs_sb_tlink(cifs_sb); | ||
315 | if (IS_ERR(tlink)) | ||
316 | return PTR_ERR(tlink); | ||
317 | pTcon = tlink_tcon(tlink); | ||
318 | |||
319 | rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, | ||
320 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
321 | cifs_sb->local_nls, | ||
322 | cifs_sb->mnt_cifs_flags & | ||
323 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
324 | if (rc != 0) | ||
325 | goto out; | ||
326 | |||
327 | if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { | ||
328 | CIFSSMBClose(xid, pTcon, netfid); | ||
329 | /* it's not a symlink */ | ||
330 | goto out; | ||
331 | } | ||
332 | |||
333 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
334 | if (!buf) { | ||
335 | rc = -ENOMEM; | ||
336 | goto out; | ||
337 | } | ||
338 | pbuf = buf; | ||
339 | io_parms.netfid = netfid; | ||
340 | io_parms.pid = current->tgid; | ||
341 | io_parms.tcon = pTcon; | ||
342 | io_parms.offset = 0; | ||
343 | io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; | ||
344 | |||
345 | rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); | ||
346 | CIFSSMBClose(xid, pTcon, netfid); | ||
347 | if (rc != 0) { | ||
348 | kfree(buf); | ||
349 | goto out; | ||
350 | } | ||
351 | |||
352 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); | ||
353 | kfree(buf); | ||
354 | if (rc == -EINVAL) { | ||
355 | /* it's not a symlink */ | ||
356 | rc = 0; | ||
357 | goto out; | ||
358 | } | ||
359 | |||
360 | if (rc != 0) | ||
361 | goto out; | ||
362 | |||
363 | /* it is a symlink */ | ||
364 | fattr->cf_eof = link_len; | ||
365 | fattr->cf_mode &= ~S_IFMT; | ||
366 | fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | ||
367 | fattr->cf_dtype = DT_LNK; | ||
368 | out: | ||
369 | cifs_put_tlink(tlink); | ||
370 | return rc; | ||
371 | } | ||
372 | |||
32 | int | 373 | int |
33 | cifs_hardlink(struct dentry *old_file, struct inode *inode, | 374 | cifs_hardlink(struct dentry *old_file, struct inode *inode, |
34 | struct dentry *direntry) | 375 | struct dentry *direntry) |
@@ -37,17 +378,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
37 | int xid; | 378 | int xid; |
38 | char *fromName = NULL; | 379 | char *fromName = NULL; |
39 | char *toName = NULL; | 380 | char *toName = NULL; |
40 | struct cifs_sb_info *cifs_sb_target; | 381 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
41 | struct cifsTconInfo *pTcon; | 382 | struct tcon_link *tlink; |
383 | struct cifs_tcon *pTcon; | ||
42 | struct cifsInodeInfo *cifsInode; | 384 | struct cifsInodeInfo *cifsInode; |
43 | 385 | ||
44 | xid = GetXid(); | 386 | tlink = cifs_sb_tlink(cifs_sb); |
45 | 387 | if (IS_ERR(tlink)) | |
46 | cifs_sb_target = CIFS_SB(inode->i_sb); | 388 | return PTR_ERR(tlink); |
47 | pTcon = cifs_sb_target->tcon; | 389 | pTcon = tlink_tcon(tlink); |
48 | 390 | ||
49 | /* No need to check for cross device links since server will do that | 391 | xid = GetXid(); |
50 | BB note DFS case in future though (when we may have to check) */ | ||
51 | 392 | ||
52 | fromName = build_path_from_dentry(old_file); | 393 | fromName = build_path_from_dentry(old_file); |
53 | toName = build_path_from_dentry(direntry); | 394 | toName = build_path_from_dentry(direntry); |
@@ -56,16 +397,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
56 | goto cifs_hl_exit; | 397 | goto cifs_hl_exit; |
57 | } | 398 | } |
58 | 399 | ||
59 | /* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ | ||
60 | if (pTcon->unix_ext) | 400 | if (pTcon->unix_ext) |
61 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, | 401 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, |
62 | cifs_sb_target->local_nls, | 402 | cifs_sb->local_nls, |
63 | cifs_sb_target->mnt_cifs_flags & | 403 | cifs_sb->mnt_cifs_flags & |
64 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 404 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
65 | else { | 405 | else { |
66 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, | 406 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, |
67 | cifs_sb_target->local_nls, | 407 | cifs_sb->local_nls, |
68 | cifs_sb_target->mnt_cifs_flags & | 408 | cifs_sb->mnt_cifs_flags & |
69 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 409 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
70 | if ((rc == -EIO) || (rc == -EINVAL)) | 410 | if ((rc == -EIO) || (rc == -EINVAL)) |
71 | rc = -EOPNOTSUPP; | 411 | rc = -EOPNOTSUPP; |
@@ -101,6 +441,7 @@ cifs_hl_exit: | |||
101 | kfree(fromName); | 441 | kfree(fromName); |
102 | kfree(toName); | 442 | kfree(toName); |
103 | FreeXid(xid); | 443 | FreeXid(xid); |
444 | cifs_put_tlink(tlink); | ||
104 | return rc; | 445 | return rc; |
105 | } | 446 | } |
106 | 447 | ||
@@ -113,10 +454,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
113 | char *full_path = NULL; | 454 | char *full_path = NULL; |
114 | char *target_path = NULL; | 455 | char *target_path = NULL; |
115 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 456 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
116 | struct cifsTconInfo *tcon = cifs_sb->tcon; | 457 | struct tcon_link *tlink = NULL; |
458 | struct cifs_tcon *tcon; | ||
117 | 459 | ||
118 | xid = GetXid(); | 460 | xid = GetXid(); |
119 | 461 | ||
462 | tlink = cifs_sb_tlink(cifs_sb); | ||
463 | if (IS_ERR(tlink)) { | ||
464 | rc = PTR_ERR(tlink); | ||
465 | tlink = NULL; | ||
466 | goto out; | ||
467 | } | ||
468 | tcon = tlink_tcon(tlink); | ||
469 | |||
120 | /* | 470 | /* |
121 | * For now, we just handle symlinks with unix extensions enabled. | 471 | * For now, we just handle symlinks with unix extensions enabled. |
122 | * Eventually we should handle NTFS reparse points, and MacOS | 472 | * Eventually we should handle NTFS reparse points, and MacOS |
@@ -130,7 +480,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
130 | * but there doesn't seem to be any harm in allowing the client to | 480 | * but there doesn't seem to be any harm in allowing the client to |
131 | * read them. | 481 | * read them. |
132 | */ | 482 | */ |
133 | if (!(tcon->ses->capabilities & CAP_UNIX)) { | 483 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
484 | && !(tcon->ses->capabilities & CAP_UNIX)) { | ||
134 | rc = -EACCES; | 485 | rc = -EACCES; |
135 | goto out; | 486 | goto out; |
136 | } | 487 | } |
@@ -141,8 +492,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
141 | 492 | ||
142 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); | 493 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); |
143 | 494 | ||
144 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | 495 | rc = -EACCES; |
145 | cifs_sb->local_nls); | 496 | /* |
497 | * First try Minshall+French Symlinks, if configured | ||
498 | * and fallback to UNIX Extensions Symlinks. | ||
499 | */ | ||
500 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) | ||
501 | rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, | ||
502 | cifs_sb->local_nls, | ||
503 | cifs_sb->mnt_cifs_flags & | ||
504 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
505 | |||
506 | if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) | ||
507 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | ||
508 | cifs_sb->local_nls); | ||
509 | |||
146 | kfree(full_path); | 510 | kfree(full_path); |
147 | out: | 511 | out: |
148 | if (rc != 0) { | 512 | if (rc != 0) { |
@@ -151,6 +515,8 @@ out: | |||
151 | } | 515 | } |
152 | 516 | ||
153 | FreeXid(xid); | 517 | FreeXid(xid); |
518 | if (tlink) | ||
519 | cifs_put_tlink(tlink); | ||
154 | nd_set_link(nd, target_path); | 520 | nd_set_link(nd, target_path); |
155 | return NULL; | 521 | return NULL; |
156 | } | 522 | } |
@@ -160,29 +526,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
160 | { | 526 | { |
161 | int rc = -EOPNOTSUPP; | 527 | int rc = -EOPNOTSUPP; |
162 | int xid; | 528 | int xid; |
163 | struct cifs_sb_info *cifs_sb; | 529 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
164 | struct cifsTconInfo *pTcon; | 530 | struct tcon_link *tlink; |
531 | struct cifs_tcon *pTcon; | ||
165 | char *full_path = NULL; | 532 | char *full_path = NULL; |
166 | struct inode *newinode = NULL; | 533 | struct inode *newinode = NULL; |
167 | 534 | ||
168 | xid = GetXid(); | 535 | xid = GetXid(); |
169 | 536 | ||
170 | cifs_sb = CIFS_SB(inode->i_sb); | 537 | tlink = cifs_sb_tlink(cifs_sb); |
171 | pTcon = cifs_sb->tcon; | 538 | if (IS_ERR(tlink)) { |
539 | rc = PTR_ERR(tlink); | ||
540 | goto symlink_exit; | ||
541 | } | ||
542 | pTcon = tlink_tcon(tlink); | ||
172 | 543 | ||
173 | full_path = build_path_from_dentry(direntry); | 544 | full_path = build_path_from_dentry(direntry); |
174 | |||
175 | if (full_path == NULL) { | 545 | if (full_path == NULL) { |
176 | rc = -ENOMEM; | 546 | rc = -ENOMEM; |
177 | FreeXid(xid); | 547 | goto symlink_exit; |
178 | return rc; | ||
179 | } | 548 | } |
180 | 549 | ||
181 | cFYI(1, "Full path: %s", full_path); | 550 | cFYI(1, "Full path: %s", full_path); |
182 | cFYI(1, "symname is %s", symname); | 551 | cFYI(1, "symname is %s", symname); |
183 | 552 | ||
184 | /* BB what if DFS and this volume is on different share? BB */ | 553 | /* BB what if DFS and this volume is on different share? BB */ |
185 | if (pTcon->unix_ext) | 554 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
555 | rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, | ||
556 | cifs_sb->local_nls, | ||
557 | cifs_sb->mnt_cifs_flags & | ||
558 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
559 | else if (pTcon->unix_ext) | ||
186 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, | 560 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, |
187 | cifs_sb->local_nls); | 561 | cifs_sb->local_nls); |
188 | /* else | 562 | /* else |
@@ -201,15 +575,12 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
201 | cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d", | 575 | cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d", |
202 | rc); | 576 | rc); |
203 | } else { | 577 | } else { |
204 | if (pTcon->nocase) | ||
205 | direntry->d_op = &cifs_ci_dentry_ops; | ||
206 | else | ||
207 | direntry->d_op = &cifs_dentry_ops; | ||
208 | d_instantiate(direntry, newinode); | 578 | d_instantiate(direntry, newinode); |
209 | } | 579 | } |
210 | } | 580 | } |
211 | 581 | symlink_exit: | |
212 | kfree(full_path); | 582 | kfree(full_path); |
583 | cifs_put_tlink(tlink); | ||
213 | FreeXid(xid); | 584 | FreeXid(xid); |
214 | return rc; | 585 | return rc; |
215 | } | 586 | } |