smbd/msdfs.c

説明を見る。
00001 /* 
00002    Unix SMB/Netbios implementation.
00003    Version 3.0
00004    MSDFS services for Samba
00005    Copyright (C) Shirish Kalele 2000
00006    Copyright (C) Jeremy Allison 2007
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012    
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017    
00018    You should have received a copy of the GNU General Public License
00019    along with this program; if not, write to the Free Software
00020    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022 */
00023 
00024 #define DBGC_CLASS DBGC_MSDFS
00025 #include "includes.h"
00026 
00027 extern uint32 global_client_caps;
00028 
00029 /**********************************************************************
00030  Parse a DFS pathname of the form \hostname\service\reqpath
00031  into the dfs_path structure.
00032  If POSIX pathnames is true, the pathname may also be of the
00033  form /hostname/service/reqpath.
00034  We cope with either here.
00035 
00036  If conn != NULL then ensure the provided service is
00037  the one pointed to by the connection.
00038 
00039  Unfortunately, due to broken clients who might set the
00040  SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then
00041  send a local path, we have to cope with that too....
00042 
00043  JRA.
00044 **********************************************************************/
00045 
00046 static NTSTATUS parse_dfs_path(connection_struct *conn,
00047                                 const char *pathname,
00048                                 BOOL allow_wcards,
00049                                 struct dfs_path *pdp,
00050                                 BOOL *ppath_contains_wcard)
00051 {
00052         pstring pathname_local;
00053         char *p,*temp, *servicename;
00054         NTSTATUS status = NT_STATUS_OK;
00055         char sepchar;
00056 
00057         ZERO_STRUCTP(pdp);
00058 
00059         pstrcpy(pathname_local,pathname);
00060         p = temp = pathname_local;
00061 
00062         pdp->posix_path = (lp_posix_pathnames() && *pathname == '/');
00063 
00064         sepchar = pdp->posix_path ? '/' : '\\';
00065 
00066         if (*pathname != sepchar) {
00067                 DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n",
00068                         pathname, sepchar ));
00069                 /*
00070                  * Possibly client sent a local path by mistake.
00071                  * Try and convert to a local path.
00072                  */
00073 
00074                 pdp->hostname[0] = '\0';
00075                 pdp->servicename[0] = '\0';
00076 
00077                 /* We've got no info about separators. */
00078                 pdp->posix_path = lp_posix_pathnames();
00079                 p = temp;
00080                 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
00081                         temp));
00082                 goto local_path;
00083         }
00084 
00085         trim_char(temp,sepchar,sepchar);
00086 
00087         DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
00088                 temp, sepchar));
00089 
00090         /* Now tokenize. */
00091         /* Parse out hostname. */
00092         p = strchr_m(temp,sepchar);
00093         if(p == NULL) {
00094                 DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n",
00095                         temp));
00096                 /*
00097                  * Possibly client sent a local path by mistake.
00098                  * Try and convert to a local path.
00099                  */
00100 
00101                 pdp->hostname[0] = '\0';
00102                 pdp->servicename[0] = '\0';
00103 
00104                 p = temp;
00105                 DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
00106                         temp));
00107                 goto local_path;
00108         }
00109         *p = '\0';
00110         fstrcpy(pdp->hostname,temp);
00111         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
00112 
00113         /* Parse out servicename. */
00114         servicename = p+1;
00115         p = strchr_m(servicename,sepchar);
00116         if (p) {
00117                 *p = '\0';
00118         }
00119 
00120         /* Is this really our servicename ? */
00121         if (conn && !( strequal(servicename, lp_servicename(SNUM(conn)))
00122                         || (strequal(servicename, HOMES_NAME)
00123                         && strequal(lp_servicename(SNUM(conn)),
00124                                 get_current_username()) )) ) {
00125 
00126                 DEBUG(10,("parse_dfs_path: %s is not our servicename\n",
00127                         servicename));
00128 
00129                 /*
00130                  * Possibly client sent a local path by mistake.
00131                  * Try and convert to a local path.
00132                  */
00133 
00134                 pdp->hostname[0] = '\0';
00135                 pdp->servicename[0] = '\0';
00136 
00137                 /* Repair the path - replace the sepchar's
00138                    we nulled out */
00139                 servicename--;
00140                 *servicename = sepchar;
00141                 if (p) {
00142                         *p = sepchar;
00143                 }
00144 
00145                 p = temp;
00146                 DEBUG(10,("parse_dfs_path: trying to convert %s "
00147                         "to a local path\n",
00148                         temp));
00149                 goto local_path;
00150         }
00151 
00152         fstrcpy(pdp->servicename,servicename);
00153 
00154         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
00155 
00156         if(p == NULL) {
00157                 pdp->reqpath[0] = '\0';
00158                 return NT_STATUS_OK;
00159         }
00160         p++;
00161 
00162   local_path:
00163 
00164         *ppath_contains_wcard = False;
00165 
00166         /* Rest is reqpath. */
00167         if (pdp->posix_path) {
00168                 status = check_path_syntax_posix(pdp->reqpath, p);
00169         } else {
00170                 if (allow_wcards) {
00171                         status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard);
00172                 } else {
00173                         status = check_path_syntax(pdp->reqpath, p);
00174                 }
00175         }
00176 
00177         if (!NT_STATUS_IS_OK(status)) {
00178                 DEBUG(10,("parse_dfs_path: '%s' failed with %s\n",
00179                         p, nt_errstr(status) ));
00180                 return status;
00181         }
00182 
00183         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
00184         return NT_STATUS_OK;
00185 }
00186 
00187 /********************************************************
00188  Fake up a connection struct for the VFS layer.
00189  Note this CHANGES CWD !!!! JRA.
00190 *********************************************************/
00191 
00192 static NTSTATUS create_conn_struct(connection_struct *conn, int snum, const char *path)
00193 {
00194         pstring connpath;
00195 
00196         ZERO_STRUCTP(conn);
00197 
00198         pstrcpy(connpath, path);
00199         pstring_sub(connpath , "%S", lp_servicename(snum));
00200 
00201         /* needed for smbd_vfs_init() */
00202 
00203         if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) {
00204                 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
00205                 return NT_STATUS_NO_MEMORY;
00206         }
00207 
00208         if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) {
00209                 DEBUG(0, ("TALLOC failed\n"));
00210                 return NT_STATUS_NO_MEMORY;
00211         }
00212 
00213         conn->params->service = snum;
00214 
00215         set_conn_connectpath(conn, connpath);
00216 
00217         if (!smbd_vfs_init(conn)) {
00218                 NTSTATUS status = map_nt_error_from_unix(errno);
00219                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
00220                 conn_free_internal(conn);
00221                 return status;
00222         }
00223 
00224         /*
00225          * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
00226          * share as the anonymous user. If we try to chdir as that user we will
00227          * fail.... WTF ? JRA.
00228          */
00229 
00230         if (vfs_ChDir(conn,conn->connectpath) != 0) {
00231                 NTSTATUS status = map_nt_error_from_unix(errno);
00232                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
00233                                         conn->connectpath, strerror(errno) ));
00234                 conn_free_internal(conn);
00235                 return status;
00236         }
00237 
00238         return NT_STATUS_OK;
00239 }
00240 
00241 /**********************************************************************
00242  Parse the contents of a symlink to verify if it is an msdfs referral
00243  A valid referral is of the form:
00244 
00245  msdfs:server1\share1,server2\share2
00246  msdfs:server1\share1\pathname,server2\share2\pathname
00247  msdfs:server1/share1,server2/share2
00248  msdfs:server1/share1/pathname,server2/share2/pathname.
00249 
00250  Note that the alternate paths returned here must be of the canonicalized
00251  form:
00252 
00253  \server\share or
00254  \server\share\path\to\file,
00255 
00256  even in posix path mode. This is because we have no knowledge if the
00257  server we're referring to understands posix paths.
00258  **********************************************************************/
00259 
00260 static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
00261                                 char *target,
00262                                 struct referral **preflist,
00263                                 int *refcount)
00264 {
00265         pstring temp;
00266         char *prot;
00267         char *alt_path[MAX_REFERRAL_COUNT];
00268         int count = 0, i;
00269         struct referral *reflist;
00270 
00271         pstrcpy(temp,target);
00272         prot = strtok(temp,":");
00273         if (!prot) {
00274                 DEBUG(0,("parse_msdfs_symlink: invalid path !\n"));
00275                 return False;
00276         }
00277 
00278         /* parse out the alternate paths */
00279         while((count<MAX_REFERRAL_COUNT) &&
00280               ((alt_path[count] = strtok(NULL,",")) != NULL)) {
00281                 count++;
00282         }
00283 
00284         DEBUG(10,("parse_msdfs_symlink: count=%d\n", count));
00285 
00286         if (count) {
00287                 reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count);
00288                 if(reflist == NULL) {
00289                         DEBUG(0,("parse_msdfs_symlink: talloc failed!\n"));
00290                         return False;
00291                 }
00292         } else {
00293                 reflist = *preflist = NULL;
00294         }
00295         
00296         for(i=0;i<count;i++) {
00297                 char *p;
00298 
00299                 /* Canonicalize link target. Replace all /'s in the path by a \ */
00300                 string_replace(alt_path[i], '/', '\\');
00301 
00302                 /* Remove leading '\\'s */
00303                 p = alt_path[i];
00304                 while (*p && (*p == '\\')) {
00305                         p++;
00306                 }
00307 
00308                 pstrcpy(reflist[i].alternate_path, "\\");
00309                 pstrcat(reflist[i].alternate_path, p);
00310 
00311                 reflist[i].proximity = 0;
00312                 reflist[i].ttl = REFERRAL_TTL;
00313                 DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
00314                 *refcount += 1;
00315         }
00316 
00317         return True;
00318 }
00319  
00320 /**********************************************************************
00321  Returns true if the unix path is a valid msdfs symlink and also
00322  returns the target string from inside the link.
00323 **********************************************************************/
00324 
00325 BOOL is_msdfs_link(connection_struct *conn,
00326                         const char *path,
00327                         pstring link_target,
00328                         SMB_STRUCT_STAT *sbufp)
00329 {
00330         SMB_STRUCT_STAT st;
00331         int referral_len = 0;
00332 
00333         if (sbufp == NULL) {
00334                 sbufp = &st;
00335         }
00336 
00337         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
00338                 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
00339                 return False;
00340         }
00341   
00342         if (!S_ISLNK(sbufp->st_mode)) {
00343                 DEBUG(5,("is_msdfs_link: %s is not a link.\n",path));
00344                 return False;
00345         }
00346 
00347         /* open the link and read it */
00348         referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1);
00349         if (referral_len == -1) {
00350                 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n",
00351                         path, strerror(errno)));
00352                 return False;
00353         }
00354         link_target[referral_len] = '\0';
00355 
00356         DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target));
00357 
00358         if (!strnequal(link_target, "msdfs:", 6)) {
00359                 return False;
00360         }
00361         return True;
00362 }
00363 
00364 /*****************************************************************
00365  Used by other functions to decide if a dfs path is remote,
00366  and to get the list of referred locations for that remote path.
00367  
00368  search_flag: For findfirsts, dfs links themselves are not
00369  redirected, but paths beyond the links are. For normal smb calls,
00370  even dfs links need to be redirected.
00371 
00372  consumedcntp: how much of the dfs path is being redirected. the client
00373  should try the remaining path on the redirected server.
00374 
00375  If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
00376  link redirect are in targetpath.
00377 *****************************************************************/
00378 
00379 static NTSTATUS dfs_path_lookup(connection_struct *conn,
00380                         const char *dfspath, /* Incoming complete dfs path */
00381                         const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */
00382                         BOOL search_flag, /* Called from a findfirst ? */
00383                         int *consumedcntp,
00384                         pstring targetpath)
00385 {
00386         char *p = NULL;
00387         char *q = NULL;
00388         SMB_STRUCT_STAT sbuf;
00389         NTSTATUS status;
00390         pstring localpath;
00391         pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */
00392 
00393         DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n",
00394                 conn->connectpath, pdp->reqpath));
00395 
00396         /* 
00397          * Note the unix path conversion here we're doing we can
00398          * throw away. We're looking for a symlink for a dfs
00399          * resolution, if we don't find it we'll do another
00400          * unix_convert later in the codepath.
00401          * If we needed to remember what we'd resolved in
00402          * dp->reqpath (as the original code did) we'd
00403          * pstrcpy(localhost, dp->reqpath) on any code
00404          * path below that returns True - but I don't
00405          * think this is needed. JRA.
00406          */
00407 
00408         pstrcpy(localpath, pdp->reqpath);
00409         status = unix_convert(conn, localpath, search_flag, NULL, &sbuf);
00410         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,
00411                                         NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
00412                 return status;
00413         }
00414 
00415         /* Optimization - check if we can redirect the whole path. */
00416 
00417         if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
00418                 if (search_flag) {
00419                         DEBUG(6,("dfs_path_lookup (FindFirst) No redirection "
00420                                  "for dfs link %s.\n", dfspath));
00421                         return NT_STATUS_OK;
00422                 }
00423 
00424                 DEBUG(6,("dfs_path_lookup: %s resolves to a "
00425                         "valid dfs link %s.\n", dfspath, targetpath));
00426 
00427                 if (consumedcntp) {
00428                         *consumedcntp = strlen(dfspath);
00429                 }
00430                 return NT_STATUS_PATH_NOT_COVERED;
00431         }
00432 
00433         /* Prepare to test only for '/' components in the given path,
00434          * so if a Windows path replace all '\\' characters with '/'.
00435          * For a POSIX DFS path we know all separators are already '/'. */
00436 
00437         pstrcpy(canon_dfspath, dfspath);
00438         if (!pdp->posix_path) {
00439                 string_replace(canon_dfspath, '\\', '/');
00440         }
00441 
00442         /*
00443          * localpath comes out of unix_convert, so it has
00444          * no trailing backslash. Make sure that canon_dfspath hasn't either.
00445          * Fix for bug #4860 from Jan Martin <Jan.Martin@rwedea.com>.
00446          */
00447 
00448         trim_char(canon_dfspath,0,'/');
00449 
00450         /*
00451          * Redirect if any component in the path is a link.
00452          * We do this by walking backwards through the 
00453          * local path, chopping off the last component
00454          * in both the local path and the canonicalized
00455          * DFS path. If we hit a DFS link then we're done.
00456          */
00457 
00458         p = strrchr_m(localpath, '/');
00459         if (consumedcntp) {
00460                 q = strrchr_m(canon_dfspath, '/');
00461         }
00462 
00463         while (p) {
00464                 *p = '\0';
00465                 if (q) {
00466                         *q = '\0';
00467                 }
00468 
00469                 if (is_msdfs_link(conn, localpath, targetpath, NULL)) {
00470                         DEBUG(4, ("dfs_path_lookup: Redirecting %s because "
00471                                 "parent %s is dfs link\n", dfspath, localpath));
00472 
00473                         if (consumedcntp) {
00474                                 *consumedcntp = strlen(canon_dfspath);
00475                                 DEBUG(10, ("dfs_path_lookup: Path consumed: %s "
00476                                         "(%d)\n", canon_dfspath, *consumedcntp));
00477                         }
00478 
00479                         return NT_STATUS_PATH_NOT_COVERED;
00480                 }
00481 
00482                 /* Step back on the filesystem. */
00483                 p = strrchr_m(localpath, '/');
00484 
00485                 if (consumedcntp) {
00486                         /* And in the canonicalized dfs path. */
00487                         q = strrchr_m(canon_dfspath, '/');
00488                 }
00489         }
00490 
00491         return NT_STATUS_OK;
00492 }
00493 
00494 /*****************************************************************
00495  Decides if a dfs pathname should be redirected or not.
00496  If not, the pathname is converted to a tcon-relative local unix path
00497 
00498  search_wcard_flag: this flag performs 2 functions bother related
00499  to searches.  See resolve_dfs_path() and parse_dfs_path_XX()
00500  for details.
00501 
00502  This function can return NT_STATUS_OK, meaning use the returned path as-is
00503  (mapped into a local path).
00504  or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or
00505  any other NT_STATUS error which is a genuine error to be
00506  returned to the client. 
00507 *****************************************************************/
00508 
00509 static NTSTATUS dfs_redirect( connection_struct *conn,
00510                         pstring dfs_path,
00511                         BOOL search_wcard_flag,
00512                         BOOL *ppath_contains_wcard)
00513 {
00514         NTSTATUS status;
00515         struct dfs_path dp;
00516         pstring targetpath;
00517         
00518         status = parse_dfs_path(conn, dfs_path, search_wcard_flag, &dp, ppath_contains_wcard);
00519         if (!NT_STATUS_IS_OK(status)) {
00520                 return status;
00521         }
00522 
00523         if (dp.reqpath[0] == '\0') {
00524                 pstrcpy(dfs_path, dp.reqpath);
00525                 DEBUG(5,("dfs_redirect: self-referral.\n"));
00526                 return NT_STATUS_OK;
00527         }
00528 
00529         /* If dfs pathname for a non-dfs share, convert to tcon-relative
00530            path and return OK */
00531 
00532         if (!lp_msdfs_root(SNUM(conn))) {
00533                 pstrcpy(dfs_path, dp.reqpath);
00534                 return NT_STATUS_OK;
00535         }
00536 
00537         /* If it looked like a local path (zero hostname/servicename)
00538          * just treat as a tcon-relative path. */ 
00539 
00540         if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') { 
00541                 pstrcpy(dfs_path, dp.reqpath);
00542                 return NT_STATUS_OK;
00543         }
00544 
00545         status = dfs_path_lookup(conn, dfs_path, &dp,
00546                         search_wcard_flag, NULL, targetpath);
00547         if (!NT_STATUS_IS_OK(status)) {
00548                 if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
00549                         DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path));
00550                 } else {
00551                         DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n",
00552                                 dfs_path, nt_errstr(status) ));
00553                 }
00554                 return status;
00555         }
00556 
00557         DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path));
00558 
00559         /* Form non-dfs tcon-relative path */
00560         pstrcpy(dfs_path, dp.reqpath);
00561 
00562         DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path));
00563         return NT_STATUS_OK;
00564 }
00565 
00566 /**********************************************************************
00567  Return a self referral.
00568 **********************************************************************/
00569 
00570 static NTSTATUS self_ref(TALLOC_CTX *ctx,
00571                         const char *dfs_path,
00572                         struct junction_map *jucn,
00573                         int *consumedcntp,
00574                         BOOL *self_referralp)
00575 {
00576         struct referral *ref;
00577 
00578         *self_referralp = True;
00579 
00580         jucn->referral_count = 1;
00581         if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
00582                 DEBUG(0,("self_ref: talloc failed for referral\n"));
00583                 return NT_STATUS_NO_MEMORY;
00584         }
00585 
00586         pstrcpy(ref->alternate_path,dfs_path);
00587         ref->proximity = 0;
00588         ref->ttl = REFERRAL_TTL;
00589         jucn->referral_list = ref;
00590         *consumedcntp = strlen(dfs_path);
00591         return NT_STATUS_OK;
00592 }
00593 
00594 /**********************************************************************
00595  Gets valid referrals for a dfs path and fills up the
00596  junction_map structure.
00597 **********************************************************************/
00598 
00599 NTSTATUS get_referred_path(TALLOC_CTX *ctx,
00600                         const char *dfs_path,
00601                         struct junction_map *jucn,
00602                         int *consumedcntp,
00603                         BOOL *self_referralp)
00604 {
00605         struct connection_struct conns;
00606         struct connection_struct *conn = &conns;
00607         struct dfs_path dp;
00608         pstring conn_path;
00609         pstring targetpath;
00610         int snum;
00611         NTSTATUS status = NT_STATUS_NOT_FOUND;
00612         BOOL dummy;
00613 
00614         ZERO_STRUCT(conns);
00615 
00616         *self_referralp = False;
00617 
00618         status = parse_dfs_path(NULL, dfs_path, False, &dp, &dummy);
00619         if (!NT_STATUS_IS_OK(status)) {
00620                 return status;
00621         }
00622 
00623         fstrcpy(jucn->service_name, dp.servicename);
00624         pstrcpy(jucn->volume_name, dp.reqpath);
00625 
00626         /* Verify the share is a dfs root */
00627         snum = lp_servicenumber(jucn->service_name);
00628         if(snum < 0) {
00629                 if ((snum = find_service(jucn->service_name)) < 0) {
00630                         return NT_STATUS_NOT_FOUND;
00631                 }
00632         }
00633 
00634         if (!lp_msdfs_root(snum)) {
00635                 DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n",
00636                          dp.servicename, dfs_path));
00637                 return NT_STATUS_NOT_FOUND;
00638         }
00639 
00640         /*
00641          * Self referrals are tested with a anonymous IPC connection and
00642          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
00643          * to an empty string). create_conn_struct cd's into the directory and will
00644          * fail if it cannot (as the anonymous user). Cope with this.
00645          */
00646 
00647         if (dp.reqpath[0] == '\0') {
00648                 struct referral *ref;
00649 
00650                 if (*lp_msdfs_proxy(snum) == '\0') {
00651                         return self_ref(ctx,
00652                                         dfs_path,
00653                                         jucn,
00654                                         consumedcntp,
00655                                         self_referralp);
00656                 }
00657 
00658                 /* 
00659                  * It's an msdfs proxy share. Redirect to
00660                  * the configured target share.
00661                  */
00662 
00663                 jucn->referral_count = 1;
00664                 if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) {
00665                         DEBUG(0, ("malloc failed for referral\n"));
00666                         return NT_STATUS_NO_MEMORY;
00667                 }
00668 
00669                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
00670                 if (dp.reqpath[0] != '\0') {
00671                         pstrcat(ref->alternate_path, dp.reqpath);
00672                 }
00673                 ref->proximity = 0;
00674                 ref->ttl = REFERRAL_TTL;
00675                 jucn->referral_list = ref;
00676                 *consumedcntp = strlen(dfs_path);
00677                 return NT_STATUS_OK;
00678         }
00679 
00680         pstrcpy(conn_path, lp_pathname(snum));
00681         status = create_conn_struct(conn, snum, conn_path);
00682         if (!NT_STATUS_IS_OK(status)) {
00683                 return status;
00684         }
00685 
00686         /* If this is a DFS path dfs_lookup should return
00687          * NT_STATUS_PATH_NOT_COVERED. */
00688 
00689         status = dfs_path_lookup(conn, dfs_path, &dp,
00690                         False, consumedcntp, targetpath);
00691 
00692         if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
00693                 DEBUG(3,("get_referred_path: No valid referrals for path %s\n",
00694                         dfs_path));
00695                 conn_free_internal(conn);
00696                 return status;
00697         }
00698 
00699         /* We know this is a valid dfs link. Parse the targetpath. */
00700         if (!parse_msdfs_symlink(ctx, targetpath,
00701                                 &jucn->referral_list,
00702                                 &jucn->referral_count)) {
00703                 DEBUG(3,("get_referred_path: failed to parse symlink "
00704                         "target %s\n", targetpath ));
00705                 conn_free_internal(conn);
00706                 return NT_STATUS_NOT_FOUND;
00707         }
00708 
00709         conn_free_internal(conn);
00710         return NT_STATUS_OK;
00711 }
00712 
00713 static int setup_ver2_dfs_referral(const char *pathname,
00714                                 char **ppdata, 
00715                                 struct junction_map *junction,
00716                                 int consumedcnt,
00717                                 BOOL self_referral)
00718 {
00719         char* pdata = *ppdata;
00720 
00721         unsigned char uni_requestedpath[1024];
00722         int uni_reqpathoffset1,uni_reqpathoffset2;
00723         int uni_curroffset;
00724         int requestedpathlen=0;
00725         int offset;
00726         int reply_size = 0;
00727         int i=0;
00728 
00729         DEBUG(10,("Setting up version2 referral\nRequested path:\n"));
00730 
00731         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
00732                                        STR_TERMINATE);
00733 
00734         if (DEBUGLVL(10)) {
00735             dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
00736         }
00737 
00738         DEBUG(10,("ref count = %u\n",junction->referral_count));
00739 
00740         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
00741                         VERSION2_REFERRAL_SIZE * junction->referral_count;
00742 
00743         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
00744 
00745         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
00746 
00747         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
00748                                         2 * requestedpathlen;
00749         DEBUG(10,("reply_size: %u\n",reply_size));
00750 
00751         /* add up the unicode lengths of all the referral paths */
00752         for(i=0;i<junction->referral_count;i++) {
00753                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
00754                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
00755         }
00756 
00757         DEBUG(10,("reply_size = %u\n",reply_size));
00758         /* add the unexplained 0x16 bytes */
00759         reply_size += 0x16;
00760 
00761         pdata = (char *)SMB_REALLOC(pdata,reply_size);
00762         if(pdata == NULL) {
00763                 DEBUG(0,("Realloc failed!\n"));
00764                 return -1;
00765         }
00766         *ppdata = pdata;
00767 
00768         /* copy in the dfs requested paths.. required for offset calculations */
00769         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
00770         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
00771 
00772         /* create the header */
00773         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
00774         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
00775         if(self_referral) {
00776                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
00777         } else {
00778                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
00779         }
00780 
00781         offset = 8;
00782         /* add the referral elements */
00783         for(i=0;i<junction->referral_count;i++) {
00784                 struct referral* ref = &junction->referral_list[i];
00785                 int unilen;
00786 
00787                 SSVAL(pdata,offset,2); /* version 2 */
00788                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
00789                 if(self_referral) {
00790                         SSVAL(pdata,offset+4,1);
00791                 } else {
00792                         SSVAL(pdata,offset+4,0);
00793                 }
00794                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
00795                 SIVAL(pdata,offset+8,ref->proximity);
00796                 SIVAL(pdata,offset+12,ref->ttl);
00797 
00798                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
00799                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
00800                 /* copy referred path into current offset */
00801                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
00802                                      sizeof(pstring), STR_UNICODE);
00803 
00804                 SSVAL(pdata,offset+20,uni_curroffset-offset);
00805 
00806                 uni_curroffset += unilen;
00807                 offset += VERSION2_REFERRAL_SIZE;
00808         }
00809         /* add in the unexplained 22 (0x16) bytes at the end */
00810         memset(pdata+uni_curroffset,'\0',0x16);
00811         return reply_size;
00812 }
00813 
00814 static int setup_ver3_dfs_referral(const char *pathname,
00815                                 char **ppdata, 
00816                                 struct junction_map *junction,
00817                                 int consumedcnt,
00818                                 BOOL self_referral)
00819 {
00820         char* pdata = *ppdata;
00821 
00822         unsigned char uni_reqpath[1024];
00823         int uni_reqpathoffset1, uni_reqpathoffset2;
00824         int uni_curroffset;
00825         int reply_size = 0;
00826 
00827         int reqpathlen = 0;
00828         int offset,i=0;
00829         
00830         DEBUG(10,("setting up version3 referral\n"));
00831 
00832         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
00833         
00834         if (DEBUGLVL(10)) {
00835             dump_data(0, (char *) uni_reqpath,reqpathlen);
00836         }
00837 
00838         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
00839         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
00840         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
00841 
00842         for(i=0;i<junction->referral_count;i++) {
00843                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
00844                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
00845         }
00846 
00847         pdata = (char *)SMB_REALLOC(pdata,reply_size);
00848         if(pdata == NULL) {
00849                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
00850                 return -1;
00851         }
00852         *ppdata = pdata;
00853 
00854         /* create the header */
00855         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
00856         SSVAL(pdata,2,junction->referral_count); /* number of referral */
00857         if(self_referral) {
00858                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
00859         } else {
00860                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
00861         }
00862         
00863         /* copy in the reqpaths */
00864         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
00865         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
00866         
00867         offset = 8;
00868         for(i=0;i<junction->referral_count;i++) {
00869                 struct referral* ref = &(junction->referral_list[i]);
00870                 int unilen;
00871 
00872                 SSVAL(pdata,offset,3); /* version 3 */
00873                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
00874                 if(self_referral) {
00875                         SSVAL(pdata,offset+4,1);
00876                 } else {
00877                         SSVAL(pdata,offset+4,0);
00878                 }
00879 
00880                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
00881                 SIVAL(pdata,offset+8,ref->ttl);
00882             
00883                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
00884                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
00885                 /* copy referred path into current offset */
00886                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
00887                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
00888                 SSVAL(pdata,offset+16,uni_curroffset-offset);
00889                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
00890                 memset(pdata+offset+18,'\0',16);
00891 
00892                 uni_curroffset += unilen;
00893                 offset += VERSION3_REFERRAL_SIZE;
00894         }
00895         return reply_size;
00896 }
00897 
00898 /******************************************************************
00899  Set up the DFS referral for the dfs pathname. This call returns
00900  the amount of the path covered by this server, and where the
00901  client should be redirected to. This is the meat of the
00902  TRANS2_GET_DFS_REFERRAL call.
00903 ******************************************************************/
00904 
00905 int setup_dfs_referral(connection_struct *orig_conn,
00906                         const char *dfs_path,
00907                         int max_referral_level,
00908                         char **ppdata, NTSTATUS *pstatus)
00909 {
00910         struct junction_map junction;
00911         int consumedcnt = 0;
00912         BOOL self_referral = False;
00913         int reply_size = 0;
00914         char *pathnamep = NULL;
00915         pstring local_dfs_path;
00916         TALLOC_CTX *ctx;
00917 
00918         if (!(ctx=talloc_init("setup_dfs_referral"))) {
00919                 *pstatus = NT_STATUS_NO_MEMORY;
00920                 return -1;
00921         }
00922 
00923         ZERO_STRUCT(junction);
00924 
00925         /* get the junction entry */
00926         if (!dfs_path) {
00927                 talloc_destroy(ctx);
00928                 *pstatus = NT_STATUS_NOT_FOUND;
00929                 return -1;
00930         }
00931 
00932         /* 
00933          * Trim pathname sent by client so it begins with only one backslash.
00934          * Two backslashes confuse some dfs clients
00935          */
00936 
00937         pstrcpy(local_dfs_path, dfs_path);
00938         pathnamep = local_dfs_path;
00939         while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) {
00940                 pathnamep++;
00941         }
00942 
00943         /* The following call can change cwd. */
00944         *pstatus = get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral);
00945         if (!NT_STATUS_IS_OK(*pstatus)) {
00946                 vfs_ChDir(orig_conn,orig_conn->connectpath);
00947                 talloc_destroy(ctx);
00948                 return -1;
00949         }
00950         vfs_ChDir(orig_conn,orig_conn->connectpath);
00951         
00952         if (!self_referral) {
00953                 pathnamep[consumedcnt] = '\0';
00954 
00955                 if( DEBUGLVL( 3 ) ) {
00956                         int i=0;
00957                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
00958                         for(i=0;i<junction.referral_count;i++)
00959                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
00960                         dbgtext(".\n");
00961                 }
00962         }
00963 
00964         /* create the referral depeding on version */
00965         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
00966 
00967         if (max_referral_level < 2) {
00968                 max_referral_level = 2;
00969         }
00970         if (max_referral_level > 3) {
00971                 max_referral_level = 3;
00972         }
00973 
00974         switch(max_referral_level) {
00975         case 2:
00976                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
00977                                                      consumedcnt, self_referral);
00978                 break;
00979         case 3:
00980                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
00981                                                      consumedcnt, self_referral);
00982                 break;
00983         default:
00984                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
00985                 talloc_destroy(ctx);
00986                 *pstatus = NT_STATUS_INVALID_LEVEL;
00987                 return -1;
00988         }
00989       
00990         if (DEBUGLVL(10)) {
00991                 DEBUGADD(0,("DFS Referral pdata:\n"));
00992                 dump_data(0,*ppdata,reply_size);
00993         }
00994 
00995         talloc_destroy(ctx);
00996         *pstatus = NT_STATUS_OK;
00997         return reply_size;
00998 }
00999 
01000 /**********************************************************************
01001  The following functions are called by the NETDFS RPC pipe functions
01002  **********************************************************************/
01003 
01004 /*********************************************************************
01005  Creates a junction structure from a DFS pathname
01006 **********************************************************************/
01007 
01008 BOOL create_junction(const char *dfs_path, struct junction_map *jucn)
01009 {
01010         int snum;
01011         BOOL dummy;
01012         struct dfs_path dp;
01013  
01014         NTSTATUS status = parse_dfs_path(NULL, dfs_path, False, &dp, &dummy);
01015 
01016         if (!NT_STATUS_IS_OK(status)) {
01017                 return False;
01018         }
01019 
01020         /* check if path is dfs : validate first token */
01021         if (!is_myname_or_ipaddr(dp.hostname)) {
01022                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
01023                         dp.hostname, dfs_path));
01024                 return False;
01025         }
01026 
01027         /* Check for a non-DFS share */
01028         snum = lp_servicenumber(dp.servicename);
01029 
01030         if(snum < 0 || !lp_msdfs_root(snum)) {
01031                 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
01032                         dp.servicename));
01033                 return False;
01034         }
01035 
01036         fstrcpy(jucn->service_name,dp.servicename);
01037         pstrcpy(jucn->volume_name,dp.reqpath);
01038         pstrcpy(jucn->comment, lp_comment(snum));
01039         return True;
01040 }
01041 
01042 /**********************************************************************
01043  Forms a valid Unix pathname from the junction 
01044  **********************************************************************/
01045 
01046 static BOOL junction_to_local_path(struct junction_map *jucn,
01047                                 char *path,
01048                                 int max_pathlen,
01049                                 connection_struct *conn_out)
01050 {
01051         int snum;
01052         pstring conn_path;
01053 
01054         snum = lp_servicenumber(jucn->service_name);
01055         if(snum < 0) {
01056                 return False;
01057         }
01058 
01059         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
01060         safe_strcat(path, "/", max_pathlen-1);
01061         safe_strcat(path, jucn->volume_name, max_pathlen-1);
01062 
01063         pstrcpy(conn_path, lp_pathname(snum));
01064         if (!NT_STATUS_IS_OK(create_conn_struct(conn_out, snum, conn_path))) {
01065                 return False;
01066         }
01067 
01068         return True;
01069 }
01070 
01071 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
01072 {
01073         pstring path;
01074         pstring msdfs_link;
01075         connection_struct conns;
01076         connection_struct *conn = &conns;
01077         int i=0;
01078         BOOL insert_comma = False;
01079         BOOL ret = False;
01080 
01081         ZERO_STRUCT(conns);
01082 
01083         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
01084                 return False;
01085         }
01086   
01087         /* Form the msdfs_link contents */
01088         pstrcpy(msdfs_link, "msdfs:");
01089         for(i=0; i<jucn->referral_count; i++) {
01090                 char* refpath = jucn->referral_list[i].alternate_path;
01091       
01092                 /* Alternate paths always use Windows separators. */
01093                 trim_char(refpath, '\\', '\\');
01094                 if(*refpath == '\0') {
01095                         if (i == 0) {
01096                                 insert_comma = False;
01097                         }
01098                         continue;
01099                 }
01100                 if (i > 0 && insert_comma) {
01101                         pstrcat(msdfs_link, ",");
01102                 }
01103 
01104                 pstrcat(msdfs_link, refpath);
01105                 if (!insert_comma) {
01106                         insert_comma = True;
01107                 }
01108         }
01109 
01110         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n",
01111                 path, msdfs_link));
01112 
01113         if(exists) {
01114                 if(SMB_VFS_UNLINK(conn,path)!=0) {
01115                         goto out;
01116                 }
01117         }
01118 
01119         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
01120                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
01121                                 path, msdfs_link, strerror(errno)));
01122                 goto out;
01123         }
01124         
01125         
01126         ret = True;
01127 
01128 out:
01129 
01130         conn_free_internal(conn);
01131         return ret;
01132 }
01133 
01134 BOOL remove_msdfs_link(struct junction_map *jucn)
01135 {
01136         pstring path;
01137         connection_struct conns;
01138         connection_struct *conn = &conns;
01139         BOOL ret = False;
01140 
01141         ZERO_STRUCT(conns);
01142 
01143         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
01144                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
01145                         ret = True;
01146                 }
01147                 talloc_destroy( conn->mem_ctx );
01148         }
01149 
01150         conn_free_internal(conn);
01151         return ret;
01152 }
01153 
01154 static int form_junctions(TALLOC_CTX *ctx,
01155                                 int snum,
01156                                 struct junction_map *jucn,
01157                                 int jn_remain)
01158 {
01159         int cnt = 0;
01160         SMB_STRUCT_DIR *dirp;
01161         char *dname;
01162         pstring connect_path;
01163         char *service_name = lp_servicename(snum);
01164         connection_struct conn;
01165         struct referral *ref = NULL;
01166  
01167         ZERO_STRUCT(conn);
01168 
01169         if (jn_remain <= 0) {
01170                 return 0;
01171         }
01172 
01173         pstrcpy(connect_path,lp_pathname(snum));
01174 
01175         if(*connect_path == '\0') {
01176                 return 0;
01177         }
01178 
01179         /*
01180          * Fake up a connection struct for the VFS layer.
01181          */
01182 
01183         if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
01184                 return 0;
01185         }
01186 
01187         /* form a junction for the msdfs root - convention 
01188            DO NOT REMOVE THIS: NT clients will not work with us
01189            if this is not present
01190         */ 
01191         fstrcpy(jucn[cnt].service_name, service_name);
01192         jucn[cnt].volume_name[0] = '\0';
01193         jucn[cnt].referral_count = 1;
01194 
01195         ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral);
01196         if (jucn[cnt].referral_list == NULL) {
01197                 DEBUG(0, ("talloc failed!\n"));
01198                 goto out;
01199         }
01200 
01201         ref->proximity = 0;
01202         ref->ttl = REFERRAL_TTL;
01203         if (*lp_msdfs_proxy(snum) != '\0') {
01204                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
01205                 cnt++;
01206                 goto out;
01207         }
01208 
01209         pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
01210                         get_local_machine_name(),
01211                         service_name);
01212         cnt++;
01213 
01214         /* Now enumerate all dfs links */
01215         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
01216         if(!dirp) {
01217                 goto out;
01218         }
01219 
01220         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
01221                 pstring link_target;
01222                 if (cnt >= jn_remain) {
01223                         SMB_VFS_CLOSEDIR(&conn,dirp);
01224                         DEBUG(2, ("ran out of MSDFS junction slots"));
01225                         goto out;
01226                 }
01227                 if (is_msdfs_link(&conn, dname, link_target, NULL)) {
01228                         if (parse_msdfs_symlink(ctx,
01229                                         link_target,
01230                                         &jucn[cnt].referral_list,
01231                                         &jucn[cnt].referral_count)) {
01232 
01233                                 fstrcpy(jucn[cnt].service_name, service_name);
01234                                 pstrcpy(jucn[cnt].volume_name, dname);
01235                                 cnt++;
01236                         }
01237                 }
01238         }
01239         
01240         SMB_VFS_CLOSEDIR(&conn,dirp);
01241 
01242 out:
01243 
01244         conn_free_internal(&conn);
01245         return cnt;
01246 }
01247 
01248 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
01249 {
01250         int i=0;
01251         int sharecount = 0;
01252         int jn_count = 0;
01253 
01254         if(!lp_host_msdfs()) {
01255                 return 0;
01256         }
01257 
01258         /* Ensure all the usershares are loaded. */
01259         become_root();
01260         sharecount = load_usershare_shares();
01261         unbecome_root();
01262 
01263         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
01264                 if(lp_msdfs_root(i)) {
01265                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
01266                 }
01267         }
01268         return jn_count;
01269 }
01270 
01271 /******************************************************************************
01272  Core function to resolve a dfs pathname.
01273 ******************************************************************************/
01274 
01275 NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name)
01276 {
01277         NTSTATUS status = NT_STATUS_OK;
01278         BOOL dummy;
01279         if (dfs_pathnames) {
01280                 status = dfs_redirect(conn, name, False, &dummy);
01281         }
01282         return status;
01283 }
01284 
01285 /******************************************************************************
01286  Core function to resolve a dfs pathname possibly containing a wildcard.
01287  This function is identical to the above except for the BOOL param to
01288  dfs_redirect but I need this to be separate so it's really clear when
01289  we're allowing wildcards and when we're not. JRA.
01290 ******************************************************************************/
01291 
01292 NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard)
01293 {
01294         NTSTATUS status = NT_STATUS_OK;
01295         if (dfs_pathnames) {
01296                 status = dfs_redirect(conn, name, True, ppath_contains_wcard);
01297         }
01298         return status;
01299 }

Sambaに対してSat Aug 29 21:23:24 2009に生成されました。  doxygen 1.4.7