#include "asd.h" #include "ftw.h" FILE *tf; static Sig_typ sigsav; static int rc; static char tfname[TMPNAML]; /* * Prepare to build a package. The package will appear on stdout. * The argument is nonzero if remarks are to be read. */ void pkgstart() { struct stat pks; rc = 0; nchk (fstat (fileno (stdout), &outsb)); /* establish a temporary file to hold the instruction information */ tmpname (tfname); tf = fopen (tfname, "w"); sigsav = signal (SIGINT, SIG_IGN); if (sigsav != SIG_IGN) signal (SIGINT, delete); nchk (chmod (tfname, 0644)); schk ((char *) tf); /* create the initial element in the component chain */ pkhead = pktail = new (struct pack); pkhead->iname = copy (instr); pkhead->ename = copy (tfname); pkhead->time = 0; pkhead->link = NULL; pkhead->uid = getuid(); nchk (fstat (fileno(tf), &pks)); pkhead->gid = pks.st_gid; pkhead->mode = 0100644; } /* * put a file (or directory) into a package */ void pkgfile (file) register char *file; { register int x; struct stat sb; /* if the file is truly not present, generate a remove request */ #ifdef S_IFLNK if (lstat (file, &sb) < 0 && errno == ENOENT) { #else if (stat (file, &sb) < 0 && errno == ENOENT) { #endif fprintf (tf, "r "); putpath (tf, transname (file)); putc ('\n', tf); } else { x = ftw (file, consider, 8); if (x != 0) { rc++; if (x == -1) perror (file); } } } /* * we are done building a package. This writes the actual file * contents, so make sure the files are still around */ int pkgend() { register struct pack *pack; struct stat s; /* write 'x' or 'X' execution item if requested */ if (xstr) { fprintf (tf, "x "); putpath (tf, xstr); fprintf (tf, "\n"); } if (Xstr) { fprintf (tf, "X "); putpath (tf, Xstr); fprintf (tf, "\n"); } /* we now know how long the first component is */ pkhead->size = ftell (tf); /* we no longer need to write the temporary file */ fclose (tf); /* describe the temp file correctly so it will pass later checks */ if (pkhead->time == 0) (void) time (&pkhead->time); nchk (stat (tfname, &s)); pkhead->dev = s.st_dev; pkhead->ino = s.st_ino; /* * write the files out into an archive */ /* first the archive header */ printf (ARMAG); /* * run through the list, creating the archive components * and deleting the list entries. One iteration per component. * We know there is at least one component, because the * "Instructions" component must be represented. */ do { struct ar_hdr ah; char buf[30]; register int c; /* "pack" is the package component under consideration */ pack = pkhead; if (pack->iname) { /* non-regular file entry, no data */ /* log it if requested */ if (vflag) fprintf (stderr, "package %s\n", strcmp (pack->iname, instr)? pack->ename: instr); /* write the archive element header */ # define ent(a,x) sprintf(buf, "%-*s", sizeof(ah.a), x); \ strncpy (ah.a, buf, sizeof (ah.a)) ent (ar_name, pack->iname); ent (ar_fmag, ARFMAG); # undef ent # define ent(a,x) sprintf(buf, "%*ld", sizeof(ah.a), (long) x); \ strncpy (ah.a, buf, sizeof (ah.a)) ent (ar_date, pack->time); ent (ar_uid, pack->uid); ent (ar_gid, pack->gid); ent (ar_size, pack->size); # undef ent # define ent(a,x) sprintf(buf, "%*o", sizeof(ah.a), x); \ strncpy (ah.a, buf, sizeof (ah.a)) ent (ar_mode, pack->mode); # undef ent fwrite ((char *) &ah, sizeof (ah), 1, stdout); /* write the archive element itself */ tf = fopen (pack->ename, "r"); if (tf == NULL) { perror (pack->iname); exit (1); } while ((c = getc (tf)) != EOF) putchar (c); /* if things now don't match, complain */ if (fstat (fileno (tf), &s) < 0 || s.st_size != pack->size || (s.st_mtime != pack->time && strcmp (pack->iname, instr)) || s.st_dev != pack->dev || s.st_ino != pack->ino || s.st_uid != pack->uid || s.st_gid != pack->gid || (s.st_mode & 07777) != (pack->mode & 07777)) { fprintf (stderr, "phase error on %s\n", pack->ename); rc++; } fclose (tf); if (pack->size & 1) putchar ('\n'); } /* delete the element, move on to the next */ if (pack->iname) free (pack->iname); free (pack->ename); pkhead = pack->link; free ((char *) pack); } while (pkhead); /* zap the tail pointer for general cleanliness */ pktail = NULL; nchk (unlink (tfname)); signal (SIGINT, sigsav); return rc; } /* internal function for package creation */ static int consider (name, buf, type) register char *name; register struct stat *buf; register int type; { register struct pack *pack; register int mode; char *biname; #ifdef S_IFLNK char *slname; #endif switch (type) { case FTW_D: fprintf (tf, "d %-*.4o ", MAXCOMP, buf->st_mode & 07777); hdrsub (name, buf); fprintf (tf, "\n"); break; case FTW_F: case FTW_SL: mode = buf->st_mode & S_IFMT; /* Has this file already appeared? If so, it's a link */ for (pack = pkhead->link; pack; pack = pack->link) { if (buf->st_dev == pack->dev && buf->st_ino == pack->ino) { fprintf (tf, "l %s ", transname (pack->ename)); fprintf (tf, "%s\n", transname (name)); return 0; } } switch (mode) { #ifdef S_IFLNK case S_IFLNK: slname = alloc((unsigned)buf->st_size+1); slname[buf->st_size] = '\0'; if (readlink(name, slname, buf->st_size) != buf->st_size) { perror(name); return (0); } fprintf (tf, "s %s ", transname (slname)); fprintf (tf, "%s\n", transname (name)); biname = NULL; break; #endif case S_IFBLK: case S_IFCHR: /* is it a special file? */ fprintf (tf, "%c %#o %d %d ", mode == S_IFBLK? 'b': 'c', buf->st_mode & 07777, major (buf->st_rdev), minor (buf->st_rdev)); hdrsub (name, buf); fprintf (tf, "\n"); biname = NULL; break; case S_IFREG: /* refuse to package the standard output */ if (buf->st_dev == outsb.st_dev && buf->st_ino == outsb.st_ino) { fprintf (stderr, "skipping output file %s\n", fullname (name)); return 0; } biname = iname (name); fprintf (tf, "f "); putpath (tf, biname); fprintf (tf, " "); hdrsub (name, buf); fprintf (tf, "\n"); break; default: fprintf (stderr, "%s: unrecognized file type\n", fullname (name)); return 0; } /* package the file */ pack = new (struct pack); pack->ename = copy (name); pack->dev = buf->st_dev; pack->ino = buf->st_ino; pack->uid = buf->st_uid; pack->gid = buf->st_gid; pack->time = buf->st_mtime; if (pack->time > pkhead->time) pkhead->time = pack->time; pack->size = buf->st_size; pack->mode = buf->st_mode; pack->link = NULL; pack->iname = biname; pktail->link = pack; pktail = pack; break; case FTW_DNR: fprintf (stderr, "cannot read directory %s\n", name); return 1; case FTW_NS: fprintf (stderr, "cannot stat %s\n", name); return 1; case FTW_DP: break; default: fprintf (stderr, "impossible code %d from ftw\n", type); exit (1); } return 0; } static hdrsub (name, buf) register char *name; register struct stat *buf; { putpath (tf, struid (buf->st_uid)); putc ('\t', tf); putpath (tf, strgid (buf->st_gid)); putc ('\t', tf); putpath (tf, transname (name)); } /* * generate a unique internal name for a file */ static char * iname (s) char *s; { register char *p; register char *lastcomp; register struct pack *pack; char trial[MAXCOMP+1]; /* point lastcomp at the last pathname component */ lastcomp = s; for (p = s; *p; p++) { if (*p == '/') lastcomp = p + 1; } /* if the name is acceptably short, modify it slightly */ if (strlen (lastcomp) <= MAXCOMP) { char prefix[MAXCOMP+1], suffix[MAXCOMP+1], num[30]; register int n; /* split the name, remove unprintables */ strcpy (prefix, lastcomp); for (p = prefix; *p; p++) if (*p == ' ' || !isprint (*p)) *p = '$'; suffix[0] = '\0'; p = strrchr (prefix, '.'); if (p != NULL) { strcpy (suffix, p); *p = '\0'; } /* generate trial names until we run out of space */ for (n = 0, num[0] = '\0'; strlen(prefix) + strlen(suffix) + strlen(num) <= MAXCOMP; n++, sprintf (num, "%.0d", n)) { /* generate the trial name */ strcpy (trial, prefix); strcat (trial, num); strcat (trial, suffix); /* if the name is unique, we're done */ pack = pkhead; while (pack != NULL && strcmp (pack->iname, trial) != 0) pack = pack->link; if (pack == NULL) return copy(trial); } } /* punt -- generate a completely new name */ do { static int tempno; tempno++; sprintf (trial, "Temp%d", tempno); pack = pkhead; while (pack != NULL && strcmp (trial, pack->iname) != 0) pack = pack->link; } while (pack != NULL); return copy(trial); } static void delete() { unlink (tfname); exit (3); }