#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; print "Sanitize script for node removal from container.\n"; system("pwd"); { my @l = (".",".."); for(1..8) { push @l, (("../" x $_)."..") } for(@l) { my $cmd = "ls -ld $_"; print "running: $cmd\n"; system $cmd; } } my $tmpdir = "tmp-sanitize"; die "Has already run, abort" if -e $tmpdir; mkdir $tmpdir; chmodWrap(0755, $tmpdir); chdir($tmpdir); system("ln -s ../uniworx.tar.gz ."); system("tar xzvf uniworx.tar.gz"); chmodWrap(0755, '.'); # tar can change the rights of '.' if it contains an entry for '.' with other rights my %truerights = (); storeRightsMake7("."); #print "=== Extended rights:\n"; #system("ls -l *"); #resetRights("."); #print "=== Reset rights:\n"; #system("ls -l *"); sub chmodWrap { my ($mode, $fn) = @_; my $tries = 0; die "file '$fn' does not exist; cannot change its permissions to $mode" unless -e $fn; RIGHTS: { chmod($mode, $fn); my $ismode = (stat($fn))[2]; my $fm = $ismode % 512; if($fm != $mode) { if($tries++ > 20) { die "Problem with file permissions, abort" } warn sprintf "File rights were meant to be set, but were not updated properly for file '%s', is %03o but was set to %03o; try again in 1 second"; sleep 1; redo RIGHTS; } } } # sub storeRightsMake7 { my ($pwd) = @_; my $dh = undef; opendir($dh, $pwd) or die "Could not read dir '$pwd', because: $!"; while(my $fn = readdir($dh)) { next if $fn=~m#^\.\.?$#; #perl -le 'my $dh = undef;opendir($dh, ".");while(my $fn = readdir($dh)) { my $mode = (stat($fn))[2];my $fm = $mode % 512;my $fmo=sprintf("%03o",$fm);print "$fn -> $fmo" }' my $fullname = "$pwd/$fn"; my $mode = (stat($fullname))[2]; my $fm = $mode % 512; #my $fmo = sprintf("%03o",$fm); $truerights{$fullname} = $fm; chmodWrap(($fm | 0700), $fullname); storeRightsMake7($fullname) if -d $fullname; } } sub resetRights { my ($pwd) = @_; print "Resetting rights to:\n" if '.' eq $pwd; print Data::Dumper::Dumper(\%truerights); my $dh = undef; opendir($dh, $pwd) or die "Could not read dir '$pwd', because: $!"; while(my $fn = readdir($dh)) { next if $fn=~m#^\.\.?$#; #perl -le 'my $dh = undef;opendir($dh, ".");while(my $fn = readdir($dh)) { my $mode = (stat($fn))[2];my $fm = $mode % 512;my $fmo=sprintf("%03o",$fm);print "$fn -> $fmo" }' my $fullname = "$pwd/$fn"; printf(" set rights of '$fullname' back to %03o\n", $truerights{$fullname}); chmodWrap($truerights{$fullname}, $fullname); resetRights($fullname) if -d $fullname; } } sub renameWithRights { my ($from, $to) = @_; print " rename file '$from' to '$to'\n"; my %oldrights = %truerights; %truerights = (); while(my ($k,$v) = each %oldrights) { $k =~ s#^\./\Q$from\E#./$to#; $truerights{$k} = $v; } #my $rights = $truerights{$from}; #delete $truerights{$from}; rename($from, $to) or die "Could not rename '$from' to '$to', because $!"; my $waittimer = 20; while(-e $from || not(-e $to) and $waittimer-- > 0) { sleep 1 } die "rename file from '$from' to '$to', but it is still there" if -e $from; die "rename file from '$from' to '$to', but there is no file under the new name" unless -e $to; #$truerights{$to} = $rights } print Data::Dumper::Dumper(\%truerights); #exit 0; # Checksummen: # outerjson c27f -- toplevel $outerjson.json, by sha256sum $outerjson.json # imageid d940 -- toplevel verzeichnis mit der layer darin; doc says: Each image’s ID is given by the SHA256 hash of its configuration JSON. # we'll try as configuration "remove nodejs $oldhash" # or we just use a random number ;) # layertar fd3d -- doc says: Each image’s ID is given by the SHA256 hash of its configuration JSON. # ##### FOUND # outerjson c27f64c8de183296ef409baecc27ddac8cd4065aac760b1b512caf482ad782dd -- in manifest.json # imageid d940253667b5ab47060e8bf537bd5b3e66a2447978f3c784a22b115a262fccbf -- in manifest.json # imageid d940253667b5ab47060e8bf537bd5b3e66a2447978f3c784a22b115a262fccbf -- as toplevel dirname # outerjson c27f64c8de183296ef409baecc27ddac8cd4065aac760b1b512caf482ad782dd -- as toplevel filename # imageid d940253667b5ab47060e8bf537bd5b3e66a2447978f3c784a22b115a262fccbf -- in $layerdir/json # layertar fd3d3cdf4ece09864ac933aa664eb5f397cf5ca28652125addd689726f8485cd -- in $outerjson.json # # ##### COMPUTE # toplevel # outerjson c27f64c8de183296ef409baecc27ddac8cd4065aac760b1b512caf482ad782dd $outerjson.json # b21db3fcc85b23d91067a2a5834e114ca9eec0364742c8680546f040598d8cd9 manifest.json # 238f234e3a1ddb27a034f4ee1e59735175741e5cc05673b5dd41d9a42bac2ebd uniworx.tar.gz # in $layerdir/ # 028c1e8d9688b420f7316bb44ce0e26f4712dc21ef93c5af8000c102b1405ad4 json # layertar fd3d3cdf4ece09864ac933aa664eb5f397cf5ca28652125addd689726f8485cd layer.tar # d0ff5974b6aa52cf562bea5921840c032a860a91a3512f7fe8f768f6bbe005f6 VERSION # # # sha256sum layer.tar fd3d3cdf4ece09864ac933aa664eb5f397cf5ca28652125addd689726f8485cd my ($outerjson, $imageid) = (); { my $dirh = undef; opendir($dirh, '.') or die "Could not read dir '.', because: $!"; while(my $fn = readdir($dirh)) { next if $fn=~m#^\.#; if($fn=~m#(.{16,})\.json#) { # it shall match on hash sums but not for example on manifest.json $outerjson = $1; next } if($fn=~m#^[0-9a-f]{64}$#) { $imageid = $fn } } } die "Bad archive, could not found expected files and directories" unless defined($outerjson) and defined($imageid); #system("pwd"); #print "will run: sha256sum $imageid/layer.tar\n"; my $oldLayerdir = qx(sha256sum $imageid/layer.tar); #print "oldLayerdir is for now $oldLayerdir\n\n"; $oldLayerdir =~ m#^([0-9a-f]{64}).*$# or die "layer.tar not found or sha256sum not installed!"; $oldLayerdir = $1; # tar --delete --file layer.tar nix/store/cdalbhzm3z4gz07wyg89maprdbjc4yah-nodejs-14.17.0 my $layerContent = qx(tar -tf $imageid/layer.tar); my @rms = $layerContent=~m#^((?:\./)?nix/store/[a-z0-9]+-(?:nodejs|openjdk|ghc)-[^/]+/)$#gm; print "rm <<$_>>\n" for @rms; system("tar --delete --file $imageid/layer.tar '$_'") for @rms; ### Deconstruction finished, now lets put everything together again after fixing the checksums my $newImageId = qx(echo 'remove nodejs $imageid' | sha256sum); $newImageId =~ m#^([0-9a-f]{64}).*$# or die "sha256sum not installed!"; $newImageId = $1; my $newLayerdir = qx(sha256sum $imageid/layer.tar); $newLayerdir =~ m#^([0-9a-f]{64}).*$# or die "sha256sum not installed!"; $newLayerdir = $1; # new outerjson is computed later, as we first have to change its content sub cautionWaiter { # some file operations give the impression that they are not instant. # Hence, we wait here a bit to see if that fixes stuff #sleep 5; # seems not to be the reason } sub replaceInFile { my ($filename, $replacer) = @_; return unless -e $filename; my $fh = undef; open($fh, '<', $filename) or die "Could not read $filename, because: $!"; my $content = join '', <$fh>; close $fh; keys %$replacer; while(my ($k,$v) = each %$replacer) { $content=~s#\Q$k\E#$v#g; } my $wh = undef; open($wh, '>', $filename) or die "Could not write $filename, because: $!"; print $wh $content; close $wh; } my %replacer = ( $oldLayerdir => $newLayerdir, $imageid => $newImageId, ); replaceInFile("$imageid/json", \%replacer); replaceInFile("$outerjson.json", \%replacer); cautionWaiter(); my $newOuterjson = qx(sha256sum '$outerjson.json'); $newOuterjson =~ m#^([0-9a-f]{64}).*$# or die "sha256sum not installed!"; $newOuterjson = $1; cautionWaiter(); renameWithRights("$outerjson.json", "$newOuterjson.json"); $replacer{$outerjson} = $newOuterjson; replaceInFile("manifest.json", \%replacer); replaceInFile("repositories", \%replacer); cautionWaiter(); renameWithRights($imageid, $newImageId); cautionWaiter(); resetRights("."); system("find"); unlink("uniworx.tar.gz"); system("tar czvf uniwox-rmnodejs.tar.gz *"); cautionWaiter(); print "Debug output, content of container:\n"; system("tar tzvf uniwox-rmnodejs.tar.gz"); cautionWaiter(); #unlink("../uniworx.tar.gz"); system("cp uniwox-rmnodejs.tar.gz ../uniworx-sanitized.tar.gz");