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.

2965 lines
95 KiB

  1. #!perl -w
  2. package raiseall;
  3. use strict;
  4. use Win32::File qw(GetAttributes SetAttributes);
  5. use File::Basename qw(basename);
  6. use Carp;
  7. use lib $ENV{ "RazzleToolPath" };
  8. use lib $ENV{ "RazzleToolPath" }. "\\PostBuildScripts";
  9. use PbuildEnv;
  10. use ParseArgs;
  11. use Logmsg;
  12. use cksku;
  13. use cklang;
  14. use BuildName;
  15. use GetIniSetting;
  16. # Set the script name for logging
  17. $ENV{ "SCRIPT_NAME" } = "raiseall.pl";
  18. # Platform = Architecutre + Debug_Type (e.g. x86fre)
  19. my( $Language, $Build_Number, $Architecture, $Debug_Type, $Time_Stamp,
  20. $Platform, $Quality, $Op, $fOverride_Safe, $fSafe, $fReplaceAtNumberedLink );
  21. my @ConglomeratorSkus = ('symbolcd', 'ddk', 'hal', 'ifs', 'mantis', 'opk', 'mui');
  22. my %Ordered_Qualities = ( pre => 1,
  23. bvt => 2,
  24. tst => 3,
  25. sav => 4,
  26. idw => 5,
  27. ids => 6,
  28. idc => 7 );
  29. #########################################################################
  30. # #
  31. # Server/Client split sku associations #
  32. # #
  33. my @ServerSkus = ( 'ads', 'bin', 'bla', 'dtc', 'sbs', 'srv', 'sym', 'resources' );
  34. my @ClientSkus = ( 'bin', 'per', 'pro', 'sym', 'winpe', 'upgadv', 'resources', @ConglomeratorSkus );
  35. # #
  36. # #
  37. #########################################################################
  38. sub globex($);
  39. sub Main {
  40. my $local_release_path = GetReleasePath();
  41. if ( !defined $local_release_path ) {
  42. errmsg( "Unable to determine release path on current machine, exiting." );
  43. return;
  44. }
  45. # Search for builds availabe to release
  46. my $build_criteria = "$local_release_path\\$Language\\$Build_Number.$Platform.$ENV{_BuildBranch}.$Time_Stamp";
  47. my @available_builds = grep { $_ if ( -d $_ ) } globex $build_criteria;
  48. #
  49. # Check for conglomerator skus
  50. #
  51. #push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\$Language\\$Build_Number.$ENV{_BuildBranch}.$Time_Stamp";
  52. push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\$Language\\$Build_Number.$ENV{_BuildBranch}";
  53. #
  54. # Check for language neutral skus
  55. #
  56. push @available_builds, grep { $_ if ( -d $_ ) } globex "$local_release_path\\misc\\$Build_Number.$ENV{_BuildBranch}";
  57. dbgmsg( "Available builds: " );
  58. dbgmsg( $_ ) foreach @available_builds;
  59. if ( !@available_builds ) {
  60. if ( $! != 2 ) {
  61. errmsg("Error querying for builds matching $build_criteria ($!)");
  62. }
  63. else {
  64. errmsg("No builds found matching $build_criteria");
  65. }
  66. return;
  67. }
  68. if ( $Op eq 'raise' ) {
  69. return RaiseAll( $local_release_path, \@available_builds );
  70. }
  71. elsif ( $Op eq "lower" ) {
  72. return LowerAll( $local_release_path, \@available_builds );
  73. }
  74. else {
  75. errmsg( "Internal error -- operation = \"$Op\"" );
  76. return;
  77. }
  78. }
  79. sub RaiseAll {
  80. my $local_release_path = shift;
  81. my $all_available_builds = shift;
  82. # Build number is required
  83. if ( $Build_Number eq '*' ) {
  84. errmsg( "Must specify build number." );
  85. return;;
  86. }
  87. # Quality is required
  88. if ( !defined $Quality ) {
  89. errmsg( "Must specify a quality." );
  90. return;
  91. }
  92. # For raiseall operations we only want to deal with the
  93. # newest build of a set of builds with the same build number
  94. my @available_builds;
  95. my ($cur_build_no_ts, $last_build_no_ts) = ('foo', 'foo');
  96. my @sorted_all_available_builds = sort @$all_available_builds;
  97. for ( my $i = $#sorted_all_available_builds; $i >= 0; $i-- ) {
  98. # Standard builds
  99. if ( $sorted_all_available_builds[$i] =~ /(\d+\.[^\.]+\.[^\.]+)\.\d+-\d+$/ ) {
  100. $cur_build_no_ts = $1;
  101. }
  102. # Conglomerator builds (no time-stamp currently)
  103. elsif ( $sorted_all_available_builds[$i] =~ /([^\\]+)\\(\d+\.[^\.]+)(?:\.\d+-\d+)?$/ ) {
  104. $cur_build_no_ts = "$1\\$2";
  105. }
  106. else {
  107. errmsg( "Unrecognized build format: $sorted_all_available_builds[$i]" );
  108. return;
  109. }
  110. if ( $cur_build_no_ts ne $last_build_no_ts ) {
  111. push @available_builds, $sorted_all_available_builds[$i];
  112. $last_build_no_ts = $cur_build_no_ts;
  113. }
  114. else {
  115. dbgmsg( "Skipping older build: ". basename($sorted_all_available_builds[$i]) );
  116. }
  117. }
  118. # Check available builds for outstanding release
  119. # errors; if there are none, set the *.qly file
  120. # to the appropriate value
  121. my $build;
  122. my $srvrel_errors = 0;
  123. foreach $build ( @available_builds ) {
  124. if ( -e "$build\\build_logs\\SrvRelFailed.txt" ) {
  125. errmsg( "SrvRel failures on ". basename($build) );
  126. errmsg( "Please fix errors and remove $build\\build_logs\\SrvRelFailed.txt" );
  127. $srvrel_errors++;
  128. }
  129. # Update the QLY file if this is not a conglomerator build
  130. elsif ( IsOSBuild( $build ) &&
  131. !ModifyQlyFile( basename($build), $Quality, "$build\\build_logs" ) ) {
  132. errmsg( "Failed updating the QLY file for ". basename($build). ", exiting." );
  133. return;
  134. }
  135. }
  136. if ( $srvrel_errors ) {
  137. errmsg( "Aborting due to $srvrel_errors SrvRel failure". ($srvrel_errors > 1?"s.":".") );
  138. return;
  139. }
  140. #
  141. # Setup parameters needed in ForEachBuild functions
  142. #
  143. my @accessed_shares = ();
  144. my %named_parameters = (
  145. ComputerName => $ENV{COMPUTERNAME},
  146. AccessedShares => \@accessed_shares,
  147. Quality => $Quality,
  148. Language => $Language,
  149. OverrideSafe => $fOverride_Safe
  150. );
  151. #
  152. # Setup the local shares on the release servers
  153. #
  154. logmsg( "Creating release shares..." );
  155. if ( !ForEachBuild( \&CreateSecuredBuildShare,
  156. \%named_parameters,
  157. $Language,
  158. \@available_builds,
  159. $fSafe ) ) {
  160. errmsg( "Failed to create shares, exiting." );
  161. return;
  162. }
  163. # Print out shares created / checked if in debug mode
  164. dbgmsg( "$_ share created/verified." ) foreach @accessed_shares;
  165. #
  166. # Do the DFS work
  167. #
  168. my ( $dfs_map, $dfs_lock ) = GetDfsAccess();
  169. return if ( !defined $dfs_map );
  170. # Next few functions need DFS access
  171. $named_parameters{ DfsMap } = $dfs_map;
  172. logmsg( "Checking numbered shares for older timestamps..." );
  173. if ( !ForEachBuild( \&UnmapInconsistentNumberedLinks,
  174. \%named_parameters,
  175. $Language,
  176. \@available_builds,
  177. $fSafe ) ) {
  178. errmsg( "Problem occurred checking numbered shares" );
  179. $dfs_lock->Unlock();
  180. return;
  181. }
  182. logmsg( "Finding/lowering any old DFS links at same quality..." );
  183. if ( !ForEachBuild( \&RemoveOldLatestDotQlyDfsLinks,
  184. \%named_parameters,
  185. $Language,
  186. \@available_builds,
  187. $fSafe ) ) {
  188. errmsg( "Problem verifying/lowering latest.$Quality and associated DFS links" );
  189. $dfs_lock->Unlock();
  190. return;
  191. }
  192. logmsg( "Creating/verifying DFS links..." );
  193. if ( !ForEachBuild( \&CreateDfsMappings,
  194. \%named_parameters,
  195. $Language,
  196. \@available_builds,
  197. $fSafe ) ) {
  198. errmsg( "Problem creating/verifying DFS links." );
  199. $dfs_lock->Unlock();
  200. return;
  201. }
  202. #
  203. # Do symbol server ops
  204. #
  205. # Override standard SKUs with our own
  206. @{$named_parameters{ DefinedSkus }} = ('sym');
  207. # Only want 'sym' on OS builds, not conglomerator builds
  208. my @os_builds = grep { $_ if IsOSBuild($_) } @available_builds;
  209. logmsg( "Checking symbol server state..." );
  210. if ( !ForEachBuild( \&CreateSymbolServerLink,
  211. \%named_parameters,
  212. $Language,
  213. \@os_builds,
  214. $fSafe ) ) {
  215. errmsg( "Problem updating/verifying symbols server DFS links." );
  216. $dfs_lock->Unlock();
  217. return;
  218. }
  219. logmsg( "Flushing outstanding DFS operations..." );
  220. if ( !(tied %$dfs_map)->Flush() ) {
  221. errmsg( "Problem processing DFS commands." );
  222. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  223. dfs_lock->Unlock();
  224. return;
  225. }
  226. #
  227. # Release the inter-machine lock
  228. #
  229. $dfs_lock->Unlock();
  230. # Done if we are not touching latest.*
  231. return 1 if ( $fSafe );
  232. #
  233. # If we are raising to BVT quality, check INI
  234. # file to determine if we should run BVT tests
  235. #
  236. # TODO: a release server may have more than one architecture
  237. # and may have just a fre or chk version, so this call
  238. # probably needs to be put in its own function and
  239. # used with ForEachBuild(), specifying many more
  240. # parameters than just -l and -p
  241. #
  242. if ( lc $Quality eq "bvt") {
  243. my $run_boot_test = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "RunBVT" );
  244. if (defined $run_boot_test &&
  245. lc $run_boot_test eq "true") {
  246. logmsg( "Starting BVT tests..." );
  247. # run tests - spit out warning if there are problems
  248. if ( !ExecuteProgram::ExecuteProgram("perl $ENV{RazzleToolPath}\\postbuildscripts\\assignbvt.pl -l:$Language -p:". ($Architecture eq '*'?"":$Architecture)) ) {
  249. wrnmsg( "Problem accessing bvt machine (error returned from assignbvt.pl)." );
  250. wrnmsg( " ". ExecuteProgram::GetLastCommand() );
  251. wrnmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  252. }
  253. }
  254. }
  255. #
  256. # If we are going to TST quality and this is
  257. # a main lab machine, start CRC computation
  258. #
  259. # This was turned off accidentally for over a month and nobody
  260. # noticed, so going to comment it out for now
  261. #
  262. #if ( lc $Quality eq "tst" && $ENV{ "MAIN_BUILD_LAB_MACHINE" } ) {
  263. # logmsg( "Calling CRC routine..." );
  264. # if ( !ExecuteProgram::ExecuteProgram( "$ENV{RazzleToolPath}\\postbuildscripts\\docrc.cmd $Build_Number" ) ) {
  265. # wrnmsg( "CRC routine reported failure!" );
  266. # wnrmsg( " ". ExecuteProgram::GetLastCommand() );
  267. # wrnmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  268. # }
  269. #}
  270. return 1;
  271. }
  272. sub LowerAll {
  273. my $local_release_path = shift;
  274. my $all_available_builds = shift;
  275. my @available_builds = sort @$all_available_builds;
  276. # Build number is normally required
  277. if ( $Build_Number eq '*' ) {
  278. if ( defined $fOverride_Safe ) {
  279. wrnmsg( "Forcibly lowering all builds on machine." );
  280. }
  281. else {
  282. errmsg( "Must specify build number." );
  283. return;;
  284. }
  285. }
  286. # Setting quality is pointless -- let the user know
  287. wrnmsg( "Quality flag '$Quality' ignored for lower operation." ) if ( defined $Quality );
  288. # Setting 'safe' is also pointless
  289. wrnmsg( "-safe ignored for lowerall operation." ) if ( defined $fSafe );
  290. # remove the *.qly file for all OS (not conglomerator) builds
  291. logmsg( "Removing the *.qly file..." );
  292. my $build;
  293. foreach $build ( @available_builds ) {
  294. if ( IsOSBuild( $build ) &&
  295. !DeleteQlyFile( "$build\\build_logs" ) ) {
  296. wrnmsg( "Failed removing the QLY file from ". basename($build) );
  297. return;
  298. }
  299. }
  300. #
  301. # Setup parameters needed in ForEachBuild functions
  302. #
  303. my @accessed_shares = ();
  304. my %named_parameters = (
  305. ComputerName => $ENV{COMPUTERNAME},
  306. AccessedShares => \@accessed_shares,
  307. Language => $Language,
  308. OverrideSafe => $fOverride_Safe
  309. );
  310. #
  311. # Lower the local shares on the release servers
  312. #
  313. logmsg( "Lowering release shares..." );
  314. if ( !ForEachBuild( \&LowerBuildShare,
  315. \%named_parameters,
  316. $Language,
  317. \@available_builds,
  318. $fSafe ) ) {
  319. errmsg( "Failed to remove shares, exiting." );
  320. return;
  321. }
  322. # Print out shares deleted / checked if in debug mode
  323. dbgmsg( "$_ share removed/verified." ) foreach @accessed_shares;
  324. #
  325. # Do the DFS work
  326. #
  327. my ( $dfs_map, $dfs_lock ) = GetDfsAccess();
  328. return if ( !defined $dfs_map );
  329. # Need DFS access in the next few functions
  330. $named_parameters{ DfsMap } = $dfs_map;
  331. logmsg( "Removing appropriate DFS links..." );
  332. if ( !ForEachBuild( \&RemoveDfsMappings,
  333. \%named_parameters,
  334. $Language,
  335. \@available_builds,
  336. $fSafe ) ) {
  337. errmsg( "Problem verifying/removing DFS links" );
  338. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  339. $dfs_lock->Unlock();
  340. return;
  341. }
  342. #
  343. # Do symbol server ops
  344. #
  345. # Note: this is explicitly the last step taken as
  346. # we special-case the symbol server "sku" to
  347. # only remove the link if it is the last one
  348. # remaining. This is done so that the
  349. # symbol link is torn down only when all
  350. # instances of the correspondig build have
  351. # been lowered.
  352. #
  353. # Override standard SKUs with our own
  354. @{$named_parameters{ DefinedSkus }} = ('sym');
  355. # Only want 'sym' on OS builds, not conglomerator builds
  356. my @os_builds = grep { $_ if IsOSBuild( $_ ) } @available_builds;
  357. logmsg( "Checking symbol server state..." );
  358. if ( !ForEachBuild( \&RemoveSymbolServerLink,
  359. \%named_parameters,
  360. $Language,
  361. \@os_builds,
  362. $fSafe ) ) {
  363. errmsg( "Problem removing symbols server DFS links." );
  364. $dfs_lock->Unlock();
  365. return;
  366. }
  367. logmsg( "Flushing outstanding DFS operations..." );
  368. if ( !(tied %$dfs_map)->Flush() ) {
  369. errmsg( "Problem processing DFS commands." );
  370. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  371. dfs_lock->Unlock();
  372. return;
  373. }
  374. #
  375. # Release the inter-machine lock
  376. #
  377. $dfs_lock->Unlock();
  378. return 1;
  379. }
  380. sub GetDfsAccess {
  381. my $dfs_root = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "DFSRootName" );
  382. if ( !defined $dfs_root ) {
  383. errmsg( "Unable to find 'DFSRootName' in INI file for $ENV{_BuildBranch}." );
  384. return;
  385. }
  386. dbgmsg( "Using DFS root $dfs_root" );
  387. my $lock_location = &GetIniSetting::GetSettingQuietEx( $ENV{ "_BuildBranch" }, $Language, "DFSSemaphore" );
  388. if ( !$lock_location ) {
  389. errmsg( "Unable to find DFSSemaphore value in INI file." );
  390. return;
  391. }
  392. # Still using a DFS hash that only gets updated on first
  393. # retrieval -- therefore we need to do our best to have
  394. # only one machine at a time updating the DFS. To that
  395. # extent we have setup a lock mechanism on a globally
  396. # accessible share (with 1 minute MAXIMUM intervals
  397. # between checks - 60000 ms = 1 min)
  398. my $synchronize_lock = new MultiMachineLock ($lock_location, 60000);
  399. if ( !defined $synchronize_lock ) {
  400. errmsg( "Problem acquiring lock using $lock_location" );
  401. return;
  402. }
  403. else {
  404. logmsg( "Acquiring lock for exclusive DFS access..." );
  405. while ( !$synchronize_lock->Lock() ) {
  406. # If we timed out, wait some more
  407. if ( 2 == $synchronize_lock->GetLastError() ) {
  408. # Last error message should contain the machine we are waiting on
  409. timemsg( $_ ) foreach ( split /\n/, $synchronize_lock->GetLastErrorMessage() );
  410. }
  411. else {
  412. errmsg ("Failed to acquire lock.");
  413. errmsg( $_ ) foreach ( split /\n/, $synchronize_lock->GetLastErrorMessage() );
  414. return;
  415. }
  416. }
  417. }
  418. # access DFS through a tied hash
  419. logmsg( "Accessing DFS information..." );
  420. my %dfs_map;
  421. if ( ! tie %dfs_map, 'StaticDfsMap', $dfs_root ) {
  422. errmsg( "Error accessing DFS." );
  423. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  424. $synchronize_lock->Unlock();
  425. return;
  426. }
  427. return (\%dfs_map, $synchronize_lock);
  428. }
  429. sub IsOSBuild {
  430. my $build_name = shift;
  431. return scalar($build_name =~ /\d+\.[^\.]+\.[^\.]+\.\d+-\d+$/)?1:0;
  432. }
  433. sub AllowQualityTransition {
  434. my $last_quality = shift;
  435. my $cur_quality = shift;
  436. if ( !exists $Ordered_Qualities{lc $last_quality} ||
  437. !exists $Ordered_Qualities{lc $cur_quality} ) {
  438. return;
  439. }
  440. # Allow transition to sav from any previous quality
  441. elsif ( lc $cur_quality eq 'sav' ) {
  442. return 1;
  443. }
  444. # Allow transition from pre/bvt to anything else
  445. elsif ( lc $last_quality eq 'pre' ||
  446. lc $last_quality eq 'bvt' ) {
  447. return 1;
  448. }
  449. # Don't allow transition from anything else to pre/bvt
  450. elsif ( lc $cur_quality eq 'pre' ||
  451. lc $cur_quality eq 'bvt' ) {
  452. return;
  453. }
  454. # Otherwise allow transitions based on order specified in %Ordered_Qualities
  455. elsif ( $Ordered_Qualities{lc $cur_quality} >= $Ordered_Qualities{lc $last_quality} ) {
  456. return 1;
  457. }
  458. else {
  459. return;
  460. }
  461. }
  462. sub CreateSymbolServerLink {
  463. my $named_parameters = shift;
  464. my ( $dfs_access, $build_number,
  465. $full_build_path, $branch, $platform, $quality, $language,
  466. $build_name ) =
  467. GetNamedParameters( $named_parameters,
  468. q(DfsMap), q(BuildNumber),
  469. q(FullBuildPath), q(Branch), q(Platform), q(Quality), q(Language),
  470. q(BuildName) );
  471. return if ( !defined $dfs_access );
  472. # Get current symbol server share
  473. my ( $current_symsrv_share, $sub_path ) = GetShareName( $named_parameters );
  474. if ( !defined $current_symsrv_share ) {
  475. dbgmsg( "No symbol server share defined for $branch -- skipping" );
  476. return 1;
  477. }
  478. # Default writeable share is just the $'d version of the non-writeable one
  479. my $current_writeable_symsrv_path = $current_symsrv_share;
  480. $current_writeable_symsrv_path =~ s/(.)$/$1\$/;
  481. # attach the subpath (if there is one)
  482. $current_writeable_symsrv_path .= "\\$sub_path" if ( $sub_path );
  483. # Default build_logs directory is a sibling to default symbols share
  484. my $current_symsrv_build_logs = $current_writeable_symsrv_path;
  485. $current_symsrv_build_logs =~ s/[^\\]+$/build_logs/;
  486. logmsg( "Verifying symbol server has updated shares for $platform..." );
  487. # Verify that the symbol server has the current build
  488. if ( ! -e $current_writeable_symsrv_path ) {
  489. errmsg( "Cannot see $current_writeable_symsrv_path ($!)." );
  490. return;
  491. }
  492. # Try to make the build_logs directory if it doesn't already exist;
  493. if ( ! -e $current_symsrv_build_logs &&
  494. ! ( mkdir $current_symsrv_build_logs, 0777 ) ) {
  495. errmsg( "Failed to create $current_symsrv_build_logs ($!)." );
  496. return;
  497. }
  498. # For some reason we maintain a QLY file in two locations
  499. if ( !ModifyQlyFile( $build_name, $quality, $current_writeable_symsrv_path ) ) {
  500. errmsg ( "Problem verifying/creating $quality.qly file on $current_writeable_symsrv_path." );
  501. return;
  502. }
  503. if ( !ModifyQlyFile( $build_name, $quality, $current_symsrv_build_logs ) ) {
  504. errmsg ( "Problem verifying/creating $quality.qly file on $current_symsrv_build_logs." );
  505. return;
  506. }
  507. # Done if going to SAV quality
  508. return 1 if ( lc $quality eq 'sav' );
  509. #
  510. # DFS work
  511. #
  512. # Check if we need to lower any latest.* shares for this build
  513. if ( !RemoveOldLatestDotQlyDfsLinks( $named_parameters ) ) {
  514. errmsg( "Problem verifying/lowering latest symbol server shares for $platform." );
  515. return;
  516. }
  517. # Remove any older numbered shares (older in timestamp)
  518. if ( !UnmapInconsistentNumberedLinks ( $named_parameters ) ) {
  519. errmsg( "Problem occurred checking numbered shares" );
  520. return;
  521. }
  522. # Now create the DFS link if necessary
  523. if ( !CreateDfsMappings( $named_parameters ) ) {
  524. errmsg( "Problem verifying/creating symbol server DFS links for $platform." );
  525. return;
  526. }
  527. return 1;
  528. }
  529. sub RemoveSymbolServerLink {
  530. my $named_parameters = shift;
  531. my ( $dfs_access, $platform, $branch,
  532. $build_number, $build_name ) =
  533. GetNamedParameters( $named_parameters,
  534. q(DfsMap), q(Platform), q(Branch),
  535. q(BuildNumber), q(BuildName) );
  536. return if ( !defined $dfs_access );
  537. # Get current symbol server share
  538. my ( $current_symsrv_share, $sub_path ) = GetShareName( $named_parameters );
  539. if ( !defined $current_symsrv_share ) {
  540. dbgmsg( "No symbol server share defined for $branch -- skipping." );
  541. return 1;
  542. }
  543. # Default writeable share is just the $'d version of the non-writeable one
  544. my $current_writeable_symsrv_path = $current_symsrv_share;
  545. $current_writeable_symsrv_path =~ s/(.)$/$1\$/;
  546. # attach the subpath (if there is one)
  547. $current_writeable_symsrv_path .= "\\$sub_path" if ( $sub_path );
  548. # Default build_logs directory is a sibling to default symbols share
  549. my $current_symsrv_build_logs = $current_writeable_symsrv_path;
  550. $current_symsrv_build_logs =~ s/[^\\]+$/build_logs/;
  551. # Now attach subpath (if any) to standard symbol share
  552. $current_symsrv_share .= "\\$sub_path" if ( $sub_path );
  553. # Need all prior DFS actions to have completed
  554. if ( !(tied %$dfs_access)->Flush() ) {
  555. errmsg( "Problem processing DFS commands." );
  556. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  557. return;
  558. }
  559. # We only remove the symbol link when it is the last
  560. # link left -- we will assume if it is the last
  561. # link under the numbered share that it is the last
  562. # relevant link under the latest.* shares as well
  563. $named_parameters->{ BuildID } = $build_number;
  564. if ( !IsLastLinkUnderParent( $named_parameters ) ) {
  565. dbgmsg( "$build_name: leaving symbol links untouched" );
  566. return 1;
  567. }
  568. logmsg( "Verifying symbol server links have been removed for $platform..." );
  569. #
  570. # Remove the QLY files
  571. #
  572. # For some reason we maintain a QLY file in two locations
  573. dbgmsg ( "Deleting QLY file from $current_writeable_symsrv_path" );
  574. if ( !DeleteQlyFile( $current_writeable_symsrv_path ) ) {
  575. errmsg ( "Problem reading/removing QLY file from $current_writeable_symsrv_path." );
  576. return;
  577. }
  578. dbgmsg( "Deleting QLY file from $current_symsrv_build_logs" );
  579. if ( !DeleteQlyFile( $current_symsrv_build_logs ) ) {
  580. errmsg ( "Problem reading/removing QLY file from $current_symsrv_build_logs." );
  581. return;
  582. }
  583. #
  584. # Now remove the DFS link
  585. #
  586. if ( !RemoveDfsMappings( $named_parameters ) ) {
  587. errmsg( "Problem verifying/removing symbol server DFS links for $platform." );
  588. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  589. return;
  590. }
  591. return 1;
  592. }
  593. sub CreateDfsMappings {
  594. my $named_parameters = shift;
  595. my ($dfs_access, $computer_name, $quality, $build_number, $language,
  596. $platform, $time_stamp, $branch, $sku, $fsafe, $fconglomerator_build,
  597. $flang_neutral_build ) =
  598. GetNamedParameters( $named_parameters,
  599. q(DfsMap), q(ComputerName), q(Quality), q(BuildNumber), q(Language),
  600. q(Platform), q(TimeStamp), q(Branch), q(Sku), q(SafeFlag), q(IsConglomeratorBuild),
  601. q(IsLangNeutralBuild) );
  602. return if ( !defined $dfs_access );
  603. # Done if going to SAV quality
  604. return 1 if ( lc $quality eq 'sav' );
  605. # Done if we are at a different and LOWER quality than other builds
  606. # in our numbered links (e.g. if our build number has been
  607. # raised to TST and we are just being raised to BVT, we don't
  608. # want to put ourselves in the number link yet)
  609. if ( IsQualityHigherAtNumberedLink( $named_parameters )) {
  610. wrnmsg( "Not creating DFS links!" );
  611. return 1;
  612. }
  613. # latest.* DFS link
  614. # only gets created if the current branch is
  615. # responsible for producing the current SKU
  616. my $latest_dfs_link;
  617. $named_parameters->{ BuildID } = "latest.$quality";
  618. $latest_dfs_link = GetDfsLinkName( $named_parameters );
  619. if ( !defined $latest_dfs_link ) {
  620. errmsg( "Do not know how to create latest.* DFS links for $branch / $sku." );
  621. return;
  622. }
  623. # build number link
  624. $named_parameters->{ BuildID } = $build_number;
  625. my $numerical_dfs_link = GetDfsLinkName( $named_parameters );
  626. if ( !defined $numerical_dfs_link ) {
  627. errmsg( "Do not know how to create numbered DFS links for $branch / $sku." );
  628. return;
  629. }
  630. # \\machine\share[\<subdir>] that makes up local release path
  631. my ( $machine_share, $sub_path ) = GetShareName( $named_parameters );
  632. if ( !defined $machine_share ) {
  633. errmsg( "Do not know how to create local shares for $branch / $sku." );
  634. return;
  635. }
  636. # Tack on the subpath to the share if it exists
  637. $machine_share .= "\\$sub_path" if ( $sub_path );
  638. # Do the DFS mappings
  639. dbgmsg( "Adding/Verifying: $numerical_dfs_link contains $machine_share" );
  640. if ( !($dfs_access->{$numerical_dfs_link} = $machine_share) ) {
  641. errmsg( "Problem mapping to DFS." );
  642. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  643. return;
  644. }
  645. # Link language neutral builds to misc directory as well
  646. if ( $flang_neutral_build) {
  647. my $misc_dfs_link = $numerical_dfs_link;
  648. $misc_dfs_link =~ s/$language/misc/i;
  649. if ( !($dfs_access->{$misc_dfs_link} = $machine_share) ) {
  650. errmsg( "Problem mapping to DFS." );
  651. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  652. return;
  653. }
  654. }
  655. if ( defined $latest_dfs_link &&
  656. !$fsafe ) {
  657. dbgmsg( "Adding/Verifying: $latest_dfs_link contains $machine_share" );
  658. if ( !($dfs_access->{$latest_dfs_link} = $machine_share) ) {
  659. errmsg( "Problem mapping to DFS." );
  660. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  661. return;
  662. }
  663. }
  664. # Time for some special magic on the DFS servers:
  665. # hide/unhide directories and create *.BLD/*.SRV files.
  666. # Skip this step for conglomerator builds
  667. if ( !$fconglomerator_build &&
  668. !MagicDFSSettings( $named_parameters ) ) {
  669. wrnmsg( "Problem verifying/writing to DFS server machines." );
  670. }
  671. return 1;
  672. }
  673. sub IsQualityHigherAtNumberedLink {
  674. my $named_parameters = shift;
  675. my ( $dfs_access, $quality, $build_number, $branch, $sku ) =
  676. GetNamedParameters( $named_parameters,
  677. q(DfsMap), q(Quality), q(BuildNumber), q(Branch), q(Sku) );
  678. return if ( !defined $dfs_access );
  679. # build number link
  680. $named_parameters->{ BuildID } = $build_number;
  681. my $numerical_dfs_link = GetDfsLinkName( $named_parameters );
  682. # Don't know if quality is higher at numbered
  683. # link, if we don't know how to make the link
  684. return 0 if ( !defined $numerical_dfs_link );
  685. # If there are no builds, then we are OK
  686. return 0 if ( !exists $dfs_access->{$numerical_dfs_link} );
  687. my @current_builds = @{$dfs_access->{$numerical_dfs_link}};
  688. # Otherwise attempt to find a latest.* link for one of the shares
  689. foreach my $share_to_check ( @current_builds ) {
  690. my @links_to_share = @{ $dfs_access->{$share_to_check} };
  691. next if ( !@links_to_share );
  692. foreach my $link ( @links_to_share ) {
  693. my $build_id = GetBuildIdFromDfsLink( $named_parameters, $link );
  694. # Determine if this is a latest.* link
  695. if ( defined $build_id &&
  696. $build_id =~ /^latest\.(.*)$/ ) {
  697. # Compare quality with our current quality
  698. if ( lc $1 ne lc $quality &&
  699. !AllowQualityTransition( $1, $quality ) ) {
  700. wrnmsg( "Other builds linked to $build_number are at $1 quality:" );
  701. wrnmsg( "($share_to_check)." );
  702. return 1;
  703. }
  704. }
  705. }
  706. }
  707. # Did not find a latest.* higher than our current quality
  708. return 0;
  709. }
  710. sub UnmapInconsistentNumberedLinks {
  711. my $named_parameters = shift;
  712. my ($dfs_access, $build_number, $build_time, $branch, $sku,
  713. $quality) =
  714. GetNamedParameters( $named_parameters,
  715. q(DfsMap), q(BuildNumber), q(TimeStamp), q(Branch), q(Sku),
  716. q(Quality) );
  717. # Done if going to SAV quality
  718. return 1 if ( lc $quality eq 'sav' );
  719. $named_parameters->{ BuildID } = $build_number;
  720. my $dfs_numbered_link = GetDfsLinkName( $named_parameters );
  721. if ( !defined $dfs_numbered_link ) {
  722. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  723. return;
  724. }
  725. my @current_builds_linked_at_number;
  726. @current_builds_linked_at_number = @{$dfs_access->{$dfs_numbered_link}} if ( exists $dfs_access->{$dfs_numbered_link} );
  727. my $ffound_inconsistency;
  728. # Check for any shares linked to number that do not match our specifications
  729. foreach ( @current_builds_linked_at_number ) {
  730. my ($current_share_build_number, $current_share_build_time) =
  731. GetBuildNumberAndTsFromShare( $named_parameters, $_ );
  732. if ( !defined $current_share_build_number ) {
  733. errmsg( "Can't determine build number at $dfs_numbered_link from $current_builds_linked_at_number[0] for $branch / $sku." );
  734. return;
  735. }
  736. # If the build number is not what we expected then something is really wrong
  737. if ( $build_number != $current_share_build_number ) {
  738. errmsg( "Share $_ linked at $dfs_numbered_link" );
  739. return;
  740. }
  741. # Check time-stamp
  742. if ( $build_time lt $current_share_build_time ) {
  743. if ( $fReplaceAtNumberedLink ) {
  744. wrnmsg( "Replacing $build_number ($current_share_build_time) which appears newer than $build_number ($build_time)." );
  745. $ffound_inconsistency = 1;
  746. last;
  747. }
  748. else {
  749. errmsg( "Won't replace $build_number ($current_share_build_time) with $build_number ($build_time) without use of -replace" );
  750. return;
  751. }
  752. }
  753. elsif ( $build_time gt $current_share_build_time ) {
  754. dbgmsg( "Replacing links of older build at $build_number: $build_number ($current_share_build_time)" );
  755. $ffound_inconsistency = 1;
  756. last;
  757. }
  758. }
  759. # Unmap the share if an inconsistency was found
  760. if ( $ffound_inconsistency ) {
  761. return if ( !RemoveDfsLink( $named_parameters ) );
  762. }
  763. return 1;
  764. }
  765. sub RemoveOldLatestDotQlyDfsLinks {
  766. my $named_parameters = shift;
  767. my ($dfs_access, $computer_name, $quality, $build_number, $build_name,
  768. $build_time, $language, $platform, $branch, $sku,
  769. $fsafe ) =
  770. GetNamedParameters( $named_parameters,
  771. q(DfsMap), q(ComputerName), q(Quality), q(BuildNumber), q(BuildName),
  772. q(TimeStamp), q(Language), q(Platform), q(Branch), q(Sku),
  773. q(SafeFlag) );
  774. return if ( !defined $dfs_access );
  775. # force release is an option
  776. my $override_safe = $named_parameters->{ q(OverrideSafe) };
  777. # Done if going to SAV quality
  778. return 1 if ( lc $quality eq 'sav' );
  779. my @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  780. # Leave latest.<quality> shares alone
  781. # with the exception of pre and bvt
  782. # if -safe was specified
  783. if ( !$fsafe ) {
  784. $named_parameters->{ BuildID } = "latest.$quality";
  785. my $latest_dfs_link = GetDfsLinkName( $named_parameters );
  786. if ( !defined $latest_dfs_link ) {
  787. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  788. return;
  789. }
  790. my @current_latest_builds;
  791. @current_latest_builds = @{$dfs_access->{$latest_dfs_link}} if ( exists $dfs_access->{$latest_dfs_link} );
  792. # Trying to enforce rule that you cannot move from a release quality
  793. # latest.* (tst, idw, ids) to a non-release quality (pre, bvt)
  794. foreach ( @build_ids ) {
  795. if ( /^latest\.(.+)$/ &&
  796. !AllowQualityTransition( $1, $quality ) ) {
  797. if ( !defined $override_safe ) {
  798. errmsg( "Not allowed to move from '$1' to '$quality' quality." );
  799. return;
  800. }
  801. else {
  802. wrnmsg( "Forcibly moving from '$1' to '$quality' quality." );
  803. }
  804. }
  805. }
  806. my $fforce_safe_flag;
  807. my ($current_latest_build_number, $current_latest_build_time) = ('', 0);
  808. # while we are at we will check for inconsistencies
  809. my ($last_build_number, $last_build_time);
  810. my $ffound_inconsistency;
  811. foreach ( @current_latest_builds ) {
  812. ($current_latest_build_number, $current_latest_build_time) =
  813. GetBuildNumberAndTsFromShare( $named_parameters, $_ );
  814. if ( !defined $current_latest_build_number ) {
  815. errmsg( "Can't determine build number at $latest_dfs_link from $current_latest_builds[0] for $branch / $sku." );
  816. return;
  817. }
  818. # Consistency check
  819. if ( $last_build_number && $last_build_number ne $current_latest_build_number ||
  820. $last_build_time && $last_build_time ne $current_latest_build_time ) {
  821. wrnmsg( "Inconsistent links found at $latest_dfs_link:" );
  822. wrnmsg( "$current_latest_build_number ($current_latest_build_time) and $last_build_number ($last_build_time)" );
  823. $ffound_inconsistency = 1;
  824. }
  825. ($last_build_number, $last_build_time) = ($current_latest_build_number, $current_latest_build_time);
  826. }
  827. # If we found an inconsistency, let's nuke the share
  828. if ( $ffound_inconsistency ) {
  829. wrnmsg( "Links were found to be inconsisent -- removing share" );
  830. $current_latest_build_number = 1;
  831. }
  832. # Usually don't want to overwrite newer builds
  833. if ( $current_latest_build_number &&
  834. ($current_latest_build_number > $build_number ||
  835. $current_latest_build_number == $build_number &&
  836. $current_latest_build_time gt $build_time) )
  837. {
  838. if ( !$override_safe ) {
  839. dbgmsg( "Current build for $platform / $sku at $quality is $current_latest_build_number ($current_latest_build_time), switching to '-safe' mode." );
  840. $named_parameters->{ForceSafeFlag}{$build_name} = 1;
  841. $fforce_safe_flag = 1;
  842. } else {
  843. wrnmsg( "Forcibly moving latest.$quality from $current_latest_build_number ($current_latest_build_time) to $build_number ($build_time)." );
  844. }
  845. }
  846. # Make sure we didn't set -safe flag for user
  847. if ( !$fforce_safe_flag ) {
  848. # If current latest.* build exists and does
  849. # not have our build number and timestamp, lower it
  850. if ( $current_latest_build_number &&
  851. ($current_latest_build_number != $build_number ||
  852. $current_latest_build_time ne $build_time) ) {
  853. if ( !$ffound_inconsistency ) {
  854. dbgmsg( "Lowering $latest_dfs_link because $current_latest_build_number ($current_latest_build_time) != current build $build_number ($build_time)." );
  855. }
  856. $named_parameters->{ BuildID } = "latest.$quality";
  857. return if ( !RemoveDfsLink( $named_parameters ) );
  858. }
  859. # Also need to lower the current numbered share if we are the
  860. # first entry in our latest.* link, as we don't want other
  861. # shares that are not up at our quality to be accessible
  862. # through the same number as ourselves
  863. if ( !exists $dfs_access->{$latest_dfs_link} ) {
  864. $named_parameters->{ BuildID } = $build_number;
  865. return if ( !RemoveDfsLink( $named_parameters ) );
  866. }
  867. }
  868. }
  869. #
  870. # Special rules for BVT and PRE:
  871. #
  872. # If we are currently at latest.bvt or latest.pre
  873. # and are moving to another quality,
  874. # remove latest.<bvt/pre>
  875. #
  876. foreach ( @build_ids ) {
  877. if ( /^latest\.(bvt|pre)$/i &&
  878. lc $1 ne lc $quality ) {
  879. $named_parameters->{ BuildID } = "latest.$1";
  880. return if ( !RemoveDfsLink( $named_parameters ) );
  881. }
  882. }
  883. return 1;
  884. }
  885. # FUTURE:
  886. # This function in most cases would seem to do the same
  887. # as delete <share_name> already implemtened in the tied hash
  888. # Might consider removing this logic...
  889. sub RemoveDfsMappings {
  890. my $named_parameters = shift;
  891. my ( $dfs_access, $branch, $language, $sku, $build_number,
  892. $flang_neutral_build ) =
  893. GetNamedParameters( $named_parameters,
  894. q(DfsMap), q(Branch), q(Language), q(Sku), q(BuildNumber),
  895. q(IsLangNeutralBuild) );
  896. my @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  897. my ( $share_name, $sub_path ) = GetShareName( $named_parameters );
  898. if ( !defined $share_name ) {
  899. dbgmsg( "No local share definition for $branch / $sku -- skipping." );
  900. return 1;
  901. }
  902. $share_name .= "\\$sub_path" if ( $sub_path );
  903. my $fsuccess = 1;
  904. foreach ( @build_ids ) {
  905. $named_parameters->{BuildID} = $_;
  906. my $dfs_link = GetDfsLinkName( $named_parameters );
  907. if ( !defined $dfs_link ) {
  908. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  909. return;
  910. }
  911. $fsuccess &&= (tied %$dfs_access)->RemoveShareFromLink( $dfs_link, $share_name );
  912. }
  913. # If we are a misc link and the only remaining link is
  914. # the misc DFS link, remove it (we're assuming we must have
  915. # just tore down the last language still raised for it)
  916. if ( $flang_neutral_build ) {
  917. my @links_to_share = $dfs_access->{$share_name};
  918. $named_parameters->{BuildID} = $build_number;
  919. my $misc_dfs_link = GetDfsLinkName( $named_parameters );
  920. if ( !defined $misc_dfs_link ) {
  921. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  922. return;
  923. }
  924. $misc_dfs_link =~ s/$language/misc/i;
  925. if ( !@links_to_share ||
  926. !grep {!/$misc_dfs_link/i} @links_to_share ) {
  927. $fsuccess &&= delete $dfs_access->{$share_name};
  928. }
  929. }
  930. return $fsuccess;
  931. }
  932. sub IsSkuLinkedToLatest {
  933. my $named_parameters = shift;
  934. my ( $branch, $sku ) =
  935. GetNamedParameters( $named_parameters,
  936. q(Branch), q(Sku) );
  937. return if ( !defined $branch );
  938. if ( IsClientSku( $named_parameters ) && IsClientBranch( $named_parameters ) ||
  939. IsServerSku( $named_parameters ) && IsServerBranch( $named_parameters ) ) {
  940. return 1;
  941. }
  942. else {
  943. return 0;
  944. }
  945. }
  946. sub IsServerSku {
  947. my $named_parameters = shift;
  948. my ( $sku ) =
  949. GetNamedParameters( $named_parameters,
  950. q(Sku) );
  951. return if ( !defined $sku );
  952. return scalar grep { lc$sku eq $_ } @ServerSkus;
  953. }
  954. sub IsClientSku {
  955. my $named_parameters = shift;
  956. my ( $sku ) =
  957. GetNamedParameters( $named_parameters,
  958. q(Sku) );
  959. return if ( !defined $sku );
  960. return scalar grep { lc$sku eq $_ } @ClientSkus;
  961. }
  962. sub IsServerBranch {
  963. my $named_parameters = shift;
  964. my ( $branch, $language ) =
  965. GetNamedParameters( $named_parameters,
  966. q(Branch), q(Language) );
  967. return if ( !defined $branch );
  968. if ( !exists $named_parameters->{ServerBranches}{$branch} ) {
  969. $named_parameters->{ServerBranches}{$branch} = (&GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSLatestServerSkus' )?1:0);
  970. dbgmsg( "$branch is a server branch." ) if ( $named_parameters->{ServerBranches}{$branch} );
  971. }
  972. return $named_parameters->{ServerBranches}{$branch};
  973. }
  974. sub IsClientBranch {
  975. my $named_parameters = shift;
  976. my ( $branch, $language ) =
  977. GetNamedParameters( $named_parameters,
  978. q(Branch), q(Language) );
  979. return if ( !defined $branch );
  980. if ( !exists $named_parameters->{ClientBranches}{$branch} ) {
  981. $named_parameters->{ClientBranches}{$branch} = (&GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSLatestClientSkus' )?1:0);
  982. dbgmsg( "$branch is a client branch." ) if ( $named_parameters->{ClientBranches}{$branch} );
  983. }
  984. return $named_parameters->{ClientBranches}{$branch};
  985. }
  986. sub GetBldFileExtension {
  987. my $named_parameters = shift;
  988. my ( $branch ) =
  989. GetNamedParameters( $named_parameters,
  990. q(Branch) );
  991. return if ( !defined $branch );
  992. # IF this branch is a client branch, or this
  993. # branch is both client and server, use BLD
  994. if ( IsClientBranch( $named_parameters ) ) {
  995. return 'BLD';
  996. }
  997. else {
  998. return 'SRV';
  999. }
  1000. }
  1001. sub RemoveDfsLink {
  1002. my $named_parameters = shift;
  1003. my ( $dfs_access, $branch, $build_id, $sku ) =
  1004. GetNamedParameters( $named_parameters,
  1005. q(DfsMap), q(Branch), q(BuildID), q(Sku) );
  1006. return if ! defined ( $build_id );
  1007. if ( IsLastLinkUnderParent( $named_parameters ) ) {
  1008. # Delete <build>.BLD / <build>.SRV file
  1009. #
  1010. # If the BLD/SRV file still exists when we unmap the link,
  1011. # there will no longer be a DFS link but the directory
  1012. # structure will remain, making it look like there is
  1013. # still a DFS link
  1014. # If we are the last numbered link, assume we are the
  1015. # last relevant latest.* link as well, and remove
  1016. # BLD/SRV files from latest.* as well (this code was added
  1017. # for the XP client/server split)
  1018. my @build_ids;
  1019. if ( $build_id =~ /^\d+$/ ) {
  1020. @build_ids = GetBuildIdsFromCurrentLinks( $named_parameters );
  1021. }
  1022. else {
  1023. @build_ids = ($build_id);
  1024. }
  1025. my $bldfile_ext = GetBldFileExtension( $named_parameters );
  1026. foreach ( @build_ids ) {
  1027. $named_parameters->{BuildID} = $_;
  1028. foreach ( GetBldFileDfsPathNames( $named_parameters ) ) {
  1029. dbgmsg( "Removing $bldfile_ext file from link's parent directory" );
  1030. my @bld_files = grep {$_ if ( ! -d $_ )} globex "$_\\*.$bldfile_ext";
  1031. # 0 = success
  1032. # 2 = couldn't find file (may be a failure or there may be no BLD file;
  1033. # have to treat as a success as there is no way
  1034. # to tell the difference - $^E doesn't help)
  1035. if ( $! != 0 && $! != 2 ) {
  1036. wrnmsg( "Trying to find existing $bldfile_ext file: $!" );
  1037. }
  1038. # Delete all BLD/SRV files
  1039. foreach ( @bld_files ) {
  1040. if ( !unlink $_ ) { wrnmsg( "Unable to delete $_ ($!)." ) }
  1041. }
  1042. }
  1043. }
  1044. # Make sure we are pointing at the right build_id after all of this
  1045. $named_parameters->{BuildID} = $build_id;
  1046. }
  1047. #
  1048. # Now actually remove the DFS link
  1049. #
  1050. my $dfs_link = GetDfsLinkName( $named_parameters );
  1051. if ( !defined $dfs_link ) {
  1052. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  1053. return;
  1054. }
  1055. dbgmsg( "Removing/Verifying $dfs_link from DFS." );
  1056. if ( !delete $dfs_access->{$dfs_link} ) {
  1057. errmsg( "Problem lowering $dfs_link from DFS." );
  1058. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1059. return;
  1060. }
  1061. return 1;
  1062. }
  1063. sub MagicDFSSettings {
  1064. my $named_parameters = shift;
  1065. my ($dfs_access, $branch, $build_number, $build_name,
  1066. $language, $sku, $platform, $quality,
  1067. $fsafe ) =
  1068. GetNamedParameters( $named_parameters,
  1069. q(DfsMap), q(Branch), q(BuildNumber), q(BuildName),
  1070. q(Language), q(Sku), q(Platform), q(Quality),
  1071. q(SafeFlag) );
  1072. return if ( !defined $dfs_access );
  1073. #
  1074. # We need all new DFS links to exist in order to find
  1075. # new paths, so flush any outstanding commands here
  1076. #
  1077. if ( !(tied %$dfs_access)->Flush() ) {
  1078. errmsg( "Problem processing DFS commands." );
  1079. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1080. return;
  1081. }
  1082. #
  1083. # Hide / Unhide paths on the DFS machine as we move
  1084. # between pre/bvt and other qualities
  1085. #
  1086. # NOTE: following original code's precedent and not
  1087. # hiding the latest.* subdirs
  1088. #
  1089. $named_parameters->{ BuildID } = $build_number;
  1090. foreach ( GetHideableDfsPathNames( $named_parameters ) ) {
  1091. my $attribs = 0; # set to avoid warning / actually first used to get return value
  1092. my $fhidden;
  1093. my $fhide_share;
  1094. if ( !GetAttributes( $_, $attribs ) ) {
  1095. wrnmsg( "Unable to get current hidden/unhidden status of $_ ($^E) -- skipping." );
  1096. }
  1097. $fhidden = $attribs & 2;
  1098. $fhide_share = ($quality =~ /^(bvt|pre)$/i);
  1099. # See if we need to switch hidden attribute on/off
  1100. if ( $fhide_share &&
  1101. !$fhidden &&
  1102. !SetAttributes( $_, $attribs | 2 ) ) {
  1103. wrnmsg( "Could not hide $_ ($^E)." );
  1104. }
  1105. elsif ( !$fhide_share &&
  1106. $fhidden &&
  1107. !SetAttributes( $_, $attribs & ~2 ) ) {
  1108. wrnmsg( "Could not unhide $_ ($^E)." );
  1109. }
  1110. }
  1111. #
  1112. # Write <build>.BLD / <build>.SRV file
  1113. #
  1114. my @builds_to_touch = ( $build_number );
  1115. if ( !$fsafe ) { push @builds_to_touch, "latest.$quality" }
  1116. my $bldfile_ext = GetBldFileExtension( $named_parameters );
  1117. foreach ( @builds_to_touch ) {
  1118. $named_parameters->{ BuildID } = $_;
  1119. foreach ( GetBldFileDfsPathNames( $named_parameters ) ) {
  1120. my $fbldfile_already_set;
  1121. my @bld_files = grep { $_ if ( ! -d $_ ) } globex "$_\\*.$bldfile_ext";
  1122. # 0 = success
  1123. # 2 = couldn't find file (may be a failure or there may be no BLD file;
  1124. # have to treat as a success as there is no way
  1125. # to tell the difference - $^E doesn't help)
  1126. if ( $! != 0 && $! != 2 ) {
  1127. wrnmsg( "Trying to find existing $bldfile_ext file: $!" );
  1128. }
  1129. # If there is more than one, delete them all and start anew
  1130. elsif ( @bld_files > 1 &&
  1131. ! unlink @bld_files ) {
  1132. wrnmsg( "Multiple *.$bldfile_ext files found at $_ -- unable to delete ($!)." );
  1133. }
  1134. # If there is just one, see if it matches current criteria
  1135. elsif ( 1 == @bld_files ) {
  1136. if ( $bld_files[0] =~ /\\$build_name.$bldfile_ext$/ ) {
  1137. unless ( open BLDFILE, "$bld_files[0]" ) {
  1138. wrnmsg( "Unable to read $bld_files[0] ($!)." );
  1139. next;
  1140. }
  1141. my $cur_bld_quality = <BLDFILE>;
  1142. close BLDFILE;
  1143. chomp $cur_bld_quality;
  1144. if ( lc $quality eq lc $cur_bld_quality ) {
  1145. # Current bld file matches all of our criteria
  1146. $fbldfile_already_set = 1;
  1147. }
  1148. }
  1149. # If something about the current bld file did not match,
  1150. # delete it in preparation for creating a new one
  1151. if ( !$fbldfile_already_set &&
  1152. ! unlink $bld_files[0] ) {
  1153. wrnmsg( "Unable to delete $bld_files[0] ($!)." );
  1154. }
  1155. }
  1156. # Write out new BLD file if necessary
  1157. if ( !$fbldfile_already_set ) {
  1158. unless ( open WRITE_BLDFILE, "> $_\\$build_name.$bldfile_ext" ) {
  1159. wrnmsg( "Unable to write $_\\$build_name.$bldfile_ext ($!)." );
  1160. next;
  1161. }
  1162. print WRITE_BLDFILE "$quality\n";
  1163. close WRITE_BLDFILE
  1164. }
  1165. }
  1166. }
  1167. return 1;
  1168. }
  1169. sub GetNamedParameters {
  1170. my $named_parameters = shift;
  1171. my ( $name, @parameter_list );
  1172. while ( $name = shift ) {
  1173. if ( !exists $named_parameters->{ $name } ) {
  1174. my $called_by = (caller 1)[3];
  1175. errmsg( "Undefined parameter \"$name\" to $called_by." );
  1176. return;
  1177. }
  1178. push @parameter_list, $named_parameters->{ $name };
  1179. }
  1180. return @parameter_list;
  1181. }
  1182. sub ForEachBuild {
  1183. my $func = shift;
  1184. my $named_parameters = shift;
  1185. my $language = shift;
  1186. my $builds = shift;
  1187. my $fsafe = shift;
  1188. my $build;
  1189. foreach $build ( @$builds ) {
  1190. my ( $build_number, $arch, $debug_type, $branch, $time_stamp );
  1191. my $fconglomerator_build = 0;
  1192. my $flang_neutral_build = 0;
  1193. # Normal build
  1194. if ( IsOSBuild( $build ) ) {
  1195. ( $build_number, $arch, $debug_type, $branch, $time_stamp ) = build_name_parts( $build );
  1196. }
  1197. # Conglomerator skus
  1198. elsif ( $build =~ /([^\\]+)\\(\d+)\.([^\.]+)(?:\.(\d+-\d+))?$/ ) {
  1199. ( $build_number, $arch, $debug_type, $branch, $time_stamp ) = ( $2, '', '', $3, $4 || 0 );
  1200. $flang_neutral_build = (lc$1 eq 'misc')?1:0;
  1201. $fconglomerator_build = 1;
  1202. }
  1203. if ( !defined $build_number ) {
  1204. errmsg( "Unable to determine build information for $build." );
  1205. return;
  1206. }
  1207. my $platform = "$arch$debug_type";
  1208. # use already defined skus or default to looking them up
  1209. my @skus;
  1210. if ( exists $named_parameters->{ DefinedSkus } ) {
  1211. @skus = @{$named_parameters->{ DefinedSkus }};
  1212. }
  1213. elsif ( $fconglomerator_build ) {
  1214. @skus = GetSkusForConglomeratorBuild( $build );
  1215. }
  1216. else {
  1217. @skus = GetSkusForBuild( $build, $language, $debug_type, $arch );
  1218. }
  1219. if ( !@skus ) {
  1220. wrnmsg( "No skus defined for $build ($language) - skipping." );
  1221. # Skip this build
  1222. next;
  1223. }
  1224. # Setup common parameters for functions
  1225. $named_parameters->{ FullBuildPath } = $build;
  1226. if ( $fconglomerator_build ) {
  1227. ($named_parameters->{ BuildName }) = $build =~ /\\([^\\]+\\[^\\]+)$/;
  1228. }
  1229. else {
  1230. $named_parameters->{ BuildName } = basename( $build );
  1231. }
  1232. $named_parameters->{ BuildNumber } = $build_number;
  1233. $named_parameters->{ DebugType } = $debug_type;
  1234. $named_parameters->{ Branch } = $branch;
  1235. $named_parameters->{ Platform } = $platform;
  1236. $named_parameters->{ TimeStamp } = $time_stamp;
  1237. $named_parameters->{ IsConglomeratorBuild } = $fconglomerator_build;
  1238. $named_parameters->{ IsLangNeutralBuild } = $flang_neutral_build;
  1239. if ( defined $fsafe ||
  1240. $named_parameters->{ForceSafeFlag}{$named_parameters->{ BuildName }}) {
  1241. $named_parameters->{ SafeFlag } = 1;
  1242. }
  1243. else {
  1244. $named_parameters->{ SafeFlag } = 0;
  1245. }
  1246. # Loop through all supported flavors
  1247. my $sku;
  1248. foreach $sku ( @skus ) {
  1249. $named_parameters->{ Sku } = $sku;
  1250. # Client/server split: skip skus
  1251. # that are not associated with branch
  1252. next if ( !IsSkuLinkedToLatest( $named_parameters ) );
  1253. if ( !&$func( $named_parameters ) ) {
  1254. errmsg( "Problem occurred during processing of $sku." );
  1255. return;
  1256. }
  1257. }
  1258. }
  1259. return 1;
  1260. }
  1261. sub IsLastLinkUnderParent {
  1262. my $named_parameters = shift;
  1263. my ( $dfs_access, $build_id, $branch, $sku ) =
  1264. GetNamedParameters( $named_parameters,
  1265. q(DfsMap), q(BuildID), q(Branch), q(Sku) );
  1266. return if ( !defined $dfs_access );
  1267. my $dfs_link = GetDfsLinkName( $named_parameters );
  1268. if ( !defined $dfs_link ) {
  1269. errmsg( "Do not know how to create DFS links for $branch / $sku." );
  1270. return;
  1271. }
  1272. my ( $cur_share_name, $sub_path ) = GetShareName( $named_parameters );
  1273. if ( !defined $cur_share_name ) {
  1274. errmsg( "Do not know how to create release shares for $branch / $sku." );
  1275. return;
  1276. }
  1277. $cur_share_name .= "\\$sub_path" if ( $sub_path );
  1278. #
  1279. # First check that we are the last entry in the current dfs link
  1280. #
  1281. my @cur_dfs_links;
  1282. @cur_dfs_links = @{ $dfs_access->{$dfs_link} } if ( exists $dfs_access->{$dfs_link} );
  1283. if ( !@cur_dfs_links ||
  1284. @cur_dfs_links != 1 ||
  1285. lc $cur_share_name ne lc $cur_dfs_links[0] ) {
  1286. return;
  1287. }
  1288. #
  1289. # Now check that we are the last link under our parent directory
  1290. #
  1291. # Construct full path to dfs link's parent directory
  1292. my $full_dfs_link = (tied %$dfs_access)->GetDfsRoot(). "\\$dfs_link";
  1293. my ($parent_directory) = $dfs_link =~ /^(.*)\\.*$/;
  1294. $parent_directory = (tied %$dfs_access)->GetDfsRoot(). '\\'. $parent_directory;
  1295. # Loop through all directory entries looking for
  1296. # any subdirectories that do not match our share
  1297. # name
  1298. my @parent_entries = grep { $_ if ( -d $_ ) } globex "$parent_directory\\*";
  1299. foreach ( @parent_entries ) {
  1300. # It is a directory -- if it isn't us assume
  1301. # it refers to another existing link
  1302. # WARNING: Special case the 'test' subdirectory
  1303. # so we don't count it during
  1304. # this check (we will clean up if
  1305. # all that is stopping us is 'test')
  1306. if ( lc $_ ne lc $full_dfs_link &&
  1307. $_ !~ /\\test$/i ) {
  1308. return;
  1309. }
  1310. }
  1311. # We must be the only link under our parent directory
  1312. return 1;
  1313. }
  1314. sub GetSkusForBuild {
  1315. my $build = shift;
  1316. my $language = shift;
  1317. my $build_type = shift;
  1318. my $architecture = shift;
  1319. my %skus = cksku::GetSkus( $language, build_arch( $build ) );
  1320. my @returned_skus;
  1321. @returned_skus = ( sort keys %skus );
  1322. # WinPE image
  1323. if ( lc $language eq 'usa' &&
  1324. lc $build_type eq 'fre' ) {
  1325. push @returned_skus, 'winpe';
  1326. }
  1327. # UpgAdv image
  1328. if ( lc $architecture eq 'x86' &&
  1329. lc $build_type eq 'fre' ) {
  1330. push @returned_skus, 'upgadv';
  1331. }
  1332. # Another special case for PSU:
  1333. # need to link to 'resources'
  1334. if ( lc $language eq 'psu' ) {
  1335. push @returned_skus, 'resources';
  1336. }
  1337. # Special case 'bin' -- everyone gets one
  1338. push @returned_skus, 'bin';
  1339. return @returned_skus;
  1340. }
  1341. sub GetSkusForConglomeratorBuild {
  1342. my $build = shift;
  1343. my @possible_skus = @ConglomeratorSkus;
  1344. # BensonT: Non US builds do not use symbolcd
  1345. if (lc$Language ne "usa") {
  1346. @possible_skus = grep {lc$_ ne 'symbolcd'} @possible_skus;
  1347. }
  1348. my @skus;
  1349. foreach ( @possible_skus )
  1350. {
  1351. push @skus, $_ if ( -e "$build\\$_" );
  1352. }
  1353. return @skus;
  1354. }
  1355. #
  1356. # Returns:
  1357. #
  1358. # ( share, subpath )
  1359. # An array consisting of the full-share name (\\server\share) and
  1360. # a subpath. \\server\share\subpath would take you to your
  1361. # expected destination
  1362. #
  1363. sub GetShareName {
  1364. my $named_parameters = shift;
  1365. my ( $computer_name, $build_name, $build_number,
  1366. $language, $platform, $branch,
  1367. $sku, $fconglomerator_build, $flang_neutral_build,
  1368. $timestamp ) =
  1369. GetNamedParameters( $named_parameters,
  1370. q(ComputerName), q(BuildName), q(BuildNumber),
  1371. q(Language), q(Platform), q(Branch),
  1372. q(Sku), q(IsConglomeratorBuild), q(IsLangNeutralBuild),
  1373. q(TimeStamp) );
  1374. return if ( !defined $computer_name );
  1375. my $lcsku = lc $sku;
  1376. # symbol server shares
  1377. if ( $lcsku eq 'sym' ) {
  1378. my $symbol_server_share = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'SymFarm' );
  1379. if ( !$symbol_server_share ) {
  1380. dbgmsg( "No INI file setting for 'SymFarm' in $branch / SymFarm." );
  1381. return;
  1382. }
  1383. return ("$symbol_server_share", "$language\\$build_name\\symbols.pri");
  1384. }
  1385. # special "skus"
  1386. if ( $fconglomerator_build ) {
  1387. return ("\\\\$computer_name\\$build_number.$branch.".($flang_neutral_build?'misc':$language), $sku);
  1388. }
  1389. # Standard skus
  1390. else {
  1391. # note the special case for "bin"
  1392. my $subdir;
  1393. $subdir = ($lcsku eq 'bin'?"":$sku);
  1394. return ("\\\\$computer_name\\$build_number.$platform.$branch.$timestamp.$language", $subdir);
  1395. }
  1396. }
  1397. sub GetBuildNumberAndTsFromShare {
  1398. my $named_parameters = shift;
  1399. my $build = shift;
  1400. my ( $branch, $language, $platform, $sku, $fconglomerator_build,
  1401. $flang_neutral_build ) =
  1402. GetNamedParameters( $named_parameters,
  1403. q(Branch), q(Language), q(Platform), q(Sku), q(IsConglomeratorBuild),
  1404. q(IsLangNeutralBuild) );
  1405. my ($build_number, $timestamp);
  1406. #
  1407. # Note: may have to reach into a share and
  1408. # extract some information if it is
  1409. # not readily apparent from the name
  1410. # of the share (as is the case with main)
  1411. #
  1412. # Symbol server points to shares
  1413. # containing the fully-qualified build-name
  1414. # (build_num.platform.branch.time)
  1415. if ( lc $sku eq 'sym' ) {
  1416. $build_number = build_number( $build );
  1417. $timestamp = build_date( $build );
  1418. }
  1419. # Special "skus"
  1420. elsif ( $fconglomerator_build &&
  1421. $build =~ /^\\\\[^\\]+\\(\d+)\.[^\.]+(?:\.(\d+-\d+))?\.([^\\]+)\\$sku$/i &&
  1422. $flang_neutral_build && lc$3 eq 'misc' ||
  1423. !$flang_neutral_build && lc$3 eq lc$language ) {
  1424. $build_number = $1;
  1425. $timestamp = $2 || 0;
  1426. }
  1427. # Standard skus
  1428. elsif ( !$fconglomerator_build &&
  1429. $build =~ /^\\\\[^\\]+\\(\d+)\.$platform\.[^\.]+\.(\d+-\d+)\.$language(?:\\$sku)?$/i ) {
  1430. $build_number = $1;
  1431. $timestamp = $2;
  1432. }
  1433. else {
  1434. # Error - don't recognize the format
  1435. return;
  1436. }
  1437. return ($build_number, $timestamp);
  1438. }
  1439. sub GetDfsLinkName {
  1440. my $named_parameters = shift;
  1441. my ($branch, $language, $build_id, $platform, $sku,
  1442. $fconglomerator_build ) =
  1443. GetNamedParameters( $named_parameters,
  1444. q(Branch), q(Language), q(BuildID), q(Platform), q(Sku),
  1445. q(IsConglomeratorBuild) );
  1446. return if ( !defined $branch );
  1447. my $lcsku = lc $sku;
  1448. # Used to add .srv onto skus that appear for both
  1449. # the client and server in the latest.* links
  1450. my $sku_name_suffix = '';
  1451. # If we haven't looked for DFSAlternateBranchName
  1452. # in the INI file, do so now
  1453. my $dfs_branch_name;
  1454. if ( !exists $named_parameters->{DefaultDFSBranches}{$branch} ) {
  1455. my $alt_branch_name = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSAlternateBranchName' );
  1456. # Default to the branch name
  1457. if ( !$alt_branch_name) {
  1458. $named_parameters->{DefaultDFSBranches}{$branch} = $branch;
  1459. }
  1460. else {
  1461. $named_parameters->{DefaultDFSBranches}{$branch} = $alt_branch_name;
  1462. }
  1463. }
  1464. $dfs_branch_name = $named_parameters->{DefaultDFSBranches}{$branch};
  1465. # Special behaviors on latest.* links
  1466. if ( $build_id =~ /^latest/i ) {
  1467. # If we haven't looked for DFSAlternateLatestBranchName
  1468. # in the INI file, do so now
  1469. if ( !exists $named_parameters->{LatestDFSBranches}{$branch} ) {
  1470. my $dfs_latest_branch_name = &GetIniSetting::GetSettingQuietEx( $branch, $language, 'DFSAlternateLatestBranchName' );
  1471. # Default to the current DFS branch name
  1472. if ( !$dfs_latest_branch_name) {
  1473. $named_parameters->{LatestDFSBranches}{$branch} = $named_parameters->{DefaultDFSBranches}{$branch};
  1474. }
  1475. else {
  1476. $named_parameters->{LatestDFSBranches}{$branch} = $dfs_latest_branch_name;
  1477. }
  1478. }
  1479. $dfs_branch_name = $named_parameters->{LatestDFSBranches}{$branch};
  1480. # If both client and serer support a sku,
  1481. # append .srv onto the server version.
  1482. # If this branch is both client and server,
  1483. # then don't use the .srv extension
  1484. if ( IsClientSku( $named_parameters ) &&
  1485. IsServerSku( $named_parameters ) &&
  1486. IsServerBranch( $named_parameters ) &&
  1487. !IsClientBranch( $named_parameters ) ) {
  1488. $sku_name_suffix = '.srv';
  1489. }
  1490. }
  1491. # Special "skus"
  1492. if ( $fconglomerator_build ) {
  1493. return "$dfs_branch_name\\$language\\$build_id\\". ($lcsku eq 'symbolcd'?'symcd2':$lcsku). $sku_name_suffix;
  1494. }
  1495. # Standard skus and symbol server sku
  1496. else {
  1497. return "$dfs_branch_name\\$language\\$build_id\\$platform\\$lcsku". $sku_name_suffix;
  1498. }
  1499. }
  1500. sub GetBuildIdsFromCurrentLinks {
  1501. my $named_parameters = shift;
  1502. my ($dfs_access, $build_number, $branch, $platform, $language, $sku ) =
  1503. GetNamedParameters( $named_parameters,
  1504. q(DfsMap), q(BuildNumber), q(Branch), q(Platform), q(Language), q(Sku) );
  1505. return if ( !defined $dfs_access );
  1506. my ( $cur_share_name, $sub_path ) = GetShareName( $named_parameters );
  1507. if ( !defined $cur_share_name ) {
  1508. errmsg( "Do not know how to create release shares for $branch / $sku." );
  1509. return;
  1510. }
  1511. $cur_share_name .= "\\$sub_path" if ( $sub_path );
  1512. my @build_ids;
  1513. # Return all recognized build IDs we are currently linked to
  1514. # (expecting <build_number> and latest.* entries)
  1515. my @current_links = @{ $dfs_access->{$cur_share_name} } if ( exists $dfs_access->{$cur_share_name} );
  1516. foreach ( @current_links ) {
  1517. my $next_build_id;
  1518. $next_build_id = GetBuildIdFromDfsLink( $named_parameters, $_ );
  1519. if ( $next_build_id ) {
  1520. push @build_ids, $next_build_id;
  1521. }
  1522. }
  1523. return @build_ids;
  1524. }
  1525. sub GetBuildIdFromDfsLink {
  1526. my $named_parameters = shift;
  1527. my $build = shift;
  1528. my ( $branch, $platform, $language, $sku,
  1529. $fconglomerator_build, $flang_neutral_build ) =
  1530. GetNamedParameters( $named_parameters,
  1531. q(Branch), q(Platform), q(Language), q(Sku),
  1532. q(IsConglomeratorBuild), q(IsLangNeutralBuild) );
  1533. my $lcsku = lc $sku;
  1534. my $build_id;
  1535. # Special "skus"
  1536. if ( $fconglomerator_build ) {
  1537. my $lang = $flang_neutral_build?'misc':$language;
  1538. my $dfs_sku_name = $lcsku eq 'symbolcd'?'symcd2':$sku;
  1539. $dfs_sku_name .= '(?:\.srv)?';
  1540. ($build_id) = $build =~ /^[^\\]+\\$lang\\([^\\]+)\\$dfs_sku_name$/i;
  1541. }
  1542. # Standard skus and symbol server sku
  1543. else {
  1544. ($build_id) = $build =~ /^[^\\]+\\$language\\([^\\]+)\\$platform\\$sku(?:\.srv)?$/i;
  1545. }
  1546. return $build_id;
  1547. }
  1548. sub GetWriteableDfsShares {
  1549. my $named_parameters = shift;
  1550. my ($dfs_access, $branch) =
  1551. GetNamedParameters( $named_parameters,
  1552. q(DfsMap), q(Branch) );
  1553. return if ( !defined $dfs_access );
  1554. # All known branches use the same writeable share format
  1555. if ( $branch ) # TRUE
  1556. {
  1557. my @dfs_servers = (tied %$dfs_access)->GetDfsHosts();
  1558. if ( !@dfs_servers ) {
  1559. errmsg( "Unable to retrieve hosting machines for DFS root ". (tied %$dfs_access)->GetDfsRoot(). "." );
  1560. errmsg( $_ ) foreach ( split /\n/, StaticDfsMap::GetLastErrorMessage() );
  1561. return;
  1562. }
  1563. # Point to writeable shares
  1564. s/(\\\\[^\\]+).*/$1\\writer\$\\release/ foreach ( @dfs_servers );
  1565. return @dfs_servers;
  1566. }
  1567. # Unknown branch
  1568. else {
  1569. return;
  1570. }
  1571. }
  1572. sub GetHideableDfsPathNames {
  1573. my $named_parameters = shift;
  1574. my ( $dfs_access, $branch, $language, $build_id, $sku ) =
  1575. GetNamedParameters( $named_parameters,
  1576. q(DfsMap), q(Branch), q(Language), q(BuildID), q(Sku) );
  1577. return if ( !defined $dfs_access );
  1578. # Get writeable DFS share for our branch
  1579. my @writeable_dfs_shares = GetWriteableDfsShares( $named_parameters );
  1580. if ( !@writeable_dfs_shares ) {
  1581. dbgmsg( "Unknown writeable DFS share for $branch." );
  1582. return;
  1583. }
  1584. my $dfs_link = GetDfsLinkName( $named_parameters );
  1585. if ( !defined $dfs_link ) {
  1586. errmsg( "Do not know how to create $build_id DFS link for $branch / $sku." );
  1587. return;
  1588. }
  1589. # Want the path up to latest.* or #
  1590. # (build_id): main\usa\<build_id>
  1591. $dfs_link =~ s/^(.*$build_id)\\.*$/$1/;
  1592. # All known branches use the same writeable share format
  1593. if ( $branch ) # TRUE
  1594. {
  1595. return map {"$_\\$dfs_link"} @writeable_dfs_shares;
  1596. }
  1597. # Unknown branch
  1598. else {
  1599. return;
  1600. }
  1601. }
  1602. sub GetBldFileDfsPathNames {
  1603. my $named_parameters = shift;
  1604. my ( $dfs_access, $branch, $language, $build_id,
  1605. $platform, $sku ) =
  1606. GetNamedParameters( $named_parameters,
  1607. q(DfsMap), q(Branch), q(Language), q(BuildID),
  1608. q(Platform), q(Sku) );
  1609. return if ( !defined $dfs_access );
  1610. # Get writeable DFS share for our branch
  1611. my @writeable_dfs_shares = GetWriteableDfsShares( $named_parameters );
  1612. if ( !@writeable_dfs_shares ) {
  1613. dbgmsg( "Unknown writeable DFS share for $branch." );
  1614. return;
  1615. }
  1616. my $dfs_link = GetDfsLinkName( $named_parameters );
  1617. if ( !defined $dfs_link ) {
  1618. errmsg( "Do not know how to create $build_id DFS link for $branch / $sku." );
  1619. return;
  1620. }
  1621. # Want the path up to platform: main\usa\<build_id>\<platform>
  1622. $dfs_link =~ s/^(.*$platform)\\.*$/$1/;
  1623. # All known branches use the same writeable share format
  1624. if ( $branch ) # TRUE
  1625. {
  1626. return map {"$_\\$dfs_link"} @writeable_dfs_shares;
  1627. }
  1628. # Unknown branch
  1629. else {
  1630. return;
  1631. }
  1632. }
  1633. sub ModifyQlyFile {
  1634. my $build = shift;
  1635. my $quality = shift;
  1636. my $path_to_quality_file = shift;
  1637. my ( @existing_qlyfiles, $qly_file );
  1638. my ( $fqlyfile_already_set );
  1639. # Check for existing QLY files
  1640. @existing_qlyfiles = grep { $_ if ( ! -d $_ ) } globex "$path_to_quality_file\\*.qly";
  1641. # 0 = success
  1642. # 2 = couldn't find file (may be a failure or there may be no QLY file;
  1643. # have to treat as a success as there is no way
  1644. # to tell the difference - $^E doesn't help)
  1645. if ( $! != 0 && $! != 2 ) {
  1646. errmsg( "Trying to find existing QLY file: $!" );
  1647. return;
  1648. }
  1649. # Remove any QLY files that don't match current status
  1650. # -- there should never be more than one, but we
  1651. # should handle that case correctly
  1652. undef $fqlyfile_already_set;
  1653. foreach $qly_file ( @existing_qlyfiles ) {
  1654. my ($qly_file_quality) = $qly_file =~ /\\([^\\]+)\.qly$/;
  1655. if ( lc $qly_file_quality eq lc $quality ) {
  1656. # Check for correct information inside
  1657. my $qly_information = ReadQlyFile( $qly_file );
  1658. if ( !defined $qly_information ) {
  1659. wrnmsg( "Problem reading $qly_file" );
  1660. }
  1661. elsif ( $qly_information =~ /^$build$/i ) {
  1662. $fqlyfile_already_set = 1;
  1663. next;
  1664. }
  1665. }
  1666. elsif ( !AllowQualityTransition( $qly_file_quality, $quality ) ) {
  1667. errmsg( "Not allowed to go from '$qly_file_quality' to '$quality' quality!" );
  1668. return;
  1669. }
  1670. # Remove the QLY file
  1671. unless ( unlink $qly_file ) {
  1672. errmsg( "Could not delete $qly_file ($!)" );
  1673. return;
  1674. }
  1675. }
  1676. # Create new QLY file if necessary
  1677. if ( !$fqlyfile_already_set ) {
  1678. unless ( open QLYFILE, "> $path_to_quality_file\\$quality.qly" ) {
  1679. errmsg( "Could not write to $path_to_quality_file\\$quality.qly ($!)" );
  1680. return;
  1681. }
  1682. print QLYFILE "$build\n";
  1683. close QLYFILE;
  1684. }
  1685. return 1;
  1686. }
  1687. sub ReadQlyFile {
  1688. my $qlyfile_path = shift;
  1689. my ( $qlyfile_contents );
  1690. unless ( open QLYFILE, $qlyfile_path ) {
  1691. errmsg( "Failure opening $qlyfile_path ($!)" );
  1692. return;
  1693. }
  1694. $qlyfile_contents = <QLYFILE>;
  1695. close QLYFILE;
  1696. chomp $qlyfile_contents;
  1697. return $qlyfile_contents;
  1698. }
  1699. sub DeleteQlyFile {
  1700. my $path_to_quality_file = shift;
  1701. my @existing_qlyfiles;
  1702. # Check for existing QLY files
  1703. @existing_qlyfiles = grep { $_ if ( ! -d $_ ) } globex "$path_to_quality_file\\*.qly";
  1704. # 0 = success
  1705. # 2 = couldn't find file (may be a failure or there may be no QLY file;
  1706. # have to treat as a success as there is no way
  1707. # to tell the difference - $^E doesn't help)
  1708. if ( $! != 0 && $! != 2 ) {
  1709. errmsg( "Trying to find existing QLY file: $!" );
  1710. return;
  1711. }
  1712. my $qly_file;
  1713. foreach $qly_file ( @existing_qlyfiles ) {
  1714. if ( !unlink $qly_file ) {
  1715. wrnmsg( "Unable to remove $qly_file ($!)" );
  1716. }
  1717. }
  1718. return 1;
  1719. }
  1720. sub CreateSecuredBuildShare {
  1721. my $named_parameters = shift;
  1722. my ( $accessed_shares, $full_build_path, $computer_name,
  1723. $quality, $build_number, $language,
  1724. $platform, $branch, $sku ) =
  1725. GetNamedParameters( $named_parameters,
  1726. q(AccessedShares), q(FullBuildPath), q(ComputerName),
  1727. q(Quality), q(BuildNumber), q(Language),
  1728. q(Platform), q(Branch), q(Sku) );
  1729. return if ( !defined $accessed_shares );
  1730. my $fshare_already_exists;
  1731. my ( @share_members, @remove_permissions );
  1732. my ( $build_share, $sub_path ) = GetShareName( $named_parameters );
  1733. if ( !defined $build_share ) {
  1734. errmsg( "Unable to determine release share for $branch / $sku." );
  1735. return;
  1736. }
  1737. # Optimization: check if we have already looked at this share
  1738. # (especially useful when each sku uses the same share)
  1739. foreach ( @$accessed_shares ) {
  1740. return 1 if ( lc $_ eq lc $build_share );
  1741. }
  1742. # Choose share permissions based on quality
  1743. if ( lc $quality eq "pre" || lc $quality eq "bvt" ) {
  1744. my $share_member_string = &GetIniSetting::GetSettingQuietEx( $branch, $language, "BVTMembers" );
  1745. if ( $share_member_string ) {
  1746. @share_members = split ' ', $share_member_string;
  1747. }
  1748. # INI file must specify permissions
  1749. else {
  1750. errmsg( "No entries specified in INI file for BVTMembers" );
  1751. return;
  1752. }
  1753. } else {
  1754. my $share_member_string = &GetIniSetting::GetSettingQuietEx( $branch, $language, "ReleaseAccess" );
  1755. if ( $share_member_string ) {
  1756. @share_members = split ' ', $share_member_string;
  1757. }
  1758. # INI file must specify permissions
  1759. else {
  1760. errmsg( "No entries specified in INI file for ReleaseAccess" );
  1761. return;
  1762. }
  1763. }
  1764. # Create share if it doesn't already exist
  1765. $fshare_already_exists = ( -e $build_share );
  1766. # This will set the current permissions and create the share if necessary
  1767. dbgmsg( "Setting share permissions for $build_share:" );
  1768. dbgmsg( " $_" ) foreach @share_members;
  1769. if ( !ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share". ($fshare_already_exists?" ":"=$full_build_path ").
  1770. join " ", map { "/GRANT $_:R" } @share_members) ) {
  1771. errmsg( "Failed creating/modifying share for $full_build_path." );
  1772. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1773. errmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1774. return;
  1775. }
  1776. # Remove any old permissions from an existing share
  1777. # We don't want to drop the share and recreate it
  1778. # because current connections will be dropped and
  1779. # we decided that was bad
  1780. if ( $fshare_already_exists ) {
  1781. my %hash_share_members;
  1782. $hash_share_members{ lc $_ } = 1 foreach @share_members;
  1783. if ( !ExecuteProgram::ExecuteProgram("rmtshare.exe $build_share") ) {
  1784. errmsg( "Failure executing ". ExecuteProgram::GetLastCommand() );
  1785. errmsg ( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1786. return;
  1787. }
  1788. my @current_share_permissions = ExecuteProgram::GetLastOutput();
  1789. undef @remove_permissions;
  1790. my $permission;
  1791. my $interesting_lines;
  1792. foreach $permission ( @current_share_permissions ) {
  1793. my $user_name;
  1794. # skip empty lines
  1795. next if ( $permission eq "" );
  1796. # Deal with no permissions set
  1797. if ( $permission eq "No permission specified." ) {
  1798. wrnmsg( "Share $build_share has no default permissions." );
  1799. last;
  1800. }
  1801. # Otherwise we only care about the lines between
  1802. # 'Permissions:' and 'The command completed successfully.'
  1803. if ( $permission eq 'Permissions:' ) {
  1804. $interesting_lines = 1;
  1805. next;
  1806. }
  1807. last if ( $permission eq 'The command completed successfully.' );
  1808. next if ( !$interesting_lines );
  1809. # Only need to verify name and not permissions as rmtshare should
  1810. # have already set the correct permissions for the name.
  1811. # Note that rmtshare also prepends a backslash for builtin
  1812. # groups, but you cannot use the backslash when specifying
  1813. # the name on the command line, so strip out any
  1814. # prepending backslash characters. This also catches
  1815. # local groups as they are reported as <machine>\Group on
  1816. # remote machines, but can only be added and removed as 'Group".
  1817. ($user_name) = $permission =~ /^\s*\\?(?:$computer_name\\)?(\S+)\s*\:/i;
  1818. if ( !$user_name ) {
  1819. errmsg( "Problem parsing user from rmtshare output:" );
  1820. errmsg( " $permission" );
  1821. return;
  1822. }
  1823. if ( !exists $hash_share_members{ lc $user_name } ) {
  1824. push @remove_permissions, $user_name;
  1825. }
  1826. }
  1827. # Now actually call rmtshare to remove unrecognized users
  1828. if ( @remove_permissions ) {
  1829. dbgmsg( "Removing permissions from $build_share:" );
  1830. dbgmsg( " $_" ) foreach @remove_permissions;
  1831. if ( !ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share ". join " ", map { "/REMOVE $_" } @remove_permissions ) ) {
  1832. errmsg( "Failed removing old permissions from $build_share." );
  1833. errmsg ( " ". ExecuteProgram::GetLastCommand() );
  1834. errmsg ( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1835. return;
  1836. }
  1837. }
  1838. }
  1839. # Verify that share is reachable
  1840. # This is making the assumption that we
  1841. # are allowed access to the share
  1842. if ( ! -e $build_share ) {
  1843. errmsg( "Unable to access $build_share ($!)" );
  1844. return;
  1845. }
  1846. # Add to accessed shares array
  1847. push @$accessed_shares, $build_share;
  1848. return 1;
  1849. }
  1850. sub LowerBuildShare {
  1851. my $named_parameters = shift;
  1852. my ( $accessed_shares, $branch, $sku, $lang ) =
  1853. GetNamedParameters( $named_parameters,
  1854. q(AccessedShares), q(Branch), q(Sku), q(Language) );
  1855. my ( $build_share, $sub_path ) = GetShareName( $named_parameters );
  1856. if ( !defined $build_share) {
  1857. errmsg( "Unable to determine release share for $branch / $sku." );
  1858. return;
  1859. }
  1860. # Optimization: check if we have already removed this share
  1861. # (especially useful when each sku uses the same share)
  1862. foreach ( @$accessed_shares ) {
  1863. return 1 if ( lc $_ eq lc $build_share );
  1864. }
  1865. # Check if the share exists
  1866. my $fshare_exists = ( -e $build_share );
  1867. # Remove the share
  1868. if ( $fshare_exists ) {
  1869. if ( !ExecuteProgram::ExecuteProgram( "rmtshare.exe $build_share /DELETE" ) ) {
  1870. errmsg( "Failed removing share $build_share." );
  1871. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1872. errmsg( " $_") foreach ExecuteProgram::GetLastOutput();
  1873. return;
  1874. }
  1875. }
  1876. # If the share doesn't exist we are done,
  1877. # but do a sanity check on the reason
  1878. elsif ( $! != 2 ) {
  1879. errmsg( "Unable to verify share $build_share ($!)." );
  1880. return;
  1881. }
  1882. # Add to accessed shares array
  1883. push @$accessed_shares, $build_share;
  1884. return 1;
  1885. }
  1886. sub GetReleasePath {
  1887. my( @net_share_output, @net_share_local_path_entry );
  1888. my( $field_name, $field_value );
  1889. if ( !ExecuteProgram::ExecuteProgram("net share release") ) {
  1890. errmsg( "Unable to find share \'release\'.");
  1891. errmsg( " ". ExecuteProgram::GetLastCommand() );
  1892. errmsg( " $_" ) foreach ExecuteProgram::GetLastOutput();
  1893. return;
  1894. }
  1895. @net_share_output = ExecuteProgram::GetLastOutput();
  1896. # note that we are using the second line of the net share
  1897. # output to get this data. if this changes, it'll break.
  1898. @net_share_local_path_entry = split /\s+/, $net_share_output[1];
  1899. dbgmsg( "Path information for release is '". (join " ", @net_share_local_path_entry). "'" );
  1900. if ( $net_share_local_path_entry[0] ne "Path" ) {
  1901. wrnmsg( "Second line of net share does not start with " .
  1902. "\'Path\' -- possibly another language?");
  1903. }
  1904. dbgmsg( "Release path is $net_share_local_path_entry[1]");
  1905. return $net_share_local_path_entry[1];
  1906. }
  1907. #
  1908. # glob does not work for UNC
  1909. # paths on Win64 so we have to
  1910. # fake it with dir /b
  1911. #
  1912. sub globex ($)
  1913. {
  1914. my $match_criteria = shift;
  1915. croak "Must specify parameter for globex" if ( !defined $match_criteria );
  1916. # If we are on an Win64 version of perl check for UNC path
  1917. if ( $^O eq 'MSWin64' &&
  1918. $match_criteria =~ /^\\\\/ ) {
  1919. my ($path) = $match_criteria =~ /(.*)\\[^\\]*$/;
  1920. my @dir_entries = `dir /b $match_criteria`;
  1921. if ( $? ) {
  1922. # Can't do much useful in case of an error
  1923. return;
  1924. }
  1925. # Need to clean the entries up to look like glob results
  1926. @dir_entries = grep { $_ } @dir_entries; # no empty results
  1927. chomp @dir_entries; # no newlines in results
  1928. # full path to results
  1929. if ( defined $path ) {
  1930. foreach ( @dir_entries ) { $_ = "$path\\$_"; }
  1931. }
  1932. return @dir_entries;
  1933. }
  1934. # otherwise just use glob
  1935. else {
  1936. return glob( $match_criteria );
  1937. }
  1938. }
  1939. parseargs('?' => \&Usage,
  1940. 'lower' => sub { $Op = 'lower' },
  1941. 'n:' => \$Build_Number,
  1942. 'q:' => \&ValidateAndSetQuality,
  1943. 'a:' => \$Architecture,
  1944. 't:' => \$Debug_Type,
  1945. 'time:' => \$Time_Stamp,
  1946. 'f' => \$fOverride_Safe,
  1947. 'safe' => \$fSafe,
  1948. 'replace' => \$fReplaceAtNumberedLink,
  1949. 'd' => \$Logmsg::DEBUG);
  1950. # 'l:' => \$Language, # Done by ParseEnv
  1951. Usage() if ( @ARGV );
  1952. $Language = $ENV{LANG};
  1953. # Initialize with default values if not specified
  1954. $Op ||= 'raise';
  1955. $Build_Number ||= '*';
  1956. $Architecture ||= '*';
  1957. $Time_Stamp ||= '*';
  1958. $Debug_Type ||= '*';
  1959. $Platform = "$Architecture$Debug_Type";
  1960. if ( !Main() ) {
  1961. errmsg( "Terminated abnormally." );
  1962. 1;
  1963. }
  1964. else {
  1965. logmsg( "Completed successfully." );
  1966. 0;
  1967. }
  1968. sub ValidateAndSetQuality {
  1969. my $quality = shift;
  1970. if ( exists $Ordered_Qualities{ lc $quality } ) {
  1971. $Quality = $quality;
  1972. return 1;
  1973. }
  1974. else {
  1975. errmsg( "Unrecognized quality \'$quality\'" );
  1976. exit( 1 );
  1977. }
  1978. }
  1979. sub Usage {
  1980. my $name = $0;
  1981. print "$name\n";
  1982. $name =~ s/.*?([^\\]+)$/$1/;
  1983. print <<USAGE;
  1984. $name -n:\# -q:qly [-lower] [-a:arc] [-l:lng] [-d] [-?]
  1985. -n:# Raise build number #
  1986. -q:qly Raise to 'qly' quality
  1987. -lower Lower a build and remove from dfs
  1988. -a:arc Act on architecture 'arc' (e.g. ??????)
  1989. -t:typ Act on debug type 'typ' (e.g. fre or chk)
  1990. -time:yymmdd-hhmm Use time-stamp criteria
  1991. -l:lng Raise for language 'lng' (from codes.txt)
  1992. -safe Used to raise an old build to TST/IDx (RAISE only)
  1993. -replace Will replace "newer" builds at a numbered link
  1994. -f Force an action that would not normally be taken
  1995. -d Debug mode
  1996. -? Display usage (this screen)
  1997. $name will issue the appropriate DFS commands to raise
  1998. a build to a specific quality.
  1999. USAGE
  2000. exit(1);
  2001. }
  2002. package ExecuteProgram;
  2003. my $Last_Command;
  2004. my @Last_Output;
  2005. sub ExecuteProgram {
  2006. $Last_Command = shift;
  2007. @Last_Output = `$Last_Command`;
  2008. chomp $_ foreach ( @Last_Output );
  2009. if ( $? ) {
  2010. return;
  2011. }
  2012. else {
  2013. return 1;
  2014. }
  2015. }
  2016. sub GetLastCommand {
  2017. return $Last_Command;
  2018. }
  2019. sub GetLastOutput {
  2020. return @Last_Output;
  2021. }
  2022. # Object to control synchronization among machines
  2023. package MultiMachineLock;
  2024. use Win32::ChangeNotify;
  2025. use Win32::Event;
  2026. sub new {
  2027. my $class = shift;
  2028. my $spool_path = shift;
  2029. my $timeout = shift;
  2030. return if not defined $spool_path;
  2031. my $instance = {
  2032. Path => $spool_path,
  2033. Timeout => $timeout || INFINITE, # default timeout is INFINITE
  2034. File => "$ENV{COMPUTERNAME}.txt",
  2035. NotificationObj => undef,
  2036. LocalSyncObj => undef,
  2037. fHaveLocalSync => 0,
  2038. Count => 0,
  2039. LastErrorMsg => "",
  2040. LastErrorValue => 0
  2041. };
  2042. return bless $instance;
  2043. }
  2044. sub Lock {
  2045. my $self = shift;
  2046. my $wait_return;
  2047. return unless ref $self;
  2048. # If we already hold the lock just bump up the ref-count
  2049. if ( 0 != $self->{Count} ) {
  2050. $self->{Count}++;
  2051. return 1;
  2052. }
  2053. # Need to synchronize local access (only one program
  2054. # at a time from the same machine can hold the lock)
  2055. if ( !defined $self->{LocalSyncObj} ) {
  2056. my $lock_path = $self->{Path};
  2057. $lock_path =~ s/\\\\/remote\\/;
  2058. $lock_path =~ s/\\/\//g;
  2059. $self->{LocalSyncObj} = new Win32::Event ( 0, 1, "Global\\$lock_path" );
  2060. if ( !defined $self->{LocalSyncObj} ) {
  2061. $self->{LastErrorMsg} = "Unable to create a local event ($^E).";
  2062. $self->{LastErrorValue} = 1;
  2063. return;
  2064. }
  2065. }
  2066. if ( !$self->{fHaveLocalSync} ) {
  2067. $wait_return = $self->{LocalSyncObj}->wait( $self->{Timeout} );
  2068. # Handle timeout
  2069. if ( 0 == $wait_return ) {
  2070. $self->{LastErrorMsg} = "Waiting on another process on machine.";
  2071. $self->{LastErrorValue} = 2; # 2 = timeout
  2072. return;
  2073. }
  2074. # Handle error condition
  2075. elsif ( 1 != $wait_return ) {
  2076. $self->{LastErrorMsg} = "Unexpected error waiting for local synchronization (wait returned ". $wait_return. ")";
  2077. $self->{LastErrorValue} = 1;
  2078. return;
  2079. }
  2080. $self->{fHaveLocalSync} = 1;
  2081. }
  2082. # Check for our existence before
  2083. # we create a file in the queue
  2084. # (this will be the case if something bad
  2085. # happened on the last run and we are
  2086. # rerunning the command to recover)
  2087. if ( ! -e "$self->{Path}\\$self->{File}" ) {
  2088. unless ( open SPOOLFILE, "> $self->{Path}\\$self->{File}" ) {
  2089. $self->{LastErrorMsg} = "Failed to create synchronization file '$self->{Path}\\$self->{File}' ($!).";
  2090. $self->{LastErrorValue} = 1;
  2091. return;
  2092. }
  2093. print SPOOLFILE "This file is used to synchronize access among machines.";
  2094. close SPOOLFILE;
  2095. }
  2096. # Create the notification object if this is the first time we were called
  2097. $self->{NotificationObj} ||= new Win32::ChangeNotify( $self->{Path}, 0, FILE_NOTIFY_CHANGE_FILE_NAME );
  2098. if ( !defined ${$self->{NotificationObj}} ) {
  2099. $self->{LastErrorMsg} = "Failed creating monitor object ($^E).";
  2100. $self->{LastErrorValue} = 1;
  2101. return;
  2102. }
  2103. # Check our position in the 'queue' at the beginning
  2104. if ( !ExecuteProgram::ExecuteProgram("dir /b /od $self->{Path}") ) {
  2105. $self->{LastErrorMsg} = "Unable to view directory $self->{Path}.\n";
  2106. $self->{LastErrorMsg} .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2107. $self->{LastErrorMsg} .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2108. $self->{LastErrorValue} = 1;
  2109. return;
  2110. }
  2111. my @ordered_spool_directory = ExecuteProgram::GetLastOutput();
  2112. # We are at the top
  2113. if ( $ordered_spool_directory[0] eq $self->{File} ) {
  2114. $self->{Count}++;
  2115. return 1;
  2116. }
  2117. # Recheck our position in the 'queue' everytime
  2118. # the state of the directory changes until we
  2119. # are in the top position or we hit the timeout
  2120. # value with no activity
  2121. while ( 1 ) {
  2122. $self->{NotificationObj}->reset();
  2123. $wait_return = $self->{NotificationObj}->wait( $self->{Timeout} );
  2124. if ( !ExecuteProgram::ExecuteProgram("dir /b /od $self->{Path}") ) {
  2125. $self->{LastErrorMsg} = "Unable to view directory $self->{Path}.\n";
  2126. $self->{LastErrorMsg} .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2127. $self->{LastErrorMsg} .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2128. $self->{LastErrorValue} = 1;
  2129. return;
  2130. }
  2131. @ordered_spool_directory = ExecuteProgram::GetLastOutput();
  2132. # We are at the top
  2133. if ( $ordered_spool_directory[0] eq $self->{File} ) {
  2134. $self->{Count}++;
  2135. return 1;
  2136. }
  2137. # Still waiting for others
  2138. else {
  2139. # Make sure we are still in the queue
  2140. my $found_self;
  2141. foreach ( @ordered_spool_directory ) {
  2142. $found_self = 1 if ( $_ eq "$self->{File}" );
  2143. }
  2144. if ( !$found_self ) {
  2145. # Lost our synchronization file -- recreating...
  2146. unless ( open SPOOLFILE, "> $self->{Path}\\$self->{File}" ) {
  2147. $self->{LastErrorMsg} = "Lost our synchronization file '$self->{Path}\\$self->{File}' -- failed to recreate. ($!).";
  2148. $self->{LastErrorValue} = 1;
  2149. return;
  2150. }
  2151. print SPOOLFILE "This file is used to synchronize access among machines.";
  2152. close SPOOLFILE;
  2153. }
  2154. }
  2155. # Handle timeout
  2156. if ( 0 == $wait_return ) {
  2157. my $machine_name = $ordered_spool_directory[0];
  2158. $machine_name =~ s/\.txt$//;
  2159. $self->{LastErrorMsg} = "Waiting on $machine_name ($self->{Path}\\$ordered_spool_directory[0]).";
  2160. $self->{LastErrorValue} = 2; # 2 = timeout
  2161. return;
  2162. }
  2163. # Handle error condition
  2164. elsif ( 1 != $wait_return ) {
  2165. $self->{LastErrorMsg} = "Unexpected error waiting on directory to change (wait returned ". $wait_return. ")";
  2166. $self->{LastErrorValue} = 1;
  2167. return;
  2168. }
  2169. }
  2170. $self->{LastErrorMsg} = "Jumped out of an infinte loop -- no idea how we got here";
  2171. $self->{LastErrorValue} = 1;
  2172. return;
  2173. }
  2174. sub Unlock {
  2175. my $self = shift;
  2176. return unless ref $self;
  2177. # Can't release a lock we don't hold
  2178. if ( 0 == $self->{Count} ) {
  2179. return;
  2180. }
  2181. # Decrement lock count
  2182. if ( 0 == --$self->{Count} ) {
  2183. if ( ! unlink "$self->{Path}\\$self->{File}" ) {
  2184. $self->{LastErrorMsg} = "Failed to delete synchronization file ($self->{Path}\\$self->{File}). May need to delete by hand.";
  2185. $self->{LastErrorValue} = 1;
  2186. }
  2187. $self->{LocalSyncObj}->set();
  2188. }
  2189. return 1;
  2190. }
  2191. sub GetLastError {
  2192. my $self = shift;
  2193. return unless ref $self;
  2194. return $self->{LastErrorValue};
  2195. }
  2196. sub GetLastErrorMessage {
  2197. my $self = shift;
  2198. return unless ref $self;
  2199. return $self->{LastErrorMsg};
  2200. }
  2201. sub DESTROY {
  2202. my $self = shift;
  2203. return unless ref $self;
  2204. if ( $self->{Count} > 0 ) {
  2205. #print "Abandoning lock file -- attempting to delete.";
  2206. $self->{Count} = 1;
  2207. return $self->Unlock();
  2208. } else {
  2209. if ( $self->{fHaveLocalSync} ) {
  2210. $self->{LocalSyncObj}->set();
  2211. }
  2212. return 1;
  2213. }
  2214. }
  2215. package StaticDfsMap;
  2216. my $Last_Error_Msg;
  2217. sub TIEHASH {
  2218. my $class = shift;
  2219. my $dfs_root = shift;
  2220. my $instance;
  2221. my ($dfs_view_info, $cur_dfs_link);
  2222. my $fchild_dfs_servers;
  2223. return if ( @_ or !defined $dfs_root );
  2224. # Check current dfscmd.exe version
  2225. if ( !VerifyDfsCmdVer() ) {
  2226. return; # Error message should already be set
  2227. }
  2228. # Remove trailing backslash (if it exists) from DFS root
  2229. $dfs_root =~ s/\\$//;
  2230. # Read in current DFS view
  2231. if ( !ExecuteProgram::ExecuteProgram("dfscmd.exe /view $dfs_root /batch") ) {
  2232. $Last_Error_Msg = "Failed viewing dfs root $dfs_root.\n";
  2233. $Last_Error_Msg .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2234. $Last_Error_Msg .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2235. return;
  2236. }
  2237. my @full_dfs_view = ExecuteProgram::GetLastOutput();
  2238. $instance = {
  2239. "DFS Root" => $dfs_root };
  2240. # "DFS Work Root" => {}
  2241. # "DFS Servers" => (),
  2242. # "Map" => {}
  2243. # Expected output:
  2244. # dfscmd /map "\\<dfs_root>\<link_name>" "\\<server>\<share>" "<comment>"
  2245. my %dfs_map;
  2246. foreach my $dfs_view_info ( @full_dfs_view ) {
  2247. chomp $dfs_view_info;
  2248. next if ( !$dfs_view_info );
  2249. next if ( $dfs_view_info =~ /^REM BATCH RESTORE SCRIPT/ );
  2250. $dfs_view_info = lc $dfs_view_info;
  2251. # Are we done?
  2252. if ( $dfs_view_info eq 'the command completed successfully.' ) {
  2253. last;
  2254. }
  2255. my ($dfs_link, $share, $comment) = $dfs_view_info =~ /dfscmd \/(?:map|add) "(.*?)" "(.*?)"(?: "(.*)")?$/;
  2256. my $relative_link_name = $dfs_link;
  2257. $relative_link_name =~ s/^\Q$dfs_root\E\\//i;
  2258. if ( !$dfs_link || !$share || !$relative_link_name )
  2259. {
  2260. $Last_Error_Msg = "Unrecognized entry parsing ". ExecuteProgram::GetLastCommand(). "\n";
  2261. $Last_Error_Msg .= " $dfs_view_info";
  2262. return;
  2263. }
  2264. # If it is just the root, then this entry represents a hosting server
  2265. if ( lc $dfs_link eq lc $dfs_root ) {
  2266. push @{$instance->{"DFS Servers"}}, $share;
  2267. }
  2268. # Otherwise associate the link and the share
  2269. else {
  2270. push @{$dfs_map{$relative_link_name}}, $share;
  2271. push @{$dfs_map{$share}}, $relative_link_name;
  2272. }
  2273. }
  2274. # Expect to find at least one DFS server under the root
  2275. if ( !exists $instance->{"DFS Servers"} ) {
  2276. $Last_Error_Msg = "Did not find the root node and specified servers for $dfs_root.";
  2277. return;
  2278. }
  2279. # FUTURE: find a nicer way around this
  2280. # In cases where more than one server is actually hosting DFS,
  2281. # we need to pick one of the hosting machines to perform
  2282. # our commands against so that we don't have to wait for
  2283. # replication (this is to help tests for just created and
  2284. # just removed directories succeed).
  2285. foreach ( @{$instance->{"DFS Servers"}} ) {
  2286. if ( -e $_ ) {
  2287. $instance->{"DFS Work Root"} = $_;
  2288. last;
  2289. }
  2290. }
  2291. # Verify we could see at least one
  2292. if ( ! exists $instance->{"DFS Work Root"} ) {
  2293. $Last_Error_Msg = "Could not find an accessible DFS server.";
  2294. return;
  2295. }
  2296. #print "\n$_:\n ". (join "\n ", @{$dfs_map{$_}}) foreach ( sort keys %dfs_map );
  2297. # Store the map we just created
  2298. # inside of our private hash data
  2299. $instance->{"Map"} = \%dfs_map;
  2300. return bless $instance, $class;
  2301. }
  2302. sub FETCH {
  2303. my $self = shift;
  2304. my $link_or_share = shift;
  2305. my $dfs_map = $self->{"Map"};
  2306. return $dfs_map->{ lc $link_or_share } if (exists $dfs_map->{ lc $link_or_share });
  2307. return;
  2308. }
  2309. sub STORE {
  2310. my $self = shift;
  2311. my $link_or_share = shift;
  2312. my $new_link = shift;
  2313. my $dfs_map = $self->{"Map"};
  2314. my $fshare = $link_or_share =~ /^\\\\/;
  2315. # Remove any trailing backslashes
  2316. $link_or_share =~ s/\\$//;
  2317. $new_link =~ s/\\$//;
  2318. # If mapping already exists, we are done
  2319. my $cur_mappings = $dfs_map->{lc $link_or_share};
  2320. foreach ( @$cur_mappings ) {
  2321. return 1 if ( lc $_ eq lc $new_link );
  2322. }
  2323. # Create new DFS mapping
  2324. if ( !ExecuteProgram::ExecuteProgram(
  2325. "dfscmd.exe ". (@$cur_mappings?"/add ":"/map "). $self->{"DFS Root"}.
  2326. ($fshare?"\\$new_link $link_or_share":"\\$link_or_share $new_link") )
  2327. ) {
  2328. #
  2329. # COMMENTED OUT: If someone else is trying to manipulate the same
  2330. # links as we are while we are doing this then
  2331. # we have a problem -- don't try to correct
  2332. #
  2333. # Might have gotten into a race-condition with another
  2334. # machine attempting to add a new link -- if that is
  2335. # a possibility try a map instead before giving up
  2336. #
  2337. #if ( !@$cur_mappings ||
  2338. # !ExecuteProgram( "dfscmd.exe /map ". $self->{"DFS Root"}.
  2339. # ($fshare?"\\$new_link $link_or_share":"\\$link_or_share $new_link") ) ) {
  2340. # errmsg( "Failed to create link \\$self->{Dfs Root}\\". ($fshare?"\\$new_link => $link_or_share":"\\$link_or_share => $new_link") . "." );
  2341. # return;
  2342. #}
  2343. $Last_Error_Msg = "Problem adding/mapping link ". ($fshare?"$link_or_share":"$new_link"). ":\n";
  2344. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2345. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2346. return;
  2347. }
  2348. # Update the hash with the new information
  2349. push @{$dfs_map->{lc $link_or_share}}, $new_link;
  2350. push @{$dfs_map->{lc $new_link}}, $link_or_share;
  2351. return 1;
  2352. }
  2353. sub DELETE {
  2354. my $self = shift;
  2355. my $link_or_share = shift;
  2356. my $dfs_map = $self->{"Map"};
  2357. my $fshare = $link_or_share =~ /^\\\\/;
  2358. # Make sure DFS mapping exists before attempting to delete it
  2359. return 1 if ( !exists $dfs_map->{lc $link_or_share} );
  2360. my $cur_mappings = $dfs_map->{lc $link_or_share};
  2361. return 1 if ( !@$cur_mappings );
  2362. if ( $fshare ) {
  2363. my $next_link;
  2364. # Remove all DFS links pointing to this share
  2365. foreach $next_link ( @$cur_mappings ) {
  2366. if ( !$self->RemoveShareFromLink( $next_link, $link_or_share ) ) {
  2367. return;
  2368. }
  2369. }
  2370. } else {
  2371. if ( !ExecuteProgram::ExecuteProgram(
  2372. "dfscmd.exe /unmap ". $self->{"DFS Root"}. "\\$link_or_share"
  2373. ) ) {
  2374. $Last_Error_Msg = "Failure trying to unmap $link_or_share:\n";
  2375. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2376. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2377. return;
  2378. }
  2379. # Remove the hash references
  2380. delete $dfs_map->{lc $link_or_share};
  2381. for ( my $i = 0; $i < scalar(@$cur_mappings); $i++) {
  2382. if ( lc $cur_mappings->[$i] eq lc $link_or_share ) {
  2383. splice @$cur_mappings, $i, 1;
  2384. }
  2385. }
  2386. }
  2387. return 1;
  2388. }
  2389. sub CLEAR {
  2390. my $self = shift;
  2391. # We don't actually want to be able to delete the entire
  2392. # DFS structure, so we will just clear our information
  2393. undef %$self;
  2394. return;
  2395. }
  2396. sub EXISTS {
  2397. my $self = shift;
  2398. my $link_or_share = shift;
  2399. my $dfs_map = $self->{"Map"};
  2400. return exists $dfs_map->{lc $link_or_share};
  2401. }
  2402. sub FIRSTKEY {
  2403. my $self = shift;
  2404. my $dfs_map = $self->{"Map"};
  2405. my $force_to_first_key = keys %$dfs_map;
  2406. return scalar each %$dfs_map;
  2407. }
  2408. sub NEXTKEY {
  2409. my $self = shift;
  2410. my $last_key = shift;
  2411. my $dfs_map = $self->{"Map"};
  2412. return scalar each %$dfs_map;
  2413. }
  2414. sub DESTROY {
  2415. my $self = shift;
  2416. return;
  2417. }
  2418. #
  2419. # Need more methods than provided by TIEHASH defaults
  2420. #
  2421. sub RemoveShareFromLink {
  2422. my $self = shift;
  2423. my $link_root = shift;
  2424. my $share_name = shift;
  2425. my $dfs_map = $self->{"Map"};
  2426. # Get current associated links
  2427. my $cur_link_mappings = $dfs_map->{lc $link_root};
  2428. my $cur_share_mappings = $dfs_map->{lc $share_name};
  2429. my $fshare_linked;
  2430. # If the share is not part of the link, return
  2431. foreach (@$cur_link_mappings) {
  2432. if ( lc $_ eq lc $share_name ) {
  2433. $fshare_linked = 1;
  2434. last;
  2435. }
  2436. }
  2437. return 1 if ( !$fshare_linked );
  2438. my $dfs_command = "dfscmd.exe /remove ".
  2439. $self->{'DFS Root'}.
  2440. "\\$link_root $share_name";
  2441. if ( !ExecuteProgram::ExecuteProgram( $dfs_command ) ) {
  2442. $Last_Error_Msg = "Could not remove $share_name from DFS link $link_root:\n";
  2443. $Last_Error_Msg .= " " . ExecuteProgram::GetLastCommand() . "\n";
  2444. $Last_Error_Msg .= join "\n ", ExecuteProgram::GetLastOutput();
  2445. return;
  2446. }
  2447. # Remove the associated hash entries
  2448. for ( my $i = 0; $i < scalar(@$cur_link_mappings); $i++) {
  2449. if ( lc $cur_link_mappings->[$i] eq lc $share_name ) {
  2450. splice @$cur_link_mappings, $i, 1;
  2451. }
  2452. }
  2453. for ( my $i = 0; $i < scalar(@$cur_share_mappings); $i++) {
  2454. if ( lc $cur_share_mappings->[$i] eq lc $link_root ) {
  2455. splice @$cur_share_mappings, $i, 1;
  2456. }
  2457. }
  2458. return 1;
  2459. }
  2460. sub Flush {
  2461. my $self = shift;
  2462. # Do nothing for now, but here in preparation for batching of commands
  2463. return 1;
  2464. }
  2465. sub GetDfsRoot {
  2466. my $self = shift;
  2467. # return $self->{ "DFS Root" };
  2468. return $self->{ "DFS Root" };
  2469. }
  2470. sub GetDfsHosts {
  2471. my $self = shift;
  2472. return @{$self->{"DFS Servers"}};
  2473. }
  2474. sub VerifyDfsCmdVer {
  2475. my ( @file_spec, $file_ver_string, @file_ver_info );
  2476. # Verify the dfscmd.exe is proper
  2477. if ( !ExecuteProgram::ExecuteProgram("where dfscmd.exe") ) {
  2478. $Last_Error_Msg = "Could not find dfscmd.exe\n";
  2479. $Last_Error_Msg .= " ". ExecuteProgram::GetLastCommand(). "\n";
  2480. $Last_Error_Msg .= " $_\n" foreach ExecuteProgram::GetLastOutput();
  2481. return;
  2482. }
  2483. @file_spec = ExecuteProgram::GetLastOutput();
  2484. if ( !ExecuteProgram::ExecuteProgram("filever $file_spec[0]") ) {
  2485. $Last_Error_Msg = "Failed executing \'filever $file_spec[0]\'.";
  2486. return;
  2487. }
  2488. $file_ver_string = join " ", ExecuteProgram::GetLastOutput();
  2489. @file_ver_info = split /\s+/, $file_ver_string;
  2490. # Fourth field is version number; compare it with known good
  2491. if ($file_ver_info[4] lt "5.0.2203.1") {
  2492. $Last_Error_Msg = "$file_ver_info[0] version is less than 5.0.2203.1. Please install DFSCMD.EXE from a 2203 or later build.";
  2493. return;
  2494. }
  2495. return 1;
  2496. }
  2497. sub GetLastErrorMessage {
  2498. return $Last_Error_Msg;
  2499. }