Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
12 KiB

  1. #---------------------------------------------------------------------
  2. package DfsMap;
  3. #
  4. # Copyright (c) Microsoft Corporation. All rights reserved.
  5. #
  6. # Version: 1.00 (01/01/2001) : BPerkins
  7. # 2.00 (10/16/2001) : SuemiaoR
  8. # Purpose: Perform DFS commands for Windows release.
  9. #---------------------------------------------------------------------
  10. use strict;
  11. use vars qw($VERSION);
  12. $VERSION = '1.00';
  13. use Logmsg;
  14. use comlib;
  15. #---------------------------------------------------------------------
  16. sub TIEHASH
  17. {
  18. my $pClass = shift;
  19. my $pDfsRoot = shift;
  20. my $instance;
  21. my ($dfs_view_info, $cur_dfs_link);
  22. my $fchild_dfs_servers;
  23. my $tmpfile="$ENV{temp}\\tmpfile";
  24. my ( $fullDfsView );
  25. return 0 if ( @_ || !$pDfsRoot );
  26. # Check current dfscmd.exe version
  27. return 0 if ( !VerifyDfsCmdVer() );
  28. # Remove trailing backslash (if it exists) from DFS root
  29. $pDfsRoot =~ s/\\$//;
  30. # Read in current DFS view
  31. if ( system("dfscmd.exe /view $pDfsRoot /batch > $tmpfile") )
  32. {
  33. errmsg( "Failed viewing dfs root $pDfsRoot." );
  34. return 0;
  35. }
  36. my @fullDfsView = &comlib::ReadFile( $tmpfile );
  37. $instance = { "DFS Root" => $pDfsRoot };
  38. # "DFS Work Root" => {}
  39. # "DFS Servers" => (),
  40. # "Map" => {}
  41. ##### Expected output:
  42. ##### dfscmd /map "\\<dfs_root>\<link_name>" "\\<server>\<share>" "<comment>"
  43. my %dfs_map;
  44. for my $dfs_view_info ( @fullDfsView )
  45. {
  46. chomp $dfs_view_info;
  47. next if ( !$dfs_view_info );
  48. next if ( $dfs_view_info =~ /^REM BATCH RESTORE SCRIPT/i );
  49. $dfs_view_info = lc $dfs_view_info;
  50. last if ( $dfs_view_info eq "the command completed successfully." );
  51. my ($dfs_link, $share, $comment) = $dfs_view_info =~ /dfscmd \/(?:map|add) "(.*?)" "(.*?)"(?: "(.*)")?$/;
  52. my $relative_link_name = $dfs_link;
  53. $relative_link_name =~ s/^\Q$pDfsRoot\E\\//i;
  54. if ( !$dfs_link || !$share || !$relative_link_name )
  55. {
  56. errmsg( "Unrecognized entry [ $dfs_view_info] " );
  57. return 0;
  58. }
  59. ##### If it is just the root, then this entry represents a hosting server
  60. if ( lc $dfs_link eq lc $pDfsRoot )
  61. {
  62. push ( @{$instance->{"DFS Servers"}}, $share ) ;
  63. }
  64. ##### Otherwise associate the link and the share
  65. else
  66. {
  67. push ( @{$dfs_map{$relative_link_name}}, $share );
  68. push ( @{$dfs_map{$share}}, $relative_link_name);
  69. }
  70. }
  71. ##### Expect to find at least one DFS server under the root
  72. if ( !exists $instance->{"DFS Servers"} )
  73. {
  74. errmsg( "Did not find the root node and specified servers for $pDfsRoot." );
  75. return 0;
  76. }
  77. #logmsg( "DFS hosting server(s) [@{$instance->{'DFS Servers'}}]." );
  78. # FUTURE: find a nicer way around this
  79. # In cases where more than one server is actually hosting DFS,
  80. # we need to pick one of the hosting machines to perform
  81. # our commands against so that we don't have to wait for
  82. # replication (this is to help tests for just created and
  83. # just removed directories succeed).
  84. for ( @{$instance->{"DFS Servers"}} )
  85. {
  86. if ( -e $_ )
  87. {
  88. $instance->{"DFS Work Root"} = $_;
  89. last;
  90. }
  91. }
  92. # Verify we could see at least one
  93. if ( ! exists $instance->{"DFS Work Root"} )
  94. {
  95. errmsg( "Could not find an accessible DFS server." );
  96. return 0;
  97. }
  98. #print "\n$_:\n ". (join "\n ", @{$dfs_map{$_}}) foreach ( sort keys %dfs_map );
  99. # Store the map we just created inside of our private hash data
  100. $instance->{"Map"} = \%dfs_map;
  101. return bless $instance, $pClass;
  102. }
  103. #---------------------------------------------------------------------
  104. sub FETCH
  105. {
  106. my $self = shift;
  107. my $link_or_share = shift;
  108. my $dfs_map = $self->{"Map"};
  109. return $dfs_map->{ lc $link_or_share } if (exists $dfs_map->{ lc $link_or_share });
  110. return 1;
  111. }
  112. #---------------------------------------------------------------------
  113. sub STORE
  114. {
  115. my $self = shift;
  116. my $link_or_share = shift;
  117. my $new_link = shift;
  118. my $dfs_map = $self->{"Map"};
  119. my $fshare = $link_or_share =~ /^\\\\/;
  120. # Remove any trailing backslashes
  121. $link_or_share =~ s/\\$//;
  122. $new_link =~ s/\\$//;
  123. # If mapping already exists, we are done
  124. my $cur_mappings = $dfs_map->{lc $link_or_share};
  125. for ( @$cur_mappings )
  126. {
  127. if ( lc $_ eq lc $new_link )
  128. {
  129. #logmsg( "Found link [$new_link] exists, skip mapping." );
  130. return 1;
  131. }
  132. }
  133. # Create new DFS mapping
  134. my $cmdLine = "dfscmd.exe ". (@$cur_mappings?"/add ":"/map "). $self->{"DFS Root"}.
  135. ($fshare?"\\$new_link $link_or_share":"\\$link_or_share $new_link");
  136. if ( system( "$cmdLine >nul 2>nul" ) )
  137. {
  138. #
  139. # COMMENTED OUT: If someone else is trying to manipulate the same
  140. # links as we are while we are doing this then
  141. # we have a problem -- don't try to correct
  142. #
  143. # Might have gotten into a race-condition with another
  144. # machine attempting to add a new link -- if that is
  145. # a possibility try a map instead before giving up
  146. $dfs_map->{lc $link_or_share} = "";
  147. return 0;
  148. }
  149. # Update the hash with the new information
  150. push @{$dfs_map->{lc $link_or_share}}, $new_link;
  151. push @{$dfs_map->{lc $new_link}}, $link_or_share;
  152. return 1;
  153. }
  154. #---------------------------------------------------------------------
  155. sub DELETE
  156. {
  157. my $self = shift;
  158. my $link_or_share = shift;
  159. my $dfs_map = $self->{"Map"};
  160. my $fshare = $link_or_share =~ /^\\\\/;
  161. # Make sure DFS mapping exists before attempting to delete it
  162. return 1 if ( !exists $dfs_map->{lc $link_or_share} );
  163. my $cur_mappings = $dfs_map->{lc $link_or_share};
  164. return 1 if ( !@$cur_mappings );
  165. if ( $fshare )
  166. {
  167. # Remove all DFS links pointing to this share
  168. for my $next_link ( @$cur_mappings )
  169. {
  170. print "Remove[$next_link][$link_or_share]\n";
  171. return 0 if ( !$self->RemoveShareFromLink( $next_link, $link_or_share ) );
  172. }
  173. }
  174. else
  175. {
  176. my $cmdLine = "dfscmd.exe /unmap $self->{\"DFS Root\"}\\$link_or_share >nul 2>nul";
  177. if ( system( $cmdLine ) )
  178. {
  179. errmsg( "Failed on [$cmdLine].");
  180. return 0;
  181. }
  182. # Remove the hash references
  183. delete $dfs_map->{lc $link_or_share};
  184. for ( my $i = 0; $i < scalar(@$cur_mappings); $i++)
  185. {
  186. if ( lc $cur_mappings->[$i] eq lc $link_or_share )
  187. {
  188. splice @$cur_mappings, $i, 1;
  189. }
  190. }
  191. }
  192. return 1;
  193. }
  194. #---------------------------------------------------------------------
  195. sub CLEAR
  196. {
  197. my $self = shift;
  198. # We don't actually want to be able to delete the entire
  199. # DFS structure, so we will just clear our information
  200. undef %$self;
  201. return;
  202. }
  203. #---------------------------------------------------------------------
  204. sub EXISTS
  205. {
  206. my $self = shift;
  207. my $link_or_share = shift;
  208. my $dfs_map = $self->{"Map"};
  209. return exists $dfs_map->{lc $link_or_share};
  210. }
  211. #---------------------------------------------------------------------
  212. sub FIRSTKEY
  213. {
  214. my $self = shift;
  215. my $dfs_map = $self->{"Map"};
  216. my $force_to_first_key = keys %$dfs_map;
  217. return scalar each %$dfs_map;
  218. }
  219. #---------------------------------------------------------------------
  220. sub NEXTKEY
  221. {
  222. my $self = shift;
  223. my $last_key = shift;
  224. my $dfs_map = $self->{"Map"};
  225. return scalar each %$dfs_map;
  226. }
  227. #---------------------------------------------------------------------
  228. sub DESTROY
  229. {
  230. my $self = shift;
  231. return;
  232. }
  233. #---------------------------------------------------------------------
  234. # Need more methods than provided by TIEHASH defaults
  235. #---------------------------------------------------------------------
  236. sub ParseShare
  237. {
  238. my $self = shift;
  239. my $lang = shift;
  240. my $server = shift;
  241. my $branch = shift;
  242. my $buildNo = shift;
  243. my $arch = shift;
  244. my $type = shift;
  245. my $results = shift;
  246. my $dfs_map = $self->{Map};
  247. for my $share ( sort keys %$dfs_map )
  248. {
  249. next if( $share !~ /^\\\\([^\\]+)\\(\d+)(?:\-\d+)?(.*)$/ );
  250. my $bldno = $2;
  251. my $cmpStr = $3;
  252. next if( $server && lc $1 ne lc $server );
  253. next if( $buildNo && $buildNo != $bldno );
  254. if( $lang && $cmpStr =~ /\.$lang/i )
  255. {
  256. next if( $branch && $cmpStr !~ /$branch/i );
  257. next if( $arch && $cmpStr !~ /$arch/i );
  258. next if( $type && $cmpStr !~ /$type/i );
  259. }
  260. else
  261. {
  262. next if( $cmpStr !~ /misc/i );
  263. }
  264. push( @$results, $share );
  265. }
  266. return 1;
  267. }
  268. #---------------------------------------------------------------------
  269. sub RemoveShareFromLink
  270. {
  271. my $self = shift;
  272. my $link_root = shift;
  273. my $share_name = shift;
  274. my $dfs_map = $self->{"Map"};
  275. # Get current associated links
  276. my $cur_link_mappings = $dfs_map->{lc $link_root};
  277. my $cur_share_mappings = $dfs_map->{lc $share_name};
  278. my $fshare_linked;
  279. # If the share is not part of the link, return
  280. for (@$cur_link_mappings)
  281. {
  282. if ( lc $_ eq lc $share_name )
  283. {
  284. $fshare_linked = 1;
  285. last;
  286. }
  287. }
  288. return 1 if ( !$fshare_linked );
  289. my $dfs_command = "dfscmd.exe /remove $self->{'DFS Root'}\\$link_root $share_name >nul 2>nul";
  290. if ( system( $dfs_command ) )
  291. {
  292. errmsg( "failed on [$dfs_command]");
  293. return 0;
  294. }
  295. # Remove the associated hash entries
  296. for ( my $i = 0; $i < scalar(@$cur_link_mappings); $i++)
  297. {
  298. if ( lc $cur_link_mappings->[$i] eq lc $share_name )
  299. {
  300. splice @$cur_link_mappings, $i, 1;
  301. }
  302. }
  303. for ( my $i = 0; $i < scalar(@$cur_share_mappings); $i++)
  304. {
  305. if ( lc $cur_share_mappings->[$i] eq lc $link_root )
  306. {
  307. splice @$cur_share_mappings, $i, 1;
  308. }
  309. }
  310. return 1;
  311. }
  312. #---------------------------------------------------------------------
  313. sub Flush
  314. {
  315. my $self = shift;
  316. # Do nothing for now, but here in preparation for batching of commands
  317. return 1;
  318. }
  319. #---------------------------------------------------------------------
  320. sub GetDfsRoot
  321. {
  322. my $self = shift;
  323. return $self->{ "DFS Root" };
  324. }
  325. #---------------------------------------------------------------------
  326. sub GetDfsHosts
  327. {
  328. my $self = shift;
  329. return @{$self->{"DFS Servers"}};
  330. }
  331. #------------------------------------------------------------
  332. sub VerifyDfsCmdVer
  333. {
  334. #####Verify dfscmd.exe version is newer than 5.0.2203.1
  335. my $tmpfile="$ENV{temp}\\tmpfile";
  336. if( system( "where dfscmd.exe > $tmpfile" ) )
  337. {
  338. errmsg( "[dfscmd.exe] not found in the %path%." );
  339. return 0;
  340. }
  341. my @output = &comlib::ReadFile( $tmpfile );
  342. system( "filever $output[0] > $tmpfile" );
  343. @output = &comlib::ReadFile( $tmpfile );
  344. @output = split( /\s+/, $output[0] );
  345. if( $output[4] lt "5.0.2203.1" )
  346. {
  347. errmsg( "Current dfscmd.exe version is [$output[4]]." );
  348. errmsg( "Please install the version is newer than \"5.0.2203.1\" ");
  349. return 0;
  350. }
  351. return 1;
  352. }
  353. #------------------------------------------------------------
  354. =head1 NAME
  355. DfsMap.pm - collections of dfs links and shares in hash array for given dfs domain.
  356. =head1 SYNOPSIS
  357. sub TIEHASH( $hash, $dfs_domain )
  358. where $hash is the hash array to collect links and shares for given $dfs_domain.
  359. where $dfs_domain is the root for the dfs.
  360. =head1 DESCRIPTION
  361. Access all the hash information through tied functions.
  362. =head1 AUTHOR
  363. Brian Perkins <[email protected]>
  364. Suemiao Rossignol <[email protected]>
  365. =head1 COPYRIGHT
  366. Copyright (c) Microsoft Corporation. All rights reserved.
  367. =cut
  368. 1;