--- toast 2003/12/13 02:36:56 1.269 +++ toast 2003/12/15 00:49:56 1.270 @@ -241,6 +241,7 @@ "ignorecase" => true, "showurls" => true, "infodir" => true, + "protect" => true, "debugrewrite" => false, ); @@ -274,7 +275,7 @@ my($name) = @_; return isopt($name) && isboolean($optdefault{$name}); } - + sub checkoptname($) { my($name) = @_; @@ -465,47 +466,6 @@ ############################################################################## -sub silentrm(@) -{ - unlink || error("rm $_: $!") foreach @_; - true; -} - -sub rm(@) -{ - announce("rm", @_); - silentrm(@_); -} - -sub silentrd(@) -{ - rmdir || error("rmdir $_: $!") foreach @_; - true; -} - -sub rd(@) -{ - announce("rmdir", @_); - silentrd(@_); -} - -sub rmall(@) -{ - @_ || error; - announce("rm", "-r", @_); - dfs($_, sub { true }, sub { silentrm($_) }, sub { silentrd($_) }) foreach @_; - true; -} - -sub rmexisting(@) -{ - my(@existing) = grep(-e || -l, @_); - rmall(@existing) if @existing; - true; -} - -############################################################################## - sub mv($$) { my($source, $dest) = @_; @@ -528,17 +488,39 @@ ############################################################################## -sub safechmod($@) +sub safestat($) { - my($mode, @files) = @_; - $mode = $mode & ~umask; + my($file) = @_; + my(@result) = stat($file); + @result || error("stat $file: $!"); + @result; +} + +sub getmode($) { (safestat($_[0]))[2] & 07777 } + +sub chmodimpl($$@) +{ + my($mode, $announce, @files) = @_; + $mode &= 01777 & ~umask; my($txtmode) = sprintf("%lo", $mode); - (($mode | 01777) == 01777) || error("unsafe mode: $txtmode"); - announce("chmod", $txtmode, @files); - chmod($mode, $_) || error("chmod $txtmode $_: $!") foreach @files; + @files = grep { getmode($_) != $mode } @files; + announce("chmod", $txtmode, @files) if @files && $announce; + chmod($mode, $_) || error("chmod $txtmode $_: $!") for @files; true; } +sub safechmod($@) +{ + my($mode, @files) = @_; + chmodimpl($mode, true, @files); +} + +sub silentchmod($@) +{ + my($mode, @files) = @_; + chmodimpl($mode, false, @files); +} + sub writefilemode($$@) { my($name, $mode, @contents) = @_; @@ -562,16 +544,6 @@ writefilemode($name, 0777, @contents); } -sub safestat($) -{ - my($file) = @_; - my(@result) = stat($file); - @result || error("stat $file: $!"); - @result; -} - -sub getmode($) { (safestat($_[0]))[2] & 0x1777; } - sub samefile($$) { my($a, $b) = @_; @@ -621,6 +593,48 @@ ############################################################################## +sub silentrm(@) +{ + unlink || error("rm $_: $!") for @_; + true; +} + +sub rm(@) +{ + announce("rm", "-f", @_) if @_; + silentrm(@_); +} + +sub silentrd(@) +{ + rmdir || error("rmdir $_: $!") for @_; + true; +} + +sub rd(@) +{ + announce("rmdir", @_) if @_; + silentrd(@_); +} + +sub rmall(@) +{ + announce("rm", "-rf", @_) if @_; + dfs + ( + $_, + sub { -w($_) ? true : silentchmod(0777, $_) }, + sub { silentrm($_) }, + sub { silentrd($_) } + ) for @_; + true; +} + +sub optrm(@) { rm(grep(-e || -l, @_)) } +sub optrmall(@) { rmall(grep(-e || -l, @_)) } + +############################################################################## + sub nice($) { eval { setpriority(0, 0, min(getpriority(0, 0) + $_[0], 20)) }; @@ -1505,7 +1519,7 @@ $redirhead =~ /^Location: ([^\r\n]+)/m || error("unexpected response from $redirurl"); $url = $1; - # wget can't always get FTP directory listing correctly w/o trailing slash + # wget can't always get FTP directory listing correctly w/o trailing slash $url .= "/" unless basename(stripquery($url)) =~ /\./; # e.g. atop } @@ -2684,16 +2698,8 @@ return true; } -sub fixrootsubdir($) +sub fixbrokenrootlink($) { - my($dir) = @_; - my(undef, undef, $mode) = safestat($dir); - safechmod(0755, $dir) unless ($mode & 07777) == 0755; - return true; -} - -sub fixrootlink($) -{ # stdlinks fool XFree86 and busybox into creating broken symlinks; fix them! my($link) = @_; my($dest) = readlink($link); @@ -2708,6 +2714,13 @@ true; } +sub fixrootfile($) +{ + my($file) = @_; + return -e($file) ? true : fixbrokenrootlink($file) if -l($file); + safechmod(-x($file) ? 0555 : 0444, $file); +} + sub polishrootdir($$) { my($rootdir, $armdir) = @_; @@ -2716,15 +2729,15 @@ -l && rm($_) foreach map { path($rootdir, $_) } (keys(%link), unpath($rootdir), unpath($armdir)); - rmexisting(path($rootdir, "info", "dir")); + optrmall(path($rootdir, "info", "dir")); my($total, %count) = 0; dfs ( $rootdir, - sub { $count{$_} = $total; fixrootsubdir($_) }, - sub { fixrootlink($_) if -l && !-e; ++$total }, - sub { $count{$_} < $total || rd($_) } + sub { $count{$_} = $total; true }, + sub { fixrootfile($_); ++$total }, + sub { $count{$_} < $total ? safechmod(0555, $_) : rd($_) } ); $total || error("no files found in $rootdir"); } @@ -2746,7 +2759,7 @@ return true unless isbuilt($name, $version, $build); my($srcdir) = path($builddir, srcdir); my($helperdir) = path($builddir, helperdir); - rmexisting($srcdir, $helperdir); + optrmall($srcdir, $helperdir); } @_; return true; @@ -2762,7 +2775,7 @@ for $version (allversions($name, $version)) { my($archivedir) = path(pkgpath($name, $version), archivedir); - rmexisting($archivedir); + optrmall($archivedir); } } @@ -2853,17 +2866,18 @@ sub { chown(0, 0, $_) || ($success = !($msg = "chown $_: $!")) }, sub { - if(-l) + if(!-l) { + return chown(0, 0, $_) || ($success = !($msg = "chown $_: $!")) + } + else + { my($source) = readlink; + error("readlink $_: $!") unless defined($source); return (unlink || ($success = !($msg = "unlink $_: $!"))) && (symlink($source, $_) || ($success = !($msg = "symlink $source $_: $!"))); } - else - { - return chown(0, 0, $_) || ($success = !($msg = "chown $_: $!")) - } }, sub { true } ); @@ -2983,14 +2997,17 @@ $oldfile = addoff($oldfile); } + my($mode) = getmode($dir); + safechmod(0777, $dir); + if(!infodir) { - rmexisting($dirfile); + optrm($dirfile); } else { my($tmpfile) = addtmp($dirfile); - rmexisting($tmpfile); + optrm($tmpfile); my($tmpsuffix) = tmpsuffix; my($offsuffix) = offsuffix; abswhiledir @@ -3000,6 +3017,8 @@ } $dir; mv($tmpfile, $dirfile) if -e($tmpfile); } + + safechmod($mode, $dir); } sub postarm() @@ -3019,6 +3038,18 @@ optmd(armdir); lock(armdir); + my($mode) = (protect ? 0555 : 0777) & ~umask; + if(getmode(armdir) != $mode) + { + dfs + ( + armdir, + sub { safechmod($mode, $_) }, + sub { true }, + sub { true } + ); + } + my(%link) = stdlinks; for(keys(%link)) { @@ -3026,8 +3057,16 @@ if(!-e($abs) && !-l($abs)) { my($dir) = dirname($abs); - -d($dir) || md($dir); + if(!-d($dir)) + { + my($parent) = dirname($dir); + safechmod(755, $parent); + md($dir); + safechmod($mode, $parent); + } + safechmod(0777, $dir); ln($link{$_}, $abs); + safechmod($mode, $dir); } } @@ -3036,9 +3075,14 @@ dfs ( $rootdir, - sub { optmd(optpath(armdir, $_[0])) }, + sub + { + my($dir) = optpath(armdir, $_[0]); + optmd($dir); + safechmod(0777, $dir); + }, sub { ln($_, displace(optpath(armdir, $_[0]))) }, - sub { true } + sub { safechmod($mode, optpath(armdir, $_[0])) } ); postarm; @@ -3071,13 +3115,24 @@ { my($name, $version, $build) = @_; my($rootdir) = path(pkgpath($name, $version, $build), rootdir); + my(@dirmodes); -d($rootdir) && dfs # ignore broken packages ( $rootdir, - sub { true }, sub { my($rel) = @_; + my($armdir) = optpath(armdir, $rel); + if(-d($armdir) && !-l($armdir)) + { + push(@dirmodes, getmode($armdir)); + safechmod(0777, $armdir); + } + return true; + }, + sub + { + my($rel) = @_; my($armfile) = path(armdir, $rel); # BUG: $rel is sometimes undefined? while(-e($armfile) || -l($armfile)) { @@ -3091,10 +3146,15 @@ { my($rel) = @_; my($armdir) = optpath(armdir, $rel); - rd($armdir) if -d($armdir) && isempty($armdir); + if(-d($armdir) && !-l($armdir)) + { + my($mode) = pop(@dirmodes); + isempty($armdir) ? rd($armdir) : safechmod($mode, $armdir); + } return true; } ); + error if @dirmodes; } @_; postarm; @@ -3638,7 +3698,7 @@ " $myname help *COMMAND*\n"; $body{"commands"} .= "For a list of available options:" . " $myname help options\n\n"; - + $head{"options"} = "${usage}where *OPTION* is one of the following:\n\n"; for(@opts) { @@ -4273,7 +4333,7 @@ sub cmd_tarself(@) { error('try "make dist" instead') if @_ || -t(STDIN) || -t(STDOUT); - + my($namever) = "$myname-$myversion"; my($tarfile) = "$namever.tar"; @@ -5299,6 +5359,14 @@ is disabled, B<toast arm> and B<toast disarm> will delete the C<info/dir> file, if present, instead of rebuilding it. Default: enabled. +=item S<B<--protect> | B<--noprotect>> + +If B<protect> is enabled, B<toast arm> will attempt to ensure that +armdir and its subdirectories are read-only, changing existing modes if +necessary. If B<protect> is disabled, B<toast arm> will make B<armdir> +and its subdirectories read-write, assuming the current umask allows it. +This option never affects the permissions of files or symbolic links. + =item S<B<--debugrewrite> | B<--nodebugrewrite>> If B<debugrewrite> is enabled, B<toast build> will always generate broken @@ -5411,7 +5479,6 @@ - "toast status" should give information about stacking order - "toast rename" should rename armed packages by rewriting symlinks - "toast clean" should optionally remove old versions/builds - - directories in armdir should be read-only (mode 0555) by default - "toast build" should be able to deal with perl modules - add "toast check": verify storedir and armdir integrity (and fix?) - toast get could be more robust w/r/t failures and concurrency