Commit cc6bcfef authored by Linus Torvalds's avatar Linus Torvalds

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

into penguin.transmeta.com:/home/torvalds/v2.5/linux
parents 7196d9df 629d5ab8
......@@ -20,3 +20,9 @@ Patch Contributors
Zwane Mwaikambo
Andi Kleen
Test case and Bug Report contributors
-------------------------------------
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 and others.
Version 0.77
------------
Fix display of NTFS DFS junctions to display as symlinks.
They are the network equivalent. Fix oops in
cifs_partialpagewrite caused by missing spinlock protection
of openfile linked list. Allow writebehind caching errors to
be returned to the application at file close.
Version 0.76
------------
Clean up options displayed in /proc/mounts by show_options to
be more consistent with other filesystems.
Version 0.75
------------
Fix delete of readonly file to Windows servers. Reflect
......
......@@ -18,6 +18,8 @@ c) multi-user mounts - multiplexed sessionsetups over single vc
d) Kerberos/SPNEGO session setup support - (started)
e) NTLMv2 authentication and MD5-HMAC signing SMB PDUs - (mostly implemented)
signing necessary for some Windows 2003 servers in domain
controller mode.
f) oplock support (ie safe CIFS distributed file caching) is not quite complete.
In addition Directory entry caching relies on a 1 second timer, rather than
......@@ -52,7 +54,8 @@ file opens on top of each other by incrementing reference count rather
than resending (helps reduce server resource utilization and avoid
spurious oplock breaks).
KNOWN BUGS (updated March 7, 2003)
KNOWN BUGS (updated May 16, 2003)
====================================
1) existing symbolic links (Windows reparse points) are recognized but
can not be created remotely. They are implemented for Samba and those that
......@@ -60,6 +63,11 @@ support the CIFS Unix extensions but Samba has a bug currently handling
symlink text beginning with slash
2) delete of file with read-only attribute set will fail (may be ok)
3) mount helper syntax not quite matching man page
4) follow_link and readdir code does not follow dfs junctions
but recognizes them
5) create of new files to FAT partitions on Windows servers can
succeed but still return access denied (appears to be Windows
not client problem). NTFS partitions do not have this problem.
Misc testing to do
=================
......@@ -68,6 +76,6 @@ types.
2) Run dbench
3) Finish FSX testing on SMP now that we workaround the Samba bug
3) Finish high stress fsx testing on SMP clients
4) Additional performance testing and optimization
......@@ -209,8 +209,8 @@ cifs_destroy_inode(struct inode *inode)
/*
* cifs_show_options() is for displaying mount options in /proc/mounts.
* It tries to avoid showing settings that were not changed from their
* defaults.
* Not all settable options are displayed but most of the important
* ones are.
*/
static int
cifs_show_options(struct seq_file *s, struct vfsmount *m)
......@@ -219,14 +219,18 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
cifs_sb = CIFS_SB(m->mnt_sb);
if (cifs_sb)
if (cifs_sb) {
if (cifs_sb->tcon) {
seq_printf(s, ", TARGET: %s ", cifs_sb->tcon->treeName);
seq_printf(s, "FS TYPE: %s ",
cifs_sb->tcon->nativeFileSystem);
seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName);
if (cifs_sb->tcon->ses->userName)
seq_printf(s, " USER: %s ",
seq_printf(s, ",username=%s",
cifs_sb->tcon->ses->userName);
if(cifs_sb->tcon->ses->domainName)
seq_printf(s, ",domain=%s",
cifs_sb->tcon->ses->domainName);
}
seq_printf(s, ",rsize=%d",cifs_sb->rsize);
seq_printf(s, ",wsize=%d",cifs_sb->wsize);
}
return 0;
}
......@@ -423,6 +427,7 @@ cifs_destroy_mids(void)
static int cifs_oplock_thread(void * dummyarg)
{
struct list_head * tmp;
struct list_head * tmp1;
struct oplock_q_entry * oplock_item;
struct file * pfile;
struct cifsTconInfo *pTcon;
......@@ -438,9 +443,8 @@ static int cifs_oplock_thread(void * dummyarg)
/* BB add missing code */
cFYI(1,("oplock thread woken up - flush inode")); /* BB remove */
write_lock(&GlobalMid_Lock);
list_for_each(tmp, &GlobalOplock_Q) {
oplock_item = list_entry(tmp, struct
oplock_q_entry,
list_for_each_safe(tmp, tmp1, &GlobalOplock_Q) {
oplock_item = list_entry(tmp, struct oplock_q_entry,
qhead);
if(oplock_item) {
pTcon = oplock_item->tcon;
......@@ -449,6 +453,9 @@ static int cifs_oplock_thread(void * dummyarg)
DeleteOplockQEntry(oplock_item);
write_unlock(&GlobalMid_Lock);
rc = filemap_fdatawrite(pfile->f_dentry->d_inode->i_mapping);
if(rc)
CIFS_I(pfile->f_dentry->d_inode)->write_behind_rc
= rc;
cFYI(1,("Oplock flush file %p rc %d",pfile,rc));
/* send oplock break */
write_lock(&GlobalMid_Lock);
......
......@@ -221,6 +221,7 @@ struct cifsInodeInfo {
struct list_head lockList;
/* BB add in lists for dirty pages - i.e. write caching info for oplock */
struct list_head openFileList;
int write_behind_rc;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
atomic_t inUse; /* num concurrent users (local openers cifs) of file*/
unsigned long time; /* jiffies of last update/check of inode */
......
......@@ -1263,6 +1263,7 @@ typedef struct dfs_referral_level_3 {
__u16 ServerType; /* 0x0001 = CIFS server */
__u16 ReferralFlags; /* or proximity - not clear which since always set to zero - SNIA spec says 0x01 means strip off PathConsumed chars before submitting RequestFileName to remote node */
__u16 TimeToLive;
__u16 Proximity;
__u16 DfsPathOffset;
__u16 DfsAlternatePathOffset;
__u16 NetworkAddressOffset;
......
......@@ -127,13 +127,15 @@ extern int CIFSSMBUnixQPathInfo(const int xid,
extern int CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
const unsigned char *searchName,
unsigned char **targetUNCs,
int *number_of_UNC_in_array,
unsigned int *number_of_UNC_in_array,
const struct nls_table *nls_codepage);
extern int connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
const char *old_path,
const struct nls_table *nls_codepage);
extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
const char *old_path, const struct nls_table *nls_codepage,
unsigned int *pnum_referrals, unsigned char ** preferrals);
extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
struct statfs *FSData,
const struct nls_table *nls_codepage);
......
......@@ -1625,15 +1625,18 @@ int
CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
const unsigned char *searchName,
unsigned char **targetUNCs,
int *number_of_UNC_in_array,
unsigned int *number_of_UNC_in_array,
const struct nls_table *nls_codepage)
{
/* TRANS2_GET_DFS_REFERRAL */
TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
struct dfs_referral_level_3 * referrals = NULL;
int rc = 0;
int bytes_returned;
int name_len;
unsigned int i;
char * temp;
*number_of_UNC_in_array = 0;
*targetUNCs = NULL;
......@@ -1701,6 +1704,76 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
cFYI(1, ("Send error in GetDFSRefer = %d", rc));
} else { /* decode response */
/* BB Add logic to parse referrals here */
pSMBr->DataOffset = le16_to_cpu(pSMBr->DataOffset);
pSMBr->DataCount = le16_to_cpu(pSMBr->DataCount);
cFYI(1,
("Decoding GetDFSRefer response. BCC: %d Offset %d",
pSMBr->ByteCount, pSMBr->DataOffset));
if ((pSMBr->ByteCount < 17) || (pSMBr->DataOffset > 512)) /* BB also check enough total bytes returned */
rc = -EIO; /* bad smb */
else {
referrals =
(struct dfs_referral_level_3 *)
(8 /* sizeof start of data block */ +
pSMBr->DataOffset +
(char *) &pSMBr->hdr.Protocol);
cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \nfor referral one refer size: 0x%x srv type: 0x%x refer flags: 0x%x ttl: 0x%x",pSMBr->NumberOfReferrals,pSMBr->DFSFlags, referrals->ReferralSize,referrals->ServerType,referrals->ReferralFlags,referrals->TimeToLive));
/* BB This field is actually two bytes in from start of
data block so we could do safety check that DataBlock
begins at address of pSMBr->NumberOfReferrals */
*number_of_UNC_in_array = le16_to_cpu(pSMBr->NumberOfReferrals);
/* BB Fix below so can return more than one referral */
if(*number_of_UNC_in_array > 1)
*number_of_UNC_in_array = 1;
/* get the length of the strings describing refs */
name_len = 0;
for(i=0;i<*number_of_UNC_in_array;i++) {
/* make sure that DfsPathOffset not past end */
referrals->DfsPathOffset = le16_to_cpu(referrals->DfsPathOffset);
if(referrals->DfsPathOffset > pSMBr->DataCount) {
/* if invalid referral, stop here and do
not try to copy any more */
*number_of_UNC_in_array = i;
break;
}
temp = ((char *)referrals) + referrals->DfsPathOffset;
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len += UniStrnlen((wchar_t *)temp,pSMBr->DataCount);
} else {
name_len += strnlen(temp,pSMBr->DataCount);
}
referrals++;
/* BB add check that referral pointer does not fall off end PDU */
}
/* BB add check for name_len bigger than bcc */
*targetUNCs =
kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL);
/* copy the ref strings */
referrals =
(struct dfs_referral_level_3 *)
(8 /* sizeof data hdr */ +
pSMBr->DataOffset +
(char *) &pSMBr->hdr.Protocol);
for(i=0;i<*number_of_UNC_in_array;i++) {
temp = ((char *)referrals) + referrals->DfsPathOffset;
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
cifs_strfromUCS_le(*targetUNCs,
(wchar_t *) temp, name_len, nls_codepage);
} else {
strncpy(*targetUNCs,temp,name_len);
}
/* BB update target_uncs pointers */
referrals++;
}
temp = *targetUNCs;
temp[name_len] = 0;
}
}
if (pSMB)
buf_release(pSMB);
......
......@@ -571,11 +571,32 @@ find_unc(__u32 new_target_ip_addr, char *uncName, char *userName)
int
connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
const char *old_path, const struct nls_table *nls_codepage)
{
unsigned char *referrals = NULL;
unsigned int num_referrals;
int rc = 0;
rc = get_dfs_path(xid, pSesInfo,old_path, nls_codepage,
&num_referrals, &referrals);
/* BB Add in code to: if valid refrl, if not ip address contact
the helper that resolves tcp names, mount to it, try to
tcon to it unmount it if fail */
/* BB free memory for referrals string BB */
return rc;
}
int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
const char *old_path, const struct nls_table *nls_codepage,
unsigned int *pnum_referrals, unsigned char ** preferrals)
{
char *temp_unc;
int rc = 0;
int num_referrals = 0;
unsigned char *referrals = NULL;
*pnum_referrals = 0;
if (pSesInfo->ipc_tid == 0) {
temp_unc = kmalloc(2 /* for slashes */ +
......@@ -594,11 +615,10 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
kfree(temp_unc);
}
if (rc == 0)
rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, &referrals,
&num_referrals, nls_codepage);
return -ENODEV; /* BB remove and add return code processing */
rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
pnum_referrals, nls_codepage);
return rc;
}
int setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, struct nls_table * nls_info)
......
......@@ -110,10 +110,14 @@ cifs_open(struct inode *inode, struct file *file)
pCifsFile->netfid = netfid;
pCifsFile->pid = current->pid;
pCifsFile->pfile = file; /* needed for writepage */
write_lock(&file->f_owner.lock);
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist,&pTcon->openFileList);
pCifsInode = CIFS_I(file->f_dentry->d_inode);
if(pCifsInode->openFileList.next)
list_add(&pCifsFile->flist,&pCifsInode->openFileList);
write_unlock(&GlobalSMBSeslock);
write_unlock(&file->f_owner.lock);
if(file->f_flags & O_CREAT) {
/* time to set mode which we can not set earlier due
to problems creating new read-only files */
......@@ -154,9 +158,11 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
struct cifsFileInfo *open_file = NULL;
struct file * file = NULL;
struct list_head *tmp;
struct list_head *tmp1;
/* list all files open on tree connection */
list_for_each(tmp, &pTcon->openFileList) {
read_lock(&GlobalSMBSeslock);
list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist);
if(open_file) {
if(open_file->search_resume_name) {
......@@ -166,7 +172,9 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
kfree(open_file);
if(file) {
file->private_data = NULL;
read_unlock(&GlobalSMBSeslock);
rc = cifs_open(file->f_dentry->d_inode,file);
read_lock(&GlobalSMBSeslock);
if(rc) {
cFYI(1,("reconnecting file %s failed with %d",
file->f_dentry->d_name.name,rc));
......@@ -177,6 +185,7 @@ int reopen_files(struct cifsTconInfo * pTcon, struct nls_table * nlsinfo)
}
}
}
read_unlock(&GlobalSMBSeslock);
return rc;
}
......@@ -195,9 +204,11 @@ cifs_close(struct inode *inode, struct file *file)
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
if (pSMBFile) {
write_lock(&file->f_owner.lock);
if(pSMBFile->flist.next)
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
write_unlock(&file->f_owner.lock);
rc = CIFSSMBClose(xid, pTcon, pSMBFile->netfid);
if(pSMBFile->search_resume_name)
kfree(pSMBFile->search_resume_name);
......@@ -206,6 +217,8 @@ cifs_close(struct inode *inode, struct file *file)
} else
rc = -EBADF;
if((rc ==0) && CIFS_I(inode)->write_behind_rc)
rc = CIFS_I(inode)->write_behind_rc;
FreeXid(xid);
return rc;
}
......@@ -407,6 +420,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
struct cifsInodeInfo *cifsInode;
struct cifsFileInfo *open_file = NULL;
struct list_head *tmp;
struct list_head *tmp1;
int xid;
xid = GetXid();
......@@ -445,14 +459,17 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
cifsInode = CIFS_I(mapping->host);
list_for_each(tmp, &cifsInode->openFileList) {
read_lock(&GlobalSMBSeslock);
list_for_each_safe(tmp, tmp1, &cifsInode->openFileList) {
open_file = list_entry(tmp,struct cifsFileInfo, flist);
/* We could check if file is open for writing first */
if((open_file->pfile) &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
read_unlock(&GlobalSMBSeslock);
bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset);
read_lock(&GlobalSMBSeslock);
/* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = CURRENT_TIME;
if ((bytes_written > 0) && (offset)) {
......@@ -462,6 +479,7 @@ cifs_partialpagewrite(struct page *page,unsigned from, unsigned to)
}
}
}
read_unlock(&GlobalSMBSeslock);
if(open_file == NULL) {
cFYI(1,("No writeable filehandles for inode"));
rc = -EIO;
......@@ -548,7 +566,8 @@ cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
dentry->d_name.name, datasync));
rc = filemap_fdatawrite(inode->i_mapping);
if(rc == 0)
CIFS_I(inode)->write_behind_rc = 0;
FreeXid(xid);
return rc;
}
......@@ -600,6 +619,9 @@ int cifs_flush(struct file *file)
/* filemapfdatawrite appears easier for the time being */
rc = filemap_fdatawrite(inode->i_mapping);
if(rc == 0) /* reset wb rc if we were able to write out dirty pages */
CIFS_I(inode)->write_behind_rc = 0;
cFYI(1,("Flush inode %p file %p rc %d",inode,file,rc));
return rc;
......@@ -678,9 +700,13 @@ static void cifs_copy_cache_pages(struct address_space *mapping,
while (bytes_read > 0) {
if(list_empty(pages))
break;
spin_lock(&mapping->page_lock);
page = list_entry(pages->prev, struct page, list);
list_del(&page->list);
spin_unlock(&mapping->page_lock);
if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) {
page_cache_release(page);
cFYI(1,("Add page cache failed"));
......@@ -739,12 +765,14 @@ cifs_readpages(struct file *file, struct address_space *mapping,
pagevec_init(&lru_pvec, 0);
for(i = 0;i<num_pages;) {
spin_lock(&mapping->page_lock);
if(list_empty(page_list)) {
break;
}
page = list_entry(page_list->prev, struct page, list);
offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
spin_unlock(&mapping->page_lock);
/* for reads over a certain size could initiate async read ahead */
cFYI(0,("Read %d pages into cache at offset %ld ",
......@@ -762,12 +790,15 @@ cifs_readpages(struct file *file, struct address_space *mapping,
if ((rc < 0) || (smb_read_data == NULL)) {
cFYI(1,("Read error in readpages: %d",rc));
/* clean up remaing pages off list */
spin_lock(&mapping->page_lock);
while (!list_empty(page_list) && (i < num_pages)) {
page = list_entry(page_list->prev, struct page, list);
list_del(&page->list);
}
spin_unlock(&mapping->page_lock);
break;
} else if (bytes_read > 0){
} else if (bytes_read > 0) {
pSMBr = (struct smb_com_read_rsp *)smb_read_data;
cifs_copy_cache_pages(mapping, page_list, bytes_read,
smb_read_data + 4 /* RFC1000 hdr */ +
......
......@@ -53,10 +53,6 @@ cifs_get_inode_info_unix(struct inode **pinode,
/* dump_mem("\nUnixQPathInfo return data", &findData, sizeof(findData)); */
if (rc) {
if (rc == -EREMOTE) {
/* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path,
&referrals,
&num_referrals,
cifs_sb->local_nls); */
tmp_path =
kmalloc(strnlen
(pTcon->treeName,
......@@ -180,11 +176,6 @@ cifs_get_inode_info(struct inode **pinode,
/* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
if (rc) {
if (rc == -EREMOTE) {
/* BB add call to new func rc = GetDFSReferral(); */
/* rc = *//* CIFSGetDFSRefer(xid, pTcon->ses, search_path,
&referrals,
&num_referrals,
cifs_sb->local_nls); */
tmp_path =
kmalloc(strnlen
(pTcon->treeName,
......
......@@ -113,6 +113,9 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
/* BB Should we be using page symlink ops here? */
if (rc == 0) {
/* BB Add special case check for Samba DFS symlinks */
target_path[PATH_MAX-1] = 0;
rc = vfs_follow_link(nd, target_path);
}
......@@ -186,7 +189,10 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
char *full_path = NULL;
char *tmp_path = NULL;
char * tmpbuffer;
unsigned char * referrals = NULL;
int num_referrals = 0;
int len;
__u16 fid;
......@@ -206,6 +212,7 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
FreeXid(xid);
return -ENOMEM;
}
/* BB add read reparse point symlink code and Unix extensions symlink code here BB */
if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
......@@ -224,8 +231,36 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
if(CIFSSMBClose(xid, pTcon, fid)) {
cFYI(1,("Error closing junction point (open for ioctl)"));
}
if(rc == -EIO) {
/* Query if DFS Junction */
tmp_path =
kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1,
GFP_KERNEL);
if (tmp_path) {
strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
strncat(tmp_path, full_path, MAX_PATHCONF);
rc = get_dfs_path(xid, pTcon->ses, tmp_path,
cifs_sb->local_nls, &num_referrals, &referrals);
cFYI(1,("Get DFS for %s rc = %d ",tmp_path, rc));
if((num_referrals == 0) && (rc == 0))
rc = -EACCES;
else {
cFYI(1,("num referral: %d",num_referrals));
if(referrals) {
cFYI(1,("referral string: %s ",referrals));
strncpy(tmpbuffer, referrals, len-1);
}
}
kfree(tmp_path);
if(referrals) {
kfree(referrals);
}
}
/* BB add code like else decode referrals then memcpy to
tmpbuffer and free referrals string array BB */
}
}
}
/* BB Anything else to do to handle recursive links? */
/* BB Should we be using page ops here? */
......@@ -238,10 +273,12 @@ cifs_readlink(struct dentry *direntry, char *pBuffer, int buflen)
rc));
}
if (tmpbuffer)
if (tmpbuffer) {
kfree(tmpbuffer);
if (full_path)
}
if (full_path) {
kfree(full_path);
}
FreeXid(xid);
return rc;
}
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