Commit 754d4c9b authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://cifs.bkbits.net/linux-2.5cifs

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents b8611623 ce0025eb
......@@ -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.
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.
......
......@@ -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);
......
......@@ -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 */
......@@ -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
......
......@@ -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 */
......@@ -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 @@ CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
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;
}
......@@ -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;
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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 = */
......
......@@ -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);
......
......@@ -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;
......
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