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