mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
[CIFS] Readdir fixes to allow search to start at arbitrary position
in directory Also includes first part of fix to compensate for servers which forget to return . and .. as well as updates to changelog and cifs readme. Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
45af7a0f2e
commit
60808233f3
@ -1,7 +1,11 @@
|
||||
Version 1.42
|
||||
------------
|
||||
Fix slow oplock break when mounted to different servers at the same time and
|
||||
the tids match and we try to find matching fid on wrong server.
|
||||
the tids match and we try to find matching fid on wrong server. Fix read
|
||||
looping when signing required by server (2.6.16 kernel only). Fix readdir
|
||||
vs. rename race which could cause each to hang. Return . and .. even
|
||||
if server does not. Allow searches to skip first three entries and
|
||||
begin at any location. Fix oops in find_writeable_file.
|
||||
|
||||
Version 1.41
|
||||
------------
|
||||
|
@ -511,6 +511,14 @@ LinuxExtensionsEnabled If set to one then the client will attempt to
|
||||
support and want to map the uid and gid fields
|
||||
to values supplied at mount (rather than the
|
||||
actual values, then set this to zero. (default 1)
|
||||
Experimental When set to 1 used to enable certain experimental
|
||||
features (currently enables multipage writes
|
||||
when signing is enabled, the multipage write
|
||||
performance enhancement was disabled when
|
||||
signing turned on in case buffer was modified
|
||||
just before it was sent, also this flag will
|
||||
be used to use the new experimental sessionsetup
|
||||
code).
|
||||
|
||||
These experimental features and tracing can be enabled by changing flags in
|
||||
/proc/fs/cifs (after the cifs module has been installed or built into the
|
||||
|
@ -3119,7 +3119,7 @@ findFirstRetry:
|
||||
psrch_inf->endOfSearch = FALSE;
|
||||
|
||||
psrch_inf->entries_in_buffer = le16_to_cpu(parms->SearchCount);
|
||||
psrch_inf->index_of_last_entry =
|
||||
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
|
||||
psrch_inf->entries_in_buffer;
|
||||
*pnetfid = parms->SearchHandle;
|
||||
} else {
|
||||
|
@ -3447,7 +3447,10 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
|
||||
pSesInfo->server->secMode,
|
||||
pSesInfo->server->capabilities,
|
||||
pSesInfo->server->timeZone));
|
||||
if (extended_security
|
||||
if(experimEnabled > 1)
|
||||
rc = CIFS_SessSetup(xid, pSesInfo, CIFS_NTLM /* type */,
|
||||
&ntlmv2_flag, nls_info);
|
||||
else if (extended_security
|
||||
&& (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
|
||||
&& (pSesInfo->server->secType == NTLMSSP)) {
|
||||
cFYI(1, ("New style sesssetup"));
|
||||
|
@ -904,8 +904,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
||||
if (rc != 0)
|
||||
break;
|
||||
}
|
||||
/* BB FIXME We can not sign across two buffers yet */
|
||||
if((pTcon->ses->server->secMode &
|
||||
if(experimEnabled || (pTcon->ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) == 0) {
|
||||
struct kvec iov[2];
|
||||
unsigned int len;
|
||||
@ -921,13 +920,13 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
||||
*poffset, &bytes_written,
|
||||
iov, 1, long_op);
|
||||
} else
|
||||
/* BB FIXME fixup indentation of line below */
|
||||
rc = CIFSSMBWrite(xid, pTcon,
|
||||
open_file->netfid,
|
||||
min_t(const int, cifs_sb->wsize,
|
||||
write_size - total_written),
|
||||
*poffset, &bytes_written,
|
||||
write_data + total_written, NULL, long_op);
|
||||
write_data + total_written,
|
||||
NULL, long_op);
|
||||
}
|
||||
if (rc || (bytes_written == 0)) {
|
||||
if (total_written)
|
||||
@ -966,6 +965,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
||||
struct cifsFileInfo *open_file;
|
||||
int rc;
|
||||
|
||||
/* Having a null inode here (because mapping->host was set to zero by
|
||||
the VFS or MM) should not happen but we had reports of on oops (due to
|
||||
it being zero) during stress testcases so we need to check for it */
|
||||
|
||||
if(cifs_inode == NULL) {
|
||||
cERROR(1,("Null inode passed to cifs_writeable_file"));
|
||||
dump_stack();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_lock(&GlobalSMBSeslock);
|
||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||
if (open_file->closePend)
|
||||
@ -1091,11 +1100,10 @@ static int cifs_writepages(struct address_space *mapping,
|
||||
if (cifs_sb->wsize < PAGE_CACHE_SIZE)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
/* BB FIXME we do not have code to sign across multiple buffers yet,
|
||||
so go to older writepage style write which we can sign if needed */
|
||||
if((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
|
||||
if(cifs_sb->tcon->ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
||||
if(!experimEnabled)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
/*
|
||||
|
@ -121,6 +121,20 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int type,
|
||||
}
|
||||
|
||||
|
||||
/* copy session key */
|
||||
|
||||
/* if Unicode, align strings to two byte boundary */
|
||||
|
||||
/* copy user name */ /* BB Do we need to special case null user name? */
|
||||
|
||||
/* copy domain name */
|
||||
|
||||
/* copy Linux version */
|
||||
|
||||
/* copy network operating system name */
|
||||
|
||||
/* update bcc and smb buffer length */
|
||||
|
||||
/* rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buf_type, 0); */
|
||||
/* SMB request buf freed in SendReceive2 */
|
||||
|
||||
|
@ -590,6 +590,13 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
|
||||
first_entry_in_buffer =
|
||||
cifsFile->srch_inf.index_of_last_entry -
|
||||
cifsFile->srch_inf.entries_in_buffer;
|
||||
|
||||
/* if first entry in buf is zero then is first buffer
|
||||
in search response data which means it is likely . and ..
|
||||
will be in this buffer, although some servers do not return
|
||||
. and .. for the root of a drive and for those we need
|
||||
to start two entries earlier */
|
||||
|
||||
/* dump_cifs_file_struct(file, "In fce ");*/
|
||||
if(((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
|
||||
is_dir_changed(file)) ||
|
||||
@ -632,23 +639,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
|
||||
char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
|
||||
smbCalcSize((struct smb_hdr *)
|
||||
cifsFile->srch_inf.ntwrk_buf_start);
|
||||
|
||||
current_entry = cifsFile->srch_inf.srch_entries_start;
|
||||
first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
|
||||
- cifsFile->srch_inf.entries_in_buffer;
|
||||
pos_in_buf = index_to_find - first_entry_in_buffer;
|
||||
cFYI(1,("found entry - pos_in_buf %d",pos_in_buf));
|
||||
current_entry = cifsFile->srch_inf.srch_entries_start;
|
||||
for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) {
|
||||
/* go entry by entry figuring out which is first */
|
||||
/* if( . or ..)
|
||||
skip */
|
||||
rc = cifs_entry_is_dot(current_entry,cifsFile);
|
||||
if(rc == 1) /* is . or .. so skip */ {
|
||||
cFYI(1,("Entry is .")); /* BB removeme BB */
|
||||
/* continue; */
|
||||
} else if (rc == 2 ) {
|
||||
cFYI(1,("Entry is ..")); /* BB removeme BB */
|
||||
/* continue; */
|
||||
}
|
||||
current_entry = nxt_dir_entry(current_entry,end_of_smb);
|
||||
}
|
||||
if((current_entry == NULL) && (i < pos_in_buf)) {
|
||||
@ -768,6 +766,11 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
|
||||
if(file->f_dentry == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
rc = cifs_entry_is_dot(pfindEntry,cifsF);
|
||||
/* skip . and .. since we added them first */
|
||||
if(rc != 0)
|
||||
return 0;
|
||||
|
||||
cifs_sb = CIFS_SB(file->f_dentry->d_sb);
|
||||
|
||||
qstring.name = scratch_buf;
|
||||
@ -896,22 +899,22 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||
|
||||
switch ((int) file->f_pos) {
|
||||
case 0:
|
||||
/*if (filldir(direntry, ".", 1, file->f_pos,
|
||||
if (filldir(direntry, ".", 1, file->f_pos,
|
||||
file->f_dentry->d_inode->i_ino, DT_DIR) < 0) {
|
||||
cERROR(1, ("Filldir for current dir failed"));
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
file->f_pos++; */
|
||||
file->f_pos++;
|
||||
case 1:
|
||||
/* if (filldir(direntry, "..", 2, file->f_pos,
|
||||
if (filldir(direntry, "..", 2, file->f_pos,
|
||||
file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
|
||||
cERROR(1, ("Filldir for parent dir failed "));
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
file->f_pos++; */
|
||||
case 2:
|
||||
file->f_pos++;
|
||||
default:
|
||||
/* 1) If search is active,
|
||||
is in current search buffer?
|
||||
if it before then restart search
|
||||
@ -925,7 +928,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
default:
|
||||
if(file->private_data == NULL) {
|
||||
rc = -EINVAL;
|
||||
FreeXid(xid);
|
||||
@ -945,8 +947,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||
kfree(cifsFile->search_resume_name);
|
||||
cifsFile->search_resume_name = NULL; */
|
||||
|
||||
/* BB account for . and .. in f_pos as special case */
|
||||
|
||||
rc = find_cifs_entry(xid,pTcon, file,
|
||||
¤t_entry,&num_to_fill);
|
||||
if(rc) {
|
||||
@ -975,7 +975,8 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||
num_to_fill, i));
|
||||
break;
|
||||
}
|
||||
|
||||
/* if buggy server returns . and .. late do
|
||||
we want to check for that here? */
|
||||
rc = cifs_filldir(current_entry, file,
|
||||
filldir, direntry,tmp_buf);
|
||||
file->f_pos++;
|
||||
|
Loading…
Reference in New Issue
Block a user