diff options
author | Steve French <sfrench@us.ibm.com> | 2008-05-20 17:52:32 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-05-20 17:52:32 -0400 |
commit | b9a3260f25ab5d2ba5c8b9508e7952848b9d704b (patch) | |
tree | 2c50578e713b4b519635a13cc568bae86729d17e /fs/cifs | |
parent | 0e4bbde94fdc33f5b3d793166b21bf768ca3e098 (diff) |
[CIFS] Enable DFS support for Windows query path info
Final piece for handling DFS in query_path_info, constructing a
fake inode for the junction directory which the submount will cover.
This handles the non-Unix (Windows etc.) code path.
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/inode.c | 324 |
1 files changed, 178 insertions, 146 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 422d4e219fa4..1cf43e101943 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -161,6 +161,12 @@ static void cifs_unix_info_to_inode(struct inode *inode, | |||
161 | spin_unlock(&inode->i_lock); | 161 | spin_unlock(&inode->i_lock); |
162 | } | 162 | } |
163 | 163 | ||
164 | |||
165 | /* | ||
166 | * Needed to setup inode data for the directory which is the | ||
167 | * junction to the new submount (ie to setup the fake directory | ||
168 | * which represents a DFS referral) | ||
169 | */ | ||
164 | static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat, | 170 | static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat, |
165 | struct super_block *sb) | 171 | struct super_block *sb) |
166 | { | 172 | { |
@@ -370,11 +376,42 @@ static int get_sfu_mode(struct inode *inode, | |||
370 | #endif | 376 | #endif |
371 | } | 377 | } |
372 | 378 | ||
379 | /* | ||
380 | * Needed to setup inode data for the directory which is the | ||
381 | * junction to the new submount (ie to setup the fake directory | ||
382 | * which represents a DFS referral) | ||
383 | */ | ||
384 | static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat, | ||
385 | struct super_block *sb) | ||
386 | { | ||
387 | memset(pfnd_dat, sizeof(FILE_ALL_INFO), 0); | ||
388 | |||
389 | /* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0); | ||
390 | __le64 pfnd_dat->EndOfFile = cpu_to_le64(0); | ||
391 | __u8 pfnd_dat->DeletePending = 0; | ||
392 | __u8 pfnd_data->Directory = 0; | ||
393 | __le32 pfnd_dat->EASize = 0; | ||
394 | __u64 pfnd_dat->IndexNumber = 0; | ||
395 | __u64 pfnd_dat->IndexNumber1 = 0; */ | ||
396 | pfnd_dat->CreationTime = | ||
397 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
398 | pfnd_dat->LastAccessTime = | ||
399 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
400 | pfnd_dat->LastWriteTime = | ||
401 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
402 | pfnd_dat->ChangeTime = | ||
403 | cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); | ||
404 | pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY); | ||
405 | pfnd_dat->NumberOfLinks = cpu_to_le32(2); | ||
406 | } | ||
407 | |||
373 | int cifs_get_inode_info(struct inode **pinode, | 408 | int cifs_get_inode_info(struct inode **pinode, |
374 | const unsigned char *full_path, FILE_ALL_INFO *pfindData, | 409 | const unsigned char *full_path, FILE_ALL_INFO *pfindData, |
375 | struct super_block *sb, int xid, const __u16 *pfid) | 410 | struct super_block *sb, int xid, const __u16 *pfid) |
376 | { | 411 | { |
377 | int rc = 0; | 412 | int rc = 0; |
413 | __u32 attr; | ||
414 | struct cifsInodeInfo *cifsInfo; | ||
378 | struct cifsTconInfo *pTcon; | 415 | struct cifsTconInfo *pTcon; |
379 | struct inode *inode; | 416 | struct inode *inode; |
380 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 417 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
@@ -399,7 +436,6 @@ int cifs_get_inode_info(struct inode **pinode, | |||
399 | return -ENOMEM; | 436 | return -ENOMEM; |
400 | pfindData = (FILE_ALL_INFO *)buf; | 437 | pfindData = (FILE_ALL_INFO *)buf; |
401 | 438 | ||
402 | try_again_CIFSSMBQPathInfo: | ||
403 | /* could do find first instead but this returns more info */ | 439 | /* could do find first instead but this returns more info */ |
404 | rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, | 440 | rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, |
405 | 0 /* not legacy */, | 441 | 0 /* not legacy */, |
@@ -417,171 +453,167 @@ try_again_CIFSSMBQPathInfo: | |||
417 | } | 453 | } |
418 | } | 454 | } |
419 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ | 455 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ |
420 | if (rc) { | 456 | if (rc == -EREMOTE) { |
421 | if (rc == -EREMOTE && !is_dfs_referral) { | 457 | is_dfs_referral = true; |
422 | is_dfs_referral = true; | 458 | fill_fake_finddata(pfindData, sb); |
423 | goto try_again_CIFSSMBQPathInfo; | 459 | rc = 0; |
424 | } | 460 | } else if (rc) |
425 | goto cgii_exit; | 461 | goto cgii_exit; |
426 | } else { | ||
427 | struct cifsInodeInfo *cifsInfo; | ||
428 | __u32 attr = le32_to_cpu(pfindData->Attributes); | ||
429 | 462 | ||
430 | /* get new inode */ | 463 | attr = le32_to_cpu(pfindData->Attributes); |
464 | |||
465 | /* get new inode */ | ||
466 | if (*pinode == NULL) { | ||
467 | *pinode = new_inode(sb); | ||
431 | if (*pinode == NULL) { | 468 | if (*pinode == NULL) { |
432 | *pinode = new_inode(sb); | 469 | rc = -ENOMEM; |
433 | if (*pinode == NULL) { | 470 | goto cgii_exit; |
434 | rc = -ENOMEM; | 471 | } |
435 | goto cgii_exit; | 472 | /* Is an i_ino of zero legal? Can we use that to check |
436 | } | 473 | if the server supports returning inode numbers? Are |
437 | /* Is an i_ino of zero legal? Can we use that to check | 474 | there other sanity checks we can use to ensure that |
438 | if the server supports returning inode numbers? Are | 475 | the server is really filling in that field? */ |
439 | there other sanity checks we can use to ensure that | ||
440 | the server is really filling in that field? */ | ||
441 | 476 | ||
442 | /* We can not use the IndexNumber field by default from | 477 | /* We can not use the IndexNumber field by default from |
443 | Windows or Samba (in ALL_INFO buf) but we can request | 478 | Windows or Samba (in ALL_INFO buf) but we can request |
444 | it explicitly. It may not be unique presumably if | 479 | it explicitly. It may not be unique presumably if |
445 | the server has multiple devices mounted under one | 480 | the server has multiple devices mounted under one share */ |
446 | share */ | ||
447 | 481 | ||
448 | /* There may be higher info levels that work but are | 482 | /* There may be higher info levels that work but are |
449 | there Windows server or network appliances for which | 483 | there Windows server or network appliances for which |
450 | IndexNumber field is not guaranteed unique? */ | 484 | IndexNumber field is not guaranteed unique? */ |
451 | 485 | ||
452 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { | 486 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { |
453 | int rc1 = 0; | 487 | int rc1 = 0; |
454 | __u64 inode_num; | 488 | __u64 inode_num; |
455 | 489 | ||
456 | rc1 = CIFSGetSrvInodeNumber(xid, pTcon, | 490 | rc1 = CIFSGetSrvInodeNumber(xid, pTcon, |
457 | full_path, &inode_num, | 491 | full_path, &inode_num, |
458 | cifs_sb->local_nls, | 492 | cifs_sb->local_nls, |
459 | cifs_sb->mnt_cifs_flags & | 493 | cifs_sb->mnt_cifs_flags & |
460 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 494 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
461 | if (rc1) { | 495 | if (rc1) { |
462 | cFYI(1, ("GetSrvInodeNum rc %d", rc1)); | 496 | cFYI(1, ("GetSrvInodeNum rc %d", rc1)); |
463 | /* BB EOPNOSUPP disable SERVER_INUM? */ | 497 | /* BB EOPNOSUPP disable SERVER_INUM? */ |
464 | } else /* do we need cast or hash to ino? */ | 498 | } else /* do we need cast or hash to ino? */ |
465 | (*pinode)->i_ino = inode_num; | 499 | (*pinode)->i_ino = inode_num; |
466 | } /* else ino incremented to unique num in new_inode*/ | 500 | } /* else ino incremented to unique num in new_inode*/ |
467 | if (sb->s_flags & MS_NOATIME) | 501 | if (sb->s_flags & MS_NOATIME) |
468 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; | 502 | (*pinode)->i_flags |= S_NOATIME | S_NOCMTIME; |
469 | insert_inode_hash(*pinode); | 503 | insert_inode_hash(*pinode); |
470 | } | 504 | } |
471 | inode = *pinode; | 505 | inode = *pinode; |
472 | cifsInfo = CIFS_I(inode); | 506 | cifsInfo = CIFS_I(inode); |
473 | cifsInfo->cifsAttrs = attr; | 507 | cifsInfo->cifsAttrs = attr; |
474 | cFYI(1, ("Old time %ld", cifsInfo->time)); | 508 | cFYI(1, ("Old time %ld", cifsInfo->time)); |
475 | cifsInfo->time = jiffies; | 509 | cifsInfo->time = jiffies; |
476 | cFYI(1, ("New time %ld", cifsInfo->time)); | 510 | cFYI(1, ("New time %ld", cifsInfo->time)); |
477 | 511 | ||
478 | /* blksize needs to be multiple of two. So safer to default to | 512 | /* blksize needs to be multiple of two. So safer to default to |
479 | blksize and blkbits set in superblock so 2**blkbits and blksize | 513 | blksize and blkbits set in superblock so 2**blkbits and blksize |
480 | will match rather than setting to: | 514 | will match rather than setting to: |
481 | (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ | 515 | (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ |
482 | 516 | ||
483 | /* Linux can not store file creation time so ignore it */ | 517 | /* Linux can not store file creation time so ignore it */ |
484 | if (pfindData->LastAccessTime) | 518 | if (pfindData->LastAccessTime) |
485 | inode->i_atime = cifs_NTtimeToUnix | 519 | inode->i_atime = cifs_NTtimeToUnix |
486 | (le64_to_cpu(pfindData->LastAccessTime)); | 520 | (le64_to_cpu(pfindData->LastAccessTime)); |
487 | else /* do not need to use current_fs_time - time not stored */ | 521 | else /* do not need to use current_fs_time - time not stored */ |
488 | inode->i_atime = CURRENT_TIME; | 522 | inode->i_atime = CURRENT_TIME; |
489 | inode->i_mtime = | 523 | inode->i_mtime = |
490 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); | 524 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); |
491 | inode->i_ctime = | 525 | inode->i_ctime = |
492 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); | 526 | cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); |
493 | cFYI(0, ("Attributes came in as 0x%x", attr)); | 527 | cFYI(DBG2, ("Attributes came in as 0x%x", attr)); |
494 | if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { | 528 | if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { |
495 | inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; | 529 | inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; |
496 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; | 530 | inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; |
497 | } | 531 | } |
498 | 532 | ||
499 | /* set default mode. will override for dirs below */ | 533 | /* set default mode. will override for dirs below */ |
500 | if (atomic_read(&cifsInfo->inUse) == 0) | 534 | if (atomic_read(&cifsInfo->inUse) == 0) |
501 | /* new inode, can safely set these fields */ | 535 | /* new inode, can safely set these fields */ |
502 | inode->i_mode = cifs_sb->mnt_file_mode; | 536 | inode->i_mode = cifs_sb->mnt_file_mode; |
503 | else /* since we set the inode type below we need to mask off | 537 | else /* since we set the inode type below we need to mask off |
504 | to avoid strange results if type changes and both | 538 | to avoid strange results if type changes and both |
505 | get orred in */ | 539 | get orred in */ |
506 | inode->i_mode &= ~S_IFMT; | 540 | inode->i_mode &= ~S_IFMT; |
507 | /* if (attr & ATTR_REPARSE) */ | 541 | /* if (attr & ATTR_REPARSE) */ |
508 | /* We no longer handle these as symlinks because we could not | 542 | /* We no longer handle these as symlinks because we could not |
509 | follow them due to the absolute path with drive letter */ | 543 | follow them due to the absolute path with drive letter */ |
510 | if (attr & ATTR_DIRECTORY) { | 544 | if (attr & ATTR_DIRECTORY) { |
511 | /* override default perms since we do not do byte range locking | 545 | /* override default perms since we do not do byte range locking |
512 | on dirs */ | 546 | on dirs */ |
513 | inode->i_mode = cifs_sb->mnt_dir_mode; | 547 | inode->i_mode = cifs_sb->mnt_dir_mode; |
514 | inode->i_mode |= S_IFDIR; | 548 | inode->i_mode |= S_IFDIR; |
515 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 549 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && |
516 | (cifsInfo->cifsAttrs & ATTR_SYSTEM) && | 550 | (cifsInfo->cifsAttrs & ATTR_SYSTEM) && |
517 | /* No need to le64 convert size of zero */ | 551 | /* No need to le64 convert size of zero */ |
518 | (pfindData->EndOfFile == 0)) { | 552 | (pfindData->EndOfFile == 0)) { |
519 | inode->i_mode = cifs_sb->mnt_file_mode; | 553 | inode->i_mode = cifs_sb->mnt_file_mode; |
520 | inode->i_mode |= S_IFIFO; | 554 | inode->i_mode |= S_IFIFO; |
521 | /* BB Finish for SFU style symlinks and devices */ | 555 | /* BB Finish for SFU style symlinks and devices */ |
522 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && | 556 | } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && |
523 | (cifsInfo->cifsAttrs & ATTR_SYSTEM)) { | 557 | (cifsInfo->cifsAttrs & ATTR_SYSTEM)) { |
524 | if (decode_sfu_inode(inode, | 558 | if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile), |
525 | le64_to_cpu(pfindData->EndOfFile), | 559 | full_path, cifs_sb, xid)) |
526 | full_path, | 560 | cFYI(1, ("Unrecognized sfu inode type")); |
527 | cifs_sb, xid)) | ||
528 | cFYI(1, ("Unrecognized sfu inode type")); | ||
529 | |||
530 | cFYI(1, ("sfu mode 0%o", inode->i_mode)); | ||
531 | } else { | ||
532 | inode->i_mode |= S_IFREG; | ||
533 | /* treat the dos attribute of read-only as read-only | ||
534 | mode e.g. 555 */ | ||
535 | if (cifsInfo->cifsAttrs & ATTR_READONLY) | ||
536 | inode->i_mode &= ~(S_IWUGO); | ||
537 | else if ((inode->i_mode & S_IWUGO) == 0) | ||
538 | /* the ATTR_READONLY flag may have been */ | ||
539 | /* changed on server -- set any w bits */ | ||
540 | /* allowed by mnt_file_mode */ | ||
541 | inode->i_mode |= (S_IWUGO & | ||
542 | cifs_sb->mnt_file_mode); | ||
543 | /* BB add code here - | ||
544 | validate if device or weird share or device type? */ | ||
545 | } | ||
546 | 561 | ||
547 | spin_lock(&inode->i_lock); | 562 | cFYI(1, ("sfu mode 0%o", inode->i_mode)); |
548 | if (is_size_safe_to_change(cifsInfo, | 563 | } else { |
549 | le64_to_cpu(pfindData->EndOfFile))) { | 564 | inode->i_mode |= S_IFREG; |
550 | /* can not safely shrink the file size here if the | 565 | /* treat dos attribute of read-only as read-only mode eg 555 */ |
551 | client is writing to it due to potential races */ | 566 | if (cifsInfo->cifsAttrs & ATTR_READONLY) |
552 | i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); | 567 | inode->i_mode &= ~(S_IWUGO); |
553 | 568 | else if ((inode->i_mode & S_IWUGO) == 0) | |
554 | /* 512 bytes (2**9) is the fake blocksize that must be | 569 | /* the ATTR_READONLY flag may have been */ |
555 | used for this calculation */ | 570 | /* changed on server -- set any w bits */ |
556 | inode->i_blocks = (512 - 1 + le64_to_cpu( | 571 | /* allowed by mnt_file_mode */ |
557 | pfindData->AllocationSize)) >> 9; | 572 | inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode); |
558 | } | 573 | /* BB add code to validate if device or weird share or device type? */ |
559 | spin_unlock(&inode->i_lock); | 574 | } |
575 | |||
576 | spin_lock(&inode->i_lock); | ||
577 | if (is_size_safe_to_change(cifsInfo, | ||
578 | le64_to_cpu(pfindData->EndOfFile))) { | ||
579 | /* can not safely shrink the file size here if the | ||
580 | client is writing to it due to potential races */ | ||
581 | i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); | ||
582 | |||
583 | /* 512 bytes (2**9) is the fake blocksize that must be | ||
584 | used for this calculation */ | ||
585 | inode->i_blocks = (512 - 1 + le64_to_cpu( | ||
586 | pfindData->AllocationSize)) >> 9; | ||
587 | } | ||
588 | spin_unlock(&inode->i_lock); | ||
560 | 589 | ||
561 | inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); | 590 | inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks); |
562 | 591 | ||
563 | /* BB fill in uid and gid here? with help from winbind? | 592 | /* BB fill in uid and gid here? with help from winbind? |
564 | or retrieve from NTFS stream extended attribute */ | 593 | or retrieve from NTFS stream extended attribute */ |
565 | #ifdef CONFIG_CIFS_EXPERIMENTAL | 594 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
566 | /* fill in 0777 bits from ACL */ | 595 | /* fill in 0777 bits from ACL */ |
567 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { | 596 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { |
568 | cFYI(1, ("Getting mode bits from ACL")); | 597 | cFYI(1, ("Getting mode bits from ACL")); |
569 | acl_to_uid_mode(inode, full_path, pfid); | 598 | acl_to_uid_mode(inode, full_path, pfid); |
570 | } | 599 | } |
571 | #endif | 600 | #endif |
572 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { | 601 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { |
573 | /* fill in remaining high mode bits e.g. SUID, VTX */ | 602 | /* fill in remaining high mode bits e.g. SUID, VTX */ |
574 | get_sfu_mode(inode, full_path, cifs_sb, xid); | 603 | get_sfu_mode(inode, full_path, cifs_sb, xid); |
575 | } else if (atomic_read(&cifsInfo->inUse) == 0) { | 604 | } else if (atomic_read(&cifsInfo->inUse) == 0) { |
576 | inode->i_uid = cifs_sb->mnt_uid; | 605 | inode->i_uid = cifs_sb->mnt_uid; |
577 | inode->i_gid = cifs_sb->mnt_gid; | 606 | inode->i_gid = cifs_sb->mnt_gid; |
578 | /* set so we do not keep refreshing these fields with | 607 | /* set so we do not keep refreshing these fields with |
579 | bad data after user has changed them in memory */ | 608 | bad data after user has changed them in memory */ |
580 | atomic_set(&cifsInfo->inUse, 1); | 609 | atomic_set(&cifsInfo->inUse, 1); |
581 | } | ||
582 | |||
583 | cifs_set_ops(inode, is_dfs_referral); | ||
584 | } | 610 | } |
611 | |||
612 | cifs_set_ops(inode, is_dfs_referral); | ||
613 | |||
614 | |||
615 | |||
616 | |||
585 | cgii_exit: | 617 | cgii_exit: |
586 | kfree(buf); | 618 | kfree(buf); |
587 | return rc; | 619 | return rc; |