Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.3-rc2 606 lines 15 kB view raw
1/* 2 * fs/cifs/link.c 3 * 4 * Copyright (C) International Business Machines Corp., 2002,2008 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> 23#include <linux/slab.h> 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" 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 rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len); 78 if (rc) { 79 cERROR(1, "%s: Could not update iwth link_str\n", __func__); 80 goto symlink_hash_err; 81 } 82 rc = crypto_shash_final(&sdescmd5->shash, md5_hash); 83 if (rc) 84 cERROR(1, "%s: Could not generate md5 hash\n", __func__); 85 86symlink_hash_err: 87 crypto_free_shash(md5); 88 kfree(sdescmd5); 89 90 return rc; 91} 92 93static int 94CIFSParseMFSymlink(const u8 *buf, 95 unsigned int buf_len, 96 unsigned int *_link_len, 97 char **_link_str) 98{ 99 int rc; 100 unsigned int link_len; 101 const char *md5_str1; 102 const char *link_str; 103 u8 md5_hash[16]; 104 char md5_str2[34]; 105 106 if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) 107 return -EINVAL; 108 109 md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET]; 110 link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET]; 111 112 rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len); 113 if (rc != 1) 114 return -EINVAL; 115 116 rc = symlink_hash(link_len, link_str, md5_hash); 117 if (rc) { 118 cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc); 119 return rc; 120 } 121 122 snprintf(md5_str2, sizeof(md5_str2), 123 CIFS_MF_SYMLINK_MD5_FORMAT, 124 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); 125 126 if (strncmp(md5_str1, md5_str2, 17) != 0) 127 return -EINVAL; 128 129 if (_link_str) { 130 *_link_str = kstrndup(link_str, link_len, GFP_KERNEL); 131 if (!*_link_str) 132 return -ENOMEM; 133 } 134 135 *_link_len = link_len; 136 return 0; 137} 138 139static int 140CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) 141{ 142 int rc; 143 unsigned int link_len; 144 unsigned int ofs; 145 u8 md5_hash[16]; 146 147 if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE) 148 return -EINVAL; 149 150 link_len = strlen(link_str); 151 152 if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN) 153 return -ENAMETOOLONG; 154 155 rc = symlink_hash(link_len, link_str, md5_hash); 156 if (rc) { 157 cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc); 158 return rc; 159 } 160 161 snprintf(buf, buf_len, 162 CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT, 163 link_len, 164 CIFS_MF_SYMLINK_MD5_ARGS(md5_hash)); 165 166 ofs = CIFS_MF_SYMLINK_LINK_OFFSET; 167 memcpy(buf + ofs, link_str, link_len); 168 169 ofs += link_len; 170 if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { 171 buf[ofs] = '\n'; 172 ofs++; 173 } 174 175 while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) { 176 buf[ofs] = ' '; 177 ofs++; 178 } 179 180 return 0; 181} 182 183static int 184CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon, 185 const char *fromName, const char *toName, 186 struct cifs_sb_info *cifs_sb) 187{ 188 int rc; 189 int oplock = 0; 190 int remap; 191 int create_options = CREATE_NOT_DIR; 192 __u16 netfid = 0; 193 u8 *buf; 194 unsigned int bytes_written = 0; 195 struct cifs_io_parms io_parms; 196 struct nls_table *nls_codepage; 197 198 nls_codepage = cifs_sb->local_nls; 199 remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; 200 201 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); 202 if (!buf) 203 return -ENOMEM; 204 205 rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); 206 if (rc != 0) { 207 kfree(buf); 208 return rc; 209 } 210 211 if (backup_cred(cifs_sb)) 212 create_options |= CREATE_OPEN_BACKUP_INTENT; 213 214 rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, 215 create_options, &netfid, &oplock, NULL, 216 nls_codepage, remap); 217 if (rc != 0) { 218 kfree(buf); 219 return rc; 220 } 221 222 io_parms.netfid = netfid; 223 io_parms.pid = current->tgid; 224 io_parms.tcon = tcon; 225 io_parms.offset = 0; 226 io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; 227 228 rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, buf, NULL, 0); 229 CIFSSMBClose(xid, tcon, netfid); 230 kfree(buf); 231 if (rc != 0) 232 return rc; 233 234 if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) 235 return -EIO; 236 237 return 0; 238} 239 240static int 241CIFSQueryMFSymLink(const int xid, struct cifs_tcon *tcon, 242 const unsigned char *searchName, char **symlinkinfo, 243 const struct nls_table *nls_codepage, int remap) 244{ 245 int rc; 246 int oplock = 0; 247 __u16 netfid = 0; 248 u8 *buf; 249 char *pbuf; 250 unsigned int bytes_read = 0; 251 int buf_type = CIFS_NO_BUFFER; 252 unsigned int link_len = 0; 253 struct cifs_io_parms io_parms; 254 FILE_ALL_INFO file_info; 255 256 rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ, 257 CREATE_NOT_DIR, &netfid, &oplock, &file_info, 258 nls_codepage, remap); 259 if (rc != 0) 260 return rc; 261 262 if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { 263 CIFSSMBClose(xid, tcon, netfid); 264 /* it's not a symlink */ 265 return -EINVAL; 266 } 267 268 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); 269 if (!buf) 270 return -ENOMEM; 271 pbuf = buf; 272 io_parms.netfid = netfid; 273 io_parms.pid = current->tgid; 274 io_parms.tcon = tcon; 275 io_parms.offset = 0; 276 io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; 277 278 rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); 279 CIFSSMBClose(xid, tcon, netfid); 280 if (rc != 0) { 281 kfree(buf); 282 return rc; 283 } 284 285 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo); 286 kfree(buf); 287 if (rc != 0) 288 return rc; 289 290 return 0; 291} 292 293bool 294CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr) 295{ 296 if (!(fattr->cf_mode & S_IFREG)) 297 /* it's not a symlink */ 298 return false; 299 300 if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE) 301 /* it's not a symlink */ 302 return false; 303 304 return true; 305} 306 307int 308CIFSCheckMFSymlink(struct cifs_fattr *fattr, 309 const unsigned char *path, 310 struct cifs_sb_info *cifs_sb, int xid) 311{ 312 int rc; 313 int oplock = 0; 314 __u16 netfid = 0; 315 struct tcon_link *tlink; 316 struct cifs_tcon *pTcon; 317 struct cifs_io_parms io_parms; 318 u8 *buf; 319 char *pbuf; 320 unsigned int bytes_read = 0; 321 int buf_type = CIFS_NO_BUFFER; 322 unsigned int link_len = 0; 323 FILE_ALL_INFO file_info; 324 325 if (!CIFSCouldBeMFSymlink(fattr)) 326 /* it's not a symlink */ 327 return 0; 328 329 tlink = cifs_sb_tlink(cifs_sb); 330 if (IS_ERR(tlink)) 331 return PTR_ERR(tlink); 332 pTcon = tlink_tcon(tlink); 333 334 rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, 335 CREATE_NOT_DIR, &netfid, &oplock, &file_info, 336 cifs_sb->local_nls, 337 cifs_sb->mnt_cifs_flags & 338 CIFS_MOUNT_MAP_SPECIAL_CHR); 339 if (rc != 0) 340 goto out; 341 342 if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { 343 CIFSSMBClose(xid, pTcon, netfid); 344 /* it's not a symlink */ 345 goto out; 346 } 347 348 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); 349 if (!buf) { 350 rc = -ENOMEM; 351 goto out; 352 } 353 pbuf = buf; 354 io_parms.netfid = netfid; 355 io_parms.pid = current->tgid; 356 io_parms.tcon = pTcon; 357 io_parms.offset = 0; 358 io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; 359 360 rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); 361 CIFSSMBClose(xid, pTcon, netfid); 362 if (rc != 0) { 363 kfree(buf); 364 goto out; 365 } 366 367 rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); 368 kfree(buf); 369 if (rc == -EINVAL) { 370 /* it's not a symlink */ 371 rc = 0; 372 goto out; 373 } 374 375 if (rc != 0) 376 goto out; 377 378 /* it is a symlink */ 379 fattr->cf_eof = link_len; 380 fattr->cf_mode &= ~S_IFMT; 381 fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; 382 fattr->cf_dtype = DT_LNK; 383out: 384 cifs_put_tlink(tlink); 385 return rc; 386} 387 388int 389cifs_hardlink(struct dentry *old_file, struct inode *inode, 390 struct dentry *direntry) 391{ 392 int rc = -EACCES; 393 int xid; 394 char *fromName = NULL; 395 char *toName = NULL; 396 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 397 struct tcon_link *tlink; 398 struct cifs_tcon *pTcon; 399 struct cifsInodeInfo *cifsInode; 400 401 tlink = cifs_sb_tlink(cifs_sb); 402 if (IS_ERR(tlink)) 403 return PTR_ERR(tlink); 404 pTcon = tlink_tcon(tlink); 405 406 xid = GetXid(); 407 408 fromName = build_path_from_dentry(old_file); 409 toName = build_path_from_dentry(direntry); 410 if ((fromName == NULL) || (toName == NULL)) { 411 rc = -ENOMEM; 412 goto cifs_hl_exit; 413 } 414 415 if (pTcon->unix_ext) 416 rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, 417 cifs_sb->local_nls, 418 cifs_sb->mnt_cifs_flags & 419 CIFS_MOUNT_MAP_SPECIAL_CHR); 420 else { 421 rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, 422 cifs_sb->local_nls, 423 cifs_sb->mnt_cifs_flags & 424 CIFS_MOUNT_MAP_SPECIAL_CHR); 425 if ((rc == -EIO) || (rc == -EINVAL)) 426 rc = -EOPNOTSUPP; 427 } 428 429 d_drop(direntry); /* force new lookup from server of target */ 430 431 /* if source file is cached (oplocked) revalidate will not go to server 432 until the file is closed or oplock broken so update nlinks locally */ 433 if (old_file->d_inode) { 434 cifsInode = CIFS_I(old_file->d_inode); 435 if (rc == 0) { 436 inc_nlink(old_file->d_inode); 437/* BB should we make this contingent on superblock flag NOATIME? */ 438/* old_file->d_inode->i_ctime = CURRENT_TIME;*/ 439 /* parent dir timestamps will update from srv 440 within a second, would it really be worth it 441 to set the parent dir cifs inode time to zero 442 to force revalidate (faster) for it too? */ 443 } 444 /* if not oplocked will force revalidate to get info 445 on source file from srv */ 446 cifsInode->time = 0; 447 448 /* Will update parent dir timestamps from srv within a second. 449 Would it really be worth it to set the parent dir (cifs 450 inode) time field to zero to force revalidate on parent 451 directory faster ie 452 CIFS_I(inode)->time = 0; */ 453 } 454 455cifs_hl_exit: 456 kfree(fromName); 457 kfree(toName); 458 FreeXid(xid); 459 cifs_put_tlink(tlink); 460 return rc; 461} 462 463void * 464cifs_follow_link(struct dentry *direntry, struct nameidata *nd) 465{ 466 struct inode *inode = direntry->d_inode; 467 int rc = -ENOMEM; 468 int xid; 469 char *full_path = NULL; 470 char *target_path = NULL; 471 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 472 struct tcon_link *tlink = NULL; 473 struct cifs_tcon *tcon; 474 475 xid = GetXid(); 476 477 tlink = cifs_sb_tlink(cifs_sb); 478 if (IS_ERR(tlink)) { 479 rc = PTR_ERR(tlink); 480 tlink = NULL; 481 goto out; 482 } 483 tcon = tlink_tcon(tlink); 484 485 /* 486 * For now, we just handle symlinks with unix extensions enabled. 487 * Eventually we should handle NTFS reparse points, and MacOS 488 * symlink support. For instance... 489 * 490 * rc = CIFSSMBQueryReparseLinkInfo(...) 491 * 492 * For now, just return -EACCES when the server doesn't support posix 493 * extensions. Note that we still allow querying symlinks when posix 494 * extensions are manually disabled. We could disable these as well 495 * but there doesn't seem to be any harm in allowing the client to 496 * read them. 497 */ 498 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) 499 && !(tcon->ses->capabilities & CAP_UNIX)) { 500 rc = -EACCES; 501 goto out; 502 } 503 504 full_path = build_path_from_dentry(direntry); 505 if (!full_path) 506 goto out; 507 508 cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); 509 510 rc = -EACCES; 511 /* 512 * First try Minshall+French Symlinks, if configured 513 * and fallback to UNIX Extensions Symlinks. 514 */ 515 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) 516 rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path, 517 cifs_sb->local_nls, 518 cifs_sb->mnt_cifs_flags & 519 CIFS_MOUNT_MAP_SPECIAL_CHR); 520 521 if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX)) 522 rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, 523 cifs_sb->local_nls); 524 525 kfree(full_path); 526out: 527 if (rc != 0) { 528 kfree(target_path); 529 target_path = ERR_PTR(rc); 530 } 531 532 FreeXid(xid); 533 if (tlink) 534 cifs_put_tlink(tlink); 535 nd_set_link(nd, target_path); 536 return NULL; 537} 538 539int 540cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) 541{ 542 int rc = -EOPNOTSUPP; 543 int xid; 544 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 545 struct tcon_link *tlink; 546 struct cifs_tcon *pTcon; 547 char *full_path = NULL; 548 struct inode *newinode = NULL; 549 550 xid = GetXid(); 551 552 tlink = cifs_sb_tlink(cifs_sb); 553 if (IS_ERR(tlink)) { 554 rc = PTR_ERR(tlink); 555 goto symlink_exit; 556 } 557 pTcon = tlink_tcon(tlink); 558 559 full_path = build_path_from_dentry(direntry); 560 if (full_path == NULL) { 561 rc = -ENOMEM; 562 goto symlink_exit; 563 } 564 565 cFYI(1, "Full path: %s", full_path); 566 cFYI(1, "symname is %s", symname); 567 568 /* BB what if DFS and this volume is on different share? BB */ 569 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) 570 rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, 571 cifs_sb); 572 else if (pTcon->unix_ext) 573 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, 574 cifs_sb->local_nls); 575 /* else 576 rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName, 577 cifs_sb_target->local_nls); */ 578 579 if (rc == 0) { 580 if (pTcon->unix_ext) 581 rc = cifs_get_inode_info_unix(&newinode, full_path, 582 inode->i_sb, xid); 583 else 584 rc = cifs_get_inode_info(&newinode, full_path, NULL, 585 inode->i_sb, xid, NULL); 586 587 if (rc != 0) { 588 cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d", 589 rc); 590 } else { 591 d_instantiate(direntry, newinode); 592 } 593 } 594symlink_exit: 595 kfree(full_path); 596 cifs_put_tlink(tlink); 597 FreeXid(xid); 598 return rc; 599} 600 601void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie) 602{ 603 char *p = nd_get_link(nd); 604 if (!IS_ERR(p)) 605 kfree(p); 606}