From e57fd2c5f00de2b11a2b44374830e89a90bc0022 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Wed, 23 Feb 2005 17:29:28 +0000 Subject: [PATCH] r5518: Add initial msdfs support to smbclient. Currently I can only cd up and down the tree and get directory listings. Still have to figure out how to get a directory listing on a 2k dfs root. Also have to work out some issues with relative paths that cross dfs mount points. We're protected from the new code paths when connecting to a non-dfs root share ( the flag from the tcon&X is stored in the struct cli_state* ) --- source/Makefile.in | 4 +- source/client/client.c | 245 ++++++++++++++++++++--------------- source/include/client.h | 1 + source/include/msdfs.h | 6 + source/libsmb/cliconnect.c | 3 + source/libsmb/clidfs.c | 257 +++++++++++++++++++++++++++++++++---- source/libsmb/clirap.c | 59 ++++++++- 7 files changed, 440 insertions(+), 135 deletions(-) diff --git a/source/Makefile.in b/source/Makefile.in index eb39dbf88bf..81ed0a37798 100644 --- a/source/Makefile.in +++ b/source/Makefile.in @@ -245,7 +245,7 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \ libsmb/clireadwrite.o libsmb/clilist.o libsmb/cliprint.o \ libsmb/clitrans.o libsmb/clisecdesc.o libsmb/clidgram.o \ libsmb/clistr.o lib/util_seaccess.o \ - libsmb/cliquota.o libsmb/clifsinfo.o libsmb/clidfs.o \ + libsmb/cliquota.o libsmb/clifsinfo.o \ libsmb/smberr.o libsmb/credentials.o libsmb/pwd_cache.o \ libsmb/clioplock.o $(ERRORMAP_OBJ) libsmb/clirap2.o \ $(DOSERR_OBJ) \ @@ -522,7 +522,7 @@ LIBBIGBALLOFMUD_OBJ = $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) $(UBIQX_OBJ) $(SECRETS_OBJ LIBBIGBALLOFMUD_PICOBJS = $(LIBBIGBALLOFMUD_OBJ:.o=.@PICSUFFIX@) -CLIENT_OBJ1 = client/client.o client/clitar.o +CLIENT_OBJ1 = client/client.o client/clitar.o libsmb/clidfs.o CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) \ $(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) \ diff --git a/source/client/client.c b/source/client/client.c index 311b372666f..53e425c5198 100644 --- a/source/client/client.c +++ b/source/client/client.c @@ -1,9 +1,10 @@ /* Unix SMB/CIFS implementation. SMB client - Copyright (C) Andrew Tridgell 1994-1998 - Copyright (C) Simo Sorce 2001-2002 - Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Gerald (Jerry) Carter 2004 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 @@ -96,19 +97,23 @@ static unsigned int put_total_time_ms = 0; /* totals globals */ static double dir_total; + struct client_connection { struct client_connection *prev, *next; + struct client_connection *parent; struct cli_state *cli; + pstring mntpath; }; struct cli_state *cli; -struct client_connection *connections; +static struct client_connection *connections; /******************************************************************** Return a connection to a server. ********************************************************************/ -static struct cli_state *do_connect(const char *server, const char *share) +static struct cli_state *do_connect( const char *server, const char *share, + BOOL show_sessetup ) { struct cli_state *c; struct nmb_name called, calling; @@ -200,13 +205,15 @@ static struct cli_state *do_connect(const char *server, const char *share) d_printf("Anonymous login successful\n"); } - if (*c->server_domain) { - DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n", - c->server_domain,c->server_os,c->server_type)); - } else if (*c->server_os || *c->server_type){ - DEBUG(1,("OS=[%s] Server=[%s]\n", - c->server_os,c->server_type)); - } + if ( show_sessetup ) { + if (*c->server_domain) { + DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n", + c->server_domain,c->server_os,c->server_type)); + } else if (*c->server_os || *c->server_type){ + DEBUG(1,("OS=[%s] Server=[%s]\n", + c->server_os,c->server_type)); + } + } DEBUG(4,(" session setup ok\n")); if (!cli_send_tconX(c, sharename, "?????", @@ -225,19 +232,29 @@ static struct cli_state *do_connect(const char *server, const char *share) Add a new connection to the list ********************************************************************/ -static struct cli_state* add_new_connection( const char *server, const char *share ) +static struct cli_state* cli_cm_connect( const char *server, const char *share, + BOOL show_hdr ) { - struct client_connection *node; + struct client_connection *node, *pparent, *p; node = SMB_XMALLOC_P( struct client_connection ); - node->cli = do_connect( server, share ); + node->cli = do_connect( server, share, show_hdr ); if ( !node->cli ) { SAFE_FREE( node ); return NULL; } + pparent = NULL; + for ( p=connections; p; p=p->next ) { + if ( strequal(cli->desthost, p->cli->desthost) && strequal(cli->share, p->cli->share) ) + pparent = p; + } + + node->parent = pparent; + pstrcpy( node->mntpath, cur_dir ); + DLIST_ADD( connections, node ); return node->cli; @@ -248,7 +265,7 @@ static struct cli_state* add_new_connection( const char *server, const char *sha Return a connection to a server. ********************************************************************/ -static struct cli_state* find_connection ( const char *server, const char *share ) +static struct cli_state* cli_cm_find( const char *server, const char *share ) { struct client_connection *p; @@ -260,6 +277,51 @@ static struct cli_state* find_connection ( const char *server, const char *share return NULL; } +/**************************************************************************** + open a client connection to a \\server\share. Set's the current *cli + 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 *c; + + /* try to reuse an existing connection */ + + c = cli_cm_find( server, share ); + + if ( !c ) + c = cli_cm_connect( server, share, show_hdr ); + + return c; +} + +/**************************************************************************** +****************************************************************************/ + +struct cli_state* cli_cm_current( void ) +{ + return cli; +} + +/**************************************************************************** +****************************************************************************/ + +const char* cli_cm_get_mntpath( struct cli_state *pcli ) +{ + struct client_connection *p; + + for ( p=connections; p; p=p->next ) { + if ( strequal(pcli->desthost, p->cli->desthost) && strequal(pcli->share, p->cli->share) ) + break; + } + + if ( !p ) + return NULL; + + return p->mntpath; +} + /**************************************************************************** Write to a local file with CR/LF->LF translation if appropriate. Return the number taken from the buffer. This may not equal the number written. @@ -374,9 +436,15 @@ static void send_message(void) static int do_dskattr(void) { int total, bsize, avail; + struct cli_state *targetcli; + pstring targetpath; - if (!cli_dskattr(cli, &bsize, &total, &avail)) { - d_printf("Error in dskattr: %s\n",cli_errstr(cli)); + if ( !cli_resolve_path( cli, cur_dir, &targetcli, targetpath ) ) { + d_printf("Error in dskattr: %s\n", cli_errstr(cli)); + } + + if (!cli_dskattr(targetcli, &bsize, &total, &avail)) { + d_printf("Error in dskattr: %s\n",cli_errstr(targetcli)); return 1; } @@ -406,31 +474,44 @@ static int do_cd(char *newdir) char *p = newdir; pstring saved_dir; pstring dname; + pstring targetpath; + struct cli_state *targetcli; dos_format(newdir); - /* Save the current directory in case the - new directory is invalid */ + /* Save the current directory in case the new directory is invalid */ + pstrcpy(saved_dir, cur_dir); + if (*p == '\\') pstrcpy(cur_dir,p); else pstrcat(cur_dir,p); + if (*(cur_dir+strlen(cur_dir)-1) != '\\') { pstrcat(cur_dir, "\\"); } + dos_clean_name(cur_dir); - pstrcpy(dname,cur_dir); + pstrcpy( dname, cur_dir ); pstrcat(cur_dir,"\\"); dos_clean_name(cur_dir); - if (!strequal(cur_dir,"\\")) { - if (!cli_chkpath(cli, dname)) { - d_printf("cd %s: %s\n", dname, cli_errstr(cli)); + if ( !cli_resolve_path( cli, dname, &targetcli, targetpath ) ) { + d_printf("cd %s: %s\n", dname, cli_errstr(cli)); + pstrcpy(cur_dir,saved_dir); + } + + pstrcat( targetpath, "\\" ); + dos_clean_name( targetpath ); + + if ( !strequal(targetpath,"\\") ) { + if ( !cli_chkpath(targetcli, targetpath) ) { + d_printf("cd %s: %s\n", dname, cli_errstr(targetcli)); pstrcpy(cur_dir,saved_dir); } } - + pstrcpy(cd_path,cur_dir); return 0; @@ -444,7 +525,7 @@ static int cmd_cd(void) { pstring buf; int rc = 0; - + if (next_token_nr(NULL,buf,NULL,sizeof(buf))) rc = do_cd(buf); else @@ -668,6 +749,8 @@ static void do_list_helper(file_info *f, const char *mask, void *state) void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, BOOL dirs) { static int in_do_list = 0; + struct cli_state *targetcli; + pstring targetpath; if (in_do_list && rec) { fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n"); @@ -694,7 +777,15 @@ void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, */ pstring head; pstrcpy(head, do_list_queue_head()); - cli_list(cli, head, attribute, do_list_helper, NULL); + + /* check for dfs */ + + if ( !cli_resolve_path( cli, head, &targetcli, targetpath ) ) { + d_printf("do_list: [%s] %s\n", head, cli_errstr(cli)); + continue; + } + + cli_list(targetcli, targetpath, attribute, do_list_helper, NULL); remove_do_list_queue_head(); if ((! do_list_queue_empty()) && (fn == display_finfo)) { char* next_file = do_list_queue_head(); @@ -713,9 +804,15 @@ void do_list(const char *mask,uint16 attribute,void (*fn)(file_info *),BOOL rec, } } } else { - if (cli_list(cli, mask, attribute, do_list_helper, NULL) == -1) { - d_printf("%s listing %s\n", cli_errstr(cli), mask); + /* check for dfs */ + + if ( cli_resolve_path( cli, mask, &targetcli, targetpath ) ) { + if (cli_list(targetcli, targetpath, attribute, do_list_helper, NULL) == -1) + d_printf("%s listing %s\n", cli_errstr(targetcli), targetpath); } + else + d_printf("do_list: [%s] %s\n", mask, cli_errstr(cli)); + } in_do_list = 0; @@ -2662,32 +2759,6 @@ static int cmd_logon(void) } -/**************************************************************************** - Add a connection to a new //server/share path -****************************************************************************/ - -static int cmd_add_connect(void) -{ - pstring path; - struct cli_state *new_cli; - - if ( !next_token_nr(NULL, path, NULL, sizeof(path)) ) { - d_printf("connect \n"); - return 0; - } - - string_replace(path, '/','\\'); - - new_cli = add_new_connection( "", path ); - - /* if successful, set this as the current connection */ - - if ( new_cli ) - cli = new_cli; - - return 0; -} - /**************************************************************************** list active connections ****************************************************************************/ @@ -2706,53 +2777,20 @@ static int cmd_list_connect(void) } /**************************************************************************** - set the current active_connection + display the current active client connection ****************************************************************************/ -static int cmd_set_connect(void) +static int cmd_show_connect( void ) { - pstring path; - char *server, *share; - struct cli_state *c; - - if ( !next_token_nr(NULL, path, NULL, sizeof(path)) ) { - d_printf("setconnect \n"); - return 0; - } - - if ( strlen(path) < 5 ) { - d_printf("Invalid UNC path [%s]\n", path ); + struct cli_state *targetcli; + pstring targetpath; + + if ( !cli_resolve_path( cli, cur_dir, &targetcli, targetpath ) ) { + d_printf("showconnect %s: %s\n", cur_dir, cli_errstr(cli)); return 1; } - - - string_replace(path, '/','\\'); - - share = strrchr_m( path, '\\' ); - if ( !share ) { - d_printf("Invalid UNC path [%s]\n", path ); - return 1; - }; - - *share = '\0'; - share++; - - if ( path[0] != '\\' || path[1]!='\\' ) { - d_printf("Invalid UNC path [%s]\n", path ); - return 1; - } - - server = path+2; - - c = find_connection( server, share ); - if ( !c ) { - d_printf("Cannot find existing connection for //%s/%s\n", - server, share); - return 1; - } - - cli = c; - + + d_printf("//%s/%s\n", targetcli->desthost, targetcli->share); return 0; } @@ -2827,9 +2865,8 @@ static struct {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}}, {"vuid",cmd_vuid,"change current vuid",{COMPL_NONE,COMPL_NONE}}, {"logon",cmd_logon,"establish new logon",{COMPL_NONE,COMPL_NONE}}, - {"addconnect",cmd_add_connect,"add a connection to a new //server/share",{COMPL_NONE,COMPL_NONE}}, {"listconnect",cmd_list_connect,"list open connections",{COMPL_NONE,COMPL_NONE}}, - {"setconnect",cmd_set_connect,"set the current active connection",{COMPL_NONE,COMPL_NONE}}, + {"showconnect",cmd_show_connect,"display the current active connection",{COMPL_NONE,COMPL_NONE}}, /* Yes, this must be here, see crh's comment above. */ {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}}, @@ -2904,7 +2941,7 @@ static int process_command_string(char *cmd) /* establish the connection if not already */ if (!cli) { - cli = add_new_connection(desthost, service); + cli = cli_cm_connect(desthost, service, True); if (!cli) return 0; } @@ -3220,7 +3257,7 @@ static int process(char *base_directory) { int rc = 0; - cli = add_new_connection(desthost, service); + cli = cli_cm_connect(desthost, service, True); if (!cli) { return 1; } @@ -3243,7 +3280,7 @@ static int process(char *base_directory) static int do_host_query(char *query_host) { - cli = add_new_connection(query_host, "IPC$"); + cli = cli_cm_connect(query_host, "IPC$", True); if (!cli) return 1; @@ -3256,7 +3293,7 @@ static int do_host_query(char *query_host) cli_shutdown(cli); port = 139; - cli = add_new_connection(query_host, "IPC$"); + cli = cli_cm_connect(query_host, "IPC$", True); } if (cli == NULL) { @@ -3281,7 +3318,7 @@ static int do_tar_op(char *base_directory) /* do we already have a connection? */ if (!cli) { - cli = add_new_connection(desthost, service); + cli = cli_cm_connect(desthost, service, True); if (!cli) return 1; } diff --git a/source/include/client.h b/source/include/client.h index 8ae8faf90dc..61c420c06b1 100644 --- a/source/include/client.h +++ b/source/include/client.h @@ -106,6 +106,7 @@ struct cli_state { int initialised; int win95; uint32 capabilities; + BOOL dfsroot; TALLOC_CTX *mem_ctx; diff --git a/source/include/msdfs.h b/source/include/msdfs.h index 32aa7ecef25..eaefa81c5b8 100644 --- a/source/include/msdfs.h +++ b/source/include/msdfs.h @@ -37,6 +37,12 @@ /* Maximum number of referrals for each Dfs volume */ #define MAX_REFERRAL_COUNT 256 +typedef struct _client_referral { + uint32 proximity; + uint32 ttl; + pstring dfspath; +} CLIENT_DFS_REFERRAL; + struct referral { pstring alternate_path; /* contains the path referred */ diff --git a/source/libsmb/cliconnect.c b/source/libsmb/cliconnect.c index fa98d55f252..01a92a89ba4 100644 --- a/source/libsmb/cliconnect.c +++ b/source/libsmb/cliconnect.c @@ -972,6 +972,9 @@ BOOL cli_send_tconX(struct cli_state *cli, /* almost certainly win95 - enable bug fixes */ cli->win95 = True; } + + if ( cli->protocol >= PROTOCOL_LANMAN2 ) + cli->dfsroot = (SVAL( cli->inbuf, smb_vwv2 ) & SMB_SHARE_IN_DFS); cli->cnum = SVAL(cli->inbuf,smb_tid); return True; diff --git a/source/libsmb/clidfs.c b/source/libsmb/clidfs.c index bb82fbfabb6..ea66eba9980 100644 --- a/source/libsmb/clidfs.c +++ b/source/libsmb/clidfs.c @@ -1,8 +1,8 @@ /* Unix SMB/CIFS implementation. client connect/disconnect routines - Copyright (C) Gerald (Jerry) Carter - + Copyright (C) Gerald (Jerry) Carter 2004 + 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 @@ -22,30 +22,12 @@ #include "includes.h" -/******************************************************************** - check for dfs referral -********************************************************************/ - -BOOL check_for_dfs_referral( struct cli_state *cli ) -{ - uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); - - /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ - - if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) ) - return False; - - if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) ) - return True; - - return False; -} /******************************************************************** split a dfs path into the server and share name components ********************************************************************/ -void split_dfs_path( const char *nodepath, fstring server, fstring share ) +static void split_dfs_path( const char *nodepath, fstring server, fstring share ) { char *p; pstring path; @@ -67,12 +49,112 @@ void split_dfs_path( const char *nodepath, fstring server, fstring share ) 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 + terminated string +****************************************************************************/ + +static void clean_path( pstring clean, const char *path ) +{ + int len; + char *p; + pstring newpath; + + pstrcpy( newpath, path ); + p = newpath; + + while ( p ) { + /* first check for '*' */ + + p = strrchr_m( newpath, '*' ); + if ( p ) { + *p = '\0'; + p = newpath; + continue; + } + + /* first check for '?' */ + + p = strrchr_m( newpath, '?' ); + if ( p ) { + *p = '\0'; + p = newpath; + } + } + + /* strip a trailing backslash */ + + len = strlen( newpath ); + if ( newpath[len-1] == '\\' ) + newpath[len-1] = '\0'; + + pstrcpy( clean, newpath ); +} + +/**************************************************************************** +****************************************************************************/ + +static BOOL make_full_path( pstring path, const char *server, const char *share, + const char *dir ) +{ + 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++; + } + + directory = dir; + if ( *directory == '\\' ) + directory++; + + pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory ); + + return True; +} + +/******************************************************************** + check for dfs referral +********************************************************************/ + +static BOOL cli_dfs_check_error( struct cli_state *cli ) +{ + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); + + /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ + + if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) ) + return False; + + if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) ) + return True; + + return False; +} + /******************************************************************** get the dfs referral link ********************************************************************/ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, - struct referral **refs, size_t *num_refs) + CLIENT_DFS_REFERRAL**refs, size_t *num_refs, + uint16 *consumed) { unsigned int data_len = 0; unsigned int param_len = 0; @@ -83,7 +165,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, char *p; size_t pathlen = 2*(strlen(path)+1); uint16 num_referrals; - struct referral *referrals; + CLIENT_DFS_REFERRAL *referrals; memset(param, 0, sizeof(param)); SSVAL(param, 0, 0x03); /* max referral level */ @@ -108,6 +190,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, return False; } + *consumed = SVAL( rdata, 0 ); num_referrals = SVAL( rdata, 2 ); if ( num_referrals != 0 ) { @@ -117,7 +200,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, uint16 node_offset; - referrals = SMB_XMALLOC_ARRAY( struct referral, num_referrals ); + referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals ); /* start at the referrals array */ @@ -132,11 +215,11 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, continue; } - referrals[0].proximity = SVAL( p, 8 ); - referrals[0].ttl = SVAL( p, 10 ); + referrals[i].proximity = SVAL( p, 8 ); + referrals[i].ttl = SVAL( p, 10 ); - clistr_pull( cli, referrals[0].alternate_path, p+node_offset, - sizeof(referrals[0].alternate_path), -1, STR_TERMINATE|STR_UNICODE ); + clistr_pull( cli, referrals[i].dfspath, p+node_offset, + sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE ); p += ref_size; } @@ -152,3 +235,121 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, return True; } +#if 0 +/******************************************************************** +********************************************************************/ + +BOOL cli_dfs_handle_referral( struct cli_state *cli, const char *path ) +{ + struct cli_state *cli_ipc; + pstring fullpath; + CLIENT_DFS_REFERRAL *refs = NULL; + size_t num_refs; + uint16 consumed; + fstring server, share; + + if ( !cli_dfs_check_error(cli) ) + return False; + + if ( !(cli_ipc = cli_cm_open( cli->desthost, "IPC$", False )) ) + return False; + + make_full_path( fullpath, cli->desthost, cli->share, path ); + + if ( !cli_dfs_get_referral( cli_ipc, fullpath, &refs, &num_refs, &consumed ) ) { + d_printf("cli_get_dfs_referral() failed!\n"); + /* reset the current client connection */ + cli_cm_open( cli->desthost, cli->share, False ); + + return False; + } + + /* just pick the first one */ + if ( num_refs ) { + split_dfs_path( refs[0].alternate_path, server, share ); + if ( cli_cm_open( server, share, False ) == NULL ) { + d_printf("Unable to follow dfs referral [\\\\%s\\%s]\n", + server, share ); + cli_cm_open( cli->desthost, cli->share, False ); + + return False; + } + } + + SAFE_FREE( refs ); + + return True; +} +#endif + +/******************************************************************** +********************************************************************/ + +BOOL cli_resolve_path( 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; + int pathlen; + fstring server, share; + + SMB_STRUCT_STAT sbuf; + uint32 attributes; + + if ( !rootcli || !path || !targetcli ) + return False; + + /* send a trans2_query_path_info to check for a referral */ + + clean_path( cleanpath, path ); + make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath ); + + /* don't bother continuing if this is not a dfs root */ + + if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, fullpath, &sbuf, &attributes ) ) { + *targetcli = rootcli; + pstrcpy( targetpath, path ); + return True; + } + + /* we got an error, check for DFS referral */ + + if ( !cli_dfs_check_error(rootcli) ) + return False; + + /* 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) + || !num_refs ) + { + return False; + } + + /* just store the first referral for now + Make sure to recreate the original string including any wildcards */ + + make_full_path( fullpath, rootcli->desthost, rootcli->share, path ); + pathlen = strlen( fullpath )*2; + consumed = MIN(pathlen, consumed ); + pstrcpy( targetpath, &fullpath[consumed/2] ); + + split_dfs_path( refs[0].dfspath, server, share ); + SAFE_FREE( refs ); + + /* open the connection to the target path */ + + if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) { + d_printf("Unable to follow dfs referral [//%s/%s]\n", + server, share ); + + return False; + } + + return True; +} diff --git a/source/libsmb/clirap.c b/source/libsmb/clirap.c index 8cc5d8bf901..6ede1b2b53a 100644 --- a/source/libsmb/clirap.c +++ b/source/libsmb/clirap.c @@ -1,7 +1,8 @@ /* Unix SMB/CIFS implementation. client RAP calls - Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Gerald (Jerry) Carter 2004 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 @@ -641,9 +642,65 @@ BOOL cli_qfileinfo(struct cli_state *cli, int fnum, return True; } + +/**************************************************************************** +send a qpathinfo BASIC_INFO call +****************************************************************************/ +BOOL cli_qpathinfo_basic( struct cli_state *cli, const char *name, + SMB_STRUCT_STAT *sbuf, uint32 *attributes ) +{ + unsigned int param_len = 0; + unsigned int data_len = 0; + uint16 setup = TRANSACT2_QPATHINFO; + char param[sizeof(pstring)+6]; + char *rparam=NULL, *rdata=NULL; + char *p; + + p = param; + memset(p, 0, 6); + SSVAL(p, 0, SMB_QUERY_FILE_BASIC_INFO); + p += 6; + p += clistr_push(cli, p, name, sizeof(pstring)-6, STR_TERMINATE); + param_len = PTR_DIFF(p, param); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, 0, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + if (data_len < 36) { + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return False; + } + + sbuf->st_atime = interpret_long_date( rdata+8 ); + sbuf->st_mtime = interpret_long_date( rdata+16 ); + sbuf->st_ctime = interpret_long_date( rdata+24 ); + + *attributes = IVAL( rdata, 32 ); + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return True; +} + /**************************************************************************** send a qfileinfo call ****************************************************************************/ + BOOL cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char **poutdata, uint32 *poutlen) { unsigned int data_len = 0;