| From jejb@kernel.org Wed Oct 15 14:35:35 2008 |
| From: Steve French <sfrench@us.ibm.com> |
| Date: Sat, 11 Oct 2008 16:55:11 GMT |
| Subject: CIFS: make sure we have the right resume info before calling CIFSFindNext |
| To: jejb@kernel.org, stable@kernel.org |
| Message-ID: <200810111655.m9BGtBTL013214@hera.kernel.org> |
| |
| From: Steve French <sfrench@us.ibm.com> |
| |
| commit 0752f1522a9120f731232919f7ad904e9e22b8ce upstream |
| |
| When we do a seekdir() or equivalent, we usually end up doing a |
| FindFirst call and then call FindNext until we get to the offset that we |
| want. The problem is that when we call FindNext, the code usually |
| doesn't have the proper info (mostly, the filename of the entry from the |
| last search) to resume the search. |
| |
| Add a "last_entry" field to the cifs_search_info that points to the last |
| entry in the search. We calculate this pointer by using the |
| LastNameOffset field from the search parms that are returned. We then |
| use that info to do a cifs_save_resume_key before we call CIFSFindNext. |
| |
| This patch allows CIFS to reliably pass the "telldir" connectathon test. |
| |
| Signed-off-by: Jeff Layton <jlayton@redhat.com> |
| Signed-off-by: Steve French <sfrench@us.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/cifs/cifsglob.h | 1 |
| fs/cifs/cifssmb.c | 4 + |
| fs/cifs/readdir.c | 128 ++++++++++++++++++++++++++--------------------------- |
| 3 files changed, 70 insertions(+), 63 deletions(-) |
| |
| --- a/fs/cifs/cifsglob.h |
| +++ b/fs/cifs/cifsglob.h |
| @@ -309,6 +309,7 @@ struct cifs_search_info { |
| __u32 resume_key; |
| char *ntwrk_buf_start; |
| char *srch_entries_start; |
| + char *last_entry; |
| char *presume_name; |
| unsigned int resume_name_len; |
| bool endOfSearch:1; |
| --- a/fs/cifs/cifssmb.c |
| +++ b/fs/cifs/cifssmb.c |
| @@ -3636,6 +3636,8 @@ findFirstRetry: |
| le16_to_cpu(parms->SearchCount); |
| psrch_inf->index_of_last_entry = 2 /* skip . and .. */ + |
| psrch_inf->entries_in_buffer; |
| + psrch_inf->last_entry = psrch_inf->srch_entries_start + |
| + le16_to_cpu(parms->LastNameOffset); |
| *pnetfid = parms->SearchHandle; |
| } else { |
| cifs_buf_release(pSMB); |
| @@ -3751,6 +3753,8 @@ int CIFSFindNext(const int xid, struct c |
| le16_to_cpu(parms->SearchCount); |
| psrch_inf->index_of_last_entry += |
| psrch_inf->entries_in_buffer; |
| + psrch_inf->last_entry = psrch_inf->srch_entries_start + |
| + le16_to_cpu(parms->LastNameOffset); |
| /* cFYI(1,("fnxt2 entries in buf %d index_of_last %d", |
| psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */ |
| |
| --- a/fs/cifs/readdir.c |
| +++ b/fs/cifs/readdir.c |
| @@ -640,6 +640,70 @@ static int is_dir_changed(struct file *f |
| |
| } |
| |
| +static int cifs_save_resume_key(const char *current_entry, |
| + struct cifsFileInfo *cifsFile) |
| +{ |
| + int rc = 0; |
| + unsigned int len = 0; |
| + __u16 level; |
| + char *filename; |
| + |
| + if ((cifsFile == NULL) || (current_entry == NULL)) |
| + return -EINVAL; |
| + |
| + level = cifsFile->srch_inf.info_level; |
| + |
| + if (level == SMB_FIND_FILE_UNIX) { |
| + FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; |
| + |
| + filename = &pFindData->FileName[0]; |
| + if (cifsFile->srch_inf.unicode) { |
| + len = cifs_unicode_bytelen(filename); |
| + } else { |
| + /* BB should we make this strnlen of PATH_MAX? */ |
| + len = strnlen(filename, PATH_MAX); |
| + } |
| + cifsFile->srch_inf.resume_key = pFindData->ResumeKey; |
| + } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { |
| + FILE_DIRECTORY_INFO *pFindData = |
| + (FILE_DIRECTORY_INFO *)current_entry; |
| + filename = &pFindData->FileName[0]; |
| + len = le32_to_cpu(pFindData->FileNameLength); |
| + cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| + } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { |
| + FILE_FULL_DIRECTORY_INFO *pFindData = |
| + (FILE_FULL_DIRECTORY_INFO *)current_entry; |
| + filename = &pFindData->FileName[0]; |
| + len = le32_to_cpu(pFindData->FileNameLength); |
| + cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| + } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { |
| + SEARCH_ID_FULL_DIR_INFO *pFindData = |
| + (SEARCH_ID_FULL_DIR_INFO *)current_entry; |
| + filename = &pFindData->FileName[0]; |
| + len = le32_to_cpu(pFindData->FileNameLength); |
| + cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| + } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { |
| + FILE_BOTH_DIRECTORY_INFO *pFindData = |
| + (FILE_BOTH_DIRECTORY_INFO *)current_entry; |
| + filename = &pFindData->FileName[0]; |
| + len = le32_to_cpu(pFindData->FileNameLength); |
| + cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| + } else if (level == SMB_FIND_FILE_INFO_STANDARD) { |
| + FIND_FILE_STANDARD_INFO *pFindData = |
| + (FIND_FILE_STANDARD_INFO *)current_entry; |
| + filename = &pFindData->FileName[0]; |
| + /* one byte length, no name conversion */ |
| + len = (unsigned int)pFindData->FileNameLength; |
| + cifsFile->srch_inf.resume_key = pFindData->ResumeKey; |
| + } else { |
| + cFYI(1, ("Unknown findfirst level %d", level)); |
| + return -EINVAL; |
| + } |
| + cifsFile->srch_inf.resume_name_len = len; |
| + cifsFile->srch_inf.presume_name = filename; |
| + return rc; |
| +} |
| + |
| /* find the corresponding entry in the search */ |
| /* Note that the SMB server returns search entries for . and .. which |
| complicates logic here if we choose to parse for them and we do not |
| @@ -703,6 +767,7 @@ static int find_cifs_entry(const int xid |
| while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && |
| (rc == 0) && !cifsFile->srch_inf.endOfSearch) { |
| cFYI(1, ("calling findnext2")); |
| + cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile); |
| rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, |
| &cifsFile->srch_inf); |
| if (rc) |
| @@ -919,69 +984,6 @@ static int cifs_filldir(char *pfindEntry |
| return rc; |
| } |
| |
| -static int cifs_save_resume_key(const char *current_entry, |
| - struct cifsFileInfo *cifsFile) |
| -{ |
| - int rc = 0; |
| - unsigned int len = 0; |
| - __u16 level; |
| - char *filename; |
| - |
| - if ((cifsFile == NULL) || (current_entry == NULL)) |
| - return -EINVAL; |
| - |
| - level = cifsFile->srch_inf.info_level; |
| - |
| - if (level == SMB_FIND_FILE_UNIX) { |
| - FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry; |
| - |
| - filename = &pFindData->FileName[0]; |
| - if (cifsFile->srch_inf.unicode) { |
| - len = cifs_unicode_bytelen(filename); |
| - } else { |
| - /* BB should we make this strnlen of PATH_MAX? */ |
| - len = strnlen(filename, PATH_MAX); |
| - } |
| - cifsFile->srch_inf.resume_key = pFindData->ResumeKey; |
| - } else if (level == SMB_FIND_FILE_DIRECTORY_INFO) { |
| - FILE_DIRECTORY_INFO *pFindData = |
| - (FILE_DIRECTORY_INFO *)current_entry; |
| - filename = &pFindData->FileName[0]; |
| - len = le32_to_cpu(pFindData->FileNameLength); |
| - cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| - } else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) { |
| - FILE_FULL_DIRECTORY_INFO *pFindData = |
| - (FILE_FULL_DIRECTORY_INFO *)current_entry; |
| - filename = &pFindData->FileName[0]; |
| - len = le32_to_cpu(pFindData->FileNameLength); |
| - cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| - } else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) { |
| - SEARCH_ID_FULL_DIR_INFO *pFindData = |
| - (SEARCH_ID_FULL_DIR_INFO *)current_entry; |
| - filename = &pFindData->FileName[0]; |
| - len = le32_to_cpu(pFindData->FileNameLength); |
| - cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| - } else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) { |
| - FILE_BOTH_DIRECTORY_INFO *pFindData = |
| - (FILE_BOTH_DIRECTORY_INFO *)current_entry; |
| - filename = &pFindData->FileName[0]; |
| - len = le32_to_cpu(pFindData->FileNameLength); |
| - cifsFile->srch_inf.resume_key = pFindData->FileIndex; |
| - } else if (level == SMB_FIND_FILE_INFO_STANDARD) { |
| - FIND_FILE_STANDARD_INFO *pFindData = |
| - (FIND_FILE_STANDARD_INFO *)current_entry; |
| - filename = &pFindData->FileName[0]; |
| - /* one byte length, no name conversion */ |
| - len = (unsigned int)pFindData->FileNameLength; |
| - cifsFile->srch_inf.resume_key = pFindData->ResumeKey; |
| - } else { |
| - cFYI(1, ("Unknown findfirst level %d", level)); |
| - return -EINVAL; |
| - } |
| - cifsFile->srch_inf.resume_name_len = len; |
| - cifsFile->srch_inf.presume_name = filename; |
| - return rc; |
| -} |
| |
| int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) |
| { |