summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-08 19:07:24 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-06-08 19:07:24 -0700
commit754d4c9b21574371164f4eddf740bcbfdd26db01 (patch)
tree65d2dc4342a669b24c43a1d308d46d046cca5fed
parentb86116237f0c6ece987c72948096b362f113a65b (diff)
parentce0025ebbefb4be36cdf6782b0127e7dca0fab3d (diff)
Merge bk://cifs.bkbits.net/linux-2.5cifs
into ppc970.osdl.org:/home/torvalds/v2.6/linux
-rw-r--r--fs/cifs/AUTHORS5
-rw-r--r--fs/cifs/CHANGES9
-rw-r--r--fs/cifs/cifs_debug.c96
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifspdu.h54
-rw-r--r--fs/cifs/cifsproto.h3
-rw-r--r--fs/cifs/cifssmb.c57
-rw-r--r--fs/cifs/connect.c54
-rw-r--r--fs/cifs/dir.c21
-rw-r--r--fs/cifs/fcntl.c18
-rw-r--r--fs/cifs/file.c148
-rw-r--r--fs/cifs/inode.c20
-rw-r--r--fs/cifs/transport.c1
13 files changed, 376 insertions, 112 deletions
diff --git a/fs/cifs/AUTHORS b/fs/cifs/AUTHORS
index 0759aab0e063..ac239020fbc7 100644
--- a/fs/cifs/AUTHORS
+++ b/fs/cifs/AUTHORS
@@ -23,6 +23,7 @@ Amrut Joshi
Shobhit Dayal
Sergey Vlasov
Richard Hughes
+Yury Umanets
Test case and Bug Report contributors
-------------------------------------
@@ -30,5 +31,7 @@ Thanks to those in the community who have submitted detailed bug reports
and debug of problems they have found: Jochen Dolze, David Blaine,
Rene Scharfe, Martin Josefsson, Alexander Wild, Anthony Liguori,
Lars Muller, Urban Widmark, Massimiliano Ferrero, Howard Owen,
-Kieron Briggs and others.
+Olaf Kirch, Kieron Briggs and others.
+And thanks to the IBM LTC and Power test teams and SuSE testers for
+finding multiple bugs during excellent stress test runs.
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index c1028f952fe7..73774bfe64df 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,3 +1,12 @@
+Version 1.17
+------------
+Update number of blocks in file so du command is happier (in Linux a fake
+blocksize of 512 is required for calculating number of blocks in inode).
+Fix prepare write of partial pages to read in data from server if possible.
+Fix race on tcpStatus field between unmount and reconnection code, causing
+cifsd process sometimes to hang around forever. Improve out of memory
+checks in cifs_filldir
+
Version 1.16
------------
Fix incorrect file size in file handle based setattr on big endian hardware.
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 0b114f0b54bb..b027bae30085 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -142,30 +142,10 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
sprintf(buf, " type: %d ",
tcon->fsDevInfo.DeviceType);
buf += length;
- if(tcon->tidStatus == CifsNeedReconnect)
+ if(tcon->tidStatus == CifsNeedReconnect) {
buf += sprintf(buf, "\tDISCONNECTED ");
-#ifdef CONFIG_CIFS_STATS
- length = sprintf(buf,"\nSMBs: %d Oplock Breaks: %d",
- atomic_read(&tcon->num_smbs_sent),
- atomic_read(&tcon->num_oplock_brks));
- buf += length;
- length = sprintf(buf,"\nReads: %d Bytes %lld",
- atomic_read(&tcon->num_reads),
- (long long)(tcon->bytes_read));
- buf += length;
- length = sprintf(buf,"\nWrites: %d Bytes: %lld",
- atomic_read(&tcon->num_writes),
- (long long)(tcon->bytes_written));
- buf += length;
- length = sprintf(buf,
- "\nOpens: %d Deletes: %d\nMkdirs: %d Rmdirs: %d",
- atomic_read(&tcon->num_opens),
- atomic_read(&tcon->num_deletes),
- atomic_read(&tcon->num_mkdirs),
- atomic_read(&tcon->num_rmdirs));
- buf += length;
-#endif
-
+ length += 14;
+ }
}
read_unlock(&GlobalSMBSeslock);
@@ -200,32 +180,80 @@ cifs_total_xid_read(char *buf, char **beginBuffer, off_t offset,
return length;
}
+#ifdef CONFIG_CIFS_STATS
int
cifs_stats_read(char *buf, char **beginBuffer, off_t offset,
int length, int *eof, void *data)
{
- int item_length;
- length =
- sprintf(buf,
- "Currently Allocated structures\nCIFS Sessions: %d\n",sesInfoAllocCount.counter);
+ int item_length,i;
+ struct list_head *tmp;
+ struct cifsTconInfo *tcon;
+
+ length = sprintf(buf,
+ "Currently Allocated structures\nCIFS Sessions: %d\n",
+ sesInfoAllocCount.counter);
buf += length;
item_length =
- sprintf(buf,"Shares (unique mount targets): %d\n",tconInfoAllocCount.counter);
+ sprintf(buf,"Shares (unique mount targets): %d\n",
+ tconInfoAllocCount.counter);
length += item_length;
buf += item_length;
item_length =
- sprintf(buf,"Allocated SMB Request and Response Buffers: %d\n",bufAllocCount.counter);
+ sprintf(buf,"Allocated SMB Request/Response Buffers: %d\n",
+ bufAllocCount.counter);
length += item_length;
buf += item_length;
item_length =
- sprintf(buf,"Active Operations (MIDs in use): %d\n",midCount.counter);
+ sprintf(buf,"Active Operations (MIDs in use): %d\n",
+ midCount.counter);
length += item_length;
buf += item_length;
- item_length = sprintf(buf,"%d sessions and %d shares reconnected after failure\n",tcpSesReconnectCount.counter,tconInfoReconnectCount.counter);
+ item_length = sprintf(buf,
+ "%d sessions and %d shares reconnected after failure\n",
+ tcpSesReconnectCount.counter,tconInfoReconnectCount.counter);
length += item_length;
+ buf += item_length;
+
+ i = 0;
+ read_lock(&GlobalSMBSeslock);
+ list_for_each(tmp, &GlobalTreeConnectionList) {
+ i++;
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ item_length = sprintf(buf,"\n%d) %s",i, tcon->treeName);
+ buf += item_length;
+ length += item_length;
+ if(tcon->tidStatus == CifsNeedReconnect) {
+ buf += sprintf(buf, "\tDISCONNECTED ");
+ length += 14;
+ }
+ item_length = sprintf(buf,"\nSMBs: %d Oplock Breaks: %d",
+ atomic_read(&tcon->num_smbs_sent),
+ atomic_read(&tcon->num_oplock_brks));
+ buf += item_length;
+ length += item_length;
+ item_length = sprintf(buf,"\nReads: %d Bytes %lld",
+ atomic_read(&tcon->num_reads),
+ (long long)(tcon->bytes_read));
+ buf += item_length;
+ item_length = sprintf(buf,"\nWrites: %d Bytes: %lld",
+ atomic_read(&tcon->num_writes),
+ (long long)(tcon->bytes_written));
+ buf += item_length;
+ item_length = sprintf(buf,
+ "\nOpens: %d Deletes: %d\nMkdirs: %d Rmdirs: %d",
+ atomic_read(&tcon->num_opens),
+ atomic_read(&tcon->num_deletes),
+ atomic_read(&tcon->num_mkdirs),
+ atomic_read(&tcon->num_rmdirs));
+ buf += item_length;
+ length += item_length;
+ }
+ read_unlock(&GlobalSMBSeslock);
+
return length;
}
+#endif
struct proc_dir_entry *proc_fs_cifs;
read_proc_t cifs_txanchor_read;
@@ -265,10 +293,10 @@ cifs_proc_init(void)
create_proc_read_entry("SimultaneousOps", 0, proc_fs_cifs,
cifs_total_xid_read, 0);
-
+#ifdef CONFIG_CIFS_STATS
create_proc_read_entry("Stats", 0, proc_fs_cifs,
cifs_stats_read, 0);
-
+#endif
pde = create_proc_read_entry("cifsFYI", 0, proc_fs_cifs,
cifsFYI_read, 0);
if (pde)
@@ -336,7 +364,9 @@ cifs_proc_clean(void)
remove_proc_entry("cifsFYI", proc_fs_cifs);
remove_proc_entry("traceSMB", proc_fs_cifs);
remove_proc_entry("SimultaneousOps", proc_fs_cifs);
+#ifdef CONFIG_CIFS_STATS
remove_proc_entry("Stats", proc_fs_cifs);
+#endif
remove_proc_entry("MultiuserMount", proc_fs_cifs);
remove_proc_entry("OplockEnabled", proc_fs_cifs);
remove_proc_entry("NTLMV2Enabled",proc_fs_cifs);
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index d960b35555f7..3500c46d3c77 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -93,5 +93,5 @@ extern int cifs_setxattr(struct dentry *, const char *, const void *,
size_t, int);
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
-#define CIFS_VERSION "1.16"
+#define CIFS_VERSION "1.17"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index 4a57daf4888d..e639ef01d32a 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -862,6 +862,10 @@ typedef struct smb_com_create_directory_rsp {
__u16 ByteCount; /* bct = 0 */
} CREATE_DIRECTORY_RSP;
+/***************************************************/
+/* NT Transact structure defintions follow */
+/* Currently only ioctl and notify are implemented */
+/***************************************************/
typedef struct smb_com_transaction_ioctl_req {
struct smb_hdr hdr; /* wct = 23 */
__u8 MaxSetupCount;
@@ -904,29 +908,45 @@ typedef struct smb_com_transaction_ioctl_rsp {
} TRANSACT_IOCTL_RSP;
typedef struct smb_com_transaction_change_notify_req {
- struct smb_hdr hdr; /* wct = 23 */
- __u8 MaxSetupCount;
- __u16 Reserved;
- __u32 TotalParameterCount;
- __u32 TotalDataCount;
- __u32 MaxParameterCount;
- __u32 MaxDataCount;
- __u32 ParameterCount;
- __u32 ParameterOffset;
- __u32 DataCount;
- __u32 DataOffset;
- __u8 SetupCount; /* four setup words follow subcommand */
- /* SNIA spec incorrectly included spurious pad here */
- __u16 SubCommand;/* 4 = Change Notify */
+ struct smb_hdr hdr; /* wct = 23 */
+ __u8 MaxSetupCount;
+ __u16 Reserved;
+ __u32 TotalParameterCount;
+ __u32 TotalDataCount;
+ __u32 MaxParameterCount;
+ __u32 MaxDataCount;
+ __u32 ParameterCount;
+ __u32 ParameterOffset;
+ __u32 DataCount;
+ __u32 DataOffset;
+ __u8 SetupCount; /* four setup words follow subcommand */
+ /* SNIA spec incorrectly included spurious pad here */
+ __u16 SubCommand;/* 4 = Change Notify */
__u32 CompletionFilter; /* operation to monitor */
__u16 Fid;
__u8 WatchTree; /* 1 = Monitor subdirectories */
+ __u8 Reserved2;
__u16 ByteCount;
- __u8 Pad[3];
- __u8 Data[1];
+/* __u8 Pad[3];*/
+/* __u8 Data[1];*/
} TRANSACT_CHANGE_NOTIFY_REQ;
-/* Completion Filter flags */
+typedef struct smb_com_transaction_change_notify_rsp {
+ struct smb_hdr hdr; /* wct = 18 */
+ __u8 Reserved[3];
+ __u32 TotalParameterCount;
+ __u32 TotalDataCount;
+ __u32 ParameterCount;
+ __u32 ParameterOffset;
+ __u32 ParameterDisplacement;
+ __u32 DataCount;
+ __u32 DataOffset;
+ __u32 DataDisplacement;
+ __u8 SetupCount; /* 0 */
+ __u16 ByteCount;
+ /* __u8 Pad[3]; */
+} TRANSACT_CHANGE_NOTIFY_RSP;
+/* Completion Filter flags for Notify */
#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
#define FILE_NOTIFY_CHANGE_NAME 0x00000003
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index cc6871542fe4..288ed183a63f 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -244,4 +244,7 @@ extern int CIFSSMBCopy(int xid,
const __u16 target_tid,
const char *toName, const int flags,
const struct nls_table *nls_codepage);
+extern int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
+ const int notify_subdirs,const __u16 netfid,__u32 filter,
+ const struct nls_table *nls_codepage);
#endif /* _CIFSPROTO_H */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 6a26f8c419fb..6b94d60ed5e7 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -383,8 +383,11 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
smb_buffer_response, &length, 0);
if (ses->server) {
atomic_dec(&ses->server->socketUseCount);
- if (atomic_read(&ses->server->socketUseCount) == 0)
+ if (atomic_read(&ses->server->socketUseCount) == 0) {
+ spin_lock(&GlobalMid_Lock);
ses->server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+ }
}
if (pSMB)
cifs_buf_release(pSMB);
@@ -1464,9 +1467,9 @@ CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
pSMB->TotalParameterCount = 0 ;
pSMB->TotalDataCount = 0;
- pSMB->MaxParameterCount = cpu_to_le16(2);
+ pSMB->MaxParameterCount = cpu_to_le32(2);
/* BB find exact data count max from sess structure BB */
- pSMB->MaxDataCount = cpu_to_le16(4000);
+ pSMB->MaxDataCount = cpu_to_le32(4000);
pSMB->MaxSetupCount = 4;
pSMB->Reserved = 0;
pSMB->ParameterOffset = 0;
@@ -2828,3 +2831,51 @@ setPermsRetry:
goto setPermsRetry;
return rc;
}
+
+int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
+ const int notify_subdirs, const __u16 netfid,
+ __u32 filter, const struct nls_table *nls_codepage)
+{
+ int rc = 0;
+ struct smb_com_transaction_change_notify_req * pSMB = NULL;
+ struct smb_com_transaction_change_notify_rsp * pSMBr = NULL;
+ int bytes_returned;
+
+ cFYI(1, ("In CIFSSMBNotify for file handle %d",(int)netfid));
+ rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
+ (void **) &pSMBr);
+ if (rc)
+ return rc;
+
+ pSMB->TotalParameterCount = 0 ;
+ pSMB->TotalDataCount = 0;
+ pSMB->MaxParameterCount = cpu_to_le32(2);
+ /* BB find exact data count max from sess structure BB */
+ pSMB->MaxDataCount = 0; /* same in little endian or be */
+ pSMB->MaxSetupCount = 4;
+ pSMB->Reserved = 0;
+ pSMB->ParameterOffset = 0;
+ pSMB->DataCount = 0;
+ pSMB->DataOffset = 0;
+ pSMB->SetupCount = 4; /* single byte does not need le conversion */
+ pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
+ pSMB->ParameterCount = pSMB->TotalParameterCount;
+ if(notify_subdirs)
+ pSMB->WatchTree = 1; /* one byte - no le conversion needed */
+ pSMB->Reserved2 = 0;
+ pSMB->CompletionFilter = cpu_to_le32(filter);
+ pSMB->Fid = netfid; /* file handle always le */
+ pSMB->ByteCount = 0;
+
+ pSMB->hdr.smb_buf_length += pSMB->ByteCount;
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+ (struct smb_hdr *) pSMBr, &bytes_returned, 0);
+ if (rc) {
+ cFYI(1, ("Error in Notify = %d", rc));
+ }
+ if (pSMB)
+ cifs_buf_release(pSMB);
+/* if (rc == -EAGAIN)
+ goto NotifyRetry; */
+ return rc;
+}
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 3f1261aa95ac..a54438b0ba33 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -95,9 +95,15 @@ cifs_reconnect(struct TCP_Server_Info *server)
struct cifsTconInfo *tcon;
struct mid_q_entry * mid_entry;
- if(server->tcpStatus == CifsExiting)
+ spin_lock(&GlobalMid_Lock);
+ if(server->tcpStatus == CifsExiting) {
+ /* the demux thread will exit normally
+ next time through the loop */
+ spin_unlock(&GlobalMid_Lock);
return rc;
- server->tcpStatus = CifsNeedReconnect;
+ } else
+ server->tcpStatus = CifsNeedReconnect;
+ spin_unlock(&GlobalMid_Lock);
server->maxBuf = 0;
cFYI(1, ("Reconnecting tcp session "));
@@ -164,7 +170,10 @@ cifs_reconnect(struct TCP_Server_Info *server)
schedule_timeout(3 * HZ);
} else {
atomic_inc(&tcpSesReconnectCount);
- server->tcpStatus = CifsGood;
+ spin_lock(&GlobalMid_Lock);
+ if(server->tcpStatus != CifsExiting)
+ server->tcpStatus = CifsGood;
+ spin_unlock(&GlobalMid_Lock);
atomic_set(&server->inFlight,0);
wake_up(&server->response_q);
}
@@ -243,12 +252,14 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
/* some servers kill tcp session rather than returning
smb negprot error in which case reconnecting here is
not going to help - return error to mount */
+ spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
wake_up(&server->response_q);
break;
}
- cFYI(1,("Reconnecting after unexpected rcvmsg error "));
+ cFYI(1,("Reconnecting after unexpected peek error %d",length));
cifs_reconnect(server);
csocket = server->ssocket;
wake_up(&server->response_q);
@@ -280,7 +291,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
/* if nack on negprot (rather than
ret of smb negprot error) reconnecting
not going to help, ret error to mount */
+ spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
/* wake up thread doing negprot */
wake_up(&server->response_q);
break;
@@ -391,7 +404,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
}
}
}
+ spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
atomic_set(&server->inFlight, 0);
/* Although there should not be any requests blocked on
this queue it can not hurt to be paranoid and try to wake up requests
@@ -595,6 +610,8 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol
}
if ((temp_len = strnlen(value, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
+ if(vol->UNC == NULL)
+ return 1;
strcpy(vol->UNC,value);
if (strncmp(vol->UNC, "//", 2) == 0) {
vol->UNC[0] = '\\';
@@ -742,6 +759,8 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol
}
if ((temp_len = strnlen(devname, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
+ if(vol->UNC == NULL)
+ return 1;
strcpy(vol->UNC,devname);
if (strncmp(vol->UNC, "//", 2) == 0) {
vol->UNC[0] = '\\';
@@ -1030,7 +1049,7 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
} else {
/* BB other socket options to set KEEPALIVE, NODELAY? */
cFYI(1,("ipv6 Socket created"));
- (*csocket)->sk->sk_allocation = GFP_NOFS;
+ (*csocket)->sk->sk_allocation = GFP_NOFS;
}
}
@@ -1226,6 +1245,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
init_waitqueue_head(&srvTcp->response_q);
init_waitqueue_head(&srvTcp->request_q);
INIT_LIST_HEAD(&srvTcp->pending_mid_q);
+ /* at this point we are the only ones with the pointer
+ to the struct since the kernel thread not created yet
+ so no need to spinlock this init of tcpStatus */
srvTcp->tcpStatus = CifsNew;
init_MUTEX(&srvTcp->tcpSem);
kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp,
@@ -1342,9 +1364,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
/* on error free sesinfo and tcon struct if needed */
if (rc) {
- if(atomic_read(&srvTcp->socketUseCount) == 0)
- srvTcp->tcpStatus = CifsExiting;
- /* If find_unc succeeded then rc == 0 so we can not end */
+ if(atomic_read(&srvTcp->socketUseCount) == 0) {
+ spin_lock(&GlobalMid_Lock);
+ srvTcp->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+ }
+ /* If find_unc succeeded then rc == 0 so we can not end */
if (tcon) /* up here accidently freeing someone elses tcon struct */
tconInfoFree(tcon);
if (existingCifsSes == 0) {
@@ -2791,7 +2816,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
int ntlmv2_flag = FALSE;
- /* what if server changes its buffer size after dropping the session? */
+ /* what if server changes its buffer size after dropping the session? */
if(pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ {
rc = CIFSSMBNegotiate(xid, pSesInfo);
if(rc == -EAGAIN) /* retry only once on 1st time connection */ {
@@ -2799,8 +2824,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
if(rc == -EAGAIN)
rc = -EHOSTDOWN;
}
- if(rc == 0)
- pSesInfo->server->tcpStatus = CifsGood;
+ if(rc == 0) {
+ spin_lock(&GlobalMid_Lock);
+ if(pSesInfo->server->tcpStatus != CifsExiting)
+ pSesInfo->server->tcpStatus = CifsGood;
+ else
+ rc = -EHOSTDOWN;
+ spin_unlock(&GlobalMid_Lock);
+
+ }
}
if (!rc) {
pSesInfo->capabilities = pSesInfo->server->capabilities;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 7f12af145aba..85cccbb05005 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -159,6 +159,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
struct cifsFileInfo * pCifsFile = NULL;
struct cifsInodeInfo * pCifsInode;
int disposition = FILE_OVERWRITE_IF;
+ int write_only = FALSE;
xid = GetXid();
@@ -176,9 +177,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if(nd) {
if ((nd->intent.open.flags & O_ACCMODE) == O_RDONLY)
desiredAccess = GENERIC_READ;
- else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY)
+ else if ((nd->intent.open.flags & O_ACCMODE) == O_WRONLY) {
desiredAccess = GENERIC_WRITE;
- else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) {
+ write_only = TRUE;
+ } else if ((nd->intent.open.flags & O_ACCMODE) == O_RDWR) {
/* GENERIC_ALL is too much permission to request */
/* can cause unnecessary access denied on create */
/* desiredAccess = GENERIC_ALL; */
@@ -262,16 +264,25 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
pCifsFile->invalidHandle = FALSE;
pCifsFile->closePend = FALSE;
init_MUTEX(&pCifsFile->fh_sem);
- /* pCifsFile->pfile = file; */ /* put in at open time */
+ /* put the following in at open now */
+ /* pCifsFile->pfile = file; */
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist,&pTcon->openFileList);
pCifsInode = CIFS_I(newinode);
if(pCifsInode) {
- list_add(&pCifsFile->flist,&pCifsInode->openFileList);
+ /* if readable file instance put first in list*/
+ if (write_only == TRUE) {
+ list_add_tail(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ } else {
+ list_add(&pCifsFile->flist,
+ &pCifsInode->openFileList);
+ }
if((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = TRUE;
pCifsInode->clientCanCacheRead = TRUE;
- cFYI(1,("Exclusive Oplock granted on inode %p",newinode));
+ cFYI(1,("Exclusive Oplock granted on inode %p",
+ newinode));
} else if((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = TRUE;
}
diff --git a/fs/cifs/fcntl.c b/fs/cifs/fcntl.c
index 39d7b89b7791..1469a406f6ad 100644
--- a/fs/cifs/fcntl.c
+++ b/fs/cifs/fcntl.c
@@ -32,9 +32,12 @@ int cifs_directory_notify(unsigned long arg, struct file * file)
{
int xid;
int rc = -EINVAL;
+ int oplock = FALSE;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
char *full_path = NULL;
+ __u32 filter = FILE_NOTIFY_CHANGE_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ __u16 netfid;
xid = GetXid();
cifs_sb = CIFS_SB(file->f_dentry->d_sb);
@@ -48,7 +51,20 @@ int cifs_directory_notify(unsigned long arg, struct file * file)
rc = -ENOMEM;
} else {
cFYI(1,("cifs dir notify on file %s",full_path));
- /* CIFSSMBNotify(xid, pTcon, full_path, cifs_sb->local_nls);*/
+ rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
+ GENERIC_READ | SYNCHRONIZE, 0 /* create options */,
+ &netfid, &oplock,NULL, cifs_sb->local_nls);
+ /* BB fixme - add this handle to a notify handle list */
+ if(rc) {
+ cFYI(1,("Could not open directory for notify"));
+ } else {
+ rc = CIFSSMBNotify(xid, pTcon, 1 /* subdirs */, netfid,
+ filter, cifs_sb->local_nls);
+ /* BB add code to close file eventually (at unmount
+ it would close automatically but may be a way
+ to do it easily when inode freed or when
+ notify info is cleared/changed */
+ }
}
FreeXid(xid);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 87d28964e66b..9bf6a0e7d7c5 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -173,7 +173,14 @@ cifs_open(struct inode *inode, struct file *file)
list_add(&pCifsFile->tlist,&pTcon->openFileList);
pCifsInode = CIFS_I(file->f_dentry->d_inode);
if(pCifsInode) {
- list_add(&pCifsFile->flist,&pCifsInode->openFileList);
+ /* want handles we can use to read with first */
+ /* in the list so we do not have to walk the */
+ /* list to search for one in prepare_write */
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ list_add_tail(&pCifsFile->flist,&pCifsInode->openFileList);
+ } else {
+ list_add(&pCifsFile->flist,&pCifsInode->openFileList);
+ }
write_unlock(&GlobalSMBSeslock);
write_unlock(&file->f_owner.lock);
if(pCifsInode->clientCanCacheRead) {
@@ -701,6 +708,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
cifsInode = CIFS_I(mapping->host);
read_lock(&GlobalSMBSeslock);
+ /* BB we should start at the end */
list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist);
if(open_file->closePend)
@@ -770,6 +778,9 @@ cifs_writepage(struct page* page, struct writeback_control *wbc)
xid = GetXid();
/* BB add check for wbc flags */
page_cache_get(page);
+ if (!PageUptodate(page)) {
+ cFYI(1,("ppw - page not up to date"));
+ }
rc = cifs_partialpagewrite(page,0,PAGE_CACHE_SIZE);
SetPageUptodate(page); /* BB add check for error and Clearuptodate? */
@@ -787,8 +798,7 @@ cifs_commit_write(struct file *file, struct page *page, unsigned offset,
int rc = 0;
struct inode *inode = page->mapping->host;
loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
- /* struct cifsFileInfo *open_file;
- struct cifs_sb_info *cifs_sb; */
+ char * page_data;
xid = GetXid();
cFYI(1,("commit write for page %p up to position %lld for %d",page,position,to));
@@ -819,7 +829,31 @@ cifs_commit_write(struct file *file, struct page *page, unsigned offset,
cFYI(1,(" SetEOF (commit write) rc = %d",rc));
}*/
}
- set_page_dirty(page);
+ if (!PageUptodate(page)) {
+ position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset;
+ /* can not rely on (or let) writepage write this data */
+ if(to < offset) {
+ cFYI(1,("Illegal offsets, can not copy from %d to %d",
+ offset,to));
+ FreeXid(xid);
+ return rc;
+ }
+ /* this is probably better than directly calling
+ partialpage_write since in this function
+ the file handle is known which we might as well
+ leverage */
+ /* BB check if anything else missing out of ppw */
+ /* such as updating last write time */
+ page_data = kmap(page);
+ rc = cifs_write(file, page_data+offset,to-offset,
+ &position);
+ if(rc > 0)
+ rc = 0;
+ /* else if rc < 0 should we set writebehind rc? */
+ kunmap(page);
+ } else {
+ set_page_dirty(page);
+ }
FreeXid(xid);
return rc;
@@ -924,6 +958,10 @@ cifs_read(struct file * file, char *read_data, size_t read_size,
}
open_file = (struct cifsFileInfo *)file->private_data;
+ if((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ cFYI(1,("attempting read on write only file instance"));
+ }
+
for (total_read = 0,current_offset=read_data; read_size > total_read;
total_read += bytes_read,current_offset+=bytes_read) {
current_read_size = min_t(const int,read_size - total_read,cifs_sb->rsize);
@@ -1169,49 +1207,58 @@ cifs_readpages(struct file *file, struct address_space *mapping,
return rc;
}
-static int
-cifs_readpage(struct file *file, struct page *page)
+static int cifs_readpage_worker(struct file *file, struct page *page, loff_t * poffset)
{
- loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
char * read_data;
- int rc = -EACCES;
- int xid;
-
- xid = GetXid();
-
- if (file->private_data == NULL) {
- FreeXid(xid);
- return -EBADF;
- }
-
- cFYI(0,("readpage %p at offset %d 0x%x\n",page,(int)offset,(int)offset));
+ int rc;
page_cache_get(page);
read_data = kmap(page);
/* for reads over a certain size could initiate async read ahead */
-
- rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, &offset);
-
+
+ rc = cifs_read(file, read_data, PAGE_CACHE_SIZE, poffset);
+
if (rc < 0)
goto io_error;
else {
cFYI(1,("Bytes read %d ",rc));
}
-
+
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
-
+
if(PAGE_CACHE_SIZE > rc) {
memset(read_data+rc, 0, PAGE_CACHE_SIZE - rc);
}
flush_dcache_page(page);
SetPageUptodate(page);
rc = 0;
-
+
io_error:
- kunmap(page);
+ kunmap(page);
+ page_cache_release(page);
+ return rc;
+}
+
+static int
+cifs_readpage(struct file *file, struct page *page)
+{
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ int rc = -EACCES;
+ int xid;
+
+ xid = GetXid();
+
+ if (file->private_data == NULL) {
+ FreeXid(xid);
+ return -EBADF;
+ }
+
+ cFYI(1,("readpage %p at offset %d 0x%x\n",page,(int)offset,(int)offset));
+
+ rc = cifs_readpage_worker(file,page,&offset);
+
unlock_page(page);
- page_cache_release(page);
FreeXid(xid);
return rc;
}
@@ -1276,8 +1323,11 @@ fill_in_inode(struct inode *tmp_inode,
}
i_size_write(tmp_inode,pfindData->EndOfFile);
- tmp_inode->i_blocks =
- (tmp_inode->i_blksize - 1 + pfindData->AllocationSize) >> tmp_inode->i_blkbits;
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation, even though the reported blocksize is larger */
+ tmp_inode->i_blocks = (512 - 1 + pfindData->AllocationSize) >> 9;
+
if (pfindData->AllocationSize < pfindData->EndOfFile)
cFYI(1, ("Possible sparse file: allocation size less than end of file "));
cFYI(1,
@@ -1350,8 +1400,10 @@ unix_fill_in_inode(struct inode *tmp_inode,
pfindData->NumOfBytes = le64_to_cpu(pfindData->NumOfBytes);
pfindData->EndOfFile = le64_to_cpu(pfindData->EndOfFile);
i_size_write(tmp_inode,pfindData->EndOfFile);
- tmp_inode->i_blocks =
- (tmp_inode->i_blksize - 1 + pfindData->NumOfBytes) >> tmp_inode->i_blkbits;
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation, not the real blocksize */
+ tmp_inode->i_blocks = (512 - 1 + pfindData->NumOfBytes) >> 9;
if (S_ISREG(tmp_inode->i_mode)) {
cFYI(1, ("File inode"));
@@ -1393,12 +1445,15 @@ construct_dentry(struct qstr *qstring, struct file *file,
/* BB overwrite the old name? i.e. tmp_dentry->d_name and tmp_dentry->d_name.len ?? */
if(*ptmp_inode == NULL) {
*ptmp_inode = new_inode(file->f_dentry->d_sb);
+ if(*ptmp_inode == NULL)
+ return;
d_instantiate(tmp_dentry, *ptmp_inode);
}
} else {
tmp_dentry = d_alloc(file->f_dentry, qstring);
if(tmp_dentry == NULL) {
cERROR(1,("Failed allocating dentry"));
+ *ptmp_inode = NULL;
return;
}
@@ -1406,6 +1461,8 @@ construct_dentry(struct qstr *qstring, struct file *file,
tmp_dentry->d_op = &cifs_dentry_ops;
cFYI(0, (" instantiate dentry 0x%p with inode 0x%p ",
tmp_dentry, *ptmp_inode));
+ if(*ptmp_inode == NULL)
+ return;
d_instantiate(tmp_dentry, *ptmp_inode);
d_rehash(tmp_dentry);
}
@@ -1462,7 +1519,9 @@ cifs_filldir(struct qstr *pqstring, FILE_DIRECTORY_INFO * pfindData,
pqstring->len = pfindData->FileNameLength;
construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry);
-
+ if((tmp_inode == NULL) || (tmp_dentry == NULL)) {
+ return -ENOMEM;
+ }
fill_in_inode(tmp_inode, pfindData, &object_type);
rc = filldir(direntry, pfindData->FileName, pqstring->len, file->f_pos,
tmp_inode->i_ino, object_type);
@@ -1488,6 +1547,9 @@ cifs_filldir_unix(struct qstr *pqstring,
pqstring->len = strnlen(pUnixFindData->FileName, MAX_PATHCONF);
construct_dentry(pqstring, file, &tmp_inode, &tmp_dentry);
+ if((tmp_inode == NULL) || (tmp_dentry == NULL)) {
+ return -ENOMEM;
+ }
unix_fill_in_inode(tmp_inode, pUnixFindData, &object_type);
rc = filldir(direntry, pUnixFindData->FileName, pqstring->len,
@@ -1950,17 +2012,34 @@ cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
int cifs_prepare_write(struct file *file, struct page *page,
unsigned from, unsigned to)
{
+ int rc = 0;
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
cFYI(1,("prepare write for page %p from %d to %d",page,from,to));
if (!PageUptodate(page)) {
- if (to - from != PAGE_CACHE_SIZE) {
+ /* if (to - from != PAGE_CACHE_SIZE) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, from);
memset(kaddr + to, 0, PAGE_CACHE_SIZE - to);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
+ } */
+ /* If we are writing a full page it will be up to date,
+ no need to read from the server */
+ if((to==PAGE_CACHE_SIZE) && (from == 0))
+ SetPageUptodate(page);
+
+ /* might as well read a page, it is fast enough */
+ if((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ rc = cifs_readpage_worker(file,page,&offset);
+ } else {
+ /* should we try using another
+ file handle if there is one - how would we lock it
+ to prevent close of that handle racing with this read? */
+ /* In any case this will be written out by commit_write */
}
- SetPageUptodate(page);
}
+
+ /* BB should we pass any errors back? e.g. if we do not have read access to the file */
return 0;
}
@@ -1969,8 +2048,7 @@ struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage,
.readpages = cifs_readpages,
.writepage = cifs_writepage,
- .prepare_write = simple_prepare_write, /* BB fixme BB */
-/* .prepare_write = cifs_prepare_write, */ /* BB removeme BB */
+ .prepare_write = cifs_prepare_write,
.commit_write = cifs_commit_write,
/* .sync_page = cifs_sync_page, */
/*.direct_IO = */
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index e3c47ee5602d..afb87251270f 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -130,8 +130,18 @@ cifs_get_inode_info_unix(struct inode **pinode,
and blkbits set in superblock so 2**blkbits and blksize will match */
/* inode->i_blksize =
(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
- inode->i_blocks =
- (inode->i_blksize - 1 + findData.NumOfBytes) >> inode->i_blkbits;
+
+ /* This seems incredibly stupid but it turns out that
+ i_blocks is not related to (i_size / i_blksize), instead a
+ size of 512 is required to be used for calculating num blocks */
+
+
+/* inode->i_blocks =
+ (inode->i_blksize - 1 + findData.NumOfBytes) >> inode->i_blkbits;*/
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation */
+ inode->i_blocks = (512 - 1 + findData.NumOfBytes) >> 9;
if (findData.NumOfBytes < findData.EndOfFile)
cFYI(1, ("Server inconsistency Error: it says allocation size less than end of file "));
@@ -275,8 +285,10 @@ cifs_get_inode_info(struct inode **pinode, const unsigned char *search_path,
}
i_size_write(inode,le64_to_cpu(pfindData->EndOfFile));
pfindData->AllocationSize = le64_to_cpu(pfindData->AllocationSize);
- inode->i_blocks =
- (inode->i_blksize - 1 + pfindData->AllocationSize) >> inode->i_blkbits;
+
+ /* 512 bytes (2**9) is the fake blocksize that must be used */
+ /* for this calculation */
+ inode->i_blocks = (512 - 1 + pfindData->AllocationSize) >> 9;
inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index c55118c58eeb..48194f7cd7a7 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -126,7 +126,6 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
if(ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
-/* ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
iov.iov_base = smb_buffer;
iov.iov_len = smb_buf_length + 4;