summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifspdu.h1
-rw-r--r--fs/cifs/cifsproto.h6
-rw-r--r--fs/cifs/cifssmb.c79
-rw-r--r--fs/cifs/connect.c32
-rw-r--r--fs/cifs/inode.c9
-rw-r--r--fs/cifs/link.c43
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;
}