diff options
Diffstat (limited to 'fs/cifs/inode.c')
| -rw-r--r-- | fs/cifs/inode.c | 644 |
1 files changed, 339 insertions, 305 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 9c548f110102..a8c833345fc9 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -665,40 +665,201 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) | |||
| 665 | return inode; | 665 | return inode; |
| 666 | } | 666 | } |
| 667 | 667 | ||
| 668 | int cifs_unlink(struct inode *inode, struct dentry *direntry) | 668 | static int |
| 669 | cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, | ||
| 670 | char *full_path, __u32 dosattr) | ||
| 671 | { | ||
| 672 | int rc; | ||
| 673 | int oplock = 0; | ||
| 674 | __u16 netfid; | ||
| 675 | __u32 netpid; | ||
| 676 | bool set_time = false; | ||
| 677 | struct cifsFileInfo *open_file; | ||
| 678 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 679 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 680 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 681 | FILE_BASIC_INFO info_buf; | ||
| 682 | |||
| 683 | if (attrs->ia_valid & ATTR_ATIME) { | ||
| 684 | set_time = true; | ||
| 685 | info_buf.LastAccessTime = | ||
| 686 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); | ||
| 687 | } else | ||
| 688 | info_buf.LastAccessTime = 0; | ||
| 689 | |||
| 690 | if (attrs->ia_valid & ATTR_MTIME) { | ||
| 691 | set_time = true; | ||
| 692 | info_buf.LastWriteTime = | ||
| 693 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); | ||
| 694 | } else | ||
| 695 | info_buf.LastWriteTime = 0; | ||
| 696 | |||
| 697 | /* | ||
| 698 | * Samba throws this field away, but windows may actually use it. | ||
| 699 | * Do not set ctime unless other time stamps are changed explicitly | ||
| 700 | * (i.e. by utimes()) since we would then have a mix of client and | ||
| 701 | * server times. | ||
| 702 | */ | ||
| 703 | if (set_time && (attrs->ia_valid & ATTR_CTIME)) { | ||
| 704 | cFYI(1, ("CIFS - CTIME changed")); | ||
| 705 | info_buf.ChangeTime = | ||
| 706 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); | ||
| 707 | } else | ||
| 708 | info_buf.ChangeTime = 0; | ||
| 709 | |||
| 710 | info_buf.CreationTime = 0; /* don't change */ | ||
| 711 | info_buf.Attributes = cpu_to_le32(dosattr); | ||
| 712 | |||
| 713 | /* | ||
| 714 | * If the file is already open for write, just use that fileid | ||
| 715 | */ | ||
| 716 | open_file = find_writable_file(cifsInode); | ||
| 717 | if (open_file) { | ||
| 718 | netfid = open_file->netfid; | ||
| 719 | netpid = open_file->pid; | ||
| 720 | goto set_via_filehandle; | ||
| 721 | } | ||
| 722 | |||
| 723 | /* | ||
| 724 | * NT4 apparently returns success on this call, but it doesn't | ||
| 725 | * really work. | ||
| 726 | */ | ||
| 727 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) { | ||
| 728 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, | ||
| 729 | &info_buf, cifs_sb->local_nls, | ||
| 730 | cifs_sb->mnt_cifs_flags & | ||
| 731 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 732 | if (rc == 0) { | ||
| 733 | cifsInode->cifsAttrs = dosattr; | ||
| 734 | goto out; | ||
| 735 | } else if (rc != -EOPNOTSUPP && rc != -EINVAL) | ||
| 736 | goto out; | ||
| 737 | } | ||
| 738 | |||
| 739 | cFYI(1, ("calling SetFileInfo since SetPathInfo for " | ||
| 740 | "times not supported by this server")); | ||
| 741 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, | ||
| 742 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
| 743 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 744 | NULL, cifs_sb->local_nls, | ||
| 745 | cifs_sb->mnt_cifs_flags & | ||
| 746 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 747 | |||
| 748 | if (rc != 0) { | ||
| 749 | if (rc == -EIO) | ||
| 750 | rc = -EINVAL; | ||
| 751 | goto out; | ||
| 752 | } | ||
| 753 | |||
| 754 | netpid = current->tgid; | ||
| 755 | |||
| 756 | set_via_filehandle: | ||
| 757 | rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid); | ||
| 758 | if (!rc) | ||
| 759 | cifsInode->cifsAttrs = dosattr; | ||
| 760 | |||
| 761 | if (open_file == NULL) | ||
| 762 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 763 | else | ||
| 764 | atomic_dec(&open_file->wrtPending); | ||
| 765 | out: | ||
| 766 | return rc; | ||
| 767 | } | ||
| 768 | |||
| 769 | /* | ||
| 770 | * open the given file (if it isn't already), set the DELETE_ON_CLOSE bit | ||
| 771 | * and rename it to a random name that hopefully won't conflict with | ||
| 772 | * anything else. | ||
| 773 | */ | ||
| 774 | static int | ||
| 775 | cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid) | ||
| 776 | { | ||
| 777 | int oplock = 0; | ||
| 778 | int rc; | ||
| 779 | __u16 netfid; | ||
| 780 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 781 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 782 | struct cifsTconInfo *tcon = cifs_sb->tcon; | ||
| 783 | __u32 dosattr; | ||
| 784 | FILE_BASIC_INFO *info_buf; | ||
| 785 | |||
| 786 | rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, | ||
| 787 | DELETE|FILE_WRITE_ATTRIBUTES, | ||
| 788 | CREATE_NOT_DIR|CREATE_DELETE_ON_CLOSE, | ||
| 789 | &netfid, &oplock, NULL, cifs_sb->local_nls, | ||
| 790 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 791 | if (rc != 0) | ||
| 792 | goto out; | ||
| 793 | |||
| 794 | /* set ATTR_HIDDEN and clear ATTR_READONLY */ | ||
| 795 | cifsInode = CIFS_I(inode); | ||
| 796 | dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; | ||
| 797 | if (dosattr == 0) | ||
| 798 | dosattr |= ATTR_NORMAL; | ||
| 799 | dosattr |= ATTR_HIDDEN; | ||
| 800 | |||
| 801 | info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); | ||
| 802 | if (info_buf == NULL) { | ||
| 803 | rc = -ENOMEM; | ||
| 804 | goto out_close; | ||
| 805 | } | ||
| 806 | info_buf->Attributes = cpu_to_le32(dosattr); | ||
| 807 | rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, current->tgid); | ||
| 808 | kfree(info_buf); | ||
| 809 | if (rc != 0) | ||
| 810 | goto out_close; | ||
| 811 | cifsInode->cifsAttrs = dosattr; | ||
| 812 | |||
| 813 | /* silly-rename the file */ | ||
| 814 | CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, | ||
| 815 | cifs_sb->mnt_cifs_flags & | ||
| 816 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 817 | |||
| 818 | /* set DELETE_ON_CLOSE */ | ||
| 819 | rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid); | ||
| 820 | |||
| 821 | /* | ||
| 822 | * some samba versions return -ENOENT when we try to set the file | ||
| 823 | * disposition here. Likely a samba bug, but work around it for now | ||
| 824 | */ | ||
| 825 | if (rc == -ENOENT) | ||
| 826 | rc = 0; | ||
| 827 | |||
| 828 | out_close: | ||
| 829 | CIFSSMBClose(xid, tcon, netfid); | ||
| 830 | out: | ||
| 831 | return rc; | ||
| 832 | } | ||
| 833 | |||
| 834 | int cifs_unlink(struct inode *dir, struct dentry *dentry) | ||
| 669 | { | 835 | { |
| 670 | int rc = 0; | 836 | int rc = 0; |
| 671 | int xid; | 837 | int xid; |
| 672 | struct cifs_sb_info *cifs_sb; | ||
| 673 | struct cifsTconInfo *pTcon; | ||
| 674 | char *full_path = NULL; | 838 | char *full_path = NULL; |
| 675 | struct cifsInodeInfo *cifsInode; | 839 | struct inode *inode = dentry->d_inode; |
| 676 | FILE_BASIC_INFO *pinfo_buf; | 840 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); |
| 841 | struct super_block *sb = dir->i_sb; | ||
| 842 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | ||
| 843 | struct cifsTconInfo *tcon = cifs_sb->tcon; | ||
| 844 | struct iattr *attrs = NULL; | ||
| 845 | __u32 dosattr = 0, origattr = 0; | ||
| 677 | 846 | ||
| 678 | cFYI(1, ("cifs_unlink, inode = 0x%p", inode)); | 847 | cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry)); |
| 679 | 848 | ||
| 680 | xid = GetXid(); | 849 | xid = GetXid(); |
| 681 | 850 | ||
| 682 | if (inode) | 851 | /* Unlink can be called from rename so we can not take the |
| 683 | cifs_sb = CIFS_SB(inode->i_sb); | 852 | * sb->s_vfs_rename_mutex here */ |
| 684 | else | 853 | full_path = build_path_from_dentry(dentry); |
| 685 | cifs_sb = CIFS_SB(direntry->d_sb); | ||
| 686 | pTcon = cifs_sb->tcon; | ||
| 687 | |||
| 688 | /* Unlink can be called from rename so we can not grab the sem here | ||
| 689 | since we deadlock otherwise */ | ||
| 690 | /* mutex_lock(&direntry->d_sb->s_vfs_rename_mutex);*/ | ||
| 691 | full_path = build_path_from_dentry(direntry); | ||
| 692 | /* mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex);*/ | ||
| 693 | if (full_path == NULL) { | 854 | if (full_path == NULL) { |
| 694 | FreeXid(xid); | 855 | FreeXid(xid); |
| 695 | return -ENOMEM; | 856 | return -ENOMEM; |
| 696 | } | 857 | } |
| 697 | 858 | ||
| 698 | if ((pTcon->ses->capabilities & CAP_UNIX) && | 859 | if ((tcon->ses->capabilities & CAP_UNIX) && |
| 699 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & | 860 | (CIFS_UNIX_POSIX_PATH_OPS_CAP & |
| 700 | le64_to_cpu(pTcon->fsUnixInfo.Capability))) { | 861 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { |
| 701 | rc = CIFSPOSIXDelFile(xid, pTcon, full_path, | 862 | rc = CIFSPOSIXDelFile(xid, tcon, full_path, |
| 702 | SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, | 863 | SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, |
| 703 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 864 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 704 | cFYI(1, ("posix del rc %d", rc)); | 865 | cFYI(1, ("posix del rc %d", rc)); |
| @@ -706,125 +867,60 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) | |||
| 706 | goto psx_del_no_retry; | 867 | goto psx_del_no_retry; |
| 707 | } | 868 | } |
| 708 | 869 | ||
| 709 | rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls, | 870 | retry_std_delete: |
| 871 | rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls, | ||
| 710 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | 872 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 873 | |||
| 711 | psx_del_no_retry: | 874 | psx_del_no_retry: |
| 712 | if (!rc) { | 875 | if (!rc) { |
| 713 | if (direntry->d_inode) | 876 | if (inode) |
| 714 | drop_nlink(direntry->d_inode); | 877 | drop_nlink(inode); |
| 715 | } else if (rc == -ENOENT) { | 878 | } else if (rc == -ENOENT) { |
| 716 | d_drop(direntry); | 879 | d_drop(dentry); |
| 717 | } else if (rc == -ETXTBSY) { | 880 | } else if (rc == -ETXTBSY) { |
| 718 | int oplock = 0; | 881 | rc = cifs_rename_pending_delete(full_path, inode, xid); |
| 719 | __u16 netfid; | 882 | if (rc == 0) |
| 720 | 883 | drop_nlink(inode); | |
| 721 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE, | 884 | } else if (rc == -EACCES && dosattr == 0) { |
| 722 | CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE, | 885 | attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); |
| 723 | &netfid, &oplock, NULL, cifs_sb->local_nls, | 886 | if (attrs == NULL) { |
| 724 | cifs_sb->mnt_cifs_flags & | 887 | rc = -ENOMEM; |
| 725 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 888 | goto out_reval; |
| 726 | if (rc == 0) { | ||
| 727 | CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL, | ||
| 728 | cifs_sb->local_nls, | ||
| 729 | cifs_sb->mnt_cifs_flags & | ||
| 730 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 731 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 732 | if (direntry->d_inode) | ||
| 733 | drop_nlink(direntry->d_inode); | ||
| 734 | } | 889 | } |
| 735 | } else if (rc == -EACCES) { | ||
| 736 | /* try only if r/o attribute set in local lookup data? */ | ||
| 737 | pinfo_buf = kzalloc(sizeof(FILE_BASIC_INFO), GFP_KERNEL); | ||
| 738 | if (pinfo_buf) { | ||
| 739 | /* ATTRS set to normal clears r/o bit */ | ||
| 740 | pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL); | ||
| 741 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) | ||
| 742 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, | ||
| 743 | pinfo_buf, | ||
| 744 | cifs_sb->local_nls, | ||
| 745 | cifs_sb->mnt_cifs_flags & | ||
| 746 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 747 | else | ||
| 748 | rc = -EOPNOTSUPP; | ||
| 749 | 890 | ||
| 750 | if (rc == -EOPNOTSUPP) { | 891 | /* try to reset dos attributes */ |
| 751 | int oplock = 0; | 892 | origattr = cifsInode->cifsAttrs; |
| 752 | __u16 netfid; | 893 | if (origattr == 0) |
| 753 | /* rc = CIFSSMBSetAttrLegacy(xid, pTcon, | 894 | origattr |= ATTR_NORMAL; |
| 754 | full_path, | 895 | dosattr = origattr & ~ATTR_READONLY; |
| 755 | (__u16)ATTR_NORMAL, | 896 | if (dosattr == 0) |
| 756 | cifs_sb->local_nls); | 897 | dosattr |= ATTR_NORMAL; |
| 757 | For some strange reason it seems that NT4 eats the | 898 | dosattr |= ATTR_HIDDEN; |
| 758 | old setattr call without actually setting the | 899 | |
| 759 | attributes so on to the third attempted workaround | 900 | rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); |
| 760 | */ | 901 | if (rc != 0) |
| 761 | 902 | goto out_reval; | |
| 762 | /* BB could scan to see if we already have it open | 903 | |
| 763 | and pass in pid of opener to function */ | 904 | goto retry_std_delete; |
| 764 | rc = CIFSSMBOpen(xid, pTcon, full_path, | ||
| 765 | FILE_OPEN, SYNCHRONIZE | | ||
| 766 | FILE_WRITE_ATTRIBUTES, 0, | ||
| 767 | &netfid, &oplock, NULL, | ||
| 768 | cifs_sb->local_nls, | ||
| 769 | cifs_sb->mnt_cifs_flags & | ||
| 770 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 771 | if (rc == 0) { | ||
| 772 | rc = CIFSSMBSetFileInfo(xid, pTcon, | ||
| 773 | pinfo_buf, | ||
| 774 | netfid, | ||
| 775 | current->tgid); | ||
| 776 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 777 | } | ||
| 778 | } | ||
| 779 | kfree(pinfo_buf); | ||
| 780 | } | ||
| 781 | if (rc == 0) { | ||
| 782 | rc = CIFSSMBDelFile(xid, pTcon, full_path, | ||
| 783 | cifs_sb->local_nls, | ||
| 784 | cifs_sb->mnt_cifs_flags & | ||
| 785 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 786 | if (!rc) { | ||
| 787 | if (direntry->d_inode) | ||
| 788 | drop_nlink(direntry->d_inode); | ||
| 789 | } else if (rc == -ETXTBSY) { | ||
| 790 | int oplock = 0; | ||
| 791 | __u16 netfid; | ||
| 792 | |||
| 793 | rc = CIFSSMBOpen(xid, pTcon, full_path, | ||
| 794 | FILE_OPEN, DELETE, | ||
| 795 | CREATE_NOT_DIR | | ||
| 796 | CREATE_DELETE_ON_CLOSE, | ||
| 797 | &netfid, &oplock, NULL, | ||
| 798 | cifs_sb->local_nls, | ||
| 799 | cifs_sb->mnt_cifs_flags & | ||
| 800 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 801 | if (rc == 0) { | ||
| 802 | CIFSSMBRenameOpenFile(xid, pTcon, | ||
| 803 | netfid, NULL, | ||
| 804 | cifs_sb->local_nls, | ||
| 805 | cifs_sb->mnt_cifs_flags & | ||
| 806 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 807 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 808 | if (direntry->d_inode) | ||
| 809 | drop_nlink(direntry->d_inode); | ||
| 810 | } | ||
| 811 | /* BB if rc = -ETXTBUSY goto the rename logic BB */ | ||
| 812 | } | ||
| 813 | } | ||
| 814 | } | ||
| 815 | if (direntry->d_inode) { | ||
| 816 | cifsInode = CIFS_I(direntry->d_inode); | ||
| 817 | cifsInode->time = 0; /* will force revalidate to get info | ||
| 818 | when needed */ | ||
| 819 | direntry->d_inode->i_ctime = current_fs_time(inode->i_sb); | ||
| 820 | } | 905 | } |
| 906 | |||
| 907 | /* undo the setattr if we errored out and it's needed */ | ||
| 908 | if (rc != 0 && dosattr != 0) | ||
| 909 | cifs_set_file_info(inode, attrs, xid, full_path, origattr); | ||
| 910 | |||
| 911 | out_reval: | ||
| 821 | if (inode) { | 912 | if (inode) { |
| 822 | inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb); | ||
| 823 | cifsInode = CIFS_I(inode); | 913 | cifsInode = CIFS_I(inode); |
| 824 | cifsInode->time = 0; /* force revalidate of dir as well */ | 914 | cifsInode->time = 0; /* will force revalidate to get info |
| 915 | when needed */ | ||
| 916 | inode->i_ctime = current_fs_time(sb); | ||
| 825 | } | 917 | } |
| 918 | dir->i_ctime = dir->i_mtime = current_fs_time(sb); | ||
| 919 | cifsInode = CIFS_I(dir); | ||
| 920 | CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ | ||
| 826 | 921 | ||
| 827 | kfree(full_path); | 922 | kfree(full_path); |
| 923 | kfree(attrs); | ||
| 828 | FreeXid(xid); | 924 | FreeXid(xid); |
| 829 | return rc; | 925 | return rc; |
| 830 | } | 926 | } |
| @@ -869,7 +965,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode, | |||
| 869 | 965 | ||
| 870 | int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) | 966 | int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) |
| 871 | { | 967 | { |
| 872 | int rc = 0; | 968 | int rc = 0, tmprc; |
| 873 | int xid; | 969 | int xid; |
| 874 | struct cifs_sb_info *cifs_sb; | 970 | struct cifs_sb_info *cifs_sb; |
| 875 | struct cifsTconInfo *pTcon; | 971 | struct cifsTconInfo *pTcon; |
| @@ -931,6 +1027,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) | |||
| 931 | kfree(pInfo); | 1027 | kfree(pInfo); |
| 932 | goto mkdir_get_info; | 1028 | goto mkdir_get_info; |
| 933 | } | 1029 | } |
| 1030 | |||
| 934 | /* Is an i_ino of zero legal? */ | 1031 | /* Is an i_ino of zero legal? */ |
| 935 | /* Are there sanity checks we can use to ensure that | 1032 | /* Are there sanity checks we can use to ensure that |
| 936 | the server is really filling in that field? */ | 1033 | the server is really filling in that field? */ |
| @@ -1019,12 +1116,20 @@ mkdir_get_info: | |||
| 1019 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && | 1116 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && |
| 1020 | (mode & S_IWUGO) == 0) { | 1117 | (mode & S_IWUGO) == 0) { |
| 1021 | FILE_BASIC_INFO pInfo; | 1118 | FILE_BASIC_INFO pInfo; |
| 1119 | struct cifsInodeInfo *cifsInode; | ||
| 1120 | u32 dosattrs; | ||
| 1121 | |||
| 1022 | memset(&pInfo, 0, sizeof(pInfo)); | 1122 | memset(&pInfo, 0, sizeof(pInfo)); |
| 1023 | pInfo.Attributes = cpu_to_le32(ATTR_READONLY); | 1123 | cifsInode = CIFS_I(newinode); |
| 1024 | CIFSSMBSetPathInfo(xid, pTcon, full_path, | 1124 | dosattrs = cifsInode->cifsAttrs|ATTR_READONLY; |
| 1025 | &pInfo, cifs_sb->local_nls, | 1125 | pInfo.Attributes = cpu_to_le32(dosattrs); |
| 1126 | tmprc = CIFSSMBSetPathInfo(xid, pTcon, | ||
| 1127 | full_path, &pInfo, | ||
| 1128 | cifs_sb->local_nls, | ||
| 1026 | cifs_sb->mnt_cifs_flags & | 1129 | cifs_sb->mnt_cifs_flags & |
| 1027 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1130 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 1131 | if (tmprc == 0) | ||
| 1132 | cifsInode->cifsAttrs = dosattrs; | ||
| 1028 | } | 1133 | } |
| 1029 | if (direntry->d_inode) { | 1134 | if (direntry->d_inode) { |
| 1030 | if (cifs_sb->mnt_cifs_flags & | 1135 | if (cifs_sb->mnt_cifs_flags & |
| @@ -1096,117 +1201,141 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) | |||
| 1096 | return rc; | 1201 | return rc; |
| 1097 | } | 1202 | } |
| 1098 | 1203 | ||
| 1204 | static int | ||
| 1205 | cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, | ||
| 1206 | struct dentry *to_dentry, const char *toPath) | ||
| 1207 | { | ||
| 1208 | struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); | ||
| 1209 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1210 | __u16 srcfid; | ||
| 1211 | int oplock, rc; | ||
| 1212 | |||
| 1213 | /* try path-based rename first */ | ||
| 1214 | rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls, | ||
| 1215 | cifs_sb->mnt_cifs_flags & | ||
| 1216 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1217 | |||
| 1218 | /* | ||
| 1219 | * don't bother with rename by filehandle unless file is busy and | ||
| 1220 | * source Note that cross directory moves do not work with | ||
| 1221 | * rename by filehandle to various Windows servers. | ||
| 1222 | */ | ||
| 1223 | if (rc == 0 || rc != -ETXTBSY) | ||
| 1224 | return rc; | ||
| 1225 | |||
| 1226 | /* open the file to be renamed -- we need DELETE perms */ | ||
| 1227 | rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, | ||
| 1228 | CREATE_NOT_DIR, &srcfid, &oplock, NULL, | ||
| 1229 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | ||
| 1230 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1231 | |||
| 1232 | if (rc == 0) { | ||
| 1233 | rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid, | ||
| 1234 | (const char *) to_dentry->d_name.name, | ||
| 1235 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | ||
| 1236 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1237 | |||
| 1238 | CIFSSMBClose(xid, pTcon, srcfid); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | return rc; | ||
| 1242 | } | ||
| 1243 | |||
| 1099 | int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, | 1244 | int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, |
| 1100 | struct inode *target_inode, struct dentry *target_direntry) | 1245 | struct inode *target_inode, struct dentry *target_direntry) |
| 1101 | { | 1246 | { |
| 1102 | char *fromName; | 1247 | char *fromName = NULL; |
| 1103 | char *toName; | 1248 | char *toName = NULL; |
| 1104 | struct cifs_sb_info *cifs_sb_source; | 1249 | struct cifs_sb_info *cifs_sb_source; |
| 1105 | struct cifs_sb_info *cifs_sb_target; | 1250 | struct cifs_sb_info *cifs_sb_target; |
| 1106 | struct cifsTconInfo *pTcon; | 1251 | struct cifsTconInfo *pTcon; |
| 1252 | FILE_UNIX_BASIC_INFO *info_buf_source = NULL; | ||
| 1253 | FILE_UNIX_BASIC_INFO *info_buf_target; | ||
| 1107 | int xid; | 1254 | int xid; |
| 1108 | int rc = 0; | 1255 | int rc; |
| 1109 | |||
| 1110 | xid = GetXid(); | ||
| 1111 | 1256 | ||
| 1112 | cifs_sb_target = CIFS_SB(target_inode->i_sb); | 1257 | cifs_sb_target = CIFS_SB(target_inode->i_sb); |
| 1113 | cifs_sb_source = CIFS_SB(source_inode->i_sb); | 1258 | cifs_sb_source = CIFS_SB(source_inode->i_sb); |
| 1114 | pTcon = cifs_sb_source->tcon; | 1259 | pTcon = cifs_sb_source->tcon; |
| 1115 | 1260 | ||
| 1261 | xid = GetXid(); | ||
| 1262 | |||
| 1263 | /* | ||
| 1264 | * BB: this might be allowed if same server, but different share. | ||
| 1265 | * Consider adding support for this | ||
| 1266 | */ | ||
| 1116 | if (pTcon != cifs_sb_target->tcon) { | 1267 | if (pTcon != cifs_sb_target->tcon) { |
| 1117 | FreeXid(xid); | 1268 | rc = -EXDEV; |
| 1118 | return -EXDEV; /* BB actually could be allowed if same server, | 1269 | goto cifs_rename_exit; |
| 1119 | but different share. | ||
| 1120 | Might eventually add support for this */ | ||
| 1121 | } | 1270 | } |
| 1122 | 1271 | ||
| 1123 | /* we already have the rename sem so we do not need to grab it again | 1272 | /* |
| 1124 | here to protect the path integrity */ | 1273 | * we already have the rename sem so we do not need to |
| 1274 | * grab it again here to protect the path integrity | ||
| 1275 | */ | ||
| 1125 | fromName = build_path_from_dentry(source_direntry); | 1276 | fromName = build_path_from_dentry(source_direntry); |
| 1277 | if (fromName == NULL) { | ||
| 1278 | rc = -ENOMEM; | ||
| 1279 | goto cifs_rename_exit; | ||
| 1280 | } | ||
| 1281 | |||
| 1126 | toName = build_path_from_dentry(target_direntry); | 1282 | toName = build_path_from_dentry(target_direntry); |
| 1127 | if ((fromName == NULL) || (toName == NULL)) { | 1283 | if (toName == NULL) { |
| 1128 | rc = -ENOMEM; | 1284 | rc = -ENOMEM; |
| 1129 | goto cifs_rename_exit; | 1285 | goto cifs_rename_exit; |
| 1130 | } | 1286 | } |
| 1131 | 1287 | ||
| 1132 | rc = CIFSSMBRename(xid, pTcon, fromName, toName, | 1288 | rc = cifs_do_rename(xid, source_direntry, fromName, |
| 1133 | cifs_sb_source->local_nls, | 1289 | target_direntry, toName); |
| 1134 | cifs_sb_source->mnt_cifs_flags & | 1290 | |
| 1135 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1136 | if (rc == -EEXIST) { | 1291 | if (rc == -EEXIST) { |
| 1137 | /* check if they are the same file because rename of hardlinked | 1292 | if (pTcon->unix_ext) { |
| 1138 | files is a noop */ | 1293 | /* |
| 1139 | FILE_UNIX_BASIC_INFO *info_buf_source; | 1294 | * Are src and dst hardlinks of same inode? We can |
| 1140 | FILE_UNIX_BASIC_INFO *info_buf_target; | 1295 | * only tell with unix extensions enabled |
| 1141 | 1296 | */ | |
| 1142 | info_buf_source = | 1297 | info_buf_source = |
| 1143 | kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); | 1298 | kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), |
| 1144 | if (info_buf_source != NULL) { | 1299 | GFP_KERNEL); |
| 1300 | if (info_buf_source == NULL) | ||
| 1301 | goto unlink_target; | ||
| 1302 | |||
| 1145 | info_buf_target = info_buf_source + 1; | 1303 | info_buf_target = info_buf_source + 1; |
| 1146 | if (pTcon->unix_ext) | 1304 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, |
| 1147 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName, | 1305 | info_buf_source, |
| 1148 | info_buf_source, | 1306 | cifs_sb_source->local_nls, |
| 1149 | cifs_sb_source->local_nls, | 1307 | cifs_sb_source->mnt_cifs_flags & |
| 1150 | cifs_sb_source->mnt_cifs_flags & | ||
| 1151 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1308 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 1152 | /* else rc is still EEXIST so will fall through to | 1309 | if (rc != 0) |
| 1153 | unlink the target and retry rename */ | 1310 | goto unlink_target; |
| 1154 | if (rc == 0) { | 1311 | |
| 1155 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName, | 1312 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, |
| 1156 | info_buf_target, | 1313 | toName, info_buf_target, |
| 1157 | cifs_sb_target->local_nls, | 1314 | cifs_sb_target->local_nls, |
| 1158 | /* remap based on source sb */ | 1315 | /* remap based on source sb */ |
| 1159 | cifs_sb_source->mnt_cifs_flags & | 1316 | cifs_sb_source->mnt_cifs_flags & |
| 1160 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1161 | } | ||
| 1162 | if ((rc == 0) && | ||
| 1163 | (info_buf_source->UniqueId == | ||
| 1164 | info_buf_target->UniqueId)) { | ||
| 1165 | /* do not rename since the files are hardlinked which | ||
| 1166 | is a noop */ | ||
| 1167 | } else { | ||
| 1168 | /* we either can not tell the files are hardlinked | ||
| 1169 | (as with Windows servers) or files are not | ||
| 1170 | hardlinked so delete the target manually before | ||
| 1171 | renaming to follow POSIX rather than Windows | ||
| 1172 | semantics */ | ||
| 1173 | cifs_unlink(target_inode, target_direntry); | ||
| 1174 | rc = CIFSSMBRename(xid, pTcon, fromName, | ||
| 1175 | toName, | ||
| 1176 | cifs_sb_source->local_nls, | ||
| 1177 | cifs_sb_source->mnt_cifs_flags | ||
| 1178 | & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1179 | } | ||
| 1180 | kfree(info_buf_source); | ||
| 1181 | } /* if we can not get memory just leave rc as EEXIST */ | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | if (rc) | ||
| 1185 | cFYI(1, ("rename rc %d", rc)); | ||
| 1186 | |||
| 1187 | if ((rc == -EIO) || (rc == -EEXIST)) { | ||
| 1188 | int oplock = 0; | ||
| 1189 | __u16 netfid; | ||
| 1190 | |||
| 1191 | /* BB FIXME Is Generic Read correct for rename? */ | ||
| 1192 | /* if renaming directory - we should not say CREATE_NOT_DIR, | ||
| 1193 | need to test renaming open directory, also GENERIC_READ | ||
| 1194 | might not right be right access to request */ | ||
| 1195 | rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ, | ||
| 1196 | CREATE_NOT_DIR, &netfid, &oplock, NULL, | ||
| 1197 | cifs_sb_source->local_nls, | ||
| 1198 | cifs_sb_source->mnt_cifs_flags & | ||
| 1199 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1200 | if (rc == 0) { | ||
| 1201 | rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName, | ||
| 1202 | cifs_sb_source->local_nls, | ||
| 1203 | cifs_sb_source->mnt_cifs_flags & | ||
| 1204 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 1317 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
| 1205 | CIFSSMBClose(xid, pTcon, netfid); | 1318 | |
| 1206 | } | 1319 | if (rc == 0 && (info_buf_source->UniqueId == |
| 1320 | info_buf_target->UniqueId)) | ||
| 1321 | /* same file, POSIX says that this is a noop */ | ||
| 1322 | goto cifs_rename_exit; | ||
| 1323 | } /* else ... BB we could add the same check for Windows by | ||
| 1324 | checking the UniqueId via FILE_INTERNAL_INFO */ | ||
| 1325 | unlink_target: | ||
| 1326 | /* | ||
| 1327 | * we either can not tell the files are hardlinked (as with | ||
| 1328 | * Windows servers) or files are not hardlinked. Delete the | ||
| 1329 | * target manually before renaming to follow POSIX rather than | ||
| 1330 | * Windows semantics | ||
| 1331 | */ | ||
| 1332 | cifs_unlink(target_inode, target_direntry); | ||
| 1333 | rc = cifs_do_rename(xid, source_direntry, fromName, | ||
| 1334 | target_direntry, toName); | ||
| 1207 | } | 1335 | } |
| 1208 | 1336 | ||
| 1209 | cifs_rename_exit: | 1337 | cifs_rename_exit: |
| 1338 | kfree(info_buf_source); | ||
| 1210 | kfree(fromName); | 1339 | kfree(fromName); |
| 1211 | kfree(toName); | 1340 | kfree(toName); |
| 1212 | FreeXid(xid); | 1341 | FreeXid(xid); |
| @@ -1507,101 +1636,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, | |||
| 1507 | } | 1636 | } |
| 1508 | 1637 | ||
| 1509 | static int | 1638 | static int |
| 1510 | cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, | ||
| 1511 | char *full_path, __u32 dosattr) | ||
| 1512 | { | ||
| 1513 | int rc; | ||
| 1514 | int oplock = 0; | ||
| 1515 | __u16 netfid; | ||
| 1516 | __u32 netpid; | ||
| 1517 | bool set_time = false; | ||
| 1518 | struct cifsFileInfo *open_file; | ||
| 1519 | struct cifsInodeInfo *cifsInode = CIFS_I(inode); | ||
| 1520 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | ||
| 1521 | struct cifsTconInfo *pTcon = cifs_sb->tcon; | ||
| 1522 | FILE_BASIC_INFO info_buf; | ||
| 1523 | |||
| 1524 | if (attrs->ia_valid & ATTR_ATIME) { | ||
| 1525 | set_time = true; | ||
| 1526 | info_buf.LastAccessTime = | ||
| 1527 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); | ||
| 1528 | } else | ||
| 1529 | info_buf.LastAccessTime = 0; | ||
| 1530 | |||
| 1531 | if (attrs->ia_valid & ATTR_MTIME) { | ||
| 1532 | set_time = true; | ||
| 1533 | info_buf.LastWriteTime = | ||
| 1534 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); | ||
| 1535 | } else | ||
| 1536 | info_buf.LastWriteTime = 0; | ||
| 1537 | |||
| 1538 | /* | ||
| 1539 | * Samba throws this field away, but windows may actually use it. | ||
| 1540 | * Do not set ctime unless other time stamps are changed explicitly | ||
| 1541 | * (i.e. by utimes()) since we would then have a mix of client and | ||
| 1542 | * server times. | ||
| 1543 | */ | ||
| 1544 | if (set_time && (attrs->ia_valid & ATTR_CTIME)) { | ||
| 1545 | cFYI(1, ("CIFS - CTIME changed")); | ||
| 1546 | info_buf.ChangeTime = | ||
| 1547 | cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); | ||
| 1548 | } else | ||
| 1549 | info_buf.ChangeTime = 0; | ||
| 1550 | |||
| 1551 | info_buf.CreationTime = 0; /* don't change */ | ||
| 1552 | info_buf.Attributes = cpu_to_le32(dosattr); | ||
| 1553 | |||
| 1554 | /* | ||
| 1555 | * If the file is already open for write, just use that fileid | ||
| 1556 | */ | ||
| 1557 | open_file = find_writable_file(cifsInode); | ||
| 1558 | if (open_file) { | ||
| 1559 | netfid = open_file->netfid; | ||
| 1560 | netpid = open_file->pid; | ||
| 1561 | goto set_via_filehandle; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | /* | ||
| 1565 | * NT4 apparently returns success on this call, but it doesn't | ||
| 1566 | * really work. | ||
| 1567 | */ | ||
| 1568 | if (!(pTcon->ses->flags & CIFS_SES_NT4)) { | ||
| 1569 | rc = CIFSSMBSetPathInfo(xid, pTcon, full_path, | ||
| 1570 | &info_buf, cifs_sb->local_nls, | ||
| 1571 | cifs_sb->mnt_cifs_flags & | ||
| 1572 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1573 | if (rc != -EOPNOTSUPP && rc != -EINVAL) | ||
| 1574 | goto out; | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | cFYI(1, ("calling SetFileInfo since SetPathInfo for " | ||
| 1578 | "times not supported by this server")); | ||
| 1579 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, | ||
| 1580 | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, | ||
| 1581 | CREATE_NOT_DIR, &netfid, &oplock, | ||
| 1582 | NULL, cifs_sb->local_nls, | ||
| 1583 | cifs_sb->mnt_cifs_flags & | ||
| 1584 | CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
| 1585 | |||
| 1586 | if (rc != 0) { | ||
| 1587 | if (rc == -EIO) | ||
| 1588 | rc = -EINVAL; | ||
| 1589 | goto out; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | netpid = current->tgid; | ||
| 1593 | |||
| 1594 | set_via_filehandle: | ||
| 1595 | rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid); | ||
| 1596 | if (open_file == NULL) | ||
| 1597 | CIFSSMBClose(xid, pTcon, netfid); | ||
| 1598 | else | ||
| 1599 | atomic_dec(&open_file->wrtPending); | ||
| 1600 | out: | ||
| 1601 | return rc; | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | static int | ||
| 1605 | cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) | 1639 | cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) |
| 1606 | { | 1640 | { |
| 1607 | int rc; | 1641 | int rc; |
