From 24cdd7c73389c9eed981313973df2c3595222781 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 12 Mar 2007 17:55:24 +0000 Subject: r21800: Check-in the DFS rewrite. I am still testing this but it works from smbclient and Windows, and I am promising to support and fix both client and server code moving forward. Still need to test the RPC admin support but I haven't changed that code. Jeremy. (This used to be commit 7a7862c01d07796ef206b255c676ad7dc2cc42fc) --- source3/libsmb/clidfs.c | 288 +++++++++++++++++++++++++----------------------- 1 file changed, 148 insertions(+), 140 deletions(-) (limited to 'source3/libsmb/clidfs.c') diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index e2cfd12114..4009b98b41 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -25,7 +25,7 @@ /******************************************************************** Important point. - DFS paths are of the form \server\share\ (the \ characters + DFS paths are *always* of the form \server\share\ (the \ characters are not C escaped here). - but if we're using POSIX paths then may contain @@ -205,14 +205,14 @@ static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt ) if ( p ) { pstrcpy( p->mount, mnt ); - clean_name( p->mount ); + clean_name(p->mount); } } /**************************************************************************** ****************************************************************************/ -const char * cli_cm_get_mntpoint( struct cli_state *c ) +const char *cli_cm_get_mntpoint( struct cli_state *c ) { struct client_connection *p; int i; @@ -232,8 +232,9 @@ const char * cli_cm_get_mntpoint( struct cli_state *c ) Add a new connection to the list ********************************************************************/ -static struct cli_state* cli_cm_connect( const char *server, const char *share, - BOOL show_hdr ) +static struct cli_state *cli_cm_connect( const char *server, + const char *share, + BOOL show_hdr) { struct client_connection *node; @@ -258,7 +259,7 @@ static struct cli_state* cli_cm_connect( const char *server, const char *share, Return a connection to a server. ********************************************************************/ -static struct cli_state* cli_cm_find( const char *server, const char *share ) +static struct cli_state *cli_cm_find( const char *server, const char *share ) { struct client_connection *p; @@ -275,7 +276,9 @@ static struct cli_state* cli_cm_find( const char *server, const char *share ) global variable as a side-effect (but only if the connection is successful). ****************************************************************************/ -struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_hdr ) +struct cli_state *cli_cm_open(const char *server, + const char *share, + BOOL show_hdr) { struct cli_state *c; @@ -283,8 +286,9 @@ struct cli_state* cli_cm_open( const char *server, const char *share, BOOL show_ c = cli_cm_find( server, share ); - if ( !c ) - c = cli_cm_connect( server, share, show_hdr ); + if ( !c ) { + c = cli_cm_connect(server, share, show_hdr); + } return c; } @@ -306,7 +310,6 @@ void cli_cm_shutdown( void ) } connections = NULL; - return; } @@ -369,20 +372,21 @@ void cli_cm_set_dest_ip(struct in_addr ip ) split a dfs path into the server, share name, and extrapath components **********************************************************************/ -static void split_dfs_path( const char *nodepath, fstring server, fstring share, fstring extrapath ) +static void split_dfs_path( const char *nodepath, fstring server, fstring share, pstring extrapath ) { char *p, *q; pstring path; pstrcpy( path, nodepath ); - if ( path[0] != '\\' ) + if ( path[0] != '\\' ) { return; + } p = strchr_m( path + 1, '\\' ); - - if ( !p ) + if ( !p ) { return; + } *p = '\0'; p++; @@ -392,9 +396,9 @@ static void split_dfs_path( const char *nodepath, fstring server, fstring share, if (q != NULL) { *q = '\0'; q++; - fstrcpy( extrapath, q ); + pstrcpy( extrapath, q ); } else { - fstrcpy( extrapath, '\0' ); + pstrcpy( extrapath, '\0' ); } fstrcpy( share, p ); @@ -402,83 +406,66 @@ static void split_dfs_path( const char *nodepath, fstring server, fstring share, } /**************************************************************************** - return the original path truncated at the first wildcard character - (also strips trailing \'s). Trust the caller to provide a NULL + Return the original path truncated at the directory component before + the first wildcard character. Trust the caller to provide a NULL terminated string ****************************************************************************/ -static void clean_path( pstring clean, const char *path ) +static void clean_path(const char *path, pstring path_out) { - int len; - char *p; - pstring newpath; + size_t len; + char *p1, *p2, *p; - pstrcpy( newpath, path ); - p = newpath; - - while ( p ) { - /* first check for '*' */ - - p = strrchr_m( newpath, '*' ); - if ( p ) { - *p = '\0'; - p = newpath; - continue; + /* No absolute paths. */ + while (IS_DIRECTORY_SEP(*path)) { + path++; + } + + pstrcpy(path_out, path); + + p1 = strchr_m(path_out, '*'); + p2 = strchr_m(path_out, '?'); + + if (p1 || p2) { + if (p1 && p2) { + p = MIN(p1,p2); + } else if (!p1) { + p = p2; + } else { + p = p1; } - - /* first check for '?' */ - - p = strrchr_m( newpath, '?' ); - if ( p ) { + *p = '\0'; + + /* Now go back to the start of this component. */ + p1 = strrchr_m(path_out, '/'); + p2 = strrchr_m(path_out, '\\'); + p = MAX(p1,p2); + if (p) { *p = '\0'; - p = newpath; } } - - /* strip a trailing backslash */ - - len = strlen( newpath ); - if ( (len > 0) && (newpath[len-1] == '\\' || newpath[len-1] == '/') ) - newpath[len-1] = '\0'; - - pstrcpy( clean, newpath ); + + /* Strip any trailing separator */ + + len = strlen(path_out); + if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) { + path_out[len-1] = '\0'; + } } /**************************************************************************** ****************************************************************************/ -BOOL cli_dfs_make_full_path( pstring path, const char *server, const char *share, - const char *dir ) +static void cli_dfs_make_full_path( struct cli_state *cli, + const char *dir, + pstring path_out) { - pstring servicename; - char *sharename; - const char *directory; - - - /* make a copy so we don't modify the global string 'service' */ - - pstrcpy(servicename, share); - sharename = servicename; - - if (*sharename == '\\') { - - server = sharename+2; - sharename = strchr_m(server,'\\'); - - if (!sharename) - return False; - - *sharename = 0; - sharename++; + /* Ensure the extrapath doesn't start with a separator. */ + while (IS_DIRECTORY_SEP(*dir)) { + dir++; } - directory = dir; - if ( *directory == '\\' || *directory == '/' ) - directory++; - - pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory ); - - return True; + pstr_sprintf( path_out, "\\%s\\%s\\%s", cli->desthost, cli->share, dir); } /******************************************************************** @@ -504,9 +491,11 @@ static BOOL cli_dfs_check_error( struct cli_state *cli, NTSTATUS status ) get the dfs referral link ********************************************************************/ -BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, - CLIENT_DFS_REFERRAL**refs, size_t *num_refs, - uint16 *consumed) +BOOL cli_dfs_get_referral( struct cli_state *cli, + const char *path, + CLIENT_DFS_REFERRAL**refs, + size_t *num_refs, + uint16 *consumed) { unsigned int data_len = 0; unsigned int param_len = 0; @@ -550,10 +539,9 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, uint16 ref_size; int i; uint16 node_offset; - - + referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals ); - + /* start at the referrals array */ p = rdata+8; @@ -575,7 +563,6 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, p += ref_size; } - } *num_refs = num_referrals; @@ -587,17 +574,21 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, return True; } + /******************************************************************** ********************************************************************/ -BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const char *path, - struct cli_state **targetcli, pstring targetpath ) +BOOL cli_resolve_path( const char *mountpt, + struct cli_state *rootcli, + const char *path, + struct cli_state **targetcli, + pstring targetpath) { CLIENT_DFS_REFERRAL *refs = NULL; size_t num_refs; uint16 consumed; struct cli_state *cli_ipc; - pstring fullpath, cleanpath, extrapath; + pstring dfs_path, cleanpath, extrapath; int pathlen; fstring server, share; struct cli_state *newcli; @@ -612,22 +603,29 @@ BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const cha return False; } - *targetcli = NULL; - - /* send a trans2_query_path_info to check for a referral */ - - clean_path( cleanpath, path ); - cli_dfs_make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath ); + /* Don't do anything if this is not a DFS root. */ - /* don't bother continuing if this is not a dfs root */ - - if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, fullpath, &sbuf, &attributes ) ) { + if ( !rootcli->dfsroot) { *targetcli = rootcli; pstrcpy( targetpath, path ); return True; } - /* special case where client asked for a path that does not exist */ + *targetcli = NULL; + + /* Send a trans2_query_path_info to check for a referral. */ + + clean_path(path, cleanpath); + cli_dfs_make_full_path(rootcli, cleanpath, dfs_path ); + + if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes ) ) { + /* This is an ordinary path, just return it. */ + *targetcli = rootcli; + pstrcpy( targetpath, path ); + goto done; + } + + /* Special case where client asked for a path that does not exist */ if ( cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND) ) { *targetcli = rootcli; @@ -635,87 +633,97 @@ BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const cha goto done; } - /* we got an error, check for DFS referral */ - + /* We got an error, check for DFS referral. */ + if ( !cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) { return False; } - /* check for the referral */ + /* Check for the referral. */ if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) ) { return False; } - if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) + if ( !cli_dfs_get_referral(cli_ipc, dfs_path, &refs, &num_refs, &consumed) || !num_refs ) { return False; } - /* just store the first referral for now - Make sure to recreate the original string including any wildcards */ - - cli_dfs_make_full_path( fullpath, rootcli->desthost, rootcli->share, path ); - pathlen = strlen( fullpath )*2; - consumed = MIN(pathlen, consumed ); - pstrcpy( targetpath, &fullpath[consumed/2] ); + /* Just store the first referral for now. */ split_dfs_path( refs[0].dfspath, server, share, extrapath ); - SAFE_FREE( refs ); + SAFE_FREE(refs); - if (strlen(extrapath) > 0) { - string_append(&temppath, extrapath); - string_append(&temppath, targetpath); - pstrcpy( targetpath, temppath ); - } + /* Make sure to recreate the original string including any wildcards. */ - /* open the connection to the target path */ + cli_dfs_make_full_path( rootcli, path, dfs_path); + pathlen = strlen( dfs_path )*2; + consumed = MIN(pathlen, consumed ); + pstrcpy( targetpath, &dfs_path[consumed/2] ); + dfs_path[consumed/2] = '\0'; + + /* + * targetpath is now the unconsumed part of the path. + * dfs_path is now the consumed part of the path (in \server\share\path format). + */ + + /* Open the connection to the target server & share */ if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) { - d_printf("Unable to follow dfs referral [//%s/%s]\n", + d_printf("Unable to follow dfs referral [\\%s\\%s]\n", server, share ); - return False; } + if (strlen(extrapath) > 0) { + string_append(&temppath, extrapath); + string_append(&temppath, targetpath); + pstrcpy( targetpath, temppath ); + } + /* parse out the consumed mount path */ /* trim off the \server\share\ */ - fullpath[consumed/2] = '\0'; - clean_name( fullpath ); - if ((ppath = strchr_m( fullpath, '\\' )) == NULL) { + ppath = dfs_path; + + if (*ppath != '\\') { + d_printf("cli_resolve_path: dfs_path (%s) not in correct format.\n", + dfs_path ); return False; } - if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) { + + ppath++; /* Now pointing at start of server name. */ + + if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) { return False; } - { - char *p1, *p2; - - /* Last component can be '\\' or '/' posix path. */ - p1 = strchr_m( ppath+1, '\\' ); - p2 = strchr_m( ppath+1, '/' ); + ppath++; /* Now pointing at start of share name. */ - ppath = MAX(p1,p2); - - if (ppath == NULL) { - return False; - } + if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) { + return False; } - ppath++; - + ppath++; /* Now pointing at path component. */ + pstr_sprintf( newmount, "%s\\%s", mountpt, ppath ); + cli_cm_set_mntpoint( *targetcli, newmount ); - /* check for another dfs referral, note that we are not - checking for loops here */ + /* Check for another dfs referral, note that we are not + checking for loops here. */ if ( !strequal( targetpath, "\\" ) && !strequal( targetpath, "/")) { if ( cli_resolve_path( newmount, *targetcli, targetpath, &newcli, newpath ) ) { + /* + * When cli_resolve_path returns true here it's always + * returning the complete path in newpath, so we're done + * here. + */ *targetcli = newcli; pstrcpy( targetpath, newpath ); + return True; } } @@ -723,9 +731,8 @@ BOOL cli_resolve_path( const char *mountpt, struct cli_state *rootcli, const cha /* If returning True ensure we return a dfs root full path. */ if ( (*targetcli)->dfsroot ) { - pstrcpy( fullpath, targetpath ); - cli_dfs_make_full_path( targetpath, (*targetcli)->desthost, - (*targetcli)->share, fullpath); + pstrcpy(dfs_path, targetpath ); + cli_dfs_make_full_path( *targetcli, dfs_path, targetpath); } return True; @@ -743,7 +750,7 @@ BOOL cli_check_msdfs_proxy( struct cli_state *cli, const char *sharename, pstring fullpath; BOOL res; uint16 cnum; - fstring newextrapath; + pstring newextrapath; if ( !cli || !sharename ) return False; @@ -752,8 +759,9 @@ BOOL cli_check_msdfs_proxy( struct cli_state *cli, const char *sharename, /* special case. never check for a referral on the IPC$ share */ - if ( strequal( sharename, "IPC$" ) ) + if ( strequal( sharename, "IPC$" ) ) { return False; + } /* send a trans2_query_path_info to check for a referral */ -- cgit