Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

CIFS: refactor cifs_get_inode_info()

Make logic of cifs_get_inode() much clearer by moving code to sub
functions and adding comments.

Document the steps this function does.

cifs_get_inode_info() gets and updates a file inode metadata from its
file path.

* If caller already has raw info data from server they can pass it.
* If inode already exists (just need to update) caller can pass it.

Step 1: get raw data from server if none was passed
Step 2: parse raw data into intermediate internal cifs_fattr struct
Step 3: set fattr uniqueid which is later used for inode number. This
can sometime be done from raw data
Step 4: tweak fattr according to mount options (file_mode, acl to mode
bits, uid, gid, etc)
Step 5: update or create inode from final fattr struct

* add is_smb1_server() helper
* add is_inode_cache_good() helper
* move SMB1-backupcreds-getinfo-retry to separate func
cifs_backup_query_path_info().
* move set-uniqueid code to separate func cifs_set_fattr_ino()
* don't clobber uniqueid from backup cred retry
* fix some probable corner cases memleaks

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Aurelien Aptel and committed by
Steve French
b8f7442b f6a6bf7c

+213 -145
+6
fs/cifs/cifsglob.h
··· 1967 1967 #define ALT_SMB311_VERSION_STRING "3.11" 1968 1968 extern struct smb_version_operations smb311_operations; 1969 1969 extern struct smb_version_values smb311_values; 1970 + 1971 + static inline bool is_smb1_server(struct TCP_Server_Info *server) 1972 + { 1973 + return strcmp(server->vals->version_string, SMB1_VERSION_STRING) == 0; 1974 + } 1975 + 1970 1976 #endif /* _CIFS_GLOB_H */
+207 -145
fs/cifs/inode.c
··· 727 727 return hash; 728 728 } 729 729 730 + /** 731 + * cifs_backup_query_path_info - SMB1 fallback code to get ino 732 + * 733 + * Fallback code to get file metadata when we don't have access to 734 + * @full_path (EACCESS) and have backup creds. 735 + * 736 + * @data will be set to search info result buffer 737 + * @resp_buf will be set to cifs resp buf and needs to be freed with 738 + * cifs_buf_release() when done with @data. 739 + */ 740 + static int 741 + cifs_backup_query_path_info(int xid, 742 + struct cifs_tcon *tcon, 743 + struct super_block *sb, 744 + const char *full_path, 745 + void **resp_buf, 746 + FILE_ALL_INFO **data) 747 + { 748 + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 749 + struct cifs_search_info info = {0}; 750 + u16 flags; 751 + int rc; 752 + 753 + *resp_buf = NULL; 754 + info.endOfSearch = false; 755 + if (tcon->unix_ext) 756 + info.info_level = SMB_FIND_FILE_UNIX; 757 + else if ((tcon->ses->capabilities & 758 + tcon->ses->server->vals->cap_nt_find) == 0) 759 + info.info_level = SMB_FIND_FILE_INFO_STANDARD; 760 + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) 761 + info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; 762 + else /* no srvino useful for fallback to some netapp */ 763 + info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; 764 + 765 + flags = CIFS_SEARCH_CLOSE_ALWAYS | 766 + CIFS_SEARCH_CLOSE_AT_END | 767 + CIFS_SEARCH_BACKUP_SEARCH; 768 + 769 + rc = CIFSFindFirst(xid, tcon, full_path, 770 + cifs_sb, NULL, flags, &info, false); 771 + if (rc) 772 + return rc; 773 + 774 + *resp_buf = (void *)info.ntwrk_buf_start; 775 + *data = (FILE_ALL_INFO *)info.srch_entries_start; 776 + return 0; 777 + } 778 + 779 + static void 780 + cifs_set_fattr_ino(int xid, 781 + struct cifs_tcon *tcon, 782 + struct super_block *sb, 783 + struct inode **inode, 784 + const char *full_path, 785 + FILE_ALL_INFO *data, 786 + struct cifs_fattr *fattr) 787 + { 788 + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 789 + struct TCP_Server_Info *server = tcon->ses->server; 790 + int rc; 791 + 792 + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { 793 + if (*inode) 794 + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 795 + else 796 + fattr->cf_uniqueid = iunique(sb, ROOT_I); 797 + return; 798 + } 799 + 800 + /* 801 + * If we have an inode pass a NULL tcon to ensure we don't 802 + * make a round trip to the server. This only works for SMB2+. 803 + */ 804 + rc = server->ops->get_srv_inum(xid, 805 + *inode ? NULL : tcon, 806 + cifs_sb, full_path, 807 + &fattr->cf_uniqueid, 808 + data); 809 + if (rc) { 810 + /* 811 + * If that fails reuse existing ino or generate one 812 + * and disable server ones 813 + */ 814 + if (*inode) 815 + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 816 + else { 817 + fattr->cf_uniqueid = iunique(sb, ROOT_I); 818 + cifs_autodisable_serverino(cifs_sb); 819 + } 820 + return; 821 + } 822 + 823 + /* If no errors, check for zero root inode (invalid) */ 824 + if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { 825 + cifs_dbg(FYI, "Invalid (0) inodenum\n"); 826 + if (*inode) { 827 + /* reuse */ 828 + fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 829 + } else { 830 + /* make an ino by hashing the UNC */ 831 + fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; 832 + fattr->cf_uniqueid = simple_hashstr(tcon->treeName); 833 + } 834 + } 835 + } 836 + 837 + static inline bool is_inode_cache_good(struct inode *ino) 838 + { 839 + return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; 840 + } 841 + 730 842 int 731 - cifs_get_inode_info(struct inode **inode, const char *full_path, 732 - FILE_ALL_INFO *data, struct super_block *sb, int xid, 843 + cifs_get_inode_info(struct inode **inode, 844 + const char *full_path, 845 + FILE_ALL_INFO *in_data, 846 + struct super_block *sb, int xid, 733 847 const struct cifs_fid *fid) 734 848 { 735 - __u16 srchflgs; 736 - int rc = 0, tmprc = ENOSYS; 849 + 737 850 struct cifs_tcon *tcon; 738 851 struct TCP_Server_Info *server; 739 852 struct tcon_link *tlink; 740 853 struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 741 - char *buf = NULL; 742 854 bool adjust_tz = false; 743 - struct cifs_fattr fattr; 744 - struct cifs_search_info *srchinf = NULL; 855 + struct cifs_fattr fattr = {0}; 745 856 bool symlink = false; 857 + FILE_ALL_INFO *data = in_data; 858 + FILE_ALL_INFO *tmp_data = NULL; 859 + void *smb1_backup_rsp_buf = NULL; 860 + int rc = 0; 861 + int tmprc = 0; 746 862 747 863 tlink = cifs_sb_tlink(cifs_sb); 748 864 if (IS_ERR(tlink)) ··· 866 750 tcon = tlink_tcon(tlink); 867 751 server = tcon->ses->server; 868 752 869 - cifs_dbg(FYI, "Getting info on %s\n", full_path); 753 + /* 754 + * 1. Fetch file metadata if not provided (data) 755 + */ 870 756 871 - if ((data == NULL) && (*inode != NULL)) { 872 - if (CIFS_CACHE_READ(CIFS_I(*inode)) && 873 - CIFS_I(*inode)->time != 0) { 757 + if (!data) { 758 + if (is_inode_cache_good(*inode)) { 874 759 cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); 875 - goto cgii_exit; 760 + goto out; 876 761 } 877 - } 878 - 879 - /* if inode info is not passed, get it from server */ 880 - if (data == NULL) { 881 - if (!server->ops->query_path_info) { 882 - rc = -ENOSYS; 883 - goto cgii_exit; 884 - } 885 - buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 886 - if (buf == NULL) { 762 + tmp_data = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); 763 + if (!tmp_data) { 887 764 rc = -ENOMEM; 888 - goto cgii_exit; 765 + goto out; 889 766 } 890 - data = (FILE_ALL_INFO *)buf; 891 - rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, 892 - data, &adjust_tz, &symlink); 767 + rc = server->ops->query_path_info(xid, tcon, cifs_sb, 768 + full_path, tmp_data, 769 + &adjust_tz, &symlink); 770 + data = tmp_data; 893 771 } 894 - 895 - if (!rc) { 896 - cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, 897 - symlink); 898 - } else if (rc == -EREMOTE) { 899 - cifs_create_dfs_fattr(&fattr, sb); 900 - rc = 0; 901 - } else if ((rc == -EACCES) && backup_cred(cifs_sb) && 902 - (strcmp(server->vals->version_string, SMB1_VERSION_STRING) 903 - == 0)) { 904 - /* 905 - * For SMB2 and later the backup intent flag is already 906 - * sent if needed on open and there is no path based 907 - * FindFirst operation to use to retry with 908 - */ 909 - 910 - srchinf = kzalloc(sizeof(struct cifs_search_info), 911 - GFP_KERNEL); 912 - if (srchinf == NULL) { 913 - rc = -ENOMEM; 914 - goto cgii_exit; 915 - } 916 - 917 - srchinf->endOfSearch = false; 918 - if (tcon->unix_ext) 919 - srchinf->info_level = SMB_FIND_FILE_UNIX; 920 - else if ((tcon->ses->capabilities & 921 - tcon->ses->server->vals->cap_nt_find) == 0) 922 - srchinf->info_level = SMB_FIND_FILE_INFO_STANDARD; 923 - else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) 924 - srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; 925 - else /* no srvino useful for fallback to some netapp */ 926 - srchinf->info_level = SMB_FIND_FILE_DIRECTORY_INFO; 927 - 928 - srchflgs = CIFS_SEARCH_CLOSE_ALWAYS | 929 - CIFS_SEARCH_CLOSE_AT_END | 930 - CIFS_SEARCH_BACKUP_SEARCH; 931 - 932 - rc = CIFSFindFirst(xid, tcon, full_path, 933 - cifs_sb, NULL, srchflgs, srchinf, false); 934 - if (!rc) { 935 - data = (FILE_ALL_INFO *)srchinf->srch_entries_start; 936 - 937 - cifs_dir_info_to_fattr(&fattr, 938 - (FILE_DIRECTORY_INFO *)data, cifs_sb); 939 - fattr.cf_uniqueid = le64_to_cpu( 940 - ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId); 941 - 942 - cifs_buf_release(srchinf->ntwrk_buf_start); 943 - } 944 - kfree(srchinf); 945 - if (rc) 946 - goto cgii_exit; 947 - } else 948 - goto cgii_exit; 949 772 950 773 /* 951 - * If an inode wasn't passed in, then get the inode number 952 - * 953 - * Is an i_ino of zero legal? Can we use that to check if the server 954 - * supports returning inode numbers? Are there other sanity checks we 955 - * can use to ensure that the server is really filling in that field? 774 + * 2. Convert it to internal cifs metadata (fattr) 956 775 */ 957 - if (*inode == NULL) { 958 - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) { 959 - if (server->ops->get_srv_inum) 960 - tmprc = server->ops->get_srv_inum(xid, 961 - tcon, cifs_sb, full_path, 962 - &fattr.cf_uniqueid, data); 963 - if (tmprc) { 964 - cifs_dbg(FYI, "GetSrvInodeNum rc %d\n", 965 - tmprc); 966 - fattr.cf_uniqueid = iunique(sb, ROOT_I); 967 - cifs_autodisable_serverino(cifs_sb); 968 - } else if ((fattr.cf_uniqueid == 0) && 969 - strlen(full_path) == 0) { 970 - /* some servers ret bad root ino ie 0 */ 971 - cifs_dbg(FYI, "Invalid (0) inodenum\n"); 972 - fattr.cf_flags |= 973 - CIFS_FATTR_FAKE_ROOT_INO; 974 - fattr.cf_uniqueid = 975 - simple_hashstr(tcon->treeName); 976 - } 977 - } else 978 - fattr.cf_uniqueid = iunique(sb, ROOT_I); 979 - } else { 980 - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) 981 - && server->ops->get_srv_inum) { 982 - /* 983 - * Pass a NULL tcon to ensure we don't make a round 984 - * trip to the server. This only works for SMB2+. 985 - */ 986 - tmprc = server->ops->get_srv_inum(xid, 987 - NULL, cifs_sb, full_path, 988 - &fattr.cf_uniqueid, data); 989 - if (tmprc) 990 - fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid; 991 - else if ((fattr.cf_uniqueid == 0) && 992 - strlen(full_path) == 0) { 993 - /* 994 - * Reuse existing root inode num since 995 - * inum zero for root causes ls of . and .. to 996 - * not be returned 997 - */ 998 - cifs_dbg(FYI, "Srv ret 0 inode num for root\n"); 999 - fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid; 1000 - } 1001 - } else 1002 - fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid; 776 + 777 + switch (rc) { 778 + case 0: 779 + cifs_all_info_to_fattr(&fattr, data, sb, adjust_tz, symlink); 780 + break; 781 + case -EREMOTE: 782 + /* DFS link, no metadata available on this server */ 783 + cifs_create_dfs_fattr(&fattr, sb); 784 + rc = 0; 785 + break; 786 + case -EACCES: 787 + /* 788 + * perm errors, try again with backup flags if possible 789 + * 790 + * For SMB2 and later the backup intent flag 791 + * is already sent if needed on open and there 792 + * is no path based FindFirst operation to use 793 + * to retry with 794 + */ 795 + if (backup_cred(cifs_sb) && is_smb1_server(server)) { 796 + /* for easier reading */ 797 + FILE_DIRECTORY_INFO *fdi; 798 + SEARCH_ID_FULL_DIR_INFO *si; 799 + 800 + rc = cifs_backup_query_path_info(xid, tcon, sb, 801 + full_path, 802 + &smb1_backup_rsp_buf, 803 + &data); 804 + if (rc) 805 + goto out; 806 + 807 + fdi = (FILE_DIRECTORY_INFO *)data; 808 + si = (SEARCH_ID_FULL_DIR_INFO *)data; 809 + 810 + cifs_dir_info_to_fattr(&fattr, fdi, cifs_sb); 811 + fattr.cf_uniqueid = le64_to_cpu(si->UniqueId); 812 + /* uniqueid set, skip get inum step */ 813 + goto handle_mnt_opt; 814 + } else { 815 + /* nothing we can do, bail out */ 816 + goto out; 817 + } 818 + break; 819 + default: 820 + cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); 821 + goto out; 1003 822 } 1004 823 824 + /* 825 + * 3. Get or update inode number (fattr.cf_uniqueid) 826 + */ 827 + 828 + cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, &fattr); 829 + 830 + /* 831 + * 4. Tweak fattr based on mount options 832 + */ 833 + 834 + handle_mnt_opt: 1005 835 /* query for SFU type info if supported and needed */ 1006 836 if (fattr.cf_cifsattrs & ATTR_SYSTEM && 1007 837 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { ··· 962 900 full_path, fid); 963 901 if (rc) { 964 902 cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", 965 - __func__, rc); 966 - goto cgii_exit; 903 + __func__, rc); 904 + goto out; 967 905 } 968 906 } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { 969 907 rc = cifs_acl_to_fattr(cifs_sb, &fattr, *inode, false, 970 - full_path, fid); 971 - if (rc) { 908 + full_path, fid); if (rc) { 972 909 cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", 973 910 __func__, rc); 974 - goto cgii_exit; 911 + goto out; 975 912 } 976 913 } 977 914 ··· 986 925 cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); 987 926 } 988 927 928 + /* 929 + * 5. Update inode with final fattr data 930 + */ 931 + 989 932 if (!*inode) { 990 933 *inode = cifs_iget(sb, &fattr); 991 934 if (!*inode) ··· 1002 937 CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) { 1003 938 CIFS_I(*inode)->time = 0; /* force reval */ 1004 939 rc = -ESTALE; 1005 - goto cgii_exit; 940 + goto out; 1006 941 } 1007 942 1008 943 /* if filetype is different, return error */ ··· 1010 945 (fattr.cf_mode & S_IFMT))) { 1011 946 CIFS_I(*inode)->time = 0; /* force reval */ 1012 947 rc = -ESTALE; 1013 - goto cgii_exit; 948 + goto out; 1014 949 } 1015 950 1016 951 cifs_fattr_to_inode(*inode, &fattr); 1017 952 } 1018 - 1019 - cgii_exit: 1020 - if ((*inode) && ((*inode)->i_ino == 0)) 1021 - cifs_dbg(FYI, "inode number of zero returned\n"); 1022 - 1023 - kfree(buf); 953 + out: 954 + cifs_buf_release(smb1_backup_rsp_buf); 1024 955 cifs_put_tlink(tlink); 956 + kfree(tmp_data); 1025 957 return rc; 1026 958 } 1027 959