]> bbs.cooldavid.org Git - net-next-2.6.git/blame - fs/cifs/link.c
xps: Transmit Packet Steering
[net-next-2.6.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#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
49static int
50CIFSParseMFSymlink(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}
1da177e4 93
0fd43ae4 94static int
18bddd10
SM
95CIFSFormatMFSymlink(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
8713d01d
SM
136static int
137CIFSCreateMFSymLink(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
180static int
0fd43ae4
SM
181CIFSQueryMFSymLink(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
8bfb50a8
SM
230bool
231CIFSCouldBeMFSymlink(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
244int
245CIFSCheckMFSymlink(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;
7ffec372
JL
252 struct tcon_link *tlink;
253 struct cifsTconInfo *pTcon;
8bfb50a8
SM
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
7ffec372
JL
265 tlink = cifs_sb_tlink(cifs_sb);
266 if (IS_ERR(tlink))
267 return PTR_ERR(tlink);
268 pTcon = tlink_tcon(tlink);
269
8bfb50a8
SM
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)
7ffec372 276 goto out;
8bfb50a8
SM
277
278 if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
279 CIFSSMBClose(xid, pTcon, netfid);
280 /* it's not a symlink */
7ffec372 281 goto out;
8bfb50a8
SM
282 }
283
284 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
7ffec372
JL
285 if (!buf) {
286 rc = -ENOMEM;
287 goto out;
288 }
8bfb50a8
SM
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);
7ffec372 298 goto out;
8bfb50a8
SM
299 }
300
301 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
302 kfree(buf);
7ffec372 303 if (rc == -EINVAL) {
8bfb50a8 304 /* it's not a symlink */
7ffec372
JL
305 rc = 0;
306 goto out;
307 }
308
8bfb50a8 309 if (rc != 0)
7ffec372 310 goto out;
8bfb50a8
SM
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;
7ffec372
JL
317out:
318 cifs_put_tlink(tlink);
319 return rc;
8bfb50a8
SM
320}
321
1da177e4
LT
322int
323cifs_hardlink(struct dentry *old_file, struct inode *inode,
324 struct dentry *direntry)
325{
326 int rc = -EACCES;
327 int xid;
328 char *fromName = NULL;
329 char *toName = NULL;
7ffec372
JL
330 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
331 struct tcon_link *tlink;
1da177e4
LT
332 struct cifsTconInfo *pTcon;
333 struct cifsInodeInfo *cifsInode;
334
7ffec372
JL
335 tlink = cifs_sb_tlink(cifs_sb);
336 if (IS_ERR(tlink))
337 return PTR_ERR(tlink);
338 pTcon = tlink_tcon(tlink);
1da177e4 339
7ffec372 340 xid = GetXid();
1da177e4 341
7f57356b
SF
342 fromName = build_path_from_dentry(old_file);
343 toName = build_path_from_dentry(direntry);
fb8c4b14 344 if ((fromName == NULL) || (toName == NULL)) {
1da177e4
LT
345 rc = -ENOMEM;
346 goto cifs_hl_exit;
347 }
348
c18c842b 349 if (pTcon->unix_ext)
1da177e4 350 rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
7ffec372
JL
351 cifs_sb->local_nls,
352 cifs_sb->mnt_cifs_flags &
737b758c 353 CIFS_MOUNT_MAP_SPECIAL_CHR);
1da177e4
LT
354 else {
355 rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
7ffec372
JL
356 cifs_sb->local_nls,
357 cifs_sb->mnt_cifs_flags &
737b758c 358 CIFS_MOUNT_MAP_SPECIAL_CHR);
fb8c4b14
SF
359 if ((rc == -EIO) || (rc == -EINVAL))
360 rc = -EOPNOTSUPP;
1da177e4
LT
361 }
362
31ec35d6
SF
363 d_drop(direntry); /* force new lookup from server of target */
364
365 /* if source file is cached (oplocked) revalidate will not go to server
366 until the file is closed or oplock broken so update nlinks locally */
fb8c4b14 367 if (old_file->d_inode) {
31ec35d6 368 cifsInode = CIFS_I(old_file->d_inode);
fb8c4b14 369 if (rc == 0) {
31ec35d6 370 old_file->d_inode->i_nlink++;
1b2b2126
SF
371/* BB should we make this contingent on superblock flag NOATIME? */
372/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
31ec35d6
SF
373 /* parent dir timestamps will update from srv
374 within a second, would it really be worth it
375 to set the parent dir cifs inode time to zero
376 to force revalidate (faster) for it too? */
377 }
fb8c4b14 378 /* if not oplocked will force revalidate to get info
31ec35d6
SF
379 on source file from srv */
380 cifsInode->time = 0;
381
fb8c4b14 382 /* Will update parent dir timestamps from srv within a second.
31ec35d6
SF
383 Would it really be worth it to set the parent dir (cifs
384 inode) time field to zero to force revalidate on parent
fb8c4b14 385 directory faster ie
31ec35d6 386 CIFS_I(inode)->time = 0; */
1da177e4 387 }
1da177e4
LT
388
389cifs_hl_exit:
f99d49ad
JJ
390 kfree(fromName);
391 kfree(toName);
1da177e4 392 FreeXid(xid);
7ffec372 393 cifs_put_tlink(tlink);
1da177e4
LT
394 return rc;
395}
396
cc314eef 397void *
1da177e4
LT
398cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
399{
400 struct inode *inode = direntry->d_inode;
8b6427a2 401 int rc = -ENOMEM;
1da177e4
LT
402 int xid;
403 char *full_path = NULL;
8b6427a2
JL
404 char *target_path = NULL;
405 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
7ffec372
JL
406 struct tcon_link *tlink = NULL;
407 struct cifsTconInfo *tcon;
1da177e4
LT
408
409 xid = GetXid();
410
7ffec372
JL
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
8b6427a2
JL
419 /*
420 * For now, we just handle symlinks with unix extensions enabled.
421 * Eventually we should handle NTFS reparse points, and MacOS
422 * symlink support. For instance...
423 *
424 * rc = CIFSSMBQueryReparseLinkInfo(...)
425 *
426 * For now, just return -EACCES when the server doesn't support posix
427 * extensions. Note that we still allow querying symlinks when posix
428 * extensions are manually disabled. We could disable these as well
429 * but there doesn't seem to be any harm in allowing the client to
430 * read them.
431 */
1b12b9c1
SM
432 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
433 && !(tcon->ses->capabilities & CAP_UNIX)) {
8b6427a2
JL
434 rc = -EACCES;
435 goto out;
436 }
1da177e4 437
8b6427a2 438 full_path = build_path_from_dentry(direntry);
1da177e4 439 if (!full_path)
460b9696 440 goto out;
1da177e4 441
b6b38f70 442 cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
1da177e4 443
1b12b9c1
SM
444 rc = -EACCES;
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
8b6427a2
JL
459 kfree(full_path);
460out:
460b9696 461 if (rc != 0) {
1da177e4
LT
462 kfree(target_path);
463 target_path = ERR_PTR(rc);
464 }
465
1da177e4 466 FreeXid(xid);
7ffec372
JL
467 if (tlink)
468 cifs_put_tlink(tlink);
1da177e4 469 nd_set_link(nd, target_path);
460b9696 470 return NULL;
1da177e4
LT
471}
472
473int
474cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
475{
476 int rc = -EOPNOTSUPP;
477 int xid;
7ffec372
JL
478 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
479 struct tcon_link *tlink;
1da177e4
LT
480 struct cifsTconInfo *pTcon;
481 char *full_path = NULL;
482 struct inode *newinode = NULL;
483
484 xid = GetXid();
485
7ffec372
JL
486 tlink = cifs_sb_tlink(cifs_sb);
487 if (IS_ERR(tlink)) {
488 rc = PTR_ERR(tlink);
489 goto symlink_exit;
490 }
491 pTcon = tlink_tcon(tlink);
1da177e4 492
7f57356b 493 full_path = build_path_from_dentry(direntry);
fb8c4b14 494 if (full_path == NULL) {
0f3bc09e 495 rc = -ENOMEM;
7ffec372 496 goto symlink_exit;
1da177e4
LT
497 }
498
b6b38f70
JP
499 cFYI(1, "Full path: %s", full_path);
500 cFYI(1, "symname is %s", symname);
1da177e4
LT
501
502 /* BB what if DFS and this volume is on different share? BB */
1b12b9c1
SM
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)
1da177e4
LT
509 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
510 cifs_sb->local_nls);
511 /* else
fb8c4b14
SF
512 rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
513 cifs_sb_target->local_nls); */
1da177e4
LT
514
515 if (rc == 0) {
c18c842b 516 if (pTcon->unix_ext)
1da177e4 517 rc = cifs_get_inode_info_unix(&newinode, full_path,
fb8c4b14 518 inode->i_sb, xid);
1da177e4
LT
519 else
520 rc = cifs_get_inode_info(&newinode, full_path, NULL,
8b1327f6 521 inode->i_sb, xid, NULL);
1da177e4
LT
522
523 if (rc != 0) {
b6b38f70
JP
524 cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d",
525 rc);
1da177e4 526 } else {
b92327fe
SF
527 if (pTcon->nocase)
528 direntry->d_op = &cifs_ci_dentry_ops;
529 else
530 direntry->d_op = &cifs_dentry_ops;
1da177e4
LT
531 d_instantiate(direntry, newinode);
532 }
533 }
7ffec372 534symlink_exit:
f99d49ad 535 kfree(full_path);
7ffec372 536 cifs_put_tlink(tlink);
1da177e4
LT
537 FreeXid(xid);
538 return rc;
539}
540
cc314eef 541void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
1da177e4
LT
542{
543 char *p = nd_get_link(nd);
544 if (!IS_ERR(p))
545 kfree(p);
546}