diff options
Diffstat (limited to 'fs/cifs/link.c')
-rw-r--r-- | fs/cifs/link.c | 372 |
1 files changed, 348 insertions, 24 deletions
diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 473ca8033656..85cdbf831e7b 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c | |||
@@ -28,6 +28,296 @@ | |||
28 | #include "cifsproto.h" | 28 | #include "cifsproto.h" |
29 | #include "cifs_debug.h" | 29 | #include "cifs_debug.h" |
30 | #include "cifs_fs_sb.h" | 30 | #include "cifs_fs_sb.h" |
31 | #include "md5.h" | ||
32 | |||
33 | #define CIFS_MF_SYMLINK_LEN_OFFSET (4+1) | ||
34 | #define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1)) | ||
35 | #define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1)) | ||
36 | #define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) | ||
37 | #define CIFS_MF_SYMLINK_FILE_SIZE \ | ||
38 | (CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN) | ||
39 | |||
40 | #define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n" | ||
41 | #define CIFS_MF_SYMLINK_MD5_FORMAT \ | ||
42 | "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n" | ||
43 | #define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \ | ||
44 | md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \ | ||
45 | md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \ | ||
46 | md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\ | ||
47 | md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15] | ||
48 | |||
49 | static int | ||
50 | CIFSParseMFSymlink(const u8 *buf, | ||
51 | unsigned int buf_len, | ||
52 | unsigned int *_link_len, | ||
53 | char **_link_str) | ||
54 | { | ||
55 | int rc; | ||
56 | unsigned int link_len; | ||
57 | const char *md5_str1; | ||
58 | const char *link_str; | ||
59 | struct MD5Context md5_ctx; | ||
60 | u8 md5_hash[16]; | ||
61 | char md5_str2[34]; | ||
62 | |||
63 | if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) | ||
64 | return -EINVAL; | ||
65 | |||
66 | md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; | ||
67 | link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; | ||
68 | |||
69 | rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); | ||
70 | if (rc != 1) | ||
71 | return -EINVAL; | ||
72 | |||
73 | cifs_MD5_init(&md5_ctx); | ||
74 | cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); | ||
75 | cifs_MD5_final(md5_hash, &md5_ctx); | ||
76 | |||
77 | snprintf(md5_str2, sizeof(md5_str2), | ||
78 | CIFS_MF_SYMLINK_MD5_FORMAT, | ||
79 | CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); | ||
80 | |||
81 | if (strncmp(md5_str1, md5_str2, 17) != 0) | ||
82 | return -EINVAL; | ||
83 | |||
84 | if (_link_str) { | ||
85 | *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); | ||
86 | if (!*_link_str) | ||
87 | return -ENOMEM; | ||
88 | } | ||
89 | |||
90 | *_link_len = link_len; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int | ||
95 | CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) | ||
96 | { | ||
97 | unsigned int link_len; | ||
98 | unsigned int ofs; | ||
99 | struct MD5Context md5_ctx; | ||
100 | u8 md5_hash[16]; | ||
101 | |||
102 | if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) | ||
103 | return -EINVAL; | ||
104 | |||
105 | link_len = strlen(link_str); | ||
106 | |||
107 | if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) | ||
108 | return -ENAMETOOLONG; | ||
109 | |||
110 | cifs_MD5_init(&md5_ctx); | ||
111 | cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len); | ||
112 | cifs_MD5_final(md5_hash, &md5_ctx); | ||
113 | |||
114 | snprintf(buf, buf_len, | ||
115 | CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, | ||
116 | link_len, | ||
117 | CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); | ||
118 | |||
119 | ofs = CIFS_MF_SYMLINK_LINK_OFFSET; | ||
120 | memcpy(buf + ofs, link_str, link_len); | ||
121 | |||
122 | ofs += link_len; | ||
123 | if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
124 | buf[ofs] = '\n'; | ||
125 | ofs++; | ||
126 | } | ||
127 | |||
128 | while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { | ||
129 | buf[ofs] = ' '; | ||
130 | ofs++; | ||
131 | } | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int | ||
137 | CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon, | ||
138 | const char *fromName, const char *toName, | ||
139 | const struct nls_table *nls_codepage, int remap) | ||
140 | { | ||
141 | int rc; | ||
142 | int oplock = 0; | ||
143 | __u16 netfid = 0; | ||
144 | u8 *buf; | ||
145 | unsigned int bytes_written = 0; | ||
146 | |||
147 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
148 | if (!buf) | ||
149 | return -ENOMEM; | ||
150 | |||
151 | rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); | ||
152 | if (rc != 0) { | ||
153 | kfree(buf); | ||
154 | return rc; | ||
155 | } | ||
156 | |||
157 | rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, | ||
158 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | ||
159 | nls_codepage, remap); | ||
160 | if (rc != 0) { | ||
161 | kfree(buf); | ||
162 | return rc; | ||
163 | } | ||
164 | |||
165 | rc = CIFSSMBWrite(xid, tcon, netfid, | ||
166 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
167 | 0 /* offset */, | ||
168 | &bytes_written, buf, NULL, 0); | ||
169 | CIFSSMBClose(xid, tcon, netfid); | ||
170 | kfree(buf); | ||
171 | if (rc != 0) | ||
172 | return rc; | ||
173 | |||
174 | if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) | ||
175 | return -EIO; | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int | ||
181 | CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon, | ||
182 | const unsigned char *searchName, char **symlinkinfo, | ||
183 | const struct nls_table *nls_codepage, int remap) | ||
184 | { | ||
185 | int rc; | ||
186 | int oplock = 0; | ||
187 | __u16 netfid = 0; | ||
188 | u8 *buf; | ||
189 | char *pbuf; | ||
190 | unsigned int bytes_read = 0; | ||
191 | int buf_type = CIFS_NO_BUFFER; | ||
192 | unsigned int link_len = 0; | ||
193 | FILE_ALL_INFO file_info; | ||
194 | |||
195 | rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, | ||
196 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
197 | nls_codepage, remap); | ||
198 | if (rc != 0) | ||
199 | return rc; | ||
200 | |||
201 | if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { | ||
202 | CIFSSMBClose(xid, tcon, netfid); | ||
203 | /* it's not a symlink */ | ||
204 | return -EINVAL; | ||
205 | } | ||
206 | |||
207 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
208 | if (!buf) | ||
209 | return -ENOMEM; | ||
210 | pbuf = buf; | ||
211 | |||
212 | rc = CIFSSMBRead(xid, tcon, netfid, | ||
213 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
214 | 0 /* offset */, | ||
215 | &bytes_read, &pbuf, &buf_type); | ||
216 | CIFSSMBClose(xid, tcon, netfid); | ||
217 | if (rc != 0) { | ||
218 | kfree(buf); | ||
219 | return rc; | ||
220 | } | ||
221 | |||
222 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); | ||
223 | kfree(buf); | ||
224 | if (rc != 0) | ||
225 | return rc; | ||
226 | |||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | bool | ||
231 | CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) | ||
232 | { | ||
233 | if (!(fattr->cf_mode & S_IFREG)) | ||
234 | /* it's not a symlink */ | ||
235 | return false; | ||
236 | |||
237 | if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) | ||
238 | /* it's not a symlink */ | ||
239 | return false; | ||
240 | |||
241 | return true; | ||
242 | } | ||
243 | |||
244 | int | ||
245 | CIFSCheckMFSymlink(struct cifs_fattr *fattr, | ||
246 | const unsigned char *path, | ||
247 | struct cifs_sb_info *cifs_sb, int xid) | ||
248 | { | ||
249 | int rc; | ||
250 | int oplock = 0; | ||
251 | __u16 netfid = 0; | ||
252 | struct tcon_link *tlink; | ||
253 | struct cifsTconInfo *pTcon; | ||
254 | u8 *buf; | ||
255 | char *pbuf; | ||
256 | unsigned int bytes_read = 0; | ||
257 | int buf_type = CIFS_NO_BUFFER; | ||
258 | unsigned int link_len = 0; | ||
259 | FILE_ALL_INFO file_info; | ||
260 | |||
261 | if (!CIFSCouldBeMFSymlink(fattr)) | ||
262 | /* it's not a symlink */ | ||
263 | return 0; | ||
264 | |||
265 | tlink = cifs_sb_tlink(cifs_sb); | ||
266 | if (IS_ERR(tlink)) | ||
267 | return PTR_ERR(tlink); | ||
268 | pTcon = tlink_tcon(tlink); | ||
269 | |||
270 | rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, | ||
271 | CREATE_NOT_DIR, &netfid, &oplock, &file_info, | ||
272 | cifs_sb->local_nls, | ||
273 | cifs_sb->mnt_cifs_flags & | ||
274 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
275 | if (rc != 0) | ||
276 | goto out; | ||
277 | |||
278 | if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) { | ||
279 | CIFSSMBClose(xid, pTcon, netfid); | ||
280 | /* it's not a symlink */ | ||
281 | goto out; | ||
282 | } | ||
283 | |||
284 | buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); | ||
285 | if (!buf) { | ||
286 | rc = -ENOMEM; | ||
287 | goto out; | ||
288 | } | ||
289 | pbuf = buf; | ||
290 | |||
291 | rc = CIFSSMBRead(xid, pTcon, netfid, | ||
292 | CIFS_MF_SYMLINK_FILE_SIZE /* length */, | ||
293 | 0 /* offset */, | ||
294 | &bytes_read, &pbuf, &buf_type); | ||
295 | CIFSSMBClose(xid, pTcon, netfid); | ||
296 | if (rc != 0) { | ||
297 | kfree(buf); | ||
298 | goto out; | ||
299 | } | ||
300 | |||
301 | rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); | ||
302 | kfree(buf); | ||
303 | if (rc == -EINVAL) { | ||
304 | /* it's not a symlink */ | ||
305 | rc = 0; | ||
306 | goto out; | ||
307 | } | ||
308 | |||
309 | if (rc != 0) | ||
310 | goto out; | ||
311 | |||
312 | /* it is a symlink */ | ||
313 | fattr->cf_eof = link_len; | ||
314 | fattr->cf_mode &= ~S_IFMT; | ||
315 | fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; | ||
316 | fattr->cf_dtype = DT_LNK; | ||
317 | out: | ||
318 | cifs_put_tlink(tlink); | ||
319 | return rc; | ||
320 | } | ||
31 | 321 | ||
32 | int | 322 | int |
33 | cifs_hardlink(struct dentry *old_file, struct inode *inode, | 323 | cifs_hardlink(struct dentry *old_file, struct inode *inode, |
@@ -37,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
37 | int xid; | 327 | int xid; |
38 | char *fromName = NULL; | 328 | char *fromName = NULL; |
39 | char *toName = NULL; | 329 | char *toName = NULL; |
40 | struct cifs_sb_info *cifs_sb_target; | 330 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
331 | struct tcon_link *tlink; | ||
41 | struct cifsTconInfo *pTcon; | 332 | struct cifsTconInfo *pTcon; |
42 | struct cifsInodeInfo *cifsInode; | 333 | struct cifsInodeInfo *cifsInode; |
43 | 334 | ||
44 | xid = GetXid(); | 335 | tlink = cifs_sb_tlink(cifs_sb); |
45 | 336 | if (IS_ERR(tlink)) | |
46 | cifs_sb_target = CIFS_SB(inode->i_sb); | 337 | return PTR_ERR(tlink); |
47 | pTcon = cifs_sb_target->tcon; | 338 | pTcon = tlink_tcon(tlink); |
48 | 339 | ||
49 | /* No need to check for cross device links since server will do that | 340 | xid = GetXid(); |
50 | BB note DFS case in future though (when we may have to check) */ | ||
51 | 341 | ||
52 | fromName = build_path_from_dentry(old_file); | 342 | fromName = build_path_from_dentry(old_file); |
53 | toName = build_path_from_dentry(direntry); | 343 | toName = build_path_from_dentry(direntry); |
@@ -56,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, | |||
56 | goto cifs_hl_exit; | 346 | goto cifs_hl_exit; |
57 | } | 347 | } |
58 | 348 | ||
59 | /* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/ | ||
60 | if (pTcon->unix_ext) | 349 | if (pTcon->unix_ext) |
61 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, | 350 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, |
62 | cifs_sb_target->local_nls, | 351 | cifs_sb->local_nls, |
63 | cifs_sb_target->mnt_cifs_flags & | 352 | cifs_sb->mnt_cifs_flags & |
64 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 353 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
65 | else { | 354 | else { |
66 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, | 355 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, |
67 | cifs_sb_target->local_nls, | 356 | cifs_sb->local_nls, |
68 | cifs_sb_target->mnt_cifs_flags & | 357 | cifs_sb->mnt_cifs_flags & |
69 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 358 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
70 | if ((rc == -EIO) || (rc == -EINVAL)) | 359 | if ((rc == -EIO) || (rc == -EINVAL)) |
71 | rc = -EOPNOTSUPP; | 360 | rc = -EOPNOTSUPP; |
@@ -101,6 +390,7 @@ cifs_hl_exit: | |||
101 | kfree(fromName); | 390 | kfree(fromName); |
102 | kfree(toName); | 391 | kfree(toName); |
103 | FreeXid(xid); | 392 | FreeXid(xid); |
393 | cifs_put_tlink(tlink); | ||
104 | return rc; | 394 | return rc; |
105 | } | 395 | } |
106 | 396 | ||
@@ -113,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
113 | char *full_path = NULL; | 403 | char *full_path = NULL; |
114 | char *target_path = NULL; | 404 | char *target_path = NULL; |
115 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | 405 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
116 | struct cifsTconInfo *tcon = cifs_sb->tcon; | 406 | struct tcon_link *tlink = NULL; |
407 | struct cifsTconInfo *tcon; | ||
117 | 408 | ||
118 | xid = GetXid(); | 409 | xid = GetXid(); |
119 | 410 | ||
411 | tlink = cifs_sb_tlink(cifs_sb); | ||
412 | if (IS_ERR(tlink)) { | ||
413 | rc = PTR_ERR(tlink); | ||
414 | tlink = NULL; | ||
415 | goto out; | ||
416 | } | ||
417 | tcon = tlink_tcon(tlink); | ||
418 | |||
120 | /* | 419 | /* |
121 | * For now, we just handle symlinks with unix extensions enabled. | 420 | * For now, we just handle symlinks with unix extensions enabled. |
122 | * Eventually we should handle NTFS reparse points, and MacOS | 421 | * Eventually we should handle NTFS reparse points, and MacOS |
@@ -130,7 +429,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 | 429 | * but there doesn't seem to be any harm in allowing the client to |
131 | * read them. | 430 | * read them. |
132 | */ | 431 | */ |
133 | if (!(tcon->ses->capabilities & CAP_UNIX)) { | 432 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
433 | && !(tcon->ses->capabilities & CAP_UNIX)) { | ||
134 | rc = -EACCES; | 434 | rc = -EACCES; |
135 | goto out; | 435 | goto out; |
136 | } | 436 | } |
@@ -141,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | |||
141 | 441 | ||
142 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); | 442 | cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); |
143 | 443 | ||
144 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | 444 | rc = -EACCES; |
145 | cifs_sb->local_nls); | 445 | /* |
446 | * First try Minshall+French Symlinks, if configured | ||
447 | * and fallback to UNIX Extensions Symlinks. | ||
448 | */ | ||
449 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) | ||
450 | rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, | ||
451 | cifs_sb->local_nls, | ||
452 | cifs_sb->mnt_cifs_flags & | ||
453 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
454 | |||
455 | if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) | ||
456 | rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, | ||
457 | cifs_sb->local_nls); | ||
458 | |||
146 | kfree(full_path); | 459 | kfree(full_path); |
147 | out: | 460 | out: |
148 | if (rc != 0) { | 461 | if (rc != 0) { |
@@ -151,6 +464,8 @@ out: | |||
151 | } | 464 | } |
152 | 465 | ||
153 | FreeXid(xid); | 466 | FreeXid(xid); |
467 | if (tlink) | ||
468 | cifs_put_tlink(tlink); | ||
154 | nd_set_link(nd, target_path); | 469 | nd_set_link(nd, target_path); |
155 | return NULL; | 470 | return NULL; |
156 | } | 471 | } |
@@ -160,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
160 | { | 475 | { |
161 | int rc = -EOPNOTSUPP; | 476 | int rc = -EOPNOTSUPP; |
162 | int xid; | 477 | int xid; |
163 | struct cifs_sb_info *cifs_sb; | 478 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); |
479 | struct tcon_link *tlink; | ||
164 | struct cifsTconInfo *pTcon; | 480 | struct cifsTconInfo *pTcon; |
165 | char *full_path = NULL; | 481 | char *full_path = NULL; |
166 | struct inode *newinode = NULL; | 482 | struct inode *newinode = NULL; |
167 | 483 | ||
168 | xid = GetXid(); | 484 | xid = GetXid(); |
169 | 485 | ||
170 | cifs_sb = CIFS_SB(inode->i_sb); | 486 | tlink = cifs_sb_tlink(cifs_sb); |
171 | pTcon = cifs_sb->tcon; | 487 | if (IS_ERR(tlink)) { |
488 | rc = PTR_ERR(tlink); | ||
489 | goto symlink_exit; | ||
490 | } | ||
491 | pTcon = tlink_tcon(tlink); | ||
172 | 492 | ||
173 | full_path = build_path_from_dentry(direntry); | 493 | full_path = build_path_from_dentry(direntry); |
174 | |||
175 | if (full_path == NULL) { | 494 | if (full_path == NULL) { |
176 | rc = -ENOMEM; | 495 | rc = -ENOMEM; |
177 | FreeXid(xid); | 496 | goto symlink_exit; |
178 | return rc; | ||
179 | } | 497 | } |
180 | 498 | ||
181 | cFYI(1, "Full path: %s", full_path); | 499 | cFYI(1, "Full path: %s", full_path); |
182 | cFYI(1, "symname is %s", symname); | 500 | cFYI(1, "symname is %s", symname); |
183 | 501 | ||
184 | /* BB what if DFS and this volume is on different share? BB */ | 502 | /* BB what if DFS and this volume is on different share? BB */ |
185 | if (pTcon->unix_ext) | 503 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) |
504 | rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, | ||
505 | cifs_sb->local_nls, | ||
506 | cifs_sb->mnt_cifs_flags & | ||
507 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
508 | else if (pTcon->unix_ext) | ||
186 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, | 509 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, |
187 | cifs_sb->local_nls); | 510 | cifs_sb->local_nls); |
188 | /* else | 511 | /* else |
@@ -208,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | |||
208 | d_instantiate(direntry, newinode); | 531 | d_instantiate(direntry, newinode); |
209 | } | 532 | } |
210 | } | 533 | } |
211 | 534 | symlink_exit: | |
212 | kfree(full_path); | 535 | kfree(full_path); |
536 | cifs_put_tlink(tlink); | ||
213 | FreeXid(xid); | 537 | FreeXid(xid); |
214 | return rc; | 538 | return rc; |
215 | } | 539 | } |