Merge branch 'for-linus' of git://git.infradead.org/ubi-2.6
[deliverable/linux.git] / fs / cifs / link.c
CommitLineData
1da177e4
LT
1/*
2 * fs/cifs/link.c
3 *
366781c1 4 * Copyright (C) International Business Machines Corp., 2002,2008
1da177e4
LT
5 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21#include <linux/fs.h>
22#include <linux/stat.h>
5a0e3ad6 23#include <linux/slab.h>
1da177e4
LT
24#include <linux/namei.h>
25#include "cifsfs.h"
26#include "cifspdu.h"
27#include "cifsglob.h"
28#include "cifsproto.h"
29#include "cifs_debug.h"
30#include "cifs_fs_sb.h"
c69c1b6e
SM
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
93c100c0
SF
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);
ee2c9258 57 if (IS_ERR(md5)) {
93c100c0 58 cERROR(1, "%s: Crypto md5 allocation error %d\n", __func__, rc);
ee2c9258 59 return PTR_ERR(md5);
93c100c0
SF
60 }
61 size = sizeof(struct shash_desc) + crypto_shash_descsize(md5);
62 sdescmd5 = kmalloc(size, GFP_KERNEL);
63 if (!sdescmd5) {
64 rc = -ENOMEM;
65 cERROR(1, "%s: Memory allocation failure\n", __func__);
66 goto symlink_hash_err;
67 }
68 sdescmd5->shash.tfm = md5;
69 sdescmd5->shash.flags = 0x0;
70
71 rc = crypto_shash_init(&sdescmd5->shash);
72 if (rc) {
73 cERROR(1, "%s: Could not init md5 shash\n", __func__);
74 goto symlink_hash_err;
75 }
76 crypto_shash_update(&sdescmd5->shash, link_str, link_len);
77 rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
78
79symlink_hash_err:
80 crypto_free_shash(md5);
81 kfree(sdescmd5);
82
83 return rc;
84}
85
c69c1b6e
SM
86static int
87CIFSParseMFSymlink(const u8 *buf,
88 unsigned int buf_len,
89 unsigned int *_link_len,
90 char **_link_str)
91{
92 int rc;
93 unsigned int link_len;
94 const char *md5_str1;
95 const char *link_str;
c69c1b6e
SM
96 u8 md5_hash[16];
97 char md5_str2[34];
98
99 if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
100 return -EINVAL;
101
102 md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
103 link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
104
105 rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
106 if (rc != 1)
107 return -EINVAL;
108
93c100c0
SF
109 rc = symlink_hash(link_len, link_str, md5_hash);
110 if (rc) {
111 cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc);
112 return rc;
113 }
c69c1b6e
SM
114
115 snprintf(md5_str2, sizeof(md5_str2),
116 CIFS_MF_SYMLINK_MD5_FORMAT,
117 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
118
119 if (strncmp(md5_str1, md5_str2, 17) != 0)
120 return -EINVAL;
121
122 if (_link_str) {
123 *_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
124 if (!*_link_str)
125 return -ENOMEM;
126 }
127
128 *_link_len = link_len;
129 return 0;
130}
1da177e4 131
0fd43ae4 132static int
18bddd10
SM
133CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
134{
93c100c0 135 int rc;
18bddd10
SM
136 unsigned int link_len;
137 unsigned int ofs;
18bddd10
SM
138 u8 md5_hash[16];
139
140 if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
141 return -EINVAL;
142
143 link_len = strlen(link_str);
144
145 if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
146 return -ENAMETOOLONG;
147
93c100c0
SF
148 rc = symlink_hash(link_len, link_str, md5_hash);
149 if (rc) {
150 cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc);
151 return rc;
152 }
18bddd10
SM
153
154 snprintf(buf, buf_len,
155 CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
156 link_len,
157 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
158
159 ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
160 memcpy(buf + ofs, link_str, link_len);
161
162 ofs += link_len;
163 if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
164 buf[ofs] = '\n';
165 ofs++;
166 }
167
168 while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
169 buf[ofs] = ' ';
170 ofs++;
171 }
172
173 return 0;
174}
175
8713d01d
SM
176static int
177CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon,
178 const char *fromName, const char *toName,
179 const struct nls_table *nls_codepage, int remap)
180{
181 int rc;
182 int oplock = 0;
183 __u16 netfid = 0;
184 u8 *buf;
185 unsigned int bytes_written = 0;
186
187 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
188 if (!buf)
189 return -ENOMEM;
190
191 rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
192 if (rc != 0) {
193 kfree(buf);
194 return rc;
195 }
196
197 rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
198 CREATE_NOT_DIR, &netfid, &oplock, NULL,
199 nls_codepage, remap);
200 if (rc != 0) {
201 kfree(buf);
202 return rc;
203 }
204
205 rc = CIFSSMBWrite(xid, tcon, netfid,
206 CIFS_MF_SYMLINK_FILE_SIZE /* length */,
207 0 /* offset */,
208 &bytes_written, buf, NULL, 0);
209 CIFSSMBClose(xid, tcon, netfid);
210 kfree(buf);
211 if (rc != 0)
212 return rc;
213
214 if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
215 return -EIO;
216
217 return 0;
218}
219
220static int
0fd43ae4
SM
221CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon,
222 const unsigned char *searchName, char **symlinkinfo,
223 const struct nls_table *nls_codepage, int remap)
224{
225 int rc;
226 int oplock = 0;
227 __u16 netfid = 0;
228 u8 *buf;
229 char *pbuf;
230 unsigned int bytes_read = 0;
231 int buf_type = CIFS_NO_BUFFER;
232 unsigned int link_len = 0;
233 FILE_ALL_INFO file_info;
234
235 rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ,
236 CREATE_NOT_DIR, &netfid, &oplock, &file_info,
237 nls_codepage, remap);
238 if (rc != 0)
239 return rc;
240
241 if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
242 CIFSSMBClose(xid, tcon, netfid);
243 /* it's not a symlink */
244 return -EINVAL;
245 }
246
247 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
248 if (!buf)
249 return -ENOMEM;
250 pbuf = buf;
251
252 rc = CIFSSMBRead(xid, tcon, netfid,
253 CIFS_MF_SYMLINK_FILE_SIZE /* length */,
254 0 /* offset */,
255 &bytes_read, &pbuf, &buf_type);
256 CIFSSMBClose(xid, tcon, netfid);
257 if (rc != 0) {
258 kfree(buf);
259 return rc;
260 }
261
262 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo);
263 kfree(buf);
264 if (rc != 0)
265 return rc;
266
267 return 0;
268}
269
8bfb50a8
SM
270bool
271CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
272{
273 if (!(fattr->cf_mode & S_IFREG))
274 /* it's not a symlink */
275 return false;
276
277 if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
278 /* it's not a symlink */
279 return false;
280
281 return true;
282}
283
284int
285CIFSCheckMFSymlink(struct cifs_fattr *fattr,
286 const unsigned char *path,
287 struct cifs_sb_info *cifs_sb, int xid)
288{
289 int rc;
290 int oplock = 0;
291 __u16 netfid = 0;
7ffec372
JL
292 struct tcon_link *tlink;
293 struct cifsTconInfo *pTcon;
8bfb50a8
SM
294 u8 *buf;
295 char *pbuf;
296 unsigned int bytes_read = 0;
297 int buf_type = CIFS_NO_BUFFER;
298 unsigned int link_len = 0;
299 FILE_ALL_INFO file_info;
300
301 if (!CIFSCouldBeMFSymlink(fattr))
302 /* it's not a symlink */
303 return 0;
304
7ffec372
JL
305 tlink = cifs_sb_tlink(cifs_sb);
306 if (IS_ERR(tlink))
307 return PTR_ERR(tlink);
308 pTcon = tlink_tcon(tlink);
309
8bfb50a8
SM
310 rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
311 CREATE_NOT_DIR, &netfid, &oplock, &file_info,
312 cifs_sb->local_nls,
313 cifs_sb->mnt_cifs_flags &
314 CIFS_MOUNT_MAP_SPECIAL_CHR);
315 if (rc != 0)
7ffec372 316 goto out;
8bfb50a8
SM
317
318 if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
319 CIFSSMBClose(xid, pTcon, netfid);
320 /* it's not a symlink */
7ffec372 321 goto out;
8bfb50a8
SM
322 }
323
324 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
7ffec372
JL
325 if (!buf) {
326 rc = -ENOMEM;
327 goto out;
328 }
8bfb50a8
SM
329 pbuf = buf;
330
331 rc = CIFSSMBRead(xid, pTcon, netfid,
332 CIFS_MF_SYMLINK_FILE_SIZE /* length */,
333 0 /* offset */,
334 &bytes_read, &pbuf, &buf_type);
335 CIFSSMBClose(xid, pTcon, netfid);
336 if (rc != 0) {
337 kfree(buf);
7ffec372 338 goto out;
8bfb50a8
SM
339 }
340
341 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
342 kfree(buf);
7ffec372 343 if (rc == -EINVAL) {
8bfb50a8 344 /* it's not a symlink */
7ffec372
JL
345 rc = 0;
346 goto out;
347 }
348
8bfb50a8 349 if (rc != 0)
7ffec372 350 goto out;
8bfb50a8
SM
351
352 /* it is a symlink */
353 fattr->cf_eof = link_len;
354 fattr->cf_mode &= ~S_IFMT;
355 fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
356 fattr->cf_dtype = DT_LNK;
7ffec372
JL
357out:
358 cifs_put_tlink(tlink);
359 return rc;
8bfb50a8
SM
360}
361
1da177e4
LT
362int
363cifs_hardlink(struct dentry *old_file, struct inode *inode,
364 struct dentry *direntry)
365{
366 int rc = -EACCES;
367 int xid;
368 char *fromName = NULL;
369 char *toName = NULL;
7ffec372
JL
370 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
371 struct tcon_link *tlink;
1da177e4
LT
372 struct cifsTconInfo *pTcon;
373 struct cifsInodeInfo *cifsInode;
374
7ffec372
JL
375 tlink = cifs_sb_tlink(cifs_sb);
376 if (IS_ERR(tlink))
377 return PTR_ERR(tlink);
378 pTcon = tlink_tcon(tlink);
1da177e4 379
7ffec372 380 xid = GetXid();
1da177e4 381
7f57356b
SF
382 fromName = build_path_from_dentry(old_file);
383 toName = build_path_from_dentry(direntry);
fb8c4b14 384 if ((fromName == NULL) || (toName == NULL)) {
1da177e4
LT
385 rc = -ENOMEM;
386 goto cifs_hl_exit;
387 }
388
c18c842b 389 if (pTcon->unix_ext)
1da177e4 390 rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
7ffec372
JL
391 cifs_sb->local_nls,
392 cifs_sb->mnt_cifs_flags &
737b758c 393 CIFS_MOUNT_MAP_SPECIAL_CHR);
1da177e4
LT
394 else {
395 rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
7ffec372
JL
396 cifs_sb->local_nls,
397 cifs_sb->mnt_cifs_flags &
737b758c 398 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14
SF
399 if ((rc == -EIO) || (rc == -EINVAL))
400 rc = -EOPNOTSUPP;
1da177e4
LT
401 }
402
31ec35d6
SF
403 d_drop(direntry); /* force new lookup from server of target */
404
405 /* if source file is cached (oplocked) revalidate will not go to server
406 until the file is closed or oplock broken so update nlinks locally */
fb8c4b14 407 if (old_file->d_inode) {
31ec35d6 408 cifsInode = CIFS_I(old_file->d_inode);
fb8c4b14 409 if (rc == 0) {
31ec35d6 410 old_file->d_inode->i_nlink++;
1b2b2126
SF
411/* BB should we make this contingent on superblock flag NOATIME? */
412/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
31ec35d6
SF
413 /* parent dir timestamps will update from srv
414 within a second, would it really be worth it
415 to set the parent dir cifs inode time to zero
416 to force revalidate (faster) for it too? */
417 }
fb8c4b14 418 /* if not oplocked will force revalidate to get info
31ec35d6
SF
419 on source file from srv */
420 cifsInode->time = 0;
421
fb8c4b14 422 /* Will update parent dir timestamps from srv within a second.
31ec35d6
SF
423 Would it really be worth it to set the parent dir (cifs
424 inode) time field to zero to force revalidate on parent
fb8c4b14 425 directory faster ie
31ec35d6 426 CIFS_I(inode)->time = 0; */
1da177e4 427 }
1da177e4
LT
428
429cifs_hl_exit:
f99d49ad
JJ
430 kfree(fromName);
431 kfree(toName);
1da177e4 432 FreeXid(xid);
7ffec372 433 cifs_put_tlink(tlink);
1da177e4
LT
434 return rc;
435}
436
cc314eef 437void *
1da177e4
LT
438cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
439{
440 struct inode *inode = direntry->d_inode;
8b6427a2 441 int rc = -ENOMEM;
1da177e4
LT
442 int xid;
443 char *full_path = NULL;
8b6427a2
JL
444 char *target_path = NULL;
445 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
7ffec372
JL
446 struct tcon_link *tlink = NULL;
447 struct cifsTconInfo *tcon;
1da177e4
LT
448
449 xid = GetXid();
450
7ffec372
JL
451 tlink = cifs_sb_tlink(cifs_sb);
452 if (IS_ERR(tlink)) {
453 rc = PTR_ERR(tlink);
454 tlink = NULL;
455 goto out;
456 }
457 tcon = tlink_tcon(tlink);
458
8b6427a2
JL
459 /*
460 * For now, we just handle symlinks with unix extensions enabled.
461 * Eventually we should handle NTFS reparse points, and MacOS
462 * symlink support. For instance...
463 *
464 * rc = CIFSSMBQueryReparseLinkInfo(...)
465 *
466 * For now, just return -EACCES when the server doesn't support posix
467 * extensions. Note that we still allow querying symlinks when posix
468 * extensions are manually disabled. We could disable these as well
469 * but there doesn't seem to be any harm in allowing the client to
470 * read them.
471 */
1b12b9c1
SM
472 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
473 && !(tcon->ses->capabilities & CAP_UNIX)) {
8b6427a2
JL
474 rc = -EACCES;
475 goto out;
476 }
1da177e4 477
8b6427a2 478 full_path = build_path_from_dentry(direntry);
1da177e4 479 if (!full_path)
460b9696 480 goto out;
1da177e4 481
b6b38f70 482 cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
1da177e4 483
1b12b9c1
SM
484 rc = -EACCES;
485 /*
486 * First try Minshall+French Symlinks, if configured
487 * and fallback to UNIX Extensions Symlinks.
488 */
489 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
490 rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path,
491 cifs_sb->local_nls,
492 cifs_sb->mnt_cifs_flags &
493 CIFS_MOUNT_MAP_SPECIAL_CHR);
494
495 if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX))
496 rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
497 cifs_sb->local_nls);
498
8b6427a2
JL
499 kfree(full_path);
500out:
460b9696 501 if (rc != 0) {
1da177e4
LT
502 kfree(target_path);
503 target_path = ERR_PTR(rc);
504 }
505
1da177e4 506 FreeXid(xid);
7ffec372
JL
507 if (tlink)
508 cifs_put_tlink(tlink);
1da177e4 509 nd_set_link(nd, target_path);
460b9696 510 return NULL;
1da177e4
LT
511}
512
513int
514cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
515{
516 int rc = -EOPNOTSUPP;
517 int xid;
7ffec372
JL
518 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
519 struct tcon_link *tlink;
1da177e4
LT
520 struct cifsTconInfo *pTcon;
521 char *full_path = NULL;
522 struct inode *newinode = NULL;
523
524 xid = GetXid();
525
7ffec372
JL
526 tlink = cifs_sb_tlink(cifs_sb);
527 if (IS_ERR(tlink)) {
528 rc = PTR_ERR(tlink);
529 goto symlink_exit;
530 }
531 pTcon = tlink_tcon(tlink);
1da177e4 532
7f57356b 533 full_path = build_path_from_dentry(direntry);
fb8c4b14 534 if (full_path == NULL) {
0f3bc09e 535 rc = -ENOMEM;
7ffec372 536 goto symlink_exit;
1da177e4
LT
537 }
538
b6b38f70
JP
539 cFYI(1, "Full path: %s", full_path);
540 cFYI(1, "symname is %s", symname);
1da177e4
LT
541
542 /* BB what if DFS and this volume is on different share? BB */
1b12b9c1
SM
543 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
544 rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
545 cifs_sb->local_nls,
546 cifs_sb->mnt_cifs_flags &
547 CIFS_MOUNT_MAP_SPECIAL_CHR);
548 else if (pTcon->unix_ext)
1da177e4
LT
549 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
550 cifs_sb->local_nls);
551 /* else
fb8c4b14
SF
552 rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
553 cifs_sb_target->local_nls); */
1da177e4
LT
554
555 if (rc == 0) {
c18c842b 556 if (pTcon->unix_ext)
1da177e4 557 rc = cifs_get_inode_info_unix(&newinode, full_path,
fb8c4b14 558 inode->i_sb, xid);
1da177e4
LT
559 else
560 rc = cifs_get_inode_info(&newinode, full_path, NULL,
8b1327f6 561 inode->i_sb, xid, NULL);
1da177e4
LT
562
563 if (rc != 0) {
b6b38f70
JP
564 cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d",
565 rc);
1da177e4 566 } else {
1da177e4
LT
567 d_instantiate(direntry, newinode);
568 }
569 }
7ffec372 570symlink_exit:
f99d49ad 571 kfree(full_path);
7ffec372 572 cifs_put_tlink(tlink);
1da177e4
LT
573 FreeXid(xid);
574 return rc;
575}
576
cc314eef 577void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
1da177e4
LT
578{
579 char *p = nd_get_link(nd);
580 if (!IS_ERR(p))
581 kfree(p);
582}
This page took 0.518398 seconds and 5 git commands to generate.