Commit 5225e3b5 authored by Steve French's avatar Steve French

Add resume key support for readdir to workaround Windows 2000 and XP server

problem.  Update oplock handling code.  Reduce excessive stack usage in 
link.c
parent df4b68ca
Version 0.72
------------
Add resume key support to search (readdir) code to workaround
Windows bug. Add /proc/fs/cifs/LookupCacheEnable which
allows disabling caching of attribute information for
lookups.
Version 0.71
------------
Add more oplock handling (distributed caching code). Remove
dead code. Remove excessive stack space utilization from
symlink routines.
Version 0.70
------------
Fix oops in get dfs referral (triggered when null path sent in to
......
......@@ -185,6 +185,8 @@ static read_proc_t cifsFYI_read;
static write_proc_t cifsFYI_write;
static read_proc_t oplockEnabled_read;
static write_proc_t oplockEnabled_write;
static read_proc_t lookupFlag_read;
static write_proc_t lookupFlag_write;
static read_proc_t traceSMB_read;
static write_proc_t traceSMB_write;
static read_proc_t multiuser_mount_read;
......@@ -243,6 +245,12 @@ cifs_proc_init(void)
if (pde)
pde->write_proc = extended_security_write;
pde =
create_proc_read_entry("LookupCacheEnable", 0, proc_fs_cifs,
lookupFlag_read, 0);
if (pde)
pde->write_proc = lookupFlag_write;
pde =
create_proc_read_entry("NTLMV2Enabled", 0, proc_fs_cifs,
ntlmv2_enabled_read, 0);
......@@ -353,6 +361,44 @@ oplockEnabled_write(struct file *file, const char *buffer,
return count;
}
static int
lookupFlag_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
len = sprintf(page, "%d\n", lookupCacheEnabled);
len -= off;
*start = page + off;
if (len > count)
len = count;
else
*eof = 1;
if (len < 0)
len = 0;
return len;
}
static int
lookupFlag_write(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char c;
int rc;
rc = get_user(c, buffer);
if (rc)
return rc;
if (c == '0' || c == 'n' || c == 'N')
lookupCacheEnabled = 0;
else if (c == '1' || c == 'y' || c == 'Y')
lookupCacheEnabled = 1;
return count;
}
static int
traceSMB_read(char *page, char **start, off_t off, int count,
int *eof, void *data)
......
......@@ -48,11 +48,13 @@ int cifsFYI = 0;
int cifsERROR = 1;
int traceSMB = 0;
unsigned int oplockEnabled = 0;
unsigned int lookupCacheEnabled = 1;
unsigned int multiuser_mount = 0;
unsigned int extended_security = 0;
unsigned int ntlmv2_support = 0;
unsigned int sign_CIFS_PDUs = 0;
unsigned int CIFSMaximumBufferSize = CIFS_MAX_MSGSIZE;
struct task_struct * oplockThread = NULL;
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
char *);
......@@ -175,6 +177,7 @@ static int cifs_permission(struct inode * inode, int mask)
static kmem_cache_t *cifs_inode_cachep;
kmem_cache_t *cifs_req_cachep;
kmem_cache_t *cifs_mid_cachep;
kmem_cache_t *cifs_oplock_cachep;
static struct inode *
cifs_alloc_inode(struct super_block *sb)
......@@ -390,10 +393,17 @@ int
cifs_init_mids(void)
{
cifs_mid_cachep = kmem_cache_create("cifs_mpx_ids",
sizeof (struct mid_q_entry), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
sizeof (struct mid_q_entry), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (cifs_mid_cachep == NULL)
return -ENOMEM;
cifs_oplock_cachep = kmem_cache_create("cifs_oplock_structs",
sizeof (struct oplock_q_entry), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (cifs_oplock_cachep == NULL) {
kmem_cache_destroy(cifs_mid_cachep);
return -ENOMEM;
}
return 0;
}
......@@ -404,6 +414,49 @@ cifs_destroy_mids(void)
if (kmem_cache_destroy(cifs_mid_cachep))
printk(KERN_WARNING
"cifs_destroy_mids: error not all structures were freed\n");
if (kmem_cache_destroy(cifs_oplock_cachep))
printk(KERN_WARNING
"error not all oplock structures were freed\n");}
static int cifs_oplock_thread(void * dummyarg)
{
struct list_head * tmp;
struct oplock_q_entry * oplock_item;
struct file * pfile;
struct cifsTconInfo *pTcon;
int rc;
daemonize("cifsoplockd");
allow_signal(SIGKILL);
oplockThread = current;
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(100*HZ);
/* BB add missing code */
cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */
write_lock(&GlobalMid_Lock);
list_for_each(tmp, &GlobalOplock_Q) {
oplock_item = list_entry(tmp, struct
oplock_q_entry,
qhead);
if(oplock_item) {
pTcon = oplock_item->tcon;
pfile = oplock_item->file_to_flush;
cFYI(1,("process item on queue"));/* BB remove */
DeleteOplockQEntry(oplock_item);
write_unlock(&GlobalMid_Lock);
rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping);
cFYI(1,("Oplock flush file %p rc %d",pfile,rc));
/* send oplock break */
write_lock(&GlobalMid_Lock);
} else
break;
cFYI(1,("next time through list")); /* BB remove */
}
write_unlock(&GlobalMid_Lock);
cFYI(1,("next time through while loop")); /* BB remove */
}
}
static int __init
......@@ -416,7 +469,7 @@ init_cifs(void)
INIT_LIST_HEAD(&GlobalServerList); /* BB not implemented yet */
INIT_LIST_HEAD(&GlobalSMBSessionList);
INIT_LIST_HEAD(&GlobalTreeConnectionList);
INIT_LIST_HEAD(&GlobalOplock_Q);
/*
* Initialize Global counters
*/
......@@ -437,9 +490,11 @@ init_cifs(void)
rc = cifs_init_request_bufs();
if (!rc) {
rc = register_filesystem(&cifs_fs_type);
if (!rc)
return rc; /* Success */
else
if (!rc) {
kernel_thread(cifs_oplock_thread, NULL,
CLONE_FS | CLONE_FILES | CLONE_VM);
return rc; /* Success */
} else
cifs_destroy_request_bufs();
}
cifs_destroy_mids();
......@@ -459,10 +514,12 @@ exit_cifs(void)
#if CONFIG_PROC_FS
cifs_proc_clean();
#endif
unregister_filesystem(&cifs_fs_type);
unregister_filesystem(&cifs_fs_type);
cifs_destroy_inodecache();
cifs_destroy_mids();
cifs_destroy_request_bufs();
if(oplockThread)
send_sig(SIGKILL, oplockThread, 1);
}
MODULE_AUTHOR("Steve French <sfrench@us.ibm.com>");
......
......@@ -207,6 +207,9 @@ struct cifsFileInfo {
struct file * pfile; /* needed for writepage */
int endOfSearch:1; /* we have reached end of search */
int closePend:1; /* file is marked to close */
char * search_resume_name;
unsigned int resume_name_length;
__u32 resume_key;
};
/*
......@@ -221,7 +224,8 @@ struct cifsInodeInfo {
atomic_t inUse; /* num concurrent users (local openers cifs) of file*/
unsigned long time; /* jiffies of last update/check of inode */
int clientCanCacheRead:1; /* read oplock */
int clientCanCacheAll:1; /* read and writebehind oplock */
int clientCanCacheAll:1; /* read and writebehind oplock */
int oplockPending:1;
struct inode vfs_inode;
};
......@@ -251,14 +255,20 @@ struct mid_q_entry {
int midState; /* wish this were enum but can not pass to wait_event */
};
struct oplock_q_entry {
struct list_head qhead;
struct file * file_to_flush;
struct cifsTconInfo * tcon;
};
#define MID_FREE 0
#define MID_REQUEST_ALLOCATED 1
#define MID_REQUEST_SUBMITTED 2
#define MID_RESPONSE_RECEIVED 4
struct servers_not_supported { /* @z4a */
struct servers_not_supported *next1; /* @z4a */
char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* @z4a */
struct servers_not_supported { /* @z4a */
struct servers_not_supported *next1; /* @z4a */
char server_Name[SERVER_NAME_LEN_WITH_NULL]; /* @z4a */
/* Server Names in SMB protocol are 15 chars + X'20' */
/* in 16th byte... @z4a */
};
......@@ -301,6 +311,8 @@ GLOBAL_EXTERN struct list_head GlobalSMBSessionList;
GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
GLOBAL_EXTERN struct list_head GlobalOplock_Q;
/*
* Global transaction id (XID) information
*/
......@@ -327,6 +339,7 @@ GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions
have the uid/password or Kerberos credential
or equivalent for current user */
GLOBAL_EXTERN unsigned int oplockEnabled;
GLOBAL_EXTERN unsigned int lookupCacheEnabled;
GLOBAL_EXTERN unsigned int extended_security; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
GLOBAL_EXTERN unsigned int ntlmv2_support; /* better optional password hash */
......
......@@ -38,6 +38,7 @@
#define SMB_COM_WRITE_ANDX 0x2F
#define SMB_COM_TRANSACTION2 0x32
#define SMB_COM_TRANSACTION2_SECONDARY 0x33
#define SMB_COM_FIND_CLOSE2 0x34
#define SMB_COM_TREE_DISCONNECT 0x71
#define SMB_COM_NEGOTIATE 0x72
#define SMB_COM_SESSION_SETUP_ANDX 0x73
......@@ -584,7 +585,7 @@ typedef union smb_com_tree_disconnect { /* as an altetnative can use flag on tre
typedef struct smb_com_close_req {
struct smb_hdr hdr; /* wct = 3 */
__u16 FileID; /* target file attributes */
__u16 FileID;
__u32 LastWriteTime; /* should be zero */
__u16 ByteCount; /* 0 */
} CLOSE_REQ;
......@@ -594,6 +595,12 @@ typedef struct smb_com_close_rsp {
__u16 ByteCount; /* bct = 0 */
} CLOSE_RSP;
typedef struct smb_com_findclose_req {
struct smb_hdr hdr; /* wct = 1 */
__u16 FileID;
__u16 ByteCount; /* 0 */
} FINDCLOSE_REQ;
/* OpenFlags */
#define REQ_OPLOCK 0x00000002
#define REQ_BATCHOPLOCK 0x00000004
......@@ -1152,7 +1159,7 @@ typedef struct smb_com_transaction2_fnext_req {
__u16 InformationLevel;
__u32 ResumeKey;
__u16 SearchFlags;
char ResumeFileName[1]; /* will be null string actually since we set bit 3 - resume from previous ending place */
char ResumeFileName[1];
} TRANSACTION2_FNEXT_REQ;
typedef struct smb_com_transaction2_fnext_rsp {
......@@ -1509,7 +1516,7 @@ typedef struct {
__u32 ExtFileAttributes;
__u32 FileNameLength;
char FileName[1];
} FILE_DIRECTORY_INFO; /* level 257 FF response data area */
} FILE_DIRECTORY_INFO; /* level 257 FF response data area */
struct gea {
unsigned char cbName;
......
......@@ -57,6 +57,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifsTconInfo *, int
/* length of fixed section (word count) in two byte units */
);
struct oplock_q_entry * AllocOplockQEntry(struct file *,struct cifsTconInfo *);
void DeleteOplockQEntry(struct oplock_q_entry *);
extern struct timespec cifs_NTtimeToUnix(u64 /* utc nanoseconds since 1601 */ );
extern u64 cifs_UnixTimeToNT(struct timespec);
extern void RevUcode_to_Ucode(char *revUnicode, char *UnicodeName);
......@@ -104,9 +106,13 @@ extern int CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
extern int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
FILE_DIRECTORY_INFO * findData,
T2_FNEXT_RSP_PARMS * findParms,
const __u16 searchHandle, const __u32 resumeKey,
const __u16 searchHandle, char * resume_name,
int name_length, __u32 resume_key,
int *UnicodeFlag, int *pUnixFlag);
extern int CIFSFindClose(const int, struct cifsTconInfo *tcon,
const __u16 search_handle);
extern int CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
const unsigned char *searchName,
FILE_ALL_INFO * findData,
......
......@@ -1449,7 +1449,7 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
ATTR_DIRECTORY);
pSMB->SearchCount = cpu_to_le16(CIFS_MAX_MSGSIZE / sizeof (FILE_DIRECTORY_INFO)); /* should this be shrunk even more ? */
pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END);
pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
/* test for Unix extensions */
if (tcon->ses->capabilities & CAP_UNIX) {
......@@ -1496,9 +1496,9 @@ CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
int
CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
FILE_DIRECTORY_INFO * findData,
T2_FNEXT_RSP_PARMS * findParms, const __u16 searchHandle,
__u32 resumeKey, int *pUnicodeFlag, int *pUnixFlag)
FILE_DIRECTORY_INFO * findData, T2_FNEXT_RSP_PARMS * findParms,
const __u16 searchHandle, char * resume_file_name, int name_len,
__u32 resume_key, int *pUnicodeFlag, int *pUnixFlag)
{
/* level 257 SMB_ */
TRANSACTION2_FNEXT_REQ *pSMB = NULL;
......@@ -1508,6 +1508,9 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
int bytes_returned;
cFYI(1, ("In FindNext"));
if(resume_file_name == NULL) {
return -EIO;
}
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr);
if (rc)
......@@ -1530,9 +1533,6 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->SearchHandle = searchHandle; /* always kept as le */
findParms->SearchCount = 0; /* set to zero in case of error */
pSMB->SearchCount =
......@@ -1546,10 +1546,19 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
*pUnixFlag = FALSE;
}
pSMB->ResumeKey = resumeKey; /* always kept as le */
pSMB->ResumeKey = resume_key;
pSMB->SearchFlags =
cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
CIFS_SEARCH_CONTINUE_FROM_LAST);
cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
/* BB add check to make sure we do not cross end of smb */
if(name_len < CIFS_MAX_MSGSIZE) {
memcpy(pSMB->ResumeFileName, resume_file_name, name_len);
pSMB->ByteCount += name_len;
}
pSMB->TotalParameterCount += name_len;
pSMB->ByteCount = pSMB->TotalParameterCount + 1 /* pad */ ;
pSMB->TotalParameterCount = cpu_to_le16(pSMB->TotalParameterCount);
pSMB->ParameterCount = pSMB->TotalParameterCount;
/* BB improve error handling here */
pSMB->hdr.smb_buf_length += pSMB->ByteCount;
pSMB->ByteCount = cpu_to_le16(pSMB->ByteCount);
......@@ -1585,6 +1594,33 @@ CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
return rc;
}
int
CIFSFindClose(const int xid, struct cifsTconInfo *tcon, const __u16 searchHandle)
{
int rc = 0;
FINDCLOSE_REQ *pSMB = NULL;
CLOSE_RSP *pSMBr = NULL;
int bytes_returned;
cFYI(1, ("In CIFSSMBFindClose"));
rc = smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **) &pSMB,
(void **) &pSMBr);
if (rc)
return rc;
pSMB->FileID = searchHandle;
pSMB->ByteCount = 0;
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cERROR(1, ("Send error in FindClose = %d", rc));
}
if (pSMB)
buf_release(pSMB);
return rc;
}
int
CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
const unsigned char *searchName,
......
......@@ -850,8 +850,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
init_waitqueue_head(&srvTcp->response_q);
INIT_LIST_HEAD(&srvTcp->pending_mid_q);
srvTcp->tcpStatus = CifsGood;
kernel_thread((void *) (void *)
cifs_demultiplex_thread, srvTcp,
kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp,
CLONE_FS | CLONE_FILES | CLONE_VM);
}
}
......
......@@ -152,28 +152,31 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
{
int rc = 0;
struct cifsFileInfo *open_file = NULL;
struct file * file = NULL;
struct list_head *tmp;
/* list all files open on tree connection */
list_for_each(tmp, &pTcon->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist);
if(open_file)
if(open_file->pfile) {
if(open_file->pfile->private_data) {
kfree(open_file->pfile->private_data);
}
rc = cifs_open(open_file->pfile->f_dentry->d_inode,
open_file->pfile);
if(open_file) {
if(open_file->search_resume_name) {
kfree(open_file->search_resume_name);
}
file = open_file->pfile;
kfree(open_file);
if(file) {
file->private_data = NULL;
rc = cifs_open(file->f_dentry->d_inode,file);
if(rc) {
cFYI(1,("reconnecting file %s failed with %d",
open_file->pfile->f_dentry->d_name.name,rc));
file->f_dentry->d_name.name,rc));
} else {
cFYI(1,("reconnection of %s succeeded",
open_file->pfile->f_dentry->d_name.name));
file->f_dentry->d_name.name));
}
}
}
}
}
return rc;
}
......@@ -196,6 +199,8 @@ cifs_close(struct inode *inode, struct file *file)
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid);
if(pSMBFile->search_resume_name)
kfree(pSMBFile->search_resume_name);
kfree(file->private_data);
file->private_data = NULL;
} else
......@@ -1055,6 +1060,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
int xid, i;
int Unicode = FALSE;
int UnixSearch = FALSE;
unsigned int bufsize;
__u16 searchHandle;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
......@@ -1065,14 +1071,19 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
T2_FFIRST_RSP_PARMS findParms;
T2_FNEXT_RSP_PARMS findNextParms;
FILE_DIRECTORY_INFO *pfindData;
FILE_DIRECTORY_INFO *lastFindData;
FILE_UNIX_INFO *pfindDataUnix;
xid = GetXid();
cifs_sb = CIFS_SB(file->f_dentry->d_sb);
pTcon = cifs_sb->tcon;
data = kmalloc(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE,
GFP_KERNEL);
bufsize = pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE;
if(bufsize > CIFS_MAX_MSGSIZE) {
FreeXid(xid);
return -EIO;
}
data = kmalloc(bufsize, GFP_KERNEL);
pfindData = (FILE_DIRECTORY_INFO *) data;
full_path = build_wildcard_path_from_dentry(file->f_dentry);
......@@ -1081,8 +1092,7 @@ 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 "));
break;
......@@ -1090,8 +1100,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
file->f_pos++;
/* fallthrough */
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 "));
break;
......@@ -1099,44 +1108,99 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
file->f_pos++;
/* fallthrough */
case 2:
/* do not reallocate search handle if rewind */
if(file->private_data == NULL) {
rc = CIFSFindFirst(xid, pTcon, full_path, pfindData,
&findParms, cifs_sb->local_nls,
&Unicode, &UnixSearch);
cFYI(1,
("Count: %d End: %d ", findParms.SearchCount,
findParms.EndofSearch));
} else {
if (file->private_data != NULL) {
cFYI(1,("Search rewinding on %s",full_path));
goto readdir_rewind;
CIFSFindClose(xid, pTcon, cifsFile->netfid);
if(cifsFile->search_resume_name) {
kfree(cifsFile->search_resume_name);
cifsFile->search_resume_name = NULL;
}
}
rc = CIFSFindFirst(xid, pTcon, full_path, pfindData,
&findParms, cifs_sb->local_nls,
&Unicode, &UnixSearch);
cFYI(1, ("Count: %d End: %d ", findParms.SearchCount,
findParms.EndofSearch));
if (rc == 0) {
searchHandle = findParms.SearchHandle;
if(file->private_data == NULL)
file->private_data =
kmalloc(sizeof(struct cifsFileInfo),
GFP_KERNEL);
else {
/* BB close search handle */
cFYI(1,("Search rewinding on %s",full_path));
}
GFP_KERNEL);
if (file->private_data) {
memset(file->private_data, 0,
sizeof (struct cifsFileInfo));
cifsFile =
(struct cifsFileInfo *) file->private_data;
cifsFile->netfid = searchHandle;
} else {
rc = -ENOMEM;
break;
}
renew_parental_timestamps(file->f_dentry);
lastFindData =
(FILE_DIRECTORY_INFO *) ((char *) pfindData +
le32_to_cpu(findParms.LastNameOffset));
if((char *)lastFindData > (char *)pfindData + bufsize) {
cFYI(1,("last search entry past end of packet"));
rc = -EIO;
break;
}
/* Offset of resume key same for levels 257 and 514 */
cifsFile->resume_key = lastFindData->FileIndex;
if(UnixSearch == FALSE) {
cifsFile->resume_name_length =
le32_to_cpu(lastFindData->FileNameLength);
if(cifsFile->resume_name_length > bufsize - 64) {
cFYI(1,("Illegal resume file name length %d",
cifsFile->resume_name_length));
rc = -ENOMEM;
break;
}
cifsFile->search_resume_name =
kmalloc(cifsFile->resume_name_length, GFP_KERNEL);
cFYI(1,("Last file: %s with name %d bytes long",
lastFindData->FileName,
cifsFile->resume_name_length));
memcpy(cifsFile->search_resume_name,
lastFindData->FileName,
cifsFile->resume_name_length);
} else {
pfindDataUnix = (FILE_UNIX_INFO *)lastFindData;
if (Unicode == TRUE) {
for(i=0;(pfindDataUnix->FileName[i]
| pfindDataUnix->FileName[i+1]);
i+=2) {
if(i > bufsize-64)
break;
}
cifsFile->resume_name_length = i + 2;
} else {
cifsFile->resume_name_length =
strnlen(pfindDataUnix->FileName,
bufsize-63);
}
if(cifsFile->resume_name_length > bufsize - 64) {
cFYI(1,("Illegal resume file name length %d",
cifsFile->resume_name_length));
rc = -ENOMEM;
break;
}
cifsFile->search_resume_name =
kmalloc(cifsFile->resume_name_length, GFP_KERNEL);
cFYI(1,("Last file: %s with name %d bytes long",
lastFindData->FileName,
cifsFile->resume_name_length));
memcpy(cifsFile->search_resume_name,
lastFindData->FileName,
cifsFile->resume_name_length);
}
for (i = 2; i < findParms.SearchCount + 2; i++) {
if (UnixSearch == FALSE) {
pfindData->FileNameLength =
le32_to_cpu(pfindData->
FileNameLength);
le32_to_cpu(pfindData->FileNameLength);
if (Unicode == TRUE)
pfindData->FileNameLength =
cifs_strfromUCS_le
......@@ -1165,18 +1229,17 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
(FILE_UNIX_INFO *) pfindData;
if (Unicode == TRUE)
qstring.len =
cifs_strfromUCS_le
(pfindDataUnix->
FileName, (wchar_t *)
pfindDataUnix->
FileName,
MAX_PATHCONF,
cifs_sb->local_nls);
cifs_strfromUCS_le
(pfindDataUnix->FileName,
(wchar_t *)
pfindDataUnix->FileName,
MAX_PATHCONF,
cifs_sb->local_nls);
else
qstring.len =
strnlen(pfindDataUnix->
FileName,
MAX_PATHCONF);
strnlen(pfindDataUnix->
FileName,
MAX_PATHCONF);
if (((qstring.len != 1)
|| (pfindDataUnix->
FileName[0] != '.'))
......@@ -1193,8 +1256,12 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
file->f_pos++;
}
}
pfindData = (FILE_DIRECTORY_INFO *) ((char *) pfindData + le32_to_cpu(pfindData->NextEntryOffset)); /* works also for Unix find struct since this is the first field of both */
/* works also for Unix ff struct since first field of both */
pfindData =
(FILE_DIRECTORY_INFO *) ((char *) pfindData
+ le32_to_cpu(pfindData->NextEntryOffset));
/* BB also should check to make sure that pointer is not beyond the end of the SMB */
/* if(pfindData > lastFindData) rc = -EIOl; break; */
} /* end for loop */
if ((findParms.EndofSearch != 0) && cifsFile) {
cifsFile->endOfSearch = TRUE;
......@@ -1202,12 +1269,11 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
} else {
if (cifsFile)
cifsFile->endOfSearch = TRUE;
rc = 0; /* unless parent directory disappeared - do not return error here (eg Access Denied or no more files) */
/* unless parent directory gone do not return error */
rc = 0;
}
break;
readdir_rewind:
default:
/* BB rewrite eventually to better handle rewind */
if (file->private_data == NULL) {
rc = -EBADF;
cFYI(1,
......@@ -1222,43 +1288,96 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
}
searchHandle = cifsFile->netfid;
rc = CIFSFindNext(xid, pTcon, pfindData,
&findNextParms, searchHandle, 0,
&Unicode, &UnixSearch);
cFYI(1,
("Count: %d End: %d ",
&findNextParms, searchHandle,
cifsFile->search_resume_name,
cifsFile->resume_name_length,
cifsFile->resume_key,
&Unicode, &UnixSearch);
cFYI(1,("Count: %d End: %d ",
findNextParms.SearchCount,
findNextParms.EndofSearch));
if ((rc == 0) && (findNextParms.SearchCount != 0)) {
/* BB save off resume key, key name and name length */
lastFindData =
(FILE_DIRECTORY_INFO *) ((char *) pfindData
+ le32_to_cpu(findNextParms.LastNameOffset));
if((char *)lastFindData > (char *)pfindData + bufsize) {
cFYI(1,("last search entry past end of packet"));
rc = -EIO;
break;
}
/* Offset of resume key same for levels 257 and 514 */
cifsFile->resume_key = lastFindData->FileIndex;
if(UnixSearch == FALSE) {
cifsFile->resume_name_length =
le32_to_cpu(lastFindData->FileNameLength);
if(cifsFile->resume_name_length > bufsize - 64) {
cFYI(1,("Illegal resume file name length %d",
cifsFile->resume_name_length));
rc = -ENOMEM;
break;
}
cifsFile->search_resume_name =
kmalloc(cifsFile->resume_name_length, GFP_KERNEL);
cFYI(1,("Last file: %s with name %d bytes long",
lastFindData->FileName,
cifsFile->resume_name_length));
memcpy(cifsFile->search_resume_name,
lastFindData->FileName,
cifsFile->resume_name_length);
} else {
pfindDataUnix = (FILE_UNIX_INFO *)lastFindData;
if (Unicode == TRUE) {
for(i=0;(pfindDataUnix->FileName[i]
| pfindDataUnix->FileName[i+1]);
i+=2) {
if(i > bufsize-64)
break;
}
cifsFile->resume_name_length = i + 2;
} else {
cifsFile->resume_name_length =
strnlen(pfindDataUnix->
FileName,
MAX_PATHCONF);
}
if(cifsFile->resume_name_length > bufsize - 64) {
cFYI(1,("Illegal resume file name length %d",
cifsFile->resume_name_length));
rc = -ENOMEM;
break;
}
cifsFile->search_resume_name =
kmalloc(cifsFile->resume_name_length, GFP_KERNEL);
cFYI(1,("fnext last file: %s with name %d bytes long",
lastFindData->FileName,
cifsFile->resume_name_length));
memcpy(cifsFile->search_resume_name,
lastFindData->FileName,
cifsFile->resume_name_length);
}
for (i = 0; i < findNextParms.SearchCount; i++) {
pfindData->FileNameLength =
le32_to_cpu(pfindData->
FileNameLength);
if (UnixSearch == FALSE) {
if (Unicode == TRUE)
pfindData->
FileNameLength
=
cifs_strfromUCS_le
(pfindData->
FileName,
(wchar_t *)
pfindData->
FileName,
(pfindData->
FileNameLength)
/ 2,
cifs_sb->
local_nls);
qstring.len =
pfindData->FileNameLength;
pfindData->FileNameLength =
cifs_strfromUCS_le
(pfindData->FileName,
(wchar_t *)
pfindData->FileName,
(pfindData->FileNameLength)/ 2,
cifs_sb->local_nls);
qstring.len =
pfindData->FileNameLength;
if (((qstring.len != 1)
|| (pfindData->
FileName[0] != '.'))
|| (pfindData->FileName[0] != '.'))
&& ((qstring.len != 2)
|| (pfindData->
FileName[0] != '.')
|| (pfindData->
FileName[1] !=
|| (pfindData->FileName[0] != '.')
|| (pfindData->FileName[1] !=
'.'))) {
cifs_filldir
(&qstring,
......@@ -1273,22 +1392,18 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
pfindData;
if (Unicode == TRUE)
qstring.len =
cifs_strfromUCS_le
(pfindDataUnix->
FileName,
(wchar_t *)
pfindDataUnix->
FileName,
MAX_PATHCONF,
cifs_sb->
local_nls);
cifs_strfromUCS_le
(pfindDataUnix->FileName,
(wchar_t *)
pfindDataUnix->FileName,
MAX_PATHCONF,
cifs_sb->local_nls);
else
qstring.len =
strnlen
(pfindDataUnix->
FileName,
MAX_PATHCONF);
strnlen
(pfindDataUnix->
FileName,
MAX_PATHCONF);
if (((qstring.len != 1)
|| (pfindDataUnix->
FileName[0] != '.'))
......@@ -1308,8 +1423,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
}
pfindData = (FILE_DIRECTORY_INFO *) ((char *) pfindData + le32_to_cpu(pfindData->NextEntryOffset)); /* works also for Unix find struct since this is the first field of both */
/* BB also should check to make sure that pointer is not beyond the end of the SMB */
} /* end for loop */
} /* end for loop */
if (findNextParms.EndofSearch != 0) {
cifsFile->endOfSearch = TRUE;
}
......@@ -1318,8 +1432,7 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
rc = 0; /* unless parent directory disappeared - do not return error here (eg Access Denied or no more files) */
}
}
} /* end switch */
} /* end switch */
if (data)
kfree(data);
if (full_path)
......@@ -1335,7 +1448,8 @@ struct address_space_operations cifs_addr_ops = {
.writepage = cifs_writepage,
.prepare_write = simple_prepare_write,
.commit_write = cifs_commit_write,
.sync_page = cifs_sync_page,
.sync_page = cifs_sync_page,
/*.direct_IO = */
};
struct address_space_operations cifs_addr_ops_writethrough = {
......@@ -1345,6 +1459,7 @@ struct address_space_operations cifs_addr_ops_writethrough = {
.prepare_write = simple_prepare_write,
.commit_write = cifs_commit_write,
.sync_page = cifs_sync_page,
/*.direct_IO = */
};
struct address_space_operations cifs_addr_ops_nocache = {
......@@ -1354,5 +1469,6 @@ struct address_space_operations cifs_addr_ops_nocache = {
.prepare_write = simple_prepare_write,
.commit_write = cifs_commit_write,
.sync_page = cifs_sync_page,
/*.direct_IO = */
};
......@@ -450,10 +450,10 @@ cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
cifs_sb_source = CIFS_SB(source_inode->i_sb);
pTcon = cifs_sb_source->tcon;
if (pTcon != cifs_sb_target->tcon) {
if (pTcon != cifs_sb_target->tcon) {
FreeXid(xid);
return -EXDEV; /* BB actually could be allowed if same server, but
different share. Might eventually add support for this */
FreeXid(xid);
}
fromName = build_path_from_dentry(source_direntry);
......@@ -471,7 +471,7 @@ cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
if (toName)
kfree(toName);
FreeXid(xid);
FreeXid(xid);
return rc;
}
......@@ -500,15 +500,15 @@ cifs_revalidate(struct dentry *direntry)
if (time_before(jiffies, cifsInode->time + HZ)) {
if((S_ISREG(direntry->d_inode->i_mode) == 0) ||
(direntry->d_inode->i_nlink == 1)) {
if (full_path)
kfree(full_path);
FreeXid(xid);
return rc;
} else {
cFYI(1,("Have to revalidate file due to hardlinks"));
}
(direntry->d_inode->i_nlink == 1) ||
(lookupCacheEnabled == 0)) {
if (full_path)
kfree(full_path);
FreeXid(xid);
return rc;
} else {
cFYI(1,("Have to revalidate file due to hardlinks"));
}
}
if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
......
......@@ -82,7 +82,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
int rc = -EACCES;
int xid;
char *full_path = NULL;
char target_path[257];
char * target_path;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
......@@ -91,7 +91,11 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode));
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
target_path = kmalloc(PATH_MAX, GFP_KERNEL);
if(target_path == NULL) {
FreeXid(xid);
return -ENOMEM;
}
/* can not call the following line due to EFAULT in vfs_readlink which is presumably expecting a user space buffer */
/* length = cifs_readlink(direntry,target_path, sizeof(target_path) - 1); */
......@@ -99,7 +103,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
if (pTcon->ses->capabilities & CAP_UNIX)
rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
target_path,
sizeof (target_path) - 1,
PATH_MAX-1,
cifs_sb->local_nls);
else {
/* rc = CIFSSMBQueryReparseLinkInfo */
......@@ -109,11 +113,13 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
/* BB Should we be using page symlink ops here? */
if (rc == 0) {
target_path[256] = 0;
target_path[PATH_MAX-1] = 0;
rc = vfs_follow_link(nd, target_path);
}
/* else EACCESS */
if (target_path)
kfree(target_path);
if (full_path)
kfree(full_path);
FreeXid(xid);
......@@ -180,7 +186,8 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
char *full_path = NULL;
char tmpbuffer[256];
char * tmpbuffer;
int len;
__u16 fid;
xid = GetXid();
......@@ -190,20 +197,28 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
cFYI(1,
("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d",
full_path, inode, pBuffer, buflen));
if(buflen > PATH_MAX)
len = PATH_MAX;
else
len = buflen;
tmpbuffer = kmalloc(len,GFP_KERNEL);
if(tmpbuffer == NULL) {
FreeXid(xid);
return -ENOMEM;
}
/* BB add read reparse point symlink code and Unix extensions symlink code here BB */
if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
tmpbuffer,
sizeof (tmpbuffer) - 1,
cifs_sb->local_nls);
tmpbuffer,
len - 1,
cifs_sb->local_nls);
else {
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ,
OPEN_REPARSE_POINT,&fid, &oplock, cifs_sb->local_nls);
if(!rc) {
rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path,
tmpbuffer,
sizeof(tmpbuffer) - 1,
len - 1,
fid,
cifs_sb->local_nls);
if(CIFSSMBClose(xid, pTcon, fid)) {
......@@ -216,15 +231,15 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
/* BB Should we be using page ops here? */
/* BB null terminate returned string in pBuffer? BB */
if (buflen > sizeof (tmpbuffer))
buflen = sizeof (tmpbuffer);
if (rc == 0) {
rc = vfs_readlink(direntry, pBuffer, buflen, tmpbuffer);
rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer);
cFYI(1,
("vfs_readlink called from cifs_readlink returned %d",
rc));
}
if (tmpbuffer)
kfree(tmpbuffer);
if (full_path)
kfree(full_path);
FreeXid(xid);
......
......@@ -27,6 +27,7 @@
#include "cifs_debug.h"
extern kmem_cache_t *cifs_req_cachep;
extern struct task_struct * oplockThread;
__u16 GlobalMid; /* multiplex id - rotating counter */
......@@ -336,6 +337,7 @@ is_valid_oplock_break(struct smb_hdr *buf)
list_for_each(tmp1,&tcon->openFileList){
netfile = list_entry(tmp1,struct cifsFileInfo,tlist);
if(pSMB->Fid == netfile->netfid) {
struct cifsInodeInfo *pCifsInode;
/* BB Add following logic:
2) look up inode from tcon->openFileList->file->f_dentry->d_inode
3) flush dirty pages and cached byte range locks and mark inode
......@@ -345,6 +347,15 @@ is_valid_oplock_break(struct smb_hdr *buf)
6) send oplock break response to server */
read_unlock(&GlobalSMBSeslock);
cFYI(1,("Matching file id, processing oplock break"));
pCifsInode =
CIFS_I(netfile->pfile->f_dentry->d_inode);
pCifsInode->clientCanCacheAll = FALSE;
if(pSMB->OplockLevel == 0)
pCifsInode->clientCanCacheRead = FALSE;
pCifsInode->oplockPending = TRUE;
AllocOplockQEntry(netfile->pfile, tcon);
cFYI(1,("about to wake up oplock thd"));
wake_up_process(oplockThread);
return TRUE;
}
}
......
......@@ -32,6 +32,7 @@
#include "cifs_debug.h"
extern kmem_cache_t *cifs_mid_cachep;
extern kmem_cache_t *cifs_oplock_cachep;
struct mid_q_entry *
AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
......@@ -95,6 +96,39 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
kmem_cache_free(cifs_mid_cachep, midEntry);
}
struct oplock_q_entry *
AllocOplockQEntry(struct file * file, struct cifsTconInfo * tcon)
{
struct oplock_q_entry *temp;
if ((file == NULL) || (tcon == NULL)) {
cERROR(1, ("Null parms passed to AllocOplockQEntry"));
return NULL;
}
temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
SLAB_KERNEL);
if (temp == NULL)
return temp;
else {
temp->file_to_flush = file;
temp->tcon = tcon;
write_lock(&GlobalMid_Lock);
list_add_tail(&temp->qhead, &GlobalOplock_Q);
write_unlock(&GlobalMid_Lock);
}
return temp;
}
void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
{
/* BB add spinlock to protect midq for each session BB */
write_lock(&GlobalMid_Lock);
/* should we check if list empty first? */
list_del(&oplockEntry->qhead);
write_unlock(&GlobalMid_Lock);
kmem_cache_free(cifs_oplock_cachep, oplockEntry);
}
int
smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
unsigned int smb_buf_length, struct sockaddr *sin)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment