#!/usr/bin/env perl -w # 2002-08-15 markus@gyger.org # pkgzip: turns Solaris SVR4 pkg into internally (b)zipped pkg $usage = <<__EOT__; usage: $0 [-b|-c|-z] [...] -b use bzip2/cpio -c use compress/cpio -z use zip (default) __EOT__ # -z: requires unzip to install pkg, available on: # Solaris 2.5 + patch 105708-02 # Solaris 2.5.1 + patch 105701-02 # Solaris 2.6 + patch 10619[34]-02 # Solaris 7 FCS and up # # -b: requires bzcat to install pkg, available on: # Solaris 2.6 + patch 11166[45]-01 # Solaris 7 + patch 11166[67]-01 # Solaris 8 FCS and up (SUNWbzip on miniroot starting with Solaris 8 7/01) # # -c: uses zcat & cpio to install pkg, available on all Solaris versions # basically the same as having a reloc.cpio.Z file, minimally faster # installation class action scripts $cas{unzip} = <<'__EOT__'; if read pkgdir then xargs -e -n 2048 unzip -o${PKG_INIT_INSTALL+q}d "${BASEDIR:-/}" \ "$pkgdir/archive/`expr \"//$0\" : '.*/i\.\(.*\)'`.zip" fi >&2 || exit 2 __EOT__ $cas{bzcat} = <<'__EOT__'; if read pkgdir then cd "${BASEDIR:-/}" && cat >$PKGSAV/filelist && bzcat "$pkgdir/archive/`expr \"//$0\" : '.*/i\.\(.*\)'`.cpio.bz2" | cpio -idmukv -C 512 -E "$PKGSAV/filelist" && rm -f "$PKGSAV/filelist" fi >&2 || exit 2 __EOT__ $cas{zcat} = <<'__EOT__'; if read pkgdir then cd "${BASEDIR:-/}" && cat >$PKGSAV/filelist && zcat "$pkgdir/archive/`expr \"//$0\" : '.*/i\.\(.*\)'`.cpio.Z" | cpio -idmukv -C 512 -E "$PKGSAV/filelist" && rm -f "$PKGSAV/filelist" fi >&2 || exit 2 __EOT__ $method = "unzip"; while (@ARGV and $ARGV[0] =~ /^[+-]/ and $_ = shift) { print($usage), exit if /^-?-h(elp)?$/; last if /^--$/; $method = "bzcat", next if /^-b$/; $method = "zcat", next if /^-c$/; $method = "unzip", next if /^-z$/; die "$0: illegal option -- $_\n$usage"; } die $usage unless @ARGV; foreach $pkgdir (@ARGV) { # read pkginfo and pkgmap unless (open PKGINFO, "<$pkgdir/pkginfo") { warn "$0: $pkgdir: not a package\n"; next } @pkginfo = ; close PKGINFO or die "close $pkgdir/pkginfo: $!"; open PKGMAP, "<$pkgdir/pkgmap" or die "open $pkgdir/pkgmap: $!"; @pkgmap = ; close PKGMAP or die "close $pkgdir/pkgmap: $!"; # turn root object only pkg into relocatable pkg with BASEDIR / unless (grep /^(?:\d+\s+)?[bcdeflpsvx]\s+\S+\s+[^\/]/, @pkgmap) { foreach (@pkgmap) { s/^((?:\d+\s+)?d\s+\S+\s+)\/+(\s)/$1.$2/; s/^((?:\d+\s+)?[bcdeflpsvx]\s+\S+\s+)\/+/$1/; } @pkginfo = grep !/^BASEDIR=/, @pkginfo; push @pkginfo, "BASEDIR=/\n"; } # get files and their attributes for each class %class = %uid = %gid = %mode = %modtime = (); foreach (@pkgmap) { # replace parametric $BASEDIR paths with relative ones if possible s/\$BASEDIR\/// if /^(?:\d+\s+)?[bcdeflpsvx]\s+\S+\s+\$BASEDIR\//; s/\$BASEDIR/./ if /^(?:\d+\s+)?[bcdeflpsvx]\s+\S+\s+\$BASEDIR\s/; if (($class, $file, $mode, $uid, $gid, $modtime) = /^(?:\d+\s+)? [efv]\s+ (\S+)\s+ (\S+)\s+ (\S+)\s+ (\S+)\s+ (\S+)\s+ \S+\s+ \S+\s+ (\d+)/x) { push @{$class{$class}}, $file; $uid{$file} = getpwnam $uid unless $uid =~ /[\$?]/; $uid{$file} = -1 unless defined $uid{$file}; $gid{$file} = getgrnam $gid unless $gid =~ /[\$?]/; $gid{$file} = -1 unless defined $gid{$file}; $mode{$file} = (0777 & oct $mode) | 0400 if $mode =~ /^[0-7]+$/; $modtime{$file} = $modtime; } } undef $prevclass; foreach $class (sort keys %class) { # skip classes using a (system) installation class action script or # if already compressed or not relative or using pattern characters if ($class =~ /^(CompCpio|awk|build|rbac|sed)$/ || $class eq "preserve" && $method ne "unzip" || -f "$pkgdir/install/i.$class" || -f "$pkgdir/reloc.cpio.Z" || grep /^\/|\$/, @{$class{$class}} || # absolute or parametric path $method ne "unzip" && grep /[?*[]/, @{$class{$class}}) { # pattern delete $class{$class}; next } # write or link installation class action script install/i. -d "$pkgdir/install" || mkdir "$pkgdir/install", 0777 or die "mkdir $pkgdir/install: $!"; if (defined $prevclass && $class ne "preserve") { link "$pkgdir/install/i.$prevclass", "$pkgdir/install/i.$class" or die "ln install/i.$prevclass $pkgdir/install/i.$class: $!"; } else { $prevclass = $class unless $class eq "preserve"; open CAS, ">$pkgdir/install/i.$class" or die "open i.$class: $!"; $cas = $cas{$method}; $cas =~ s/-o/-n/ if $class eq "preserve"; # unzip only print CAS $cas; close CAS or die "close $pkgdir/install/i.$class: $!"; } # in case it was a root object only pkg before unless (-d "$pkgdir/reloc") { rename "$pkgdir/root", "$pkgdir/reloc" or die "mv $pkgdir/root $pkgdir/reloc: $!"; } # set owner, group, mode, timestamp according to pkgmap foreach $file (sort @{$class{$class}}) { chown $uid{$file}, $gid{$file}, "$pkgdir/reloc/$file"; # if root chmod $mode{$file}, "$pkgdir/reloc/$file" if defined $mode{$file}; utime $modtime{$file}, $modtime{$file}, "$pkgdir/reloc/$file" or warn "utime $pkgdir/reloc/$file: $!"; } # create zip/bzip2/compress/cpio archive and rm the files in reloc -d "$pkgdir/archive" || mkdir "$pkgdir/archive", 0777 or die "mkdir $pkgdir/archive: $!"; if ($method eq "unzip") { open ZIP, "| cd '$pkgdir/reloc' && " . "zip -9qmT@ '../archive/$class.zip'" or die "can't fork: $!"; print ZIP join "\n", @{$class{$class}}, ""; close ZIP or die "zip error: $! $?"; } elsif ($method eq "bzcat") { open BZIP2, "| cd '$pkgdir/reloc' && " . "cpio -oc -C 512 | bzcat -z9 >'../archive/$class.cpio.bz2'" or die "can't fork: $!"; print BZIP2 join "\n", @{$class{$class}}, ""; close BZIP2 or die "cpio/bzip2 error: $! $?"; unlink map "$pkgdir/reloc/$_", @{$class{$class}} or warn "rm: $!"; } elsif ($method eq "zcat") { open COMPRESS, "| cd '$pkgdir/reloc' && " . "cpio -oc -C 512 | compress >'../archive/$class.cpio.Z'" or die "can't fork: $!"; print COMPRESS join "\n", @{$class{$class}}, ""; close COMPRESS or die "cpio/compress error: $! $?"; unlink map "$pkgdir/reloc/$_", @{$class{$class}} or warn "rm: $!"; } } if (%class) { # rmdir empty directories in reloc system "cd '$pkgdir' && find reloc -type d -exec rmdir -ps {} +"; # PKG_SRC_NOVERIFY: don't check for files in reloc directory # PKG_DST_QKVERIFY: quick verify: fix instead of check of file attribs # PKG_CAS_PASSRELATIVE: instead of pair lines, first # pass $SUNW_PKG_DIR and then lines to cas $classes = join " ", sort keys %class; push @pkginfo, "PKG_SRC_NOVERIFY=$classes\n", "PKG_DST_QKVERIFY=$classes\n", "PKG_CAS_PASSRELATIVE=$classes\n"; # write new pkginfo open PKGINFO, ">$pkgdir/pkginfo" or die "open $pkgdir/pkginfo: $!"; print PKGINFO @pkginfo; close PKGINFO or die "close $pkgdir/pkginfo: $!"; # fix mtime, checksum & size of pkginfo, add compressed size of package $mtime = (stat "$pkgdir/pkginfo")[9]; $size = -s _ or die "empty file $pkgdir/pkginfo"; ($sum) = `sum '$pkgdir/pkginfo'` =~ /(\d+)/ or die "sum pkginfo: $?"; ($blocks) = `du -s '$pkgdir'` =~ /(\d+)/ or die "du -s '$pkgdir': $?"; foreach (@pkgmap) { s/^(:\s*\d+\s+\d+).*/$1 $blocks/; s/^((?:\d+\s+)?i\s+pkginfo\s+)\d+\s+\d+\s+\d+/$1 $size $sum $mtime/; } # write new pkgmap open PKGMAP, ">$pkgdir/pkgmap" or die "open $pkgdir/pkgmap: $!"; print PKGMAP @pkgmap; close PKGMAP or die "close $pkgdir/pkgmap: $!"; } else { warn "$0: nothing to zip in $pkgdir\n"; } }