diff options
Diffstat (limited to 'fs/cifs/readdir.c')
-rw-r--r-- | fs/cifs/readdir.c | 215 |
1 files changed, 117 insertions, 98 deletions
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 770d5a9781c1..69d2c826a23b 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -111,6 +111,14 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, | |||
111 | return; | 111 | return; |
112 | } | 112 | } |
113 | 113 | ||
114 | /* | ||
115 | * If we know that the inode will need to be revalidated immediately, | ||
116 | * then don't create a new dentry for it. We'll end up doing an on | ||
117 | * the wire call either way and this spares us an invalidation. | ||
118 | */ | ||
119 | if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) | ||
120 | return; | ||
121 | |||
114 | dentry = d_alloc(parent, name); | 122 | dentry = d_alloc(parent, name); |
115 | if (!dentry) | 123 | if (!dentry) |
116 | return; | 124 | return; |
@@ -126,6 +134,22 @@ out: | |||
126 | dput(dentry); | 134 | dput(dentry); |
127 | } | 135 | } |
128 | 136 | ||
137 | /* | ||
138 | * Is it possible that this directory might turn out to be a DFS referral | ||
139 | * once we go to try and use it? | ||
140 | */ | ||
141 | static bool | ||
142 | cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) | ||
143 | { | ||
144 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
145 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | ||
146 | |||
147 | if (tcon->Flags & SMB_SHARE_IS_IN_DFS) | ||
148 | return true; | ||
149 | #endif | ||
150 | return false; | ||
151 | } | ||
152 | |||
129 | static void | 153 | static void |
130 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | 154 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) |
131 | { | 155 | { |
@@ -135,6 +159,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | |||
135 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 159 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
136 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 160 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
137 | fattr->cf_dtype = DT_DIR; | 161 | fattr->cf_dtype = DT_DIR; |
162 | /* | ||
163 | * Windows CIFS servers generally make DFS referrals look | ||
164 | * like directories in FIND_* responses with the reparse | ||
165 | * attribute flag also set (since DFS junctions are | ||
166 | * reparse points). We must revalidate at least these | ||
167 | * directory inodes before trying to use them (if | ||
168 | * they are DFS we will get PATH_NOT_COVERED back | ||
169 | * when queried directly and can then try to connect | ||
170 | * to the DFS target) | ||
171 | */ | ||
172 | if (cifs_dfs_is_possible(cifs_sb) && | ||
173 | (fattr->cf_cifsattrs & ATTR_REPARSE)) | ||
174 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; | ||
138 | } else { | 175 | } else { |
139 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; | 176 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
140 | fattr->cf_dtype = DT_REG; | 177 | fattr->cf_dtype = DT_REG; |
@@ -537,14 +574,14 @@ static int cifs_save_resume_key(const char *current_entry, | |||
537 | * every entry (do not increment for . or .. entry). | 574 | * every entry (do not increment for . or .. entry). |
538 | */ | 575 | */ |
539 | static int | 576 | static int |
540 | find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, | 577 | find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, |
541 | struct file *file, char **current_entry, int *num_to_ret) | 578 | struct file *file, char **current_entry, int *num_to_ret) |
542 | { | 579 | { |
543 | __u16 search_flags; | 580 | __u16 search_flags; |
544 | int rc = 0; | 581 | int rc = 0; |
545 | int pos_in_buf = 0; | 582 | int pos_in_buf = 0; |
546 | loff_t first_entry_in_buffer; | 583 | loff_t first_entry_in_buffer; |
547 | loff_t index_to_find = file->f_pos; | 584 | loff_t index_to_find = pos; |
548 | struct cifsFileInfo *cfile = file->private_data; | 585 | struct cifsFileInfo *cfile = file->private_data; |
549 | struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); | 586 | struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); |
550 | struct TCP_Server_Info *server = tcon->ses->server; | 587 | struct TCP_Server_Info *server = tcon->ses->server; |
@@ -659,8 +696,9 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, | |||
659 | return rc; | 696 | return rc; |
660 | } | 697 | } |
661 | 698 | ||
662 | static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir, | 699 | static int cifs_filldir(char *find_entry, struct file *file, |
663 | void *dirent, char *scratch_buf, unsigned int max_len) | 700 | struct dir_context *ctx, |
701 | char *scratch_buf, unsigned int max_len) | ||
664 | { | 702 | { |
665 | struct cifsFileInfo *file_info = file->private_data; | 703 | struct cifsFileInfo *file_info = file->private_data; |
666 | struct super_block *sb = file->f_path.dentry->d_sb; | 704 | struct super_block *sb = file->f_path.dentry->d_sb; |
@@ -740,13 +778,11 @@ static int cifs_filldir(char *find_entry, struct file *file, filldir_t filldir, | |||
740 | cifs_prime_dcache(file->f_dentry, &name, &fattr); | 778 | cifs_prime_dcache(file->f_dentry, &name, &fattr); |
741 | 779 | ||
742 | ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); | 780 | ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); |
743 | rc = filldir(dirent, name.name, name.len, file->f_pos, ino, | 781 | return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype); |
744 | fattr.cf_dtype); | ||
745 | return rc; | ||
746 | } | 782 | } |
747 | 783 | ||
748 | 784 | ||
749 | int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) | 785 | int cifs_readdir(struct file *file, struct dir_context *ctx) |
750 | { | 786 | { |
751 | int rc = 0; | 787 | int rc = 0; |
752 | unsigned int xid; | 788 | unsigned int xid; |
@@ -772,103 +808,86 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) | |||
772 | goto rddir2_exit; | 808 | goto rddir2_exit; |
773 | } | 809 | } |
774 | 810 | ||
775 | switch ((int) file->f_pos) { | 811 | if (!dir_emit_dots(file, ctx)) |
776 | case 0: | 812 | goto rddir2_exit; |
777 | if (filldir(direntry, ".", 1, file->f_pos, | ||
778 | file_inode(file)->i_ino, DT_DIR) < 0) { | ||
779 | cifs_dbg(VFS, "Filldir for current dir failed\n"); | ||
780 | rc = -ENOMEM; | ||
781 | break; | ||
782 | } | ||
783 | file->f_pos++; | ||
784 | case 1: | ||
785 | if (filldir(direntry, "..", 2, file->f_pos, | ||
786 | parent_ino(file->f_path.dentry), DT_DIR) < 0) { | ||
787 | cifs_dbg(VFS, "Filldir for parent dir failed\n"); | ||
788 | rc = -ENOMEM; | ||
789 | break; | ||
790 | } | ||
791 | file->f_pos++; | ||
792 | default: | ||
793 | /* 1) If search is active, | ||
794 | is in current search buffer? | ||
795 | if it before then restart search | ||
796 | if after then keep searching till find it */ | ||
797 | |||
798 | if (file->private_data == NULL) { | ||
799 | rc = -EINVAL; | ||
800 | free_xid(xid); | ||
801 | return rc; | ||
802 | } | ||
803 | cifsFile = file->private_data; | ||
804 | if (cifsFile->srch_inf.endOfSearch) { | ||
805 | if (cifsFile->srch_inf.emptyDir) { | ||
806 | cifs_dbg(FYI, "End of search, empty dir\n"); | ||
807 | rc = 0; | ||
808 | break; | ||
809 | } | ||
810 | } /* else { | ||
811 | cifsFile->invalidHandle = true; | ||
812 | tcon->ses->server->close(xid, tcon, &cifsFile->fid); | ||
813 | } */ | ||
814 | 813 | ||
815 | tcon = tlink_tcon(cifsFile->tlink); | 814 | /* 1) If search is active, |
816 | rc = find_cifs_entry(xid, tcon, file, ¤t_entry, | 815 | is in current search buffer? |
817 | &num_to_fill); | 816 | if it before then restart search |
818 | if (rc) { | 817 | if after then keep searching till find it */ |
819 | cifs_dbg(FYI, "fce error %d\n", rc); | 818 | |
820 | goto rddir2_exit; | 819 | if (file->private_data == NULL) { |
821 | } else if (current_entry != NULL) { | 820 | rc = -EINVAL; |
822 | cifs_dbg(FYI, "entry %lld found\n", file->f_pos); | 821 | goto rddir2_exit; |
823 | } else { | 822 | } |
824 | cifs_dbg(FYI, "could not find entry\n"); | 823 | cifsFile = file->private_data; |
824 | if (cifsFile->srch_inf.endOfSearch) { | ||
825 | if (cifsFile->srch_inf.emptyDir) { | ||
826 | cifs_dbg(FYI, "End of search, empty dir\n"); | ||
827 | rc = 0; | ||
825 | goto rddir2_exit; | 828 | goto rddir2_exit; |
826 | } | 829 | } |
827 | cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", | 830 | } /* else { |
828 | num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); | 831 | cifsFile->invalidHandle = true; |
829 | max_len = tcon->ses->server->ops->calc_smb_size( | 832 | tcon->ses->server->close(xid, tcon, &cifsFile->fid); |
830 | cifsFile->srch_inf.ntwrk_buf_start); | 833 | } */ |
831 | end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; | 834 | |
832 | 835 | tcon = tlink_tcon(cifsFile->tlink); | |
833 | tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); | 836 | rc = find_cifs_entry(xid, tcon, ctx->pos, file, ¤t_entry, |
834 | if (tmp_buf == NULL) { | 837 | &num_to_fill); |
835 | rc = -ENOMEM; | 838 | if (rc) { |
839 | cifs_dbg(FYI, "fce error %d\n", rc); | ||
840 | goto rddir2_exit; | ||
841 | } else if (current_entry != NULL) { | ||
842 | cifs_dbg(FYI, "entry %lld found\n", ctx->pos); | ||
843 | } else { | ||
844 | cifs_dbg(FYI, "could not find entry\n"); | ||
845 | goto rddir2_exit; | ||
846 | } | ||
847 | cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", | ||
848 | num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); | ||
849 | max_len = tcon->ses->server->ops->calc_smb_size( | ||
850 | cifsFile->srch_inf.ntwrk_buf_start); | ||
851 | end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; | ||
852 | |||
853 | tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); | ||
854 | if (tmp_buf == NULL) { | ||
855 | rc = -ENOMEM; | ||
856 | goto rddir2_exit; | ||
857 | } | ||
858 | |||
859 | for (i = 0; i < num_to_fill; i++) { | ||
860 | if (current_entry == NULL) { | ||
861 | /* evaluate whether this case is an error */ | ||
862 | cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", | ||
863 | num_to_fill, i); | ||
836 | break; | 864 | break; |
837 | } | 865 | } |
838 | 866 | /* | |
839 | for (i = 0; (i < num_to_fill) && (rc == 0); i++) { | 867 | * if buggy server returns . and .. late do we want to |
840 | if (current_entry == NULL) { | 868 | * check for that here? |
841 | /* evaluate whether this case is an error */ | 869 | */ |
842 | cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n", | 870 | rc = cifs_filldir(current_entry, file, ctx, |
843 | num_to_fill, i); | 871 | tmp_buf, max_len); |
844 | break; | 872 | if (rc) { |
845 | } | 873 | if (rc > 0) |
846 | /* | ||
847 | * if buggy server returns . and .. late do we want to | ||
848 | * check for that here? | ||
849 | */ | ||
850 | rc = cifs_filldir(current_entry, file, filldir, | ||
851 | direntry, tmp_buf, max_len); | ||
852 | if (rc == -EOVERFLOW) { | ||
853 | rc = 0; | 874 | rc = 0; |
854 | break; | 875 | break; |
855 | } | ||
856 | |||
857 | file->f_pos++; | ||
858 | if (file->f_pos == | ||
859 | cifsFile->srch_inf.index_of_last_entry) { | ||
860 | cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", | ||
861 | file->f_pos, tmp_buf); | ||
862 | cifs_save_resume_key(current_entry, cifsFile); | ||
863 | break; | ||
864 | } else | ||
865 | current_entry = | ||
866 | nxt_dir_entry(current_entry, end_of_smb, | ||
867 | cifsFile->srch_inf.info_level); | ||
868 | } | 876 | } |
869 | kfree(tmp_buf); | 877 | |
870 | break; | 878 | ctx->pos++; |
871 | } /* end switch */ | 879 | if (ctx->pos == |
880 | cifsFile->srch_inf.index_of_last_entry) { | ||
881 | cifs_dbg(FYI, "last entry in buf at pos %lld %s\n", | ||
882 | ctx->pos, tmp_buf); | ||
883 | cifs_save_resume_key(current_entry, cifsFile); | ||
884 | break; | ||
885 | } else | ||
886 | current_entry = | ||
887 | nxt_dir_entry(current_entry, end_of_smb, | ||
888 | cifsFile->srch_inf.info_level); | ||
889 | } | ||
890 | kfree(tmp_buf); | ||
872 | 891 | ||
873 | rddir2_exit: | 892 | rddir2_exit: |
874 | free_xid(xid); | 893 | free_xid(xid); |