aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/link.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/link.c')
-rw-r--r--fs/cifs/link.c431
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
48static int
49symlink_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
80symlink_hash_err:
81 crypto_free_shash(md5);
82 kfree(sdescmd5);
83
84 return rc;
85}
86
87static int
88CIFSParseMFSymlink(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
133static int
134CIFSFormatMFSymlink(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
177static int
178CIFSCreateMFSymLink(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
225static int
226CIFSQueryMFSymLink(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
278bool
279CIFSCouldBeMFSymlink(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
292int
293CIFSCheckMFSymlink(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;
368out:
369 cifs_put_tlink(tlink);
370 return rc;
371}
372
32int 373int
33cifs_hardlink(struct dentry *old_file, struct inode *inode, 374cifs_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);
147out: 511out:
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 581symlink_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}