diff options
| -rw-r--r-- | fs/cifs/cifspdu.h | 1 | ||||
| -rw-r--r-- | fs/cifs/cifsproto.h | 6 | ||||
| -rw-r--r-- | fs/cifs/cifssmb.c | 79 | ||||
| -rw-r--r-- | fs/cifs/connect.c | 32 | ||||
| -rw-r--r-- | fs/cifs/inode.c | 9 | ||||
| -rw-r--r-- | fs/cifs/link.c | 43 |
6 files changed, 147 insertions, 23 deletions
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 937ed3eea91a..1e6be8054126 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -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; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index ceb46ae4aae9..0506717b6c54 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -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); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d5128cddeee4..978f5cacd82a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -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; @@ -1654,8 +1657,8 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, if (ses->capabilities & CAP_DFS) { pSMB->hdr.Flags2 |= SMBFLG2_DFS; } - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_UNICODE) { pSMB->hdr.Flags2 |= SMBFLG2_UNICODE; @@ -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); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ff8065cc90ed..89fd14b85887 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -572,10 +572,31 @@ 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) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 66b93e3bdb26..7091afc6bd21 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -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, diff --git a/fs/cifs/link.c b/fs/cifs/link.c index c62ce66a8161..1301198c46ed 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -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; } |
