diff options
Diffstat (limited to 'source/libsmb/clidfs.c')
-rw-r--r-- | source/libsmb/clidfs.c | 328 |
1 files changed, 195 insertions, 133 deletions
diff --git a/source/libsmb/clidfs.c b/source/libsmb/clidfs.c index 916e4cefc6e..4009b98b418 100644 --- a/source/libsmb/clidfs.c +++ b/source/libsmb/clidfs.c @@ -3,6 +3,7 @@ client connect/disconnect routines Copyright (C) Andrew Tridgell 1994-1998 Copyright (C) Gerald (Jerry) Carter 2004 + 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 @@ -21,6 +22,16 @@ #include "includes.h" +/******************************************************************** + Important point. + + DFS paths are *always* of the form \server\share\<pathname> (the \ characters + are not C escaped here). + + - but if we're using POSIX paths then <pathname> may contain + '/' separators, not '\\' separators. So cope with '\\' or '/' + as a separator when looking at the pathname part.... JRA. +********************************************************************/ struct client_connection { struct client_connection *prev, *next; @@ -194,14 +205,14 @@ static void cli_cm_set_mntpoint( struct cli_state *c, const char *mnt ) if ( p ) { pstrcpy( p->mount, mnt ); - dos_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; @@ -221,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; @@ -247,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; @@ -264,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; @@ -272,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; } @@ -295,7 +310,6 @@ void cli_cm_shutdown( void ) } connections = NULL; - return; } @@ -354,110 +368,104 @@ void cli_cm_set_dest_ip(struct in_addr ip ) have_ip = True; } -/******************************************************************** - split a dfs path into the server and share name components -********************************************************************/ +/********************************************************************** + split a dfs path into the server, share name, and extrapath components +**********************************************************************/ -static void split_dfs_path( const char *nodepath, fstring server, fstring share ) +static void split_dfs_path( const char *nodepath, fstring server, fstring share, pstring extrapath ) { - char *p; + char *p, *q; pstring path; pstrcpy( path, nodepath ); - if ( path[0] != '\\' ) + if ( path[0] != '\\' ) { return; + } - p = strrchr_m( path, '\\' ); - - if ( !p ) + p = strchr_m( path + 1, '\\' ); + if ( !p ) { return; + } *p = '\0'; p++; + /* Look for any extra/deep path */ + q = strchr_m(p, '\\'); + if (q != NULL) { + *q = '\0'; + q++; + pstrcpy( extrapath, q ); + } else { + pstrcpy( extrapath, '\0' ); + } + fstrcpy( share, p ); fstrcpy( server, &path[1] ); } /**************************************************************************** - 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; - - pstrcpy( newpath, path ); - p = newpath; - - while ( p ) { - /* first check for '*' */ + size_t len; + char *p1, *p2, *p; - 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] = '\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++; - - pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory ); - - return True; + pstr_sprintf( path_out, "\\%s\\%s\\%s", cli->desthost, cli->share, dir); } /******************************************************************** @@ -483,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; @@ -529,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; @@ -554,7 +563,6 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, p += ref_size; } - } *num_refs = num_referrals; @@ -566,115 +574,167 @@ 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; + pstring dfs_path, cleanpath, extrapath; int pathlen; fstring server, share; struct cli_state *newcli; pstring newpath; pstring newmount; - char *ppath; + char *ppath, *temppath = NULL; SMB_STRUCT_STAT sbuf; uint32 attributes; - if ( !rootcli || !path || !targetcli ) + if ( !rootcli || !path || !targetcli ) { 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, cleanpath, &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; pstrcpy( targetpath, path ); - return True; + goto done; } - /* we got an error, check for DFS referral */ - - if ( !cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED) ) + /* 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 )) ) + if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) ) { return False; + } - if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) - || !num_refs ) - { + 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 */ + /* Just store the first referral for now. */ + + split_dfs_path( refs[0].dfspath, server, share, extrapath ); + SAFE_FREE(refs); + + /* 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; + cli_dfs_make_full_path( rootcli, path, dfs_path); + pathlen = strlen( dfs_path )*2; consumed = MIN(pathlen, consumed ); - pstrcpy( targetpath, &fullpath[consumed/2] ); + pstrcpy( targetpath, &dfs_path[consumed/2] ); + dfs_path[consumed/2] = '\0'; - split_dfs_path( refs[0].dfspath, server, share ); - SAFE_FREE( refs ); - - /* open the connection to the target path */ + /* + * 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'; - dos_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; - if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) + } + + ppath++; /* Now pointing at start of share name. */ + + 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, "\\" ) ) { + 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; } } + done: + + /* If returning True ensure we return a dfs root full path. */ + if ( (*targetcli)->dfsroot ) { + pstrcpy(dfs_path, targetpath ); + cli_dfs_make_full_path( *targetcli, dfs_path, targetpath); + } + return True; } @@ -690,6 +750,7 @@ BOOL cli_check_msdfs_proxy( struct cli_state *cli, const char *sharename, pstring fullpath; BOOL res; uint16 cnum; + pstring newextrapath; if ( !cli || !sharename ) return False; @@ -698,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 */ @@ -719,13 +781,13 @@ BOOL cli_check_msdfs_proxy( struct cli_state *cli, const char *sharename, } cli->cnum = cnum; - + if (!res || !num_refs ) { SAFE_FREE( refs ); return False; } - split_dfs_path( refs[0].dfspath, newserver, newshare ); + split_dfs_path( refs[0].dfspath, newserver, newshare, newextrapath ); /* check that this is not a self-referral */ |