diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-06-08 19:07:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-06-08 19:07:24 -0700 |
| commit | 754d4c9b21574371164f4eddf740bcbfdd26db01 (patch) | |
| tree | 65d2dc4342a669b24c43a1d308d46d046cca5fed | |
| parent | b86116237f0c6ece987c72948096b362f113a65b (diff) | |
| parent | ce0025ebbefb4be36cdf6782b0127e7dca0fab3d (diff) | |
Merge bk://cifs.bkbits.net/linux-2.5cifs
into ppc970.osdl.org:/home/torvalds/v2.6/linux
| -rw-r--r-- | fs/cifs/AUTHORS | 5 | ||||
| -rw-r--r-- | fs/cifs/CHANGES | 9 | ||||
| -rw-r--r-- | fs/cifs/cifs_debug.c | 96 | ||||
| -rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
| -rw-r--r-- | fs/cifs/cifspdu.h | 54 | ||||
| -rw-r--r-- | fs/cifs/cifsproto.h | 3 | ||||
| -rw-r--r-- | fs/cifs/cifssmb.c | 57 | ||||
| -rw-r--r-- | fs/cifs/connect.c | 54 | ||||
| -rw-r--r-- | fs/cifs/dir.c | 21 | ||||
| -rw-r--r-- | fs/cifs/fcntl.c | 18 | ||||
| -rw-r--r-- | fs/cifs/file.c | 148 | ||||
| -rw-r--r-- | fs/cifs/inode.c | 20 | ||||
| -rw-r--r-- | fs/cifs/transport.c | 1 |
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; |
