Filesystem.pm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. # -*- Mode: perl; indent-tabs-mode: nil -*-
  2. #
  3. # The contents of this file are subject to the Mozilla Public
  4. # License Version 1.1 (the "License"); you may not use this file
  5. # except in compliance with the License. You may obtain a copy of
  6. # the License at http://www.mozilla.org/MPL/
  7. #
  8. # Software distributed under the License is distributed on an "AS
  9. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10. # implied. See the License for the specific language governing
  11. # rights and limitations under the License.
  12. #
  13. # The Original Code is the Bugzilla Bug Tracking System.
  14. #
  15. # Contributor(s): Max Kanat-Alexander <mkanat@bugzilla.org>
  16. # Bill Barry <after.fallout@gmail.com>
  17. package Bugzilla::Install::Filesystem;
  18. # NOTE: This package may "use" any modules that it likes,
  19. # and localconfig is available. However, all functions in this
  20. # package should assume that:
  21. #
  22. # * Templates are not available.
  23. # * Files do not have the correct permissions.
  24. # * The database does not exist.
  25. use strict;
  26. use Bugzilla::Constants;
  27. use Bugzilla::Error;
  28. use Bugzilla::Install::Localconfig;
  29. use Bugzilla::Util;
  30. use File::Find;
  31. use File::Path;
  32. use File::Basename;
  33. use IO::File;
  34. use POSIX ();
  35. use base qw(Exporter);
  36. our @EXPORT = qw(
  37. update_filesystem
  38. create_htaccess
  39. fix_all_file_permissions
  40. );
  41. # This looks like a constant because it effectively is, but
  42. # it has to call other subroutines and read the current filesystem,
  43. # so it's defined as a sub. This is not exported, so it doesn't have
  44. # a perldoc. However, look at the various hashes defined inside this
  45. # function to understand what it returns. (There are comments throughout.)
  46. #
  47. # The rationale for the file permissions is that the web server generally
  48. # runs as apache, so the cgi scripts should not be writable for apache,
  49. # otherwise someone may find it possible to change the cgis when exploiting
  50. # some security flaw somewhere (not necessarily in Bugzilla!)
  51. sub FILESYSTEM {
  52. my $datadir = bz_locations()->{'datadir'};
  53. my $attachdir = bz_locations()->{'attachdir'};
  54. my $extensionsdir = bz_locations()->{'extensionsdir'};
  55. my $webdotdir = bz_locations()->{'webdotdir'};
  56. my $templatedir = bz_locations()->{'templatedir'};
  57. my $libdir = bz_locations()->{'libpath'};
  58. my $extlib = bz_locations()->{'ext_libpath'};
  59. my $skinsdir = bz_locations()->{'skinsdir'};
  60. my $ws_group = Bugzilla->localconfig->{'webservergroup'};
  61. # The set of permissions that we use:
  62. # FILES
  63. # Executable by the web server
  64. my $ws_executable = $ws_group ? 0750 : 0755;
  65. # Executable by the owner only.
  66. my $owner_executable = 0700;
  67. # Readable by the web server.
  68. my $ws_readable = $ws_group ? 0640 : 0644;
  69. # Readable by the owner only.
  70. my $owner_readable = 0600;
  71. # Writeable by the web server.
  72. my $ws_writeable = $ws_group ? 0660 : 0666;
  73. # DIRECTORIES
  74. # Readable by the web server.
  75. my $ws_dir_readable = $ws_group ? 0750 : 0755;
  76. # Readable only by the owner.
  77. my $owner_dir_readable = 0700;
  78. # Writeable by the web server.
  79. my $ws_dir_writeable = $ws_group ? 0770 : 01777;
  80. # The web server can overwrite files owned by other users,
  81. # in this directory.
  82. my $ws_dir_full_control = $ws_group ? 0770 : 0777;
  83. # Note: When being processed by checksetup, these have their permissions
  84. # set in this order: %all_dirs, %recurse_dirs, %all_files.
  85. #
  86. # Each is processed in alphabetical order of keys, so shorter keys
  87. # will have their permissions set before longer keys (thus setting
  88. # the permissions on parent directories before setting permissions
  89. # on their children).
  90. # --- FILE PERMISSIONS (Non-created files) --- #
  91. my %files = (
  92. '*' => { perms => $ws_readable },
  93. '*.cgi' => { perms => $ws_executable },
  94. 'whineatnews.pl' => { perms => $ws_executable },
  95. 'collectstats.pl' => { perms => $ws_executable },
  96. 'checksetup.pl' => { perms => $owner_executable },
  97. 'importxml.pl' => { perms => $ws_executable },
  98. 'runtests.pl' => { perms => $owner_executable },
  99. 'testserver.pl' => { perms => $ws_executable },
  100. 'whine.pl' => { perms => $ws_executable },
  101. 'customfield.pl' => { perms => $owner_executable },
  102. 'email_in.pl' => { perms => $ws_executable },
  103. 'sanitycheck.pl' => { perms => $ws_executable },
  104. 'install-module.pl' => { perms => $owner_executable },
  105. 'docs/makedocs.pl' => { perms => $owner_executable },
  106. 'docs/style.css' => { perms => $ws_readable },
  107. 'docs/*/rel_notes.txt' => { perms => $ws_readable },
  108. 'docs/*/README.docs' => { perms => $owner_readable },
  109. "$datadir/bugzilla-update.xml" => { perms => $ws_writeable },
  110. "$datadir/params" => { perms => $ws_writeable },
  111. "$datadir/mailer.testfile" => { perms => $ws_writeable },
  112. );
  113. # Directories that we want to set the perms on, but not
  114. # recurse through. These are directories we didn't create
  115. # in checkesetup.pl.
  116. my %non_recurse_dirs = (
  117. '.' => $ws_dir_readable,
  118. docs => $ws_dir_readable,
  119. );
  120. # This sets the permissions for each item inside each of these
  121. # directories, including the directory itself.
  122. # 'CVS' directories are special, though, and are never readable by
  123. # the webserver.
  124. my %recurse_dirs = (
  125. # Writeable directories
  126. "$datadir/template" => { files => $ws_readable,
  127. dirs => $ws_dir_full_control },
  128. $attachdir => { files => $ws_writeable,
  129. dirs => $ws_dir_writeable },
  130. $webdotdir => { files => $ws_writeable,
  131. dirs => $ws_dir_writeable },
  132. graphs => { files => $ws_writeable,
  133. dirs => $ws_dir_writeable },
  134. # Readable directories
  135. "$datadir/mining" => { files => $ws_readable,
  136. dirs => $ws_dir_readable },
  137. "$datadir/duplicates" => { files => $ws_readable,
  138. dirs => $ws_dir_readable },
  139. "$libdir/Bugzilla" => { files => $ws_readable,
  140. dirs => $ws_dir_readable },
  141. $extlib => { files => $ws_readable,
  142. dirs => $ws_dir_readable },
  143. $templatedir => { files => $ws_readable,
  144. dirs => $ws_dir_readable },
  145. $extensionsdir => { files => $ws_readable,
  146. dirs => $ws_dir_readable },
  147. images => { files => $ws_readable,
  148. dirs => $ws_dir_readable },
  149. css => { files => $ws_readable,
  150. dirs => $ws_dir_readable },
  151. js => { files => $ws_readable,
  152. dirs => $ws_dir_readable },
  153. $skinsdir => { files => $ws_readable,
  154. dirs => $ws_dir_readable },
  155. t => { files => $owner_readable,
  156. dirs => $owner_dir_readable },
  157. 'docs/*/html' => { files => $ws_readable,
  158. dirs => $ws_dir_readable },
  159. 'docs/*/pdf' => { files => $ws_readable,
  160. dirs => $ws_dir_readable },
  161. 'docs/*/txt' => { files => $ws_readable,
  162. dirs => $ws_dir_readable },
  163. 'docs/*/images' => { files => $ws_readable,
  164. dirs => $ws_dir_readable },
  165. 'docs/lib' => { files => $owner_readable,
  166. dirs => $owner_dir_readable },
  167. 'docs/*/xml' => { files => $owner_readable,
  168. dirs => $owner_dir_readable },
  169. );
  170. # --- FILES TO CREATE --- #
  171. # The name of each directory that we should actually *create*,
  172. # pointing at its default permissions.
  173. my %create_dirs = (
  174. $datadir => $ws_dir_full_control,
  175. "$datadir/mining" => $ws_dir_readable,
  176. "$datadir/duplicates" => $ws_dir_readable,
  177. $attachdir => $ws_dir_writeable,
  178. $extensionsdir => $ws_dir_readable,
  179. graphs => $ws_dir_writeable,
  180. $webdotdir => $ws_dir_writeable,
  181. "$skinsdir/custom" => $ws_dir_readable,
  182. "$skinsdir/contrib" => $ws_dir_readable,
  183. );
  184. # The name of each file, pointing at its default permissions and
  185. # default contents.
  186. my %create_files = ();
  187. # Each standard stylesheet has an associated custom stylesheet that
  188. # we create. Also, we create placeholders for standard stylesheets
  189. # for contrib skins which don't provide them themselves.
  190. foreach my $skin_dir ("$skinsdir/custom", <$skinsdir/contrib/*>) {
  191. next if basename($skin_dir) =~ /^cvs$/i;
  192. $create_dirs{"$skin_dir/yui"} = $ws_dir_readable;
  193. foreach my $base_css (<$skinsdir/standard/*.css>) {
  194. _add_custom_css($skin_dir, basename($base_css), \%create_files, $ws_readable);
  195. }
  196. foreach my $dir_css (<$skinsdir/standard/*/*.css>) {
  197. $dir_css =~ s{.+?([^/]+/[^/]+)$}{$1};
  198. _add_custom_css($skin_dir, $dir_css, \%create_files, $ws_readable);
  199. }
  200. }
  201. # Because checksetup controls the creation of index.html separately
  202. # from all other files, it gets its very own hash.
  203. my %index_html = (
  204. 'index.html' => { perms => $ws_readable, contents => <<EOT
  205. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  206. <html>
  207. <head>
  208. <meta http-equiv="Refresh" content="0; URL=index.cgi">
  209. </head>
  210. <body>
  211. <h1>I think you are looking for <a href="index.cgi">index.cgi</a></h1>
  212. </body>
  213. </html>
  214. EOT
  215. }
  216. );
  217. # Because checksetup controls the .htaccess creation separately
  218. # by a localconfig variable, these go in a separate variable from
  219. # %create_files.
  220. my $ht_default_deny = <<EOT;
  221. # nothing in this directory is retrievable unless overridden by an .htaccess
  222. # in a subdirectory
  223. deny from all
  224. EOT
  225. my %htaccess = (
  226. "$attachdir/.htaccess" => { perms => $ws_readable,
  227. contents => $ht_default_deny },
  228. "$libdir/Bugzilla/.htaccess" => { perms => $ws_readable,
  229. contents => $ht_default_deny },
  230. "$extlib/.htaccess" => { perms => $ws_readable,
  231. contents => $ht_default_deny },
  232. "$templatedir/.htaccess" => { perms => $ws_readable,
  233. contents => $ht_default_deny },
  234. '.htaccess' => { perms => $ws_readable, contents => <<EOT
  235. # Don't allow people to retrieve non-cgi executable files or our private data
  236. <FilesMatch ^(.*\\.pm|.*\\.pl|.*localconfig.*)\$>
  237. deny from all
  238. </FilesMatch>
  239. EOT
  240. },
  241. "$webdotdir/.htaccess" => { perms => $ws_readable, contents => <<EOT
  242. # Restrict access to .dot files to the public webdot server at research.att.com
  243. # if research.att.com ever changes their IP, or if you use a different
  244. # webdot server, you'll need to edit this
  245. <FilesMatch \\.dot\$>
  246. Allow from 192.20.225.0/24
  247. Deny from all
  248. </FilesMatch>
  249. # Allow access to .png files created by a local copy of 'dot'
  250. <FilesMatch \\.png\$>
  251. Allow from all
  252. </FilesMatch>
  253. # And no directory listings, either.
  254. Deny from all
  255. EOT
  256. },
  257. # Even though $datadir may not (and should not) be accessible from the
  258. # web server, we can't know for sure, so create the .htaccess anyway.
  259. # It's harmless if it isn't accessible...
  260. "$datadir/.htaccess" => { perms => $ws_readable, contents => <<EOT
  261. # Nothing in this directory is retrievable unless overridden by an .htaccess
  262. # in a subdirectory; the only exception is duplicates.rdf, which is used by
  263. # duplicates.xul and must be accessible from the web server
  264. deny from all
  265. <Files duplicates.rdf>
  266. allow from all
  267. </Files>
  268. EOT
  269. },
  270. );
  271. my %all_files = (%create_files, %htaccess, %index_html, %files);
  272. my %all_dirs = (%create_dirs, %non_recurse_dirs);
  273. return {
  274. create_dirs => \%create_dirs,
  275. recurse_dirs => \%recurse_dirs,
  276. all_dirs => \%all_dirs,
  277. create_files => \%create_files,
  278. htaccess => \%htaccess,
  279. index_html => \%index_html,
  280. all_files => \%all_files,
  281. };
  282. }
  283. sub update_filesystem {
  284. my ($params) = @_;
  285. my $fs = FILESYSTEM();
  286. my %dirs = %{$fs->{create_dirs}};
  287. my %files = %{$fs->{create_files}};
  288. my $datadir = bz_locations->{'datadir'};
  289. # If the graphs/ directory doesn't exist, we're upgrading from
  290. # a version old enough that we need to update the $datadir/mining
  291. # format.
  292. if (-d "$datadir/mining" && !-d 'graphs') {
  293. _update_old_charts($datadir);
  294. }
  295. # By sorting the dirs, we assure that shorter-named directories
  296. # (meaning parent directories) are always created before their
  297. # child directories.
  298. foreach my $dir (sort keys %dirs) {
  299. unless (-d $dir) {
  300. print "Creating $dir directory...\n";
  301. mkdir $dir || die $!;
  302. # For some reason, passing in the permissions to "mkdir"
  303. # doesn't work right, but doing a "chmod" does.
  304. chmod $dirs{$dir}, $dir || die $!;
  305. }
  306. }
  307. _create_files(%files);
  308. if ($params->{index_html}) {
  309. _create_files(%{$fs->{index_html}});
  310. }
  311. elsif (-e 'index.html') {
  312. my $templatedir = bz_locations()->{'templatedir'};
  313. print <<EOT;
  314. *** It appears that you still have an old index.html hanging around.
  315. Either the contents of this file should be moved into a template and
  316. placed in the '$templatedir/en/custom' directory, or you should delete
  317. the file.
  318. EOT
  319. }
  320. # Delete old files that no longer need to exist
  321. # 2001-04-29 jake@bugzilla.org - Remove oldemailtech
  322. # http://bugzilla.mozilla.org/show_bugs.cgi?id=71552
  323. if (-d 'shadow') {
  324. print "Removing shadow directory...\n";
  325. rmtree("shadow");
  326. }
  327. if (-e "$datadir/versioncache") {
  328. print "Removing versioncache...\n";
  329. unlink "$datadir/versioncache";
  330. }
  331. }
  332. # A simple helper for creating "empty" CSS files.
  333. sub _add_custom_css {
  334. my ($skin_dir, $path, $create_files, $perms) = @_;
  335. $create_files->{"$skin_dir/$path"} = { perms => $perms, contents => <<EOT
  336. /*
  337. * Custom rules for $path.
  338. * The rules you put here override rules in that stylesheet.
  339. */
  340. EOT
  341. };
  342. }
  343. sub create_htaccess {
  344. _create_files(%{FILESYSTEM()->{htaccess}});
  345. # Repair old .htaccess files
  346. my $htaccess = new IO::File('.htaccess', 'r') || die ".htaccess: $!";
  347. my $old_data;
  348. { local $/; $old_data = <$htaccess>; }
  349. $htaccess->close;
  350. my $repaired = 0;
  351. if ($old_data =~ s/\|localconfig\|/\|.*localconfig.*\|/) {
  352. $repaired = 1;
  353. }
  354. if ($old_data !~ /\(\.\*\\\.pm\|/) {
  355. $old_data =~ s/\(/(.*\\.pm\|/;
  356. $repaired = 1;
  357. }
  358. if ($repaired) {
  359. print "Repairing .htaccess...\n";
  360. $htaccess = new IO::File('.htaccess', 'w') || die $!;
  361. print $htaccess $old_data;
  362. $htaccess->close;
  363. }
  364. my $webdot_dir = bz_locations()->{'webdotdir'};
  365. # The public webdot IP address changed.
  366. my $webdot = new IO::File("$webdot_dir/.htaccess", 'r')
  367. || die "$webdot_dir/.htaccess: $!";
  368. my $webdot_data;
  369. { local $/; $webdot_data = <$webdot>; }
  370. $webdot->close;
  371. if ($webdot_data =~ /192\.20\.225\.10/) {
  372. print "Repairing $webdot_dir/.htaccess...\n";
  373. $webdot_data =~ s/192\.20\.225\.10/192.20.225.0\/24/g;
  374. $webdot = new IO::File("$webdot_dir/.htaccess", 'w') || die $!;
  375. print $webdot $webdot_data;
  376. $webdot->close;
  377. }
  378. }
  379. # A helper for the above functions.
  380. sub _create_files {
  381. my (%files) = @_;
  382. # It's not necessary to sort these, but it does make the
  383. # output of checksetup.pl look a bit nicer.
  384. foreach my $file (sort keys %files) {
  385. unless (-e $file) {
  386. print "Creating $file...\n";
  387. my $info = $files{$file};
  388. my $fh = new IO::File($file, O_WRONLY | O_CREAT, $info->{perms})
  389. || die $!;
  390. print $fh $info->{contents} if $info->{contents};
  391. $fh->close;
  392. }
  393. }
  394. }
  395. # If you ran a REALLY old version of Bugzilla, your chart files are in the
  396. # wrong format. This code is a little messy, because it's very old, and
  397. # when moving it into this module, I couldn't test it so I left it almost
  398. # completely alone.
  399. sub _update_old_charts {
  400. my ($datadir) = @_;
  401. print "Updating old chart storage format...\n";
  402. foreach my $in_file (glob("$datadir/mining/*")) {
  403. # Don't try and upgrade image or db files!
  404. next if (($in_file =~ /\.gif$/i) ||
  405. ($in_file =~ /\.png$/i) ||
  406. ($in_file =~ /\.db$/i) ||
  407. ($in_file =~ /\.orig$/i));
  408. rename("$in_file", "$in_file.orig") or next;
  409. open(IN, "$in_file.orig") or next;
  410. open(OUT, '>', $in_file) or next;
  411. # Fields in the header
  412. my @declared_fields;
  413. # Fields we changed to half way through by mistake
  414. # This list comes from an old version of collectstats.pl
  415. # This part is only for people who ran later versions of 2.11 (devel)
  416. my @intermediate_fields = qw(DATE UNCONFIRMED NEW ASSIGNED REOPENED
  417. RESOLVED VERIFIED CLOSED);
  418. # Fields we actually want (matches the current collectstats.pl)
  419. my @out_fields = qw(DATE NEW ASSIGNED REOPENED UNCONFIRMED RESOLVED
  420. VERIFIED CLOSED FIXED INVALID WONTFIX LATER REMIND
  421. DUPLICATE WORKSFORME MOVED);
  422. while (<IN>) {
  423. if (/^# fields?: (.*)\s$/) {
  424. @declared_fields = map uc, (split /\||\r/, $1);
  425. print OUT "# fields: ", join('|', @out_fields), "\n";
  426. }
  427. elsif (/^(\d+\|.*)/) {
  428. my @data = split(/\||\r/, $1);
  429. my %data;
  430. if (@data == @declared_fields) {
  431. # old format
  432. for my $i (0 .. $#declared_fields) {
  433. $data{$declared_fields[$i]} = $data[$i];
  434. }
  435. }
  436. elsif (@data == @intermediate_fields) {
  437. # Must have changed over at this point
  438. for my $i (0 .. $#intermediate_fields) {
  439. $data{$intermediate_fields[$i]} = $data[$i];
  440. }
  441. }
  442. elsif (@data == @out_fields) {
  443. # This line's fine - it has the right number of entries
  444. for my $i (0 .. $#out_fields) {
  445. $data{$out_fields[$i]} = $data[$i];
  446. }
  447. }
  448. else {
  449. print "Oh dear, input line $. of $in_file had " .
  450. scalar(@data) . " fields\nThis was unexpected.",
  451. " You may want to check your data files.\n";
  452. }
  453. print OUT join('|',
  454. map { defined ($data{$_}) ? ($data{$_}) : "" } @out_fields),
  455. "\n";
  456. }
  457. else {
  458. print OUT;
  459. }
  460. }
  461. close(IN);
  462. close(OUT);
  463. }
  464. }
  465. sub fix_all_file_permissions {
  466. my ($output) = @_;
  467. my $ws_group = Bugzilla->localconfig->{'webservergroup'};
  468. my $group_id = _check_web_server_group($ws_group, $output);
  469. return if ON_WINDOWS;
  470. my $fs = FILESYSTEM();
  471. my %files = %{$fs->{all_files}};
  472. my %dirs = %{$fs->{all_dirs}};
  473. my %recurse_dirs = %{$fs->{recurse_dirs}};
  474. print get_text('install_file_perms_fix') . "\n" if $output;
  475. my $owner_id = POSIX::getuid();
  476. $group_id = POSIX::getgid() unless defined $group_id;
  477. foreach my $dir (sort keys %dirs) {
  478. next unless -d $dir;
  479. _fix_perms($dir, $owner_id, $group_id, $dirs{$dir});
  480. }
  481. foreach my $dir (sort keys %recurse_dirs) {
  482. next unless -d $dir;
  483. # Set permissions on the directory itself.
  484. my $perms = $recurse_dirs{$dir};
  485. _fix_perms($dir, $owner_id, $group_id, $perms->{dirs});
  486. # Now recurse through the directory and set the correct permissions
  487. # on subdirectories and files.
  488. find({ no_chdir => 1, wanted => sub {
  489. my $name = $File::Find::name;
  490. if (-d $name) {
  491. _fix_perms($name, $owner_id, $group_id, $perms->{dirs});
  492. }
  493. else {
  494. _fix_perms($name, $owner_id, $group_id, $perms->{files});
  495. }
  496. }}, $dir);
  497. }
  498. foreach my $file (sort keys %files) {
  499. # %files supports globs
  500. foreach my $filename (glob $file) {
  501. # Don't touch directories.
  502. next if -d $filename || !-e $filename;
  503. _fix_perms($filename, $owner_id, $group_id,
  504. $files{$file}->{perms});
  505. }
  506. }
  507. _fix_cvs_dirs($owner_id, '.');
  508. }
  509. # A helper for fix_all_file_permissions
  510. sub _fix_cvs_dirs {
  511. my ($owner_id, $dir) = @_;
  512. my $owner_gid = POSIX::getgid();
  513. find({ no_chdir => 1, wanted => sub {
  514. my $name = $File::Find::name;
  515. if ($File::Find::dir =~ /\/CVS/ || $_ eq '.cvsignore'
  516. || (-d $name && $_ eq 'CVS')) {
  517. _fix_perms($name, $owner_id, $owner_gid, 0700);
  518. }
  519. }}, $dir);
  520. }
  521. sub _fix_perms {
  522. my ($name, $owner, $group, $perms) = @_;
  523. #printf ("Changing $name to %o\n", $perms);
  524. chown $owner, $group, $name
  525. || warn "Failed to change ownership of $name: $!";
  526. chmod $perms, $name
  527. || warn "Failed to change permissions of $name: $!";
  528. }
  529. sub _check_web_server_group {
  530. my ($group, $output) = @_;
  531. my $filename = bz_locations()->{'localconfig'};
  532. my $group_id;
  533. # If we are on Windows, webservergroup does nothing
  534. if (ON_WINDOWS && $group && $output) {
  535. print "\n\n" . get_text('install_webservergroup_windows') . "\n\n";
  536. }
  537. # If we're not on Windows, make sure that webservergroup isn't
  538. # empty.
  539. elsif (!ON_WINDOWS && !$group && $output) {
  540. print "\n\n" . get_text('install_webservergroup_empty') . "\n\n";
  541. }
  542. # If we're not on Windows, make sure we are actually a member of
  543. # the webservergroup.
  544. elsif (!ON_WINDOWS && $group) {
  545. $group_id = getgrnam($group);
  546. ThrowCodeError('invalid_webservergroup', { group => $group })
  547. unless defined $group_id;
  548. # If on unix, see if we need to print a warning about a webservergroup
  549. # that we can't chgrp to
  550. if ($output && $< != 0 && !grep($_ eq $group_id, split(" ", $)))) {
  551. print "\n\n" . get_text('install_webservergroup_not_in') . "\n\n";
  552. }
  553. }
  554. return $group_id;
  555. }
  556. 1;
  557. __END__
  558. =head1 NAME
  559. Bugzilla::Install::Filesystem - Fix up the filesystem during
  560. installation.
  561. =head1 DESCRIPTION
  562. This module is used primarily by L<checksetup.pl> to modify the
  563. filesystem during installation, including creating the data/ directory.
  564. =head1 SUBROUTINES
  565. =over
  566. =item C<update_filesystem({ index_html => 0 })>
  567. Description: Creates all the directories and files that Bugzilla
  568. needs to function but doesn't ship with. Also does
  569. any updates to these files as necessary during an
  570. upgrade.
  571. Params: C<index_html> - Whether or not we should create
  572. the F<index.html> file.
  573. Returns: nothing
  574. =item C<create_htaccess()>
  575. Description: Creates all of the .htaccess files for Apache,
  576. in the various Bugzilla directories. Also updates
  577. the .htaccess files if they need updating.
  578. Params: none
  579. Returns: nothing
  580. =item C<fix_all_file_permissions($output)>
  581. Description: Sets all the file permissions on all of Bugzilla's files
  582. to what they should be. Note that permissions are different
  583. depending on whether or not C<$webservergroup> is set
  584. in F<localconfig>.
  585. Params: C<$output> - C<true> if you want this function to print
  586. out information about what it's doing.
  587. Returns: nothing
  588. =back