1#!/usr/bin/perl -w
2# IBM_PROLOG_BEGIN_TAG
3# This is an automatically generated prolog.
4#
5# bos52S src/bos/usr/sbin/install/insttools/makebff.pl 1.9.1.1
6#
7# Licensed Materials - Property of IBM
8#
9# COPYRIGHT International Business Machines Corp. 2003,2008
10# All Rights Reserved
11#
12# US Government Users Restricted Rights - Use, duplication or
13# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
14#
15# IBM_PROLOG_END_TAG
16# @(#)10        1.9.1.1  src/bos/usr/sbin/install/insttools/makebff.pl, cmdmkinstallp, bos52S, s2008_51A3 9/30/08 16:43:46
17use strict;
18
19my $CONFIGDIR="./.info";  # Directory containing all the fileset config files
20my $MESG="";              # Message for dspmsg
21my $STRING="";            # Status string
22
23# Maps associating filesets with their variables
24my %bosboot=();           # BOS Boot flag
25my %description=();
26my %laf=();               # License Agreement File
27my %lar=();               # License Agreement Requirement
28my %requisite=();
29my %version=();           # VRMF
30
31# Globals
32my $configFileBlocks=0;   # Number of blocks for all cfg files (for INSTWORK)
33my @copyrights=();        # CRs go into the archive first, so keep a list of them
34my $fileset="";
35my $filesetDir="";        # Part of the dir for storing update fileset liblpp.a
36my $hasRoot=0;            # Keep track of whether or not we have a ROOT part
37my $hasRootFiles=0;       # Keep track of whether or not we have ROOT part files
38my $hasUsrFiles=0;        # Keep track of whether or not we have USR part files
39my $instrootDir="";       # Part of the dir for storing ROOT part files in an update's liblpp.a
40my @liblpp=();            # Update pkgs may have more than one liblpp
41my @listFileLine=();      # One line of list file data
42my $listFileLineNum=0;
43my $numFiles=0;           # Total number of files for this package
44my $package="";
45my $packageType="";       # I - regular, S - update
46my $packageVersion="";    # Pkg VRMF
47my %rootSize=();          # Maps ROOT part directories with their sizes for each fileset
48my %size=();              # Maps USR part directories with their sizes for each fileset
49my $symlinkKey=0;
50my @updateLibDirs=();     # Need the liblpp.a directories for listing in the backup tree
51my $vrmfDir="";           # Part of the dir for storing update fileset liblpp.a
52
53my @usrFilesForArchive=();    # USR part config files from all filesets for the non-update liblpp.a
54my @rootFilesForArchive=();   # ROOT part config files from all filesets for the non-update liblpp.a
55my @configFiles=(
56  "al","cfginfo","cfgfiles","config","config_u","copyright","err","fixdata",
57  "inventory","namelist","odmadd","odmdel","post_i","post_u","pre_d","pre_i",
58  "pre_rm","pre_u","productid","README","rm_inv","size","trc","unconfig","unconfig_u",
59  "unodmadd","unpost_i","unpost_u","unpre_i","unpre_u"
60);
61
62mkdir("./usr",0755) if ( ! -d "./usr");
63symlink("/etc","./etc") if ( ! -d "./etc");
64symlink("/var","./var") if ( ! -d "./var");
65symlink("/opt","./opt") if ( ! -d "./opt");
66symlink("/usr/local","./usr/local") if ( ! -s "./usr/local");
67
68# Open our lpp_name for writing data based on package info
69open (LPP,">lpp_name");
70
71unless ( -f "$CONFIGDIR/list" ){
72  $STRING="0503-884 makebff: $CONFIGDIR/list does not exist.\n";
73  $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 1 "$STRING");
74  die "$MESG";
75}
76
77# Collect package info one colon-delimitted line at a time
78open(LIST,"$CONFIGDIR/list");
79while (<LIST>){
80  $listFileLineNum++;
81  chomp;                  # Get rid of the newline
82  next if (/^#/ || /^$/); # Skip if we have a blank line
83
84  @listFileLine=split(/:/,$_);
85  if ($package eq ""){
86    $package=$listFileLine[0];
87    $packageVersion=$listFileLine[1];
88    if ($listFileLine[2]){
89      $packageType=$listFileLine[2];
90    } else {
91      $packageType="I";
92    }
93    printf(LPP "4 R %s %s {\n",$packageType,$package);
94    $STRING="processing %s %s %s package\n";
95    $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 2 "$STRING" "$package" "$packageVersion" "$packageType");
96    printf "$MESG"
97  } elsif (/^\*/){    # If the line starts with a '*', we have a requisite
98    if ($fileset){
99      $requisite{$fileset}="@listFileLine";
100    } else {
101      $STRING="0503-885 makebff: Requisite line for empty fileset in $CONFIGDIR/list. (%ld)\n";
102      $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 3 "$STRING" "$listFileLineNum");
103      die "$MESG";
104    }
105  } elsif (/^LAR/){   # If the line starts with 'LAR', we have a LAR
106    if ($fileset){
107      $lar{$fileset}="@listFileLine";
108    } else {
109      $STRING="0503-886 makebff: License agreement line for empty fileset in $CONFIGDIR/list. (%ld)\n";
110      $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 4 "$STRING" "$listFileLineNum");
111      die "$MESG";
112    }
113  } elsif (/^LAF/){   # If the line starts with 'LAF', we have a LAF. Ha.
114    if ($fileset){
115      $laf{$fileset}="@listFileLine";
116    } else {
117      $STRING="0503-887 makebff: License file line for empty fileset in $CONFIGDIR/list. (%ld)\n";
118      $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 5 "$STRING" "$listFileLineNum");
119      die "$MESG";
120    }
121  } else {
122    # If we have a fileset name already (and it's not the name at the beginning
123    # of this line), then we need to process this fileset with the data we
124    # collected last time through the loop. The last fileset of the package gets
125    # processed when we exit the while loop.
126    if ($fileset && $fileset ne $listFileLine[0]){
127      &FilesetInfo;
128    }
129    $fileset=$listFileLine[0];
130    $version{$fileset}=$listFileLine[1];
131    $description{$fileset}=$listFileLine[2];
132    $bosboot{$fileset}=$listFileLine[3];
133
134    $STRING="processing %s %s fileset\n";
135    $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 7 "$STRING" "$fileset" "$version{$fileset}");
136    printf "$MESG";
137
138    # Copyrights have to go into the liblpp.a first, so keep a
139    # separate array of them handy.
140    if ( -f "$CONFIGDIR/$fileset.copyright"){
141      push @copyrights, "$CONFIGDIR/$fileset.copyright";
142    }
143
144    if ( -f "$CONFIGDIR/$fileset.al" ){
145      $hasUsrFiles=1;
146      %size=&MakeInv("$CONFIGDIR/$fileset.al","$CONFIGDIR/$fileset.inventory");
147    }
148
149    # Check for ROOT part
150    if ( -f "$CONFIGDIR/.create_root" ){
151      $hasRoot=1;
152
153      if ( -f "$CONFIGDIR/root/$fileset.al" ){
154        $hasRootFiles=1;
155        %rootSize=&MakeInv("$CONFIGDIR/root/$fileset.al","$CONFIGDIR/root/$fileset.inventory");
156      }
157    }
158  }
159}
160&FilesetInfo;
161printf(LPP "}\n");
162close(LPP);
163close(LIST);
164
165mkdir("./usr/lpp",0755) if ( ! -d "./usr/lpp");
166mkdir("./usr/lpp/$package",0755) if ( ! -d "./usr/lpp/$package");
167
168# create big liblpp.a for install packages, and fileset liblpp.a's for update packages
169if ($packageType eq "I"){
170  $STRING="creating ./usr/lpp/$package/liblpp.a\n";
171  $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 9 "$STRING");
172  printf "$MESG";
173  unlink("./usr/lpp/$package/liblpp.a");
174  system("ar -crlg ./usr/lpp/$package/liblpp.a @copyrights @usrFilesForArchive");
175
176  if ( $hasRoot ){
177    mkdir("./usr/lpp/$package/inst_root",0755) if ( ! -d "./usr/lpp/$package/inst_root");
178    unlink("./usr/lpp/$package/inst_root/liblpp.a");
179    system("ar -crlg ./usr/lpp/$package/inst_root/liblpp.a @rootFilesForArchive");
180  }
181} else {
182  # The update fileset.a files are created in sub FilesetInfo
183  foreach my $fileset (keys %version){
184    $filesetDir=sprintf("./usr/lpp/%s/%s",$package,$fileset);
185    mkdir($filesetDir,0755) if ( ! -d $filesetDir);
186    push @updateLibDirs, $filesetDir;
187
188    $vrmfDir=sprintf("./usr/lpp/%s/%s/%s",$package,$fileset,$version{$fileset});
189    mkdir($vrmfDir,0755) if ( ! -d $vrmfDir);
190    push @updateLibDirs, $vrmfDir;
191
192    $STRING="copying $CONFIGDIR/%s.a to %s/liblpp.a\n";
193    $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 10 "$STRING" "$fileset" "$vrmfDir");
194    printf "$MESG";
195    system("cp $CONFIGDIR/$fileset.a $vrmfDir/liblpp.a");
196    push @liblpp, "$vrmfDir/liblpp.a";
197
198    if ($hasRoot){
199      $instrootDir=sprintf("./usr/lpp/%s/%s/%s/inst_root",$package,$fileset,$version{$fileset});
200      mkdir($instrootDir,0755) if ( ! -d $instrootDir);
201      push @updateLibDirs, $instrootDir;
202
203      system("cp $CONFIGDIR/root/$fileset.a $instrootDir/liblpp.a");
204      push @liblpp, "$instrootDir/liblpp.a";
205    }
206  }
207}
208
209mkdir("./tmp",0755) if ( ! -d "./tmp");
210$STRING="creating ./tmp/%s.%s.bff\n";
211$MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 11 "$STRING" "$package" "$packageVersion");
212printf "$MESG";
213
214# ./usr/lpp/<pkg> needs to immediately follow lpp_name
215open(TT,"|backup -irqpf ./tmp/$package.$packageVersion.bff -b1");
216print TT "./\n";
217print TT "./lpp_name\n";
218print TT "./usr\n";
219print TT "./usr/lpp\n";
220print TT "./usr/lpp/$package\n";
221
222if ($packageType eq "I"){
223  print TT "./usr/lpp/$package/liblpp.a\n";
224  if ( $hasRoot ){
225    print TT "./usr/lpp/$package/inst_root\n";
226    print TT "./usr/lpp/$package/inst_root/liblpp.a\n";
227  }
228} else {
229  # Backup all the libdirs (may be multiple for update packages)
230  foreach my $libDir (@updateLibDirs){
231    print TT "$libDir\n";
232  }
233
234  # Backup all liblpp.a files (may be multiple for update packages)
235  foreach my $archive (@liblpp){
236    print TT "$archive\n";
237  }
238}
239
240# Backup all files in each fileset's .al, and the LAFs if they exist
241foreach my $fileset (keys %version){
242  if ( $hasUsrFiles ){
243    my $applyListFile="$CONFIGDIR/$fileset.al";
244    open(AL,"<$applyListFile");
245    while (<AL>){
246      print TT $_;
247    }
248    close(AL);
249  }
250
251  if ( $hasRootFiles ){
252    my $rootAL="$CONFIGDIR/root/$fileset.al";
253    open(RAL,"<$rootAL");
254    while (<RAL>){
255      print TT $_;
256    }
257    close(RAL);
258  }
259
260  # Put LAFs in the backup
261  if ($laf{$fileset}) {
262    my @lafs=split (/;/, $laf{$fileset});
263    foreach my $laf (@lafs) {
264      $laf =~ s/^LAF//g;
265      $laf =~ s/^<.._..>//;
266      print TT "." . $laf . "\n";
267    }
268  }
269}
270
271close(TT);
272exit(0);
273
274
275# Gather fileset-specific info. Called for each fileset in this package.
276sub FilesetInfo {
277  # some fileset-specific vars
278  my $extraSizeFile="";
279  my @lafs=();
280  my @reqs=();
281  my $rootSizeFile="";
282  my @rootConfigFiles=();
283  my $usrSizeFile="";
284  my @usrConfigFiles=();
285  my $totalSize=0;
286
287  # stat output vars
288  my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
289         $atime, $mtime, $ctime, $blksize, $blocks);
290
291  $usrSizeFile   = "$CONFIGDIR/$fileset.size";
292  $rootSizeFile  = "$CONFIGDIR/root/$fileset.size" if ( $hasRootFiles );
293  $extraSizeFile = "$CONFIGDIR/$fileset.upsize" if ( -f "$CONFIGDIR/$fileset.upsize");
294  $extraSizeFile = "$CONFIGDIR/$fileset.insize" if ( -f "$CONFIGDIR/$fileset.insize");
295
296  # Print fileset info to lpp_name. Set correct header if we have a ROOT part.
297  if ( $hasRoot ){
298    printf(LPP "%s %s 01 %s B en_US %s\n",
299      $fileset,$version{$fileset},$bosboot{$fileset},$description{$fileset});
300  } else {
301    printf(LPP "%s %s 01 %s U en_US %s\n",
302      $fileset,$version{$fileset},$bosboot{$fileset},$description{$fileset});
303  }
304  printf(LPP "[\n");
305
306  # Print reqs on separate lines in lpp_name (if we have any)
307  if ($requisite{$fileset}){
308    # If the line starts with "*." read in the lines of the file
309    if ($requisite{$fileset} =~ m/^\*\./) {
310       my $req = $requisite{$fileset};
311       $req =~ s/^\*//;
312       open(RF,"<$req");
313       while (<RF>){
314         print LPP $_;
315       }
316       close(RF);
317    } else {
318       # Else process the requisite list
319       @reqs=split (/;/, $requisite{$fileset});
320       foreach my $req (@reqs){
321         printf(LPP "%s\n",$req);
322       }
323    }
324  }
325  printf(LPP "%%\n");
326
327  # Open our fileset.size file for writing (data is dup'd in lpp_name).
328  open(SF,">$usrSizeFile");
329  if ( $hasRootFiles ){
330    open(RSF,">$rootSizeFile");
331  }
332
333  # If we don't have an extra size file, just use the size data stored in %size.
334  # We have to put this data in the lpp_name AND fileset.size file.
335  if ($extraSizeFile eq ""){
336    foreach my $dir (sort keys %size){
337      printf(LPP "%s %d\n",$dir,$size{$dir});
338      printf(SF "%s %d\n",$dir,$size{$dir});
339      $totalSize+=$size{$dir};
340    }
341  }
342  # Otherwise, include our extra size file's data
343  else{
344    my @extraSizeFileEntryParts=();
345    my $extraSizeFileEntryPath="";
346    my $extraSizeFileEntryPathNotFound=1;
347    my $extraSizeFileEntrySize=0;
348
349    foreach my $dir (sort keys %size){
350      open (FUSIZE, $extraSizeFile);
351      while (<FUSIZE>){
352        $extraSizeFileEntryPathNotFound = 1;
353        @extraSizeFileEntryParts=split(/ /, $_);
354        $extraSizeFileEntryPath=$extraSizeFileEntryParts[0];
355        $extraSizeFileEntrySize=$extraSizeFileEntryParts[1];
356
357        if ($dir eq "$extraSizeFileEntryPath"){
358          printf(LPP "%s %d\n",$dir,$size{$dir}+$extraSizeFileEntrySize);
359          printf(SF "%s %d\n",$dir,$size{$dir}+$extraSizeFileEntrySize);
360          $totalSize+=$size{$dir};
361          $extraSizeFileEntryPathNotFound = 0;
362          last;
363        }
364      }
365      close (FUSIZE);
366      if ($extraSizeFileEntryPathNotFound){
367        printf(LPP "%s %d\n",$dir,$size{$dir});
368        printf(SF "%s %d\n",$dir,$size{$dir});
369        $totalSize+=$size{$dir};
370      }
371    }
372
373    open (FUSIZE, $extraSizeFile);
374    while (<FUSIZE>){
375      @extraSizeFileEntryParts=split(/ /, $_);
376      $extraSizeFileEntryPath=$extraSizeFileEntryParts[0];
377      $extraSizeFileEntryPathNotFound = 1;
378      $extraSizeFileEntrySize=$extraSizeFileEntryParts[1];
379
380      foreach my $dir (sort keys %size){
381        if ($dir eq "$extraSizeFileEntryPath"){
382          $extraSizeFileEntryPathNotFound = 0;
383        }
384      }
385      if ($extraSizeFileEntryPathNotFound){
386        printf(LPP "%s %d\n", "$extraSizeFileEntryPath", $extraSizeFileEntrySize);
387        printf(SF "%s %d\n", "$extraSizeFileEntryPath", $extraSizeFileEntrySize);
388        $totalSize+=$extraSizeFileEntrySize;
389      }
390    }
391    close (FUSIZE);
392  }
393
394  # Gather ROOT part size info (there's not insize/upsize for ROOT parts)
395  if ( $hasRootFiles ){
396    foreach my $dir (sort keys %rootSize){
397      printf(LPP "%s %d\n",$dir,$rootSize{$dir});
398      printf(RSF "%s %d\n",$dir,$rootSize{$dir});
399      $totalSize+=$rootSize{$dir};
400    }
401  }
402
403  # Gather USR part size info for each existing config file
404  foreach my $ext (@configFiles){
405    my $configFile="";
406
407    if ($ext eq "README"){
408      $configFile="$CONFIGDIR/lpp.README";
409    } elsif ($ext eq "productid") {
410      $configFile="$CONFIGDIR/productid";
411    } else {
412      $configFile="$CONFIGDIR/$fileset.$ext";
413    }
414    if ( -f $configFile ){
415      ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
416       $atime, $mtime, $ctime, $blksize, $blocks) = stat "$configFile";
417      $configFileBlocks+=$blocks;
418
419      # Dont push the copyright on - it has to be first in the liblpp.a
420      next if ($ext eq "copyright");
421      push @usrConfigFiles, $configFile;
422    }
423  }
424
425  # Gather ROOT part size info for root config files
426  if ( $hasRoot ){
427    # Gather size info for each existing config file
428    foreach my $ext (@configFiles){
429      my $configFile="";
430      $configFile="$CONFIGDIR/root/$fileset.$ext";
431
432      if ( -f $configFile ){
433        ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
434         $atime, $mtime, $ctime, $blksize, $blocks) = stat "$configFile";
435        $configFileBlocks+=$blocks;
436
437        push @rootConfigFiles, $configFile;
438      }
439    }
440  }
441
442  # Archive the config files
443  if ($packageType=~/^S/){
444    # SAVESPACE is what's needed if this update is removed
445    printf(LPP "/usr/lpp/SAVESPACE %d\n",$totalSize);
446    printf(SF "/usr/lpp/SAVESPACE %d\n",$totalSize);
447
448    $STRING="creating $CONFIGDIR/%s.a\n";
449    $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 6 "$STRING" "$fileset");
450    printf "$MESG";
451    unlink("$CONFIGDIR/$fileset.a");
452    system("ar -crlg $CONFIGDIR/$fileset.a @usrConfigFiles");
453    if ( $hasRoot ){
454      unlink("$CONFIGDIR/root/$fileset.a");
455      system("ar -crlg $CONFIGDIR/root/$fileset.a @rootConfigFiles");
456    }
457  } else {
458    # All of this fileset's config files will be archived with
459    # other filsets' config files (if they exist)
460    push @usrFilesForArchive, @usrConfigFiles;
461
462    if ( $hasRoot ){
463      push @rootFilesForArchive, @rootConfigFiles;
464    }
465  }
466
467  # Assuming 1 block per file for objrepos, and equal perm/temp space for INSTWORK
468  if ( $hasRoot ){
469    printf(LPP "/etc/objrepos $numFiles\n");
470
471    if ($hasRootFiles) {
472      printf(RSF "/etc/objrepos $numFiles\n");
473      printf(RSF "INSTWORK $configFileBlocks $configFileBlocks\n");
474    }
475  }
476  printf(LPP "/usr/lib/objrepos $numFiles\n");
477  printf(LPP "INSTWORK $configFileBlocks $configFileBlocks\n");
478  printf(SF "/usr/lib/objrepos $numFiles\n");
479  printf(SF "INSTWORK $configFileBlocks $configFileBlocks\n");
480
481  # Put LAFs/LAR in lpp_name
482  if ($laf{$fileset}) {
483    @lafs=split (/;/, $laf{$fileset});
484    foreach my $laf (@lafs) {
485      my $lafString = $laf;
486      $laf =~ s/^LAF//g;
487      $laf =~ s/^<.._..>//;
488      if ( -f ".$laf") {
489        ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
490         $atime, $mtime, $ctime, $blksize, $blocks) = stat ".$laf";
491
492        printf(LPP "%s %d\n", $lafString, $blocks);
493      }
494    }
495  }
496  if ($lar{$fileset}) {
497    printf(LPP "%s 0\n", $lar{$fileset});
498  }
499
500  printf(LPP "%%\n");
501
502  # Put supersede info in lpp_name
503  if ( -f "$CONFIGDIR/$fileset.supersede") {
504    open (SUPER, "<$CONFIGDIR/$fileset.supersede");
505    while (<SUPER>) {
506      printf(LPP $_);
507    }
508    close (SUPER);
509  }
510  printf(LPP "%%\n");
511  printf(LPP "%%\n");
512  printf(LPP "]\n");
513  close (SF);
514  if ( $hasRootFiles ){
515    close (RSF);
516  }
517}
518
519
520# Gather file-specific data and create .inventory for each fileset in this package.
521sub MakeInv {
522  # The .al and .inventory filenames are passed in
523  my ($al, $inv)=@_;
524
525  my $dir="";          # Dir name for keeping track of dir size reqs
526  my %dirSize=();      # Map associating directories and their size
527  my $fileName="";     # Filename for .inventory
528  my %gname=();        # Map group names/gids
529  my @ilist=();        # Stores files to be listed in the .inventory
530  my $key="";          # Key for referencing files
531  my $passwd="";       # Needed for getgrent
532  my $sum="";          # Output of the sum command
533  my $sysname="";      # Needed for getgrent
534  my $type="";         # File/Link/Dir
535  my %uname=();        # Map user names/uids
536
537  # stat output vars
538  my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
539         $atime, $mtime, $ctime, $blksize, $blocks);
540
541  # .inventory related
542  my (%name, %owner, %group, %mode, %type, %class, %size, %checksum);
543
544  # link related
545  my (%hardlinkList, %nlink, %target);
546
547  # Maps associating files and their attributes
548  %name=();
549  %owner=();
550  %group=();
551  %mode=();
552  %type=();
553  %class=();
554  %size=();
555  %checksum=();
556  %hardlinkList=();    # List of hard links to a file
557  %nlink=();           # Number of hard links to a file
558  %target=();          # Symlink target
559
560  # Create maps of gids to groups and uids to users
561  while (($sysname, $passwd, $gid) = getgrent){
562    next if ($sysname =~ /^\+/); # skip NIS password lines (begin with plus sign)
563    $gname{$gid}=$sysname;
564  }
565  endgrent();
566  while (($sysname, $passwd, $uid) = getpwent){
567    next if ($sysname =~ /^\+/); # skip NIS password lines (begin with plus sign)
568    $uname{$uid}=$sysname;
569  }
570  endpwent();
571
572  # Ensure a uid/gid of 0 is root/system
573  $gname{0}="system";
574  $uname{0}="root";
575
576  open(AL,"<$al");
577  open(INV,">$inv");
578
579  unlink("$al.withoutlinks");
580  open(ALWOL,">$al.withoutlinks"); # Need to strip hard links out of the applylist
581  while (<AL>){
582    chomp;
583    $dir=$_;
584    if (-l){
585      $type="SYMLINK";
586      $dir=~s?/[^/]*$??;
587    } elsif (-d){
588      $type="DIRECTORY";
589    } elsif (-f){
590      $type="FILE";
591      $dir=~s?/[^/]*$??;
592    } else {
593      $STRING="no such file: %s\n";
594      $MESG=qx(/usr/bin/dspmsg -s 21 cmdinstl_e.cat 8 "$STRING" "$_");
595      printf(STDERR "$MESG");
596      next;
597    }
598
599    # Increment our file count
600    $numFiles++;
601
602    ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
603     $atime, $mtime, $ctime, $blksize, $blocks) = lstat "$_";
604    if ($type eq "SYMLINK"){
605      $key=$symlinkKey++;
606    } else {
607      $key="$dev.$ino";
608    }
609
610    # If we have this key already, then make a hardlink list
611    if ($name{$key} && ($nlink > 1) && ($type eq "FILE")){
612      if ($hardlinkList{$key}){
613        $hardlinkList{$key} .= "," . substr $_, 1;
614      } else {
615        $hardlinkList{$key} = substr $_, 1;
616      }
617    } else {
618      printf(ALWOL "%s\n", $_);
619      push(@ilist,$key);
620      $name{$key}=$_;
621      $owner{$key}=$uname{$uid};
622      $group{$key}=$gname{$gid};
623      $mode{$key}=$mode;
624      $type{$key}=$type;
625      $class{$key}="apply,inventory,$fileset";
626      $size{$key}=$size;
627      if ($type eq "FILE"){
628        $sum=`/usr/bin/sum $_`;
629        $sum=~s/ +/ /g;
630        ($a, $b)=(split(/ /,$sum))[0,1];
631        $checksum{$key}=sprintf("\"%s    %s \"",$a,$b);
632      } elsif ($type eq "SYMLINK"){
633        $target{$key}=readlink($_);
634      }
635      $hardlinkList{$key}="";
636      $dirSize{substr $dir, 1}+=$blocks;
637    }
638  }
639
640  # Print out the inventory file; spacing and order is done
641  # on purpose to look like BOS install images.
642  foreach my $key (@ilist){
643    $fileName=$name{$key};
644    $fileName=~s/^.//;
645    printf(INV "%s:\n",$fileName);
646    if ($type{$key} eq "FILE" || $type{$key} eq "DIRECTORY"){
647      printf(INV "          owner = %s\n",$owner{$key});
648      printf(INV "          group = %s\n",$group{$key});
649      printf(INV "          mode = %o\n",$mode{$key} & 07777);
650    }
651    printf(INV "          type = %s\n",$type{$key});
652    if ($hardlinkList{$key}){
653      printf(INV "          links = %s\n",$hardlinkList{$key});
654    }
655    printf(INV "          class = %s\n",$class{$key});
656    if ($type{$key} eq "FILE"){
657      printf(INV "          size = %d\n",$size{$key});
658      printf(INV "          checksum = %s\n",$checksum{$key});
659    } elsif ($type{$key} eq "SYMLINK"){
660      printf(INV "          target = %s\n",$target{$key});
661    }
662    printf(INV "\n");
663  }
664  close(AL);
665  close(ALWOL);
666  close(INV);
667
668  # Get rid of our original apply list in favor of the non-hardlink'd one
669  unlink($al);
670  rename("$al.withoutlinks", $al);
671
672  return %dirSize;
673}
674