r25009: Large patch discussed with Volker. Move unix_convert to a talloc-based
[abartlet/samba.git/.git] / source3 / smbd / msdfs.c
index c66f0477a84073b2ea02dd01a01f8d5e4a12508b..10652874c513817cab6601962bf63b5693580c68 100644 (file)
@@ -1,12 +1,13 @@
 /* 
    Unix SMB/Netbios implementation.
    Version 3.0
-   MSDfs services for Samba
+   MSDFS services for Samba
    Copyright (C) Shirish Kalele 2000
+   Copyright (C) Jeremy Allison 2007
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 
+#define DBGC_CLASS DBGC_MSDFS
 #include "includes.h"
 
-extern fstring local_machine;
 extern uint32 global_client_caps;
 
 /**********************************************************************
-  Parse the pathname  of the form \hostname\service\reqpath
-  into the dfs_path structure 
- **********************************************************************/
+ Parse a DFS pathname of the form \hostname\service\reqpath
+ into the dfs_path structure.
+ If POSIX pathnames is true, the pathname may also be of the
+ form /hostname/service/reqpath.
+ We cope with either here.
+
+ Unfortunately, due to broken clients who might set the
+ SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
+ send a local path, we have to cope with that too....
 
-static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
+ JRA.
+**********************************************************************/
+
+static NTSTATUS parse_dfs_path(const char *pathname,
+                               BOOL allow_wcards,
+                               struct dfs_path *pdp,
+                               BOOL *ppath_contains_wcard)
 {
        pstring pathname_local;
-       char* p,*temp;
+       char *p,*temp;
+       NTSTATUS status = NT_STATUS_OK;
+       char sepchar;
+
+       ZERO_STRUCTP(pdp);
 
        pstrcpy(pathname_local,pathname);
        p = temp = pathname_local;
 
-       ZERO_STRUCTP(pdp);
+       pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
 
-       trim_char(temp,'\\','\\');
-       DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
+       sepchar = pdp->posix_path ? '/' : '\\';
 
-       /* now tokenize */
-       /* parse out hostname */
-       p = strchr_m(temp,'\\');
-       if(p == NULL)
-               return False;
+       if (*pathname != sepchar) {
+               DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
+                       pathname, sepchar ));
+               /*
+                * Possibly client sent a local path by mistake.
+                * Try and convert to a local path.
+                */
+
+               pdp->hostname[0] = '\0';
+               pdp->servicename[0] = '\0';
+
+               /* We've got no info about separators. */
+               pdp->posix_path = lp_posix_pathnames();
+               p = temp;
+               DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
+                       temp));
+               goto local_path;
+       }
+
+       trim_char(temp,sepchar,sepchar);
+
+       DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
+               temp, sepchar));
+
+       /* Now tokenize. */
+       /* Parse out hostname. */
+       p = strchr_m(temp,sepchar);
+       if(p == NULL) {
+               DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
+                       temp));
+               /*
+                * Possibly client sent a local path by mistake.
+                * Try and convert to a local path.
+                */
+
+               pdp->hostname[0] = '\0';
+               pdp->servicename[0] = '\0';
+
+               p = temp;
+               DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
+                       temp));
+               goto local_path;
+       }
        *p = '\0';
-       pstrcpy(pdp->hostname,temp);
+       fstrcpy(pdp->hostname,temp);
        DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
 
-       /* parse out servicename */
+       /* If we got a hostname, is it ours (or an IP address) ? */
+       if (!is_myname_or_ipaddr(pdp->hostname)) {
+               /* Repair path. */
+               *p = sepchar;
+               DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n",
+                       pdp->hostname, temp));
+               /*
+                * Possibly client sent a local path by mistake.
+                * Try and convert to a local path.
+                */
+
+               pdp->hostname[0] = '\0';
+               pdp->servicename[0] = '\0';
+
+               p = temp;
+               DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
+                       temp));
+               goto local_path;
+       }
+
+       /* Parse out servicename. */
        temp = p+1;
-       p = strchr_m(temp,'\\');
+       p = strchr_m(temp,sepchar);
        if(p == NULL) {
-               pstrcpy(pdp->servicename,temp);
+               fstrcpy(pdp->servicename,temp);
                pdp->reqpath[0] = '\0';
-               return True;
+               return NT_STATUS_OK;
        }
        *p = '\0';
-       pstrcpy(pdp->servicename,temp);
+       fstrcpy(pdp->servicename,temp);
        DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
 
-       /* rest is reqpath */
-       check_path_syntax(pdp->reqpath, p+1);
+       p++;
 
-       DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
-       return True;
-}
+  local_path:
 
-/**********************************************************************
-  Parse the pathname  of the form /hostname/service/reqpath
-  into the dfs_path structure 
- **********************************************************************/
+       *ppath_contains_wcard = False;
 
-static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
-{
-       pstring pathname_local;
-       char* p,*temp;
+       pstrcpy(pdp->reqpath, p);
 
-       pstrcpy(pathname_local,pathname);
-       p = temp = pathname_local;
-
-       ZERO_STRUCTP(pdp);
-
-       trim_char(temp,'/','/');
-       DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
-
-       /* now tokenize */
-       /* parse out hostname */
-       p = strchr_m(temp,'/');
-       if(p == NULL)
-               return False;
-       *p = '\0';
-       pstrcpy(pdp->hostname,temp);
-       DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
-
-       /* parse out servicename */
-       temp = p+1;
-       p = strchr_m(temp,'/');
-       if(p == NULL) {
-               pstrcpy(pdp->servicename,temp);
-               pdp->reqpath[0] = '\0';
-               return True;
+       /* Rest is reqpath. */
+       if (pdp->posix_path) {
+               status = check_path_syntax_posix(pdp->reqpath);
+       } else {
+               if (allow_wcards) {
+                       status = check_path_syntax_wcard(pdp->reqpath, ppath_contains_wcard);
+               } else {
+                       status = check_path_syntax(pdp->reqpath);
+               }
        }
-       *p = '\0';
-       pstrcpy(pdp->servicename,temp);
-       DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
 
-       /* rest is reqpath */
-       check_path_syntax(pdp->reqpath, p+1);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
+                       p, nt_errstr(status) ));
+               return status;
+       }
 
-       DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
-       return True;
+       DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
+       return NT_STATUS_OK;
 }
 
 /********************************************************
@@ -122,24 +171,36 @@ static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
  Note this CHANGES CWD !!!! JRA.
 *********************************************************/
 
-static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
+static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
 {
+       pstring connpath;
+
        ZERO_STRUCTP(conn);
-       conn->service = snum;
-       conn->connectpath = path;
-       pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
+
+       pstrcpy(connpath, path);
+       pstring_sub(connpath , "%S", lp_servicename(snum));
 
        /* needed for smbd_vfs_init() */
-       
-        if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
-                DEBUG(0,("talloc_init(connection_struct) failed!\n"));
-                return False;
-        }
-       
+
+       if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
+               DEBUG(0,("talloc_init(connection_struct) failed!\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
+               DEBUG(0, ("TALLOC failed\n"));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       conn->params->service = snum;
+
+       set_conn_connectpath(conn, connpath);
+
        if (!smbd_vfs_init(conn)) {
+               NTSTATUS status = map_nt_error_from_unix(errno);
                DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
-               talloc_destroy( conn->mem_ctx );
-               return False;
+               conn_free_internal(conn);
+               return status;
        }
 
        /*
@@ -149,59 +210,76 @@ static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
         */
 
        if (vfs_ChDir(conn,conn->connectpath) != 0) {
+               NTSTATUS status = map_nt_error_from_unix(errno);
                DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
                                        conn->connectpath, strerror(errno) ));
-               talloc_destroy( conn->mem_ctx );
-               return False;
+               conn_free_internal(conn);
+               return status;
        }
-       return True;
-}
 
+       return NT_STATUS_OK;
+}
 
 /**********************************************************************
  Parse the contents of a symlink to verify if it is an msdfs referral
- A valid referral is of the form: msdfs:server1\share1,server2\share2
+ A valid referral is of the form:
+
+ msdfs:server1\share1,server2\share2
+ msdfs:server1\share1\pathname,server2\share2\pathname
+ msdfs:server1/share1,server2/share2
+ msdfs:server1/share1/pathname,server2/share2/pathname.
+
+ Note that the alternate paths returned here must be of the canonicalized
+ form:
+
+ \server\share or
+ \server\share\path\to\file,
+
+ even in posix path mode. This is because we have no knowledge if the
+ server we're referring to understands posix paths.
  **********************************************************************/
 
-static BOOL parse_symlink(char* buf,struct referral** preflist, 
-                                int* refcount)
+static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
+                               char *target,
+                               struct referral **preflist,
+                               int *refcount)
 {
        pstring temp;
-       charprot;
-       charalt_path[MAX_REFERRAL_COUNT];
-       int count=0, i;
-       struct referralreflist;
+       char *prot;
+       char *alt_path[MAX_REFERRAL_COUNT];
+       int count = 0, i;
+       struct referral *reflist;
 
-       pstrcpy(temp,buf);
-  
+       pstrcpy(temp,target);
        prot = strtok(temp,":");
-
-       if (!strequal(prot, "msdfs"))
+       if (!prot) {
+               DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
                return False;
-
-       /* No referral list requested. Just yes/no. */
-       if (!preflist) 
-               return True;
+       }
 
        /* parse out the alternate paths */
-       while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
+       while((count<MAX_REFERRAL_COUNT) &&
+             ((alt_path[count] = strtok(NULL,",")) != NULL)) {
                count++;
+       }
 
-       DEBUG(10,("parse_symlink: count=%d\n", count));
+       DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
 
-       reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
-       if(reflist == NULL) {
-               DEBUG(0,("parse_symlink: Malloc failed!\n"));
-               return False;
+       if (count) {
+               reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
+               if(reflist == NULL) {
+                       DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
+                       return False;
+               }
+       } else {
+               reflist = *preflist = NULL;
        }
        
        for(i=0;i<count;i++) {
                char *p;
 
-               /* replace all /'s in the alternate path by a \ */
-               for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
-                       *p = '\\'; 
-               }
+               /* Canonicalize link target. Replace all /'s in the path by a \ */
+               string_replace(alt_path[i], '/', '\\');
 
                /* Remove leading '\\'s */
                p = alt_path[i];
@@ -211,280 +289,351 @@ static BOOL parse_symlink(char* buf,struct referral** preflist,
 
                pstrcpy(reflist[i].alternate_path, "\\");
                pstrcat(reflist[i].alternate_path, p);
+
                reflist[i].proximity = 0;
                reflist[i].ttl = REFERRAL_TTL;
-               DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
+               DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
+               *refcount += 1;
        }
 
-       if(refcount)
-               *refcount = count;
-
        return True;
 }
  
 /**********************************************************************
- Returns true if the unix path is a valid msdfs symlink
- **********************************************************************/
+ Returns true if the unix path is a valid msdfs symlink and also
+ returns the target string from inside the link.
+**********************************************************************/
 
-BOOL is_msdfs_link(connection_struct* conn, char * path,
-                  struct referral** reflistp, int* refcnt,
-                  SMB_STRUCT_STAT *sbufp)
+BOOL is_msdfs_link(connection_struct *conn,
+                       const char *path,
+                       pstring link_target,
+                       SMB_STRUCT_STAT *sbufp)
 {
        SMB_STRUCT_STAT st;
-       pstring referral;
        int referral_len = 0;
 
-       if (!path || !conn)
-               return False;
-
-       if (sbufp == NULL)
+       if (sbufp == NULL) {
                sbufp = &st;
+       }
 
        if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
                DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
                return False;
        }
   
-       if (S_ISLNK(sbufp->st_mode)) {
-               /* open the link and read it */
-               referral_len = SMB_VFS_READLINK(conn, path, referral, 
-                                                     sizeof(pstring));
-               if (referral_len == -1) {
-                       DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
-                       return False;
-               }
+       if (!S_ISLNK(sbufp->st_mode)) {
+               DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
+               return False;
+       }
 
-               referral[referral_len] = '\0';
-               DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
-               if (parse_symlink(referral, reflistp, refcnt))
-                       return True;
+       /* open the link and read it */
+       referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
+       if (referral_len == -1) {
+               DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
+                       path, strerror(errno)));
+               return False;
        }
-       return False;
+       link_target[referral_len] = '\0';
+
+       DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
+
+       if (!strnequal(link_target, "msdfs:", 6)) {
+               return False;
+       }
+       return True;
 }
 
 /*****************************************************************
  Used by other functions to decide if a dfs path is remote,
-and to get the list of referred locations for that remote path.
+ and to get the list of referred locations for that remote path.
  
-findfirst_flag: For findfirsts, dfs links themselves are not
-redirected, but paths beyond the links are. For normal smb calls,
-even dfs links need to be redirected.
+ search_flag: For findfirsts, dfs links themselves are not
+ redirected, but paths beyond the links are. For normal smb calls,
+ even dfs links need to be redirected.
 
-self_referralp: clients expect a dfs referral for the same share when
-they request referrals for dfs roots on a server. 
+ consumedcntp: how much of the dfs path is being redirected. the client
+ should try the remaining path on the redirected server.
 
-consumedcntp: how much of the dfs path is being redirected. the client
-should try the remaining path on the redirected server.
-               
+ If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
+ link redirect are in targetpath.
 *****************************************************************/
 
-static BOOL resolve_dfs_path(pstring dfspath, struct dfs_path* dp, 
-                     connection_struct* conn,
-                     BOOL findfirst_flag,
-                     struct referral** reflistpp, int* refcntp,
-                     BOOL* self_referralp, int* consumedcntp)
+static NTSTATUS dfs_path_lookup(connection_struct *conn,
+                       const char *dfspath, /* Incoming complete dfs path */
+                       const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
+                       BOOL search_flag, /* Called from a findfirst ? */
+                       int *consumedcntp,
+                       pstring targetpath)
 {
-       pstring localpath;
-       int consumed_level = 1;
-       char *p;
-       BOOL bad_path = False;
+       char *p = NULL;
+       char *q = NULL;
        SMB_STRUCT_STAT sbuf;
-       pstring reqpath;
+       NTSTATUS status;
+       pstring localpath_in;
+       char *localpath = NULL;
+       pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
+
+       DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
+               conn->connectpath, pdp->reqpath));
+
+       /* 
+        * Note the unix path conversion here we're doing we can
+        * throw away. We're looking for a symlink for a dfs
+        * resolution, if we don't find it we'll do another
+        * unix_convert later in the codepath.
+        * If we needed to remember what we'd resolved in
+        * dp->reqpath (as the original code did) we'd
+        * pstrcpy(localhost, dp->reqpath) on any code
+        * path below that returns True - but I don't
+        * think this is needed. JRA.
+        */
 
-       if (!dp || !conn) {
-               DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
-               return False;
+       pstrcpy(localpath_in, pdp->reqpath);
+       status = unix_convert(conn, localpath_in, search_flag, &localpath, NULL, &sbuf);
+       if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
+                                       NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+               return status;
        }
 
-       if (dp->reqpath[0] == '\0') {
-               if (self_referralp) {
-                       DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
-                       *self_referralp = True;
+       /* Optimization - check if we can redirect the whole path. */
+
+       if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
+               if (search_flag) {
+                       DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
+                                "for dfs link %s.\n", dfspath));
+                       return NT_STATUS_OK;
                }
-               return False;
+
+               DEBUG(6,("dfs_path_lookup: %s resolves to a "
+                       "valid dfs link %s.\n", dfspath, targetpath));
+
+               if (consumedcntp) {
+                       *consumedcntp = strlen(dfspath);
+               }
+               return NT_STATUS_PATH_NOT_COVERED;
        }
 
-       DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
+       /* Prepare to test only for '/' components in the given path,
+        * so if a Windows path replace all '\\' characters with '/'.
+        * For a POSIX DFS path we know all separators are already '/'. */
 
-       unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
-       /* JRA... should we strlower the last component here.... ? */
-       pstrcpy(localpath, dp->reqpath);
+       pstrcpy(canon_dfspath, dfspath);
+       if (!pdp->posix_path) {
+               string_replace(canon_dfspath, '\\', '/');
+       }
 
-       /* check if need to redirect */
-       if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
-               if (findfirst_flag) {
-                       DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
-                                "for dfs link %s.\n", dfspath));
-                       return False;
-               } else {                
-                       DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
-                                dfspath));
-                       if (consumedcntp) 
-                               *consumedcntp = strlen(dfspath);
-                       return True;
-               }
-       } 
+       /*
+        * localpath comes out of unix_convert, so it has
+        * no trailing backslash. Make sure that canon_dfspath hasn't either.
+        * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
+        */
+
+       trim_char(canon_dfspath,0,'/');
+
+       /*
+        * Redirect if any component in the path is a link.
+        * We do this by walking backwards through the 
+        * local path, chopping off the last component
+        * in both the local path and the canonicalized
+        * DFS path. If we hit a DFS link then we're done.
+        */
+
+       p = strrchr_m(localpath, '/');
+       if (consumedcntp) {
+               q = strrchr_m(canon_dfspath, '/');
+       }
 
-       /* redirect if any component in the path is a link */
-       pstrcpy(reqpath, dp->reqpath);
-       p = strrchr_m(reqpath, '/');
        while (p) {
                *p = '\0';
-               pstrcpy(localpath, reqpath);
-               if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
-                       DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
-
-                       /* To find the path consumed, we truncate the original
-                          DFS pathname passed to use to remove the last
-                          component. The length of the resulting string is
-                          the path consumed 
-                       */
+               if (q) {
+                       *q = '\0';
+               }
+
+               if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
+                       DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
+                               "parent %s is dfs link\n", dfspath, localpath));
+
                        if (consumedcntp) {
-                               char *q;
-                               pstring buf;
-                               pstrcpy(buf, dfspath);
-                               trim_char(buf, '\0', '\\');
-                               for (; consumed_level; consumed_level--) {
-                                       q = strrchr_m(buf, '\\');
-                                       if (q)
-                                               *q = 0;
-                               }
-                               *consumedcntp = strlen(buf);
-                               DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
+                               *consumedcntp = strlen(canon_dfspath);
+                               DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
+                                       "(%d)\n", canon_dfspath, *consumedcntp));
                        }
-                       
-                       return True;
+
+                       return NT_STATUS_PATH_NOT_COVERED;
+               }
+
+               /* Step back on the filesystem. */
+               p = strrchr_m(localpath, '/');
+
+               if (consumedcntp) {
+                       /* And in the canonicalized dfs path. */
+                       q = strrchr_m(canon_dfspath, '/');
                }
-               p = strrchr_m(reqpath, '/');
-               consumed_level++;
        }
-       
-       return False;
+
+       return NT_STATUS_OK;
 }
 
 /*****************************************************************
-  Decides if a dfs pathname should be redirected or not.
-  If not, the pathname is converted to a tcon-relative local unix path
+ Decides if a dfs pathname should be redirected or not.
+ If not, the pathname is converted to a tcon-relative local unix path
+
+ search_wcard_flag: this flag performs 2 functions bother related
+ to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
+ for details.
+
+ This function can return NT_STATUS_OK, meaning use the returned path as-is
+ (mapped into a local path).
+ or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
+ any other NT_STATUS error which is a genuine error to be
+ returned to the client. 
 *****************************************************************/
 
-BOOL dfs_redirect(pstring pathname, connection_struct* conn,
-                 BOOL findfirst_flag)
+static NTSTATUS dfs_redirect( connection_struct *conn,
+                       pstring dfs_path,
+                       BOOL search_wcard_flag,
+                       BOOL *ppath_contains_wcard)
 {
+       NTSTATUS status;
        struct dfs_path dp;
+       pstring targetpath;
        
-       if (!conn || !pathname)
-               return False;
+       status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (dp.reqpath[0] == '\0') {
+               pstrcpy(dfs_path, dp.reqpath);
+               DEBUG(5,("dfs_redirect: self-referral.\n"));
+               return NT_STATUS_OK;
+       }
 
-       parse_processed_dfs_path(pathname, &dp);
+       /* If dfs pathname for a non-dfs share, convert to tcon-relative
+          path and return OK */
 
-       /* if dfs pathname for a non-dfs share, convert to tcon-relative
-          path and return false */
        if (!lp_msdfs_root(SNUM(conn))) {
-               pstrcpy(pathname, dp.reqpath);
-               return False;
+               pstrcpy(dfs_path, dp.reqpath);
+               return NT_STATUS_OK;
        }
-       
-       if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) )) 
-               return False;
 
-       if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
-                            NULL, NULL, NULL, NULL)) {
-               DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
-               return True;
-       } else {
-               DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
-               
-               /* Form non-dfs tcon-relative path */
-               pstrcpy(pathname, dp.reqpath);
-               DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
-                        pathname));
-               return False;
+       /* If it looked like a local path (zero hostname/servicename)
+        * just treat as a tcon-relative path. */ 
+
+       if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') { 
+               pstrcpy(dfs_path, dp.reqpath);
+               return NT_STATUS_OK;
+       }
+
+       if (!( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
+                       || (strequal(dp.servicename, HOMES_NAME) 
+                       && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) {
+
+               /* The given sharename doesn't match this connection. */
+
+               return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+       }
+
+       status = dfs_path_lookup(conn, dfs_path, &dp,
+                       search_wcard_flag, NULL, targetpath);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
+                       DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
+               } else {
+                       DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
+                               dfs_path, nt_errstr(status) ));
+               }
+               return status;
        }
 
-       /* never reached */
+       DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
+
+       /* Form non-dfs tcon-relative path */
+       pstrcpy(dfs_path, dp.reqpath);
+
+       DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
+       return NT_STATUS_OK;
 }
 
 /**********************************************************************
  Return a self referral.
 **********************************************************************/
 
-static BOOL self_ref(char *pathname, struct junction_map *jucn,
-                       int *consumedcntp, BOOL *self_referralp)
+static NTSTATUS self_ref(TALLOC_CTX *ctx,
+                       const char *dfs_path,
+                       struct junction_map *jucn,
+                       int *consumedcntp,
+                       BOOL *self_referralp)
 {
        struct referral *ref;
 
-       if (self_referralp != NULL)
-               *self_referralp = True;
+       *self_referralp = True;
 
        jucn->referral_count = 1;
-       if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
-               DEBUG(0,("self_ref: malloc failed for referral\n"));
-               return False;
+       if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
+               DEBUG(0,("self_ref: talloc failed for referral\n"));
+               return NT_STATUS_NO_MEMORY;
        }
 
-       pstrcpy(ref->alternate_path,pathname);
+       pstrcpy(ref->alternate_path,dfs_path);
        ref->proximity = 0;
        ref->ttl = REFERRAL_TTL;
        jucn->referral_list = ref;
-       if (consumedcntp)
-               *consumedcntp = strlen(pathname);
-
-       return True;
+       *consumedcntp = strlen(dfs_path);
+       return NT_STATUS_OK;
 }
 
 /**********************************************************************
  Gets valid referrals for a dfs path and fills up the
- junction_map structure
+ junction_map structure.
 **********************************************************************/
 
-BOOL get_referred_path(char *pathname, struct junction_map *jucn,
-                      int *consumedcntp, BOOL *self_referralp)
+NTSTATUS get_referred_path(TALLOC_CTX *ctx,
+                       const char *dfs_path,
+                       struct junction_map *jucn,
+                       int *consumedcntp,
+                       BOOL *self_referralp)
 {
-       struct dfs_path dp;
-
        struct connection_struct conns;
-       struct connection_struct* conn = &conns;
+       struct connection_struct *conn = &conns;
+       struct dfs_path dp;
        pstring conn_path;
+       pstring targetpath;
        int snum;
-       BOOL ret = False;
-       BOOL self_referral = False;
-
-       if (!pathname || !jucn)
-               return False;
+       NTSTATUS status = NT_STATUS_NOT_FOUND;
+       BOOL dummy;
 
        ZERO_STRUCT(conns);
 
-       if (self_referralp)
-               *self_referralp = False;
-       else
-               self_referralp = &self_referral;
+       *self_referralp = False;
 
-       parse_dfs_path(pathname, &dp);
+       status = parse_dfs_path(dfs_path, False, &dp, &dummy);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
        /* Verify hostname in path */
-       if (local_machine && (!strequal(local_machine, dp.hostname))) {
-               /* Hostname mismatch, check if one of our IP addresses */
-               if (!ismyip(*interpret_addr2(dp.hostname))) {
-                       DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
-                               dp.hostname, pathname));
-                       return False;
-               }
+       if (!is_myname_or_ipaddr(dp.hostname)) {
+               DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
+                       dp.hostname, dfs_path));
+               return NT_STATUS_NOT_FOUND;
        }
 
-       pstrcpy(jucn->service_name, dp.servicename);
+       fstrcpy(jucn->service_name, dp.servicename);
        pstrcpy(jucn->volume_name, dp.reqpath);
 
        /* Verify the share is a dfs root */
        snum = lp_servicenumber(jucn->service_name);
        if(snum < 0) {
-               if ((snum = find_service(jucn->service_name)) < 0)
-                       return False;
+               if ((snum = find_service(jucn->service_name)) < 0) {
+                       return NT_STATUS_NOT_FOUND;
+               }
        }
 
        if (!lp_msdfs_root(snum)) {
-               DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
-                        dp.servicename, pathname));
-               goto out;
+               DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
+                        dp.servicename, dfs_path));
+               return NT_STATUS_NOT_FOUND;
        }
 
        /*
@@ -495,65 +644,76 @@ BOOL get_referred_path(char *pathname, struct junction_map *jucn,
         */
 
        if (dp.reqpath[0] == '\0') {
+               struct referral *ref;
 
-               struct referral* ref;
-
-               if (*lp_msdfs_proxy(snum) == '\0')
-                       return self_ref(pathname, jucn, consumedcntp,
+               if (*lp_msdfs_proxy(snum) == '\0') {
+                       return self_ref(ctx,
+                                       dfs_path,
+                                       jucn,
+                                       consumedcntp,
                                        self_referralp);
+               }
+
+               /* 
+                * It's an msdfs proxy share. Redirect to
+                * the configured target share.
+                */
 
                jucn->referral_count = 1;
-               if ((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
+               if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
                        DEBUG(0, ("malloc failed for referral\n"));
-                       goto out;
+                       return NT_STATUS_NO_MEMORY;
                }
 
                pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
-               if (dp.reqpath[0] != '\0')
+               if (dp.reqpath[0] != '\0') {
                        pstrcat(ref->alternate_path, dp.reqpath);
+               }
                ref->proximity = 0;
                ref->ttl = REFERRAL_TTL;
                jucn->referral_list = ref;
-               if (consumedcntp)
-                       *consumedcntp = strlen(pathname);
-               ret = True;
-               goto out;
+               *consumedcntp = strlen(dfs_path);
+               return NT_STATUS_OK;
        }
 
        pstrcpy(conn_path, lp_pathname(snum));
-       if (!create_conn_struct(conn, snum, conn_path))
-               return False;
+       status = create_conn_struct(conn, snum, conn_path);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       /* If not remote & not a self referral, return False */
-       if (!resolve_dfs_path(pathname, &dp, conn, False, 
-                             &jucn->referral_list, &jucn->referral_count,
-                             self_referralp, consumedcntp)) {
-               if (!*self_referralp) {
-                       DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
-                       goto out;
-               }
+       /* If this is a DFS path dfs_lookup should return
+        * NT_STATUS_PATH_NOT_COVERED. */
+
+       status = dfs_path_lookup(conn, dfs_path, &dp,
+                       False, consumedcntp, targetpath);
+
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
+               DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
+                       dfs_path));
+               conn_free_internal(conn);
+               return status;
        }
-       
-       /* if self_referral, fill up the junction map */
-       if (*self_referralp) {
-               if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
-                       goto out;
-               }
+
+       /* We know this is a valid dfs link. Parse the targetpath. */
+       if (!parse_msdfs_symlink(ctx, targetpath,
+                               &jucn->referral_list,
+                               &jucn->referral_count)) {
+               DEBUG(3,("get_referred_path: failed to parse symlink "
+                       "target %s\n", targetpath ));
+               conn_free_internal(conn);
+               return NT_STATUS_NOT_FOUND;
        }
-       
-       ret = True;
 
-out:
-       if (conn->mem_ctx)
-               talloc_destroy( conn->mem_ctx );
-       
-       return ret;
+       conn_free_internal(conn);
+       return NT_STATUS_OK;
 }
 
-static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
-                                  struct junction_map* junction,
-                                  int consumedcnt,
-                                  BOOL self_referral)
+static int setup_ver2_dfs_referral(const char *pathname,
+                               char **ppdata, 
+                               struct junction_map *junction,
+                               int consumedcnt,
+                               BOOL self_referral)
 {
        char* pdata = *ppdata;
 
@@ -565,12 +725,14 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
        int reply_size = 0;
        int i=0;
 
-       DEBUG(10,("setting up version2 referral\nRequested path:\n"));
+       DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
 
-       requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
+       requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
                                       STR_TERMINATE);
 
-       dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
+       if (DEBUGLVL(10)) {
+           dump_data(0, uni_requestedpath,requestedpathlen);
+       }
 
        DEBUG(10,("ref count = %u\n",junction->referral_count));
 
@@ -595,12 +757,12 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
        /* add the unexplained 0x16 bytes */
        reply_size += 0x16;
 
-       pdata = Realloc(pdata,reply_size);
+       pdata = (char *)SMB_REALLOC(pdata,reply_size);
        if(pdata == NULL) {
-               DEBUG(0,("malloc failed for Realloc!\n"));
+               DEBUG(0,("Realloc failed!\n"));
                return -1;
-       } else
-               *ppdata = pdata;
+       }
+       *ppdata = pdata;
 
        /* copy in the dfs requested paths.. required for offset calculations */
        memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
@@ -609,10 +771,11 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
        /* create the header */
        SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
        SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
-       if(self_referral)
+       if(self_referral) {
                SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
-       else
+       } else {
                SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+       }
 
        offset = 8;
        /* add the referral elements */
@@ -622,10 +785,11 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
 
                SSVAL(pdata,offset,2); /* version 2 */
                SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
-               if(self_referral)
+               if(self_referral) {
                        SSVAL(pdata,offset+4,1);
-               else
+               } else {
                        SSVAL(pdata,offset+4,0);
+               }
                SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
                SIVAL(pdata,offset+8,ref->proximity);
                SIVAL(pdata,offset+12,ref->ttl);
@@ -634,7 +798,7 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
                SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
                /* copy referred path into current offset */
                unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
-                                    -1, STR_UNICODE);
+                                    sizeof(pstring), STR_UNICODE);
 
                SSVAL(pdata,offset+20,uni_curroffset-offset);
 
@@ -646,10 +810,11 @@ static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
        return reply_size;
 }
 
-static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
-                                  struct junction_map* junction,
-                                  int consumedcnt,
-                                  BOOL self_referral)
+static int setup_ver3_dfs_referral(const char *pathname,
+                               char **ppdata, 
+                               struct junction_map *junction,
+                               int consumedcnt,
+                               BOOL self_referral)
 {
        char* pdata = *ppdata;
 
@@ -663,9 +828,11 @@ static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
        
        DEBUG(10,("setting up version3 referral\n"));
 
-       reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
+       reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
        
-       dump_data(10, (char *) uni_reqpath,reqpathlen);
+       if (DEBUGLVL(10)) {
+           dump_data(0, uni_reqpath,reqpathlen);
+       }
 
        uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
        uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
@@ -676,20 +843,21 @@ static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
                reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
        }
 
-       pdata = Realloc(pdata,reply_size);
+       pdata = (char *)SMB_REALLOC(pdata,reply_size);
        if(pdata == NULL) {
                DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
                return -1;
-       } else
-               *ppdata = pdata;
+       }
+       *ppdata = pdata;
 
        /* create the header */
        SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
        SSVAL(pdata,2,junction->referral_count); /* number of referral */
-       if(self_referral)
+       if(self_referral) {
                SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
-       else
+       } else {
                SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
+       }
        
        /* copy in the reqpaths */
        memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
@@ -702,10 +870,11 @@ static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
 
                SSVAL(pdata,offset,3); /* version 3 */
                SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
-               if(self_referral)
+               if(self_referral) {
                        SSVAL(pdata,offset+4,1);
-               else
+               } else {
                        SSVAL(pdata,offset+4,0);
+               }
 
                SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
                SIVAL(pdata,offset+8,ref->ttl);
@@ -714,7 +883,7 @@ static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
                SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
                /* copy referred path into current offset */
                unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
-                                    -1, STR_UNICODE | STR_TERMINATE);
+                                    sizeof(pstring), STR_UNICODE | STR_TERMINATE);
                SSVAL(pdata,offset+16,uni_curroffset-offset);
                /* copy 0x10 bytes of 00's in the ServiceSite GUID */
                memset(pdata+offset+18,'\0',16);
@@ -726,34 +895,55 @@ static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
 }
 
 /******************************************************************
- * Set up the Dfs referral for the dfs pathname
- ******************************************************************/
-
-int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
+ Set up the DFS referral for the dfs pathname. This call returns
+ the amount of the path covered by this server, and where the
+ client should be redirected to. This is the meat of the
+ TRANS2_GET_DFS_REFERRAL call.
+******************************************************************/
+
+int setup_dfs_referral(connection_struct *orig_conn,
+                       const char *dfs_path,
+                       int max_referral_level,
+                       char **ppdata, NTSTATUS *pstatus)
 {
        struct junction_map junction;
-       int consumedcnt;
+       int consumedcnt = 0;
        BOOL self_referral = False;
-       pstring buf;
        int reply_size = 0;
-       char *pathnamep = pathname;
+       char *pathnamep = NULL;
+       pstring local_dfs_path;
+       TALLOC_CTX *ctx;
+
+       if (!(ctx=talloc_init("setup_dfs_referral"))) {
+               *pstatus = NT_STATUS_NO_MEMORY;
+               return -1;
+       }
 
        ZERO_STRUCT(junction);
 
        /* get the junction entry */
-       if (!pathnamep)
+       if (!dfs_path) {
+               talloc_destroy(ctx);
+               *pstatus = NT_STATUS_NOT_FOUND;
                return -1;
+       }
 
-       /* Trim pathname sent by client so it begins with only one backslash.
-          Two backslashes confuse some dfs clients
+       /* 
+        * Trim pathname sent by client so it begins with only one backslash.
+        * Two backslashes confuse some dfs clients
         */
-       while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
+
+       pstrcpy(local_dfs_path, dfs_path);
+       pathnamep = local_dfs_path;
+       while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
                pathnamep++;
+       }
 
-       pstrcpy(buf, pathnamep);
        /* The following call can change cwd. */
-       if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
+       *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
+       if (!NT_STATUS_IS_OK(*pstatus)) {
                vfs_ChDir(orig_conn,orig_conn->connectpath);
+               talloc_destroy(ctx);
                return -1;
        }
        vfs_ChDir(orig_conn,orig_conn->connectpath);
@@ -772,27 +962,37 @@ int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_ref
 
        /* create the referral depeding on version */
        DEBUG(10,("max_referral_level :%d\n",max_referral_level));
-       if(max_referral_level<2 || max_referral_level>3)
+
+       if (max_referral_level < 2) {
                max_referral_level = 2;
+       }
+       if (max_referral_level > 3) {
+               max_referral_level = 3;
+       }
 
        switch(max_referral_level) {
        case 2:
                reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
                                                     consumedcnt, self_referral);
-               SAFE_FREE(junction.referral_list);
                break;
        case 3:
                reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
                                                     consumedcnt, self_referral);
-               SAFE_FREE(junction.referral_list);
                break;
        default:
                DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
+               talloc_destroy(ctx);
+               *pstatus = NT_STATUS_INVALID_LEVEL;
                return -1;
        }
       
-       DEBUG(10,("DFS Referral pdata:\n"));
-       dump_data(10,*ppdata,reply_size);
+       if (DEBUGLVL(10)) {
+               DEBUGADD(0,("DFS Referral pdata:\n"));
+               dump_data(0,(uint8 *)*ppdata,reply_size);
+       }
+
+       talloc_destroy(ctx);
+       *pstatus = NT_STATUS_OK;
        return reply_size;
 }
 
@@ -800,62 +1000,69 @@ int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_ref
  The following functions are called by the NETDFS RPC pipe functions
  **********************************************************************/
 
-/**********************************************************************
- Creates a junction structure from a Dfs pathname
- **********************************************************************/
-BOOL create_junction(char* pathname, struct junction_map* jucn)
+/*********************************************************************
+ Creates a junction structure from a DFS pathname
+**********************************************************************/
+
+BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
 {
-        struct dfs_path dp;
+       int snum;
+       BOOL dummy;
+       struct dfs_path dp;
  
-        parse_dfs_path(pathname,&dp);
+       NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy);
 
-        /* check if path is dfs : validate first token */
-        if (local_machine && (!strequal(local_machine,dp.hostname))) {
-           
-          /* Hostname mismatch, check if one of our IP addresses */
-          if (!ismyip(*interpret_addr2(dp.hostname))) {
-                DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
-                        dp.hostname, pathname));
-                return False;
-          }
-        }
-
-        /* Check for a non-DFS share */
-        if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
-                DEBUG(4,("create_junction: %s is not an msdfs root.\n", 
-                        dp.servicename));
-                return False;
-        }
-
-        pstrcpy(jucn->service_name,dp.servicename);
-        pstrcpy(jucn->volume_name,dp.reqpath);
-        return True;
+       if (!NT_STATUS_IS_OK(status)) {
+               return False;
+       }
+
+       /* check if path is dfs : validate first token */
+       if (!is_myname_or_ipaddr(dp.hostname)) {
+               DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
+                       dp.hostname, dfs_path));
+               return False;
+       }
+
+       /* Check for a non-DFS share */
+       snum = lp_servicenumber(dp.servicename);
+
+       if(snum < 0 || !lp_msdfs_root(snum)) {
+               DEBUG(4,("create_junction: %s is not an msdfs root.\n",
+                       dp.servicename));
+               return False;
+       }
+
+       fstrcpy(jucn->service_name,dp.servicename);
+       pstrcpy(jucn->volume_name,dp.reqpath);
+       pstrcpy(jucn->comment, lp_comment(snum));
+       return True;
 }
 
 /**********************************************************************
  Forms a valid Unix pathname from the junction 
  **********************************************************************/
 
-static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
-                                  int max_pathlen, connection_struct *conn)
+static BOOL junction_to_local_path(struct junction_map *jucn,
+                               char *path,
+                               int max_pathlen,
+                               connection_struct *conn_out)
 {
        int snum;
        pstring conn_path;
 
-       if(!path || !jucn)
-               return False;
-
        snum = lp_servicenumber(jucn->service_name);
-       if(snum < 0)
+       if(snum < 0) {
                return False;
+       }
 
        safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
        safe_strcat(path, "/", max_pathlen-1);
        safe_strcat(path, jucn->volume_name, max_pathlen-1);
 
        pstrcpy(conn_path, lp_pathname(snum));
-       if (!create_conn_struct(conn, snum, conn_path))
+       if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
                return False;
+       }
 
        return True;
 }
@@ -870,34 +1077,43 @@ BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
        BOOL insert_comma = False;
        BOOL ret = False;
 
-       if(!junction_to_local_path(jucn, path, sizeof(path), conn))
+       ZERO_STRUCT(conns);
+
+       if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
                return False;
+       }
   
-       /* form the msdfs_link contents */
+       /* Form the msdfs_link contents */
        pstrcpy(msdfs_link, "msdfs:");
        for(i=0; i<jucn->referral_count; i++) {
                char* refpath = jucn->referral_list[i].alternate_path;
       
+               /* Alternate paths always use Windows separators. */
                trim_char(refpath, '\\', '\\');
                if(*refpath == '\0') {
-                       if (i == 0)
+                       if (i == 0) {
                                insert_comma = False;
+                       }
                        continue;
                }
-               if (i > 0 && insert_comma)
+               if (i > 0 && insert_comma) {
                        pstrcat(msdfs_link, ",");
+               }
 
                pstrcat(msdfs_link, refpath);
-               if (!insert_comma)
+               if (!insert_comma) {
                        insert_comma = True;
-               
+               }
        }
 
-       DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
+       DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
+               path, msdfs_link));
 
-       if(exists)
-               if(SMB_VFS_UNLINK(conn,path)!=0)
+       if(exists) {
+               if(SMB_VFS_UNLINK(conn,path)!=0) {
                        goto out;
+               }
+       }
 
        if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
                DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
@@ -907,65 +1123,77 @@ BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
        
        
        ret = True;
-       
+
 out:
-       talloc_destroy( conn->mem_ctx );
+
+       conn_free_internal(conn);
        return ret;
 }
 
-BOOL remove_msdfs_link(struct junction_mapjucn)
+BOOL remove_msdfs_link(struct junction_map *jucn)
 {
        pstring path;
        connection_struct conns;
        connection_struct *conn = &conns;
        BOOL ret = False;
 
+       ZERO_STRUCT(conns);
+
        if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
-               if( SMB_VFS_UNLINK(conn, path) == 0 )
+               if( SMB_VFS_UNLINK(conn, path) == 0 ) {
                        ret = True;
-
+               }
                talloc_destroy( conn->mem_ctx );
        }
-       
+
+       conn_free_internal(conn);
        return ret;
 }
 
-static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
+static int form_junctions(TALLOC_CTX *ctx,
+                               int snum,
+                               struct junction_map *jucn,
+                               int jn_remain)
 {
-       int cnt = *jn_count;
-       DIR *dirp;
-       chardname;
+       int cnt = 0;
+       SMB_STRUCT_DIR *dirp;
+       char *dname;
        pstring connect_path;
-       char* service_name = lp_servicename(snum);
-       connection_struct conns;
-       connection_struct *conn = &conns;
+       char *service_name = lp_servicename(snum);
+       connection_struct conn;
        struct referral *ref = NULL;
-       BOOL ret = False;
  
+       ZERO_STRUCT(conn);
+
+       if (jn_remain <= 0) {
+               return 0;
+       }
+
        pstrcpy(connect_path,lp_pathname(snum));
 
-       if(*connect_path == '\0')
-               return False;
+       if(*connect_path == '\0') {
+               return 0;
+       }
 
        /*
         * Fake up a connection struct for the VFS layer.
         */
 
-       if (!create_conn_struct(conn, snum, connect_path))
-               return False;
+       if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
+               return 0;
+       }
 
        /* form a junction for the msdfs root - convention 
           DO NOT REMOVE THIS: NT clients will not work with us
           if this is not present
        */ 
-       pstrcpy(jucn[cnt].service_name, service_name);
+       fstrcpy(jucn[cnt].service_name, service_name);
        jucn[cnt].volume_name[0] = '\0';
        jucn[cnt].referral_count = 1;
 
-       ref = jucn[cnt].referral_list
-               = (struct referral*) malloc(sizeof(struct referral));
+       ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
        if (jucn[cnt].referral_list == NULL) {
-               DEBUG(0, ("Malloc failed!\n"));
+               DEBUG(0, ("talloc failed!\n"));
                goto out;
        }
 
@@ -973,47 +1201,99 @@ static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
        ref->ttl = REFERRAL_TTL;
        if (*lp_msdfs_proxy(snum) != '\0') {
                pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
-               *jn_count = ++cnt;
-               ret = True;
+               cnt++;
                goto out;
        }
-               
-       slprintf(ref->alternate_path, sizeof(pstring)-1,
-                "\\\\%s\\%s", local_machine, service_name);
+
+       pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
+                       get_local_machine_name(),
+                       service_name);
        cnt++;
-       
+
        /* Now enumerate all dfs links */
-       dirp = SMB_VFS_OPENDIR(conn, ".");
-       if(!dirp)
+       dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
+       if(!dirp) {
                goto out;
+       }
 
-       while((dname = vfs_readdirname(conn, dirp)) != NULL) {
-               if (is_msdfs_link(conn, dname, &(jucn[cnt].referral_list),
-                                 &(jucn[cnt].referral_count), NULL)) {
-                       pstrcpy(jucn[cnt].service_name, service_name);
-                       pstrcpy(jucn[cnt].volume_name, dname);
-                       cnt++;
+       while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
+               pstring link_target;
+               if (cnt >= jn_remain) {
+                       SMB_VFS_CLOSEDIR(&conn,dirp);
+                       DEBUG(2, ("ran out of MSDFS junction slots"));
+                       goto out;
+               }
+               if (is_msdfs_link(&conn, dname, link_target, NULL)) {
+                       if (parse_msdfs_symlink(ctx,
+                                       link_target,
+                                       &jucn[cnt].referral_list,
+                                       &jucn[cnt].referral_count)) {
+
+                               fstrcpy(jucn[cnt].service_name, service_name);
+                               pstrcpy(jucn[cnt].volume_name, dname);
+                               cnt++;
+                       }
                }
        }
        
-       SMB_VFS_CLOSEDIR(conn,dirp);
-       *jn_count = cnt;
+       SMB_VFS_CLOSEDIR(&conn,dirp);
+
 out:
-       talloc_destroy(conn->mem_ctx);
-       return ret;
+
+       conn_free_internal(&conn);
+       return cnt;
 }
 
-int enum_msdfs_links(struct junction_map* jucn)
+int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
 {
        int i=0;
+       int sharecount = 0;
        int jn_count = 0;
 
-       if(!lp_host_msdfs())
+       if(!lp_host_msdfs()) {
                return 0;
+       }
 
-       for(i=0;i < lp_numservices();i++) {
-               if(lp_msdfs_root(i)) 
-                       form_junctions(i,jucn,&jn_count);
+       /* Ensure all the usershares are loaded. */
+       become_root();
+       load_registry_shares();
+       sharecount = load_usershare_shares();
+       unbecome_root();
+
+       for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
+               if(lp_msdfs_root(i)) {
+                       jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
+               }
        }
        return jn_count;
 }
+
+/******************************************************************************
+ Core function to resolve a dfs pathname.
+******************************************************************************/
+
+NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       BOOL dummy;
+       if (dfs_pathnames) {
+               status = dfs_redirect(conn, name, False, &dummy);
+       }
+       return status;
+}
+
+/******************************************************************************
+ Core function to resolve a dfs pathname possibly containing a wildcard.
+ This function is identical to the above except for the BOOL param to
+ dfs_redirect but I need this to be separate so it's really clear when
+ we're allowing wildcards and when we're not. JRA.
+******************************************************************************/
+
+NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       if (dfs_pathnames) {
+               status = dfs_redirect(conn, name, True, ppath_contains_wcard);
+       }
+       return status;
+}