/*
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/>.
*/
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(const 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;
- }
- *p = '\0';
- pstrcpy(pdp->hostname,temp);
- DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
+ 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.
+ */
- /* parse out servicename */
- temp = p+1;
- p = strchr_m(temp,'\\');
- if(p == NULL) {
- pstrcpy(pdp->servicename,temp);
- pdp->reqpath[0] = '\0';
- return True;
+ 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;
}
- *p = '\0';
- pstrcpy(pdp->servicename,temp);
- DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
- /* rest is reqpath */
- check_path_syntax(pdp->reqpath, p+1);
+ trim_char(temp,sepchar,sepchar);
- DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
- return True;
-}
+ DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n",
+ temp, sepchar));
-/**********************************************************************
- Parse the pathname of the form /hostname/service/reqpath
- into the dfs_path structure
- This code is dependent on the fact that check_path_syntax() will
- convert '\\' characters to '/'.
- When POSIX pathnames have been selected this doesn't happen, so we
- must look for the unaltered separator of '\\' instead of the modified '/'.
- JRA.
- **********************************************************************/
+ /* 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.
+ */
-static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path *pdp, BOOL allow_wcards)
-{
- pstring pathname_local;
- char *p,*temp;
- const char sepchar = lp_posix_pathnames() ? '\\' : '/';
+ pdp->hostname[0] = '\0';
+ pdp->servicename[0] = '\0';
- pstrcpy(pathname_local,pathname);
- p = temp = pathname_local;
+ p = temp;
+ DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n",
+ temp));
+ goto local_path;
+ }
+ *p = '\0';
+ fstrcpy(pdp->hostname,temp);
+ DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
- ZERO_STRUCTP(pdp);
+ /* 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.
+ */
- trim_char(temp,sepchar,sepchar);
- DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
+ pdp->hostname[0] = '\0';
+ pdp->servicename[0] = '\0';
- /* now tokenize */
- /* parse out hostname */
- p = strchr_m(temp,sepchar);
- if(p == NULL) {
- return False;
+ 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);
- DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
- /* parse out servicename */
+ /* Parse out servicename. */
temp = p+1;
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);
- DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
+ fstrcpy(pdp->servicename,temp);
+ DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
+
+ p++;
+
+ local_path:
- /* rest is reqpath */
- if (allow_wcards) {
- BOOL path_contains_wcard;
- check_path_syntax_wcard(pdp->reqpath, p+1, &path_contains_wcard);
+ *ppath_contains_wcard = False;
+
+ pstrcpy(pdp->reqpath, p);
+
+ /* Rest is reqpath. */
+ if (pdp->posix_path) {
+ status = check_path_syntax_posix(pdp->reqpath);
} else {
- check_path_syntax(pdp->reqpath, p+1);
+ if (allow_wcards) {
+ status = check_path_syntax_wcard(pdp->reqpath, ppath_contains_wcard);
+ } else {
+ status = check_path_syntax(pdp->reqpath);
+ }
}
- DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
- return True;
+ 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_dfs_path: rest of the path: %s\n",pdp->reqpath));
+ return NT_STATUS_OK;
}
/********************************************************
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;
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->params = TALLOC_P(conn->mem_ctx, struct share_params))) {
+ 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 False;
+ 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"));
conn_free_internal(conn);
- return False;
+ return status;
}
/*
*/
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) ));
conn_free_internal(conn);
- return False;
+ 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
- talloc CTX can be NULL here if preflist and refcount pointers are null.
+ 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(TALLOC_CTX *ctx, char *buf, struct referral **preflist, int *refcount)
+static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx,
+ char *target,
+ struct referral **preflist,
+ int *refcount)
{
pstring temp;
char *prot;
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;
- }
-
- if (!ctx) {
- DEBUG(0,("parse_symlink: logic error. TALLOC_CTX should not be null.\n"));
- return True;
- }
-
/* parse out the alternate paths */
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 = TALLOC_ARRAY(ctx, struct referral, count);
- if(reflist == NULL) {
- DEBUG(0,("parse_symlink: talloc 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];
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));
- }
-
- if(refcount) {
- *refcount = count;
+ DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path));
+ *refcount += 1;
}
return True;
}
/**********************************************************************
- Returns true if the unix path is a valid msdfs symlink
- talloc CTX can be NULL here if reflistp and refcnt pointers are null.
- **********************************************************************/
+ 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(TALLOC_CTX *ctx, 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) {
sbufp = &st;
}
return False;
}
- if (S_ISLNK(sbufp->st_mode)) {
- /* open the link and read it */
- referral_len = SMB_VFS_READLINK(conn, path, referral, sizeof(pstring)-1);
- 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(ctx, 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.
-
-TALLOC_CTX can be NULL here if struct referral **reflistpp, int *refcntp
-are also NULL.
+ If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs
+ link redirect are in targetpath.
*****************************************************************/
-static BOOL resolve_dfs_path(TALLOC_CTX *ctx, const char *dfspath,
- struct dfs_path *dp,
- connection_struct *conn, BOOL search_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;
-
- if (!dp || !conn) {
- DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
- return False;
- }
-
- if (!ctx && (reflistpp || refcntp)) {
- DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n"));
- }
+ 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->reqpath[0] == '\0') {
- if (self_referralp) {
- DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
- *self_referralp = True;
- }
- 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;
}
- DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
-
- unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
- /* JRA... should we strlower the last component here.... ? */
- pstrcpy(localpath, dp->reqpath);
+ /* Optimization - check if we can redirect the whole path. */
- /* check if need to redirect */
- if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
- if ( search_flag ) {
- DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
+ 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 False;
+ return NT_STATUS_OK;
}
- DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath));
+ DEBUG(6,("dfs_path_lookup: %s resolves to a "
+ "valid dfs link %s.\n", dfspath, targetpath));
+
if (consumedcntp) {
*consumedcntp = strlen(dfspath);
}
- return True;
+ return NT_STATUS_PATH_NOT_COVERED;
+ }
+
+ /* 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 '/'. */
+
+ pstrcpy(canon_dfspath, dfspath);
+ if (!pdp->posix_path) {
+ string_replace(canon_dfspath, '\\', '/');
+ }
+
+ /*
+ * 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(ctx, 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
-
- search_wcard_flag: this flag performs 2 functions bother related
- to searches. See resolve_dfs_path() and parse_processed_dfs_path()
- for details.
+ 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 search_wcard_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;
}
- parse_processed_dfs_path(pathname, &dp, search_wcard_flag);
+ if (dp.reqpath[0] == '\0') {
+ pstrcpy(dfs_path, dp.reqpath);
+ DEBUG(5,("dfs_redirect: self-referral.\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* 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)))
- || ( strequal(dp.servicename, HOMES_NAME)
- && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) )
- {
- 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 (resolve_dfs_path(NULL, pathname, &dp, conn, search_wcard_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 (!( 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;
}
- /* never reached */
+ 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;
+ }
+
+ 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(TALLOC_CTX *ctx, const 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 = TALLOC_P(ctx, 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;
}
/**********************************************************************
junction_map structure.
**********************************************************************/
-BOOL get_referred_path(TALLOC_CTX *ctx, const 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 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 (!is_myname_or_ipaddr(dp.hostname)) {
DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
- dp.hostname, pathname));
- return False;
+ 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;
+ 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;
}
/*
*/
if (dp.reqpath[0] == '\0') {
-
- struct referral* ref;
+ struct referral *ref;
if (*lp_msdfs_proxy(snum) == '\0') {
- return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp);
+ 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 = TALLOC_P(ctx, 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));
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(ctx, 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 self_referral, fill up the junction map */
- if (*self_referralp) {
- if (self_ref(ctx, pathname, jucn, consumedcntp, self_referralp) == False) {
- 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;
}
-
- ret = True;
-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;
+ }
conn_free_internal(conn);
- return ret;
+ 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;
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, sizeof(pstring),
STR_TERMINATE);
if (DEBUGLVL(10)) {
- dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
+ dump_data(0, uni_requestedpath,requestedpathlen);
}
DEBUG(10,("ref count = %u\n",junction->referral_count));
pdata = (char *)SMB_REALLOC(pdata,reply_size);
if(pdata == NULL) {
- DEBUG(0,("malloc failed for Realloc!\n"));
+ DEBUG(0,("Realloc failed!\n"));
return -1;
}
*ppdata = pdata;
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;
reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
if (DEBUGLVL(10)) {
- dump_data(0, (char *) uni_reqpath,reqpathlen);
+ dump_data(0, uni_reqpath,reqpathlen);
}
uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
}
/******************************************************************
- Set up the Dfs referral for the dfs pathname
+ 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, char *pathname, int max_referral_level, char **ppdata)
+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(ctx, 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;
/* 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:
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;
}
if (DEBUGLVL(10)) {
DEBUGADD(0,("DFS Referral pdata:\n"));
- dump_data(0,*ppdata,reply_size);
+ dump_data(0,(uint8 *)*ppdata,reply_size);
}
talloc_destroy(ctx);
+ *pstatus = NT_STATUS_OK;
return reply_size;
}
**********************************************************************/
/*********************************************************************
- Creates a junction structure from a Dfs pathname
+ Creates a junction structure from a DFS pathname
**********************************************************************/
-BOOL create_junction(const char *pathname, struct junction_map *jucn)
+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);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return False;
+ }
- /* check if path is dfs : validate first token */
+ /* 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, pathname));
+ dp.hostname, dfs_path));
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));
+ 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;
}
- pstrcpy(jucn->service_name,dp.servicename);
+ fstrcpy(jucn->service_name,dp.servicename);
pstrcpy(jucn->volume_name,dp.reqpath);
- pstrcpy(jucn->comment, lp_comment(lp_servicenumber(dp.servicename)));
+ 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) {
return False;
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 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) {
}
}
- 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) {
ret = True;
-
+
out:
conn_free_internal(conn);
return ret;
}
-static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
+static int form_junctions(TALLOC_CTX *ctx,
+ int snum,
+ struct junction_map *jucn,
+ int jn_remain)
{
int cnt = 0;
SMB_STRUCT_DIR *dirp;
- char* dname;
+ char *dname;
pstring connect_path;
- char* service_name = lp_servicename(snum);
+ char *service_name = lp_servicename(snum);
connection_struct conn;
struct referral *ref = NULL;
* Fake up a connection struct for the VFS layer.
*/
- if (!create_conn_struct(&conn, snum, connect_path)) {
+ if (!NT_STATUS_IS_OK(create_conn_struct(&conn, snum, connect_path))) {
return 0;
}
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 = TALLOC_P(ctx, 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;
}
cnt++;
goto out;
}
-
- slprintf(ref->alternate_path, sizeof(pstring)-1,
- "\\\\%s\\%s", get_local_machine_name(), service_name);
+
+ pstr_sprintf(ref->alternate_path, "\\\\%s\\%s",
+ get_local_machine_name(),
+ service_name);
cnt++;
/* Now enumerate all dfs links */
}
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(ctx, &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++;
+ 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++;
+ }
}
}
}
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;
+}