#include #include #include #include #include #include #include #include void usage (char * p) { fprintf (stderr, "usage: %s [ -c | -e | -l ] -f archive file ...\n", p); exit (1); } #define NONE 0 #define LIST 'l' #define CREATE 'c' #define EXTRACT 'e' #define REG 'R' #define LINK 'L' struct mar_header { char type; unsigned int namelen; unsigned int filesize; char * name; }; struct mar_header * read_head (int fd) { struct mar_header * h = malloc (sizeof (struct mar_header)); assert (h); int ret = read (fd, &h->type, 1); if (ret == 0) return NULL; if (ret < 0) { perror ("read"); exit (1); } switch (h->type) { case REG: case LINK: /* OK */ break; default: fprintf (stderr, "Bad file type %x\n", h->type); exit (1); } if ((read (fd, &h->namelen, sizeof (int)) != sizeof (int)) || (read (fd, &h->filesize, sizeof (int)) != sizeof (int))) { perror ("read"); exit (1); } h->name = malloc ((h->namelen+1) * sizeof (char)); assert (h->name); if (read (fd, h->name, h->namelen) != h->namelen) { perror ("read"); exit (1); } h->name[h->namelen] = 0; return h; } char * read_linkname (int fd, int filesize) { char * buf = malloc ((filesize+1) * sizeof (char)); int ret; assert (buf); ret = read (fd, buf, filesize); if (ret != filesize) { perror ("read"); exit (1); } buf[filesize] = 0; return buf; } void do_copy (int src, int dst, int len) { char buf[512]; while (len) { int thislen = len; if (thislen > 512) thislen = 512; if (read (src, buf, thislen) != thislen) { perror ("read"); exit (1); } if (write (dst, buf, thislen) != thislen) { perror ("write"); exit (1); } len -= thislen; } } void do_list_extract (int fd, int type) { struct mar_header * h; char * link; while ((h = read_head (fd))) { switch (h->type) { case REG: printf ("REG %s\n", h->name); break; case LINK: link = read_linkname (fd, h->filesize); printf ("LINK %s => %s\n", h->name, link); break; default: assert (0); } if (type == EXTRACT) { if (h->type == REG) { int outfd = open (h->name, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (outfd < 0) { perror ("open"); exit (1); } do_copy (fd, outfd, h->filesize); close (outfd); } else { if (symlink (link, h->name) < 0) { perror ("symlink"); exit (1); } } } else if (type == LIST) { if (h->type == REG) { int ret; ret = lseek (fd, h->filesize, SEEK_CUR); if (ret < 0) { perror ("lseek"); exit (1); } } } if (h->type == LINK) { free (link); } free (h->name); free (h); } close (fd); } void do_create (int fd, char ** argv) { ino_t archive; int i; struct stat statbuf; if (fstat (fd, &statbuf) < 0) { perror ("fstat"); exit (1); } archive = statbuf.st_ino; for (i=0; argv[i]; ++i) { char * headbuf; int infd; int headbuflen; int ftype; int tmp; char linkbuf[512]; int linklen; if (lstat (argv[i], &statbuf) < 0) { perror ("lstat"); exit (1); } if (statbuf.st_ino == archive) { printf ("%s is the archive. Skipping\n", argv[i]); continue; } headbuflen = 1 + 2 * sizeof (int) + strlen (argv[i]); headbuf = malloc (headbuflen); assert (headbuf); ftype = 0; switch ((statbuf.st_mode & S_IFMT)) { case S_IFREG: infd = open (argv[i], O_RDONLY); if (infd < 0) { perror ("open"); exit (1); } ftype = REG; break; case S_IFLNK: ftype = LINK; linklen = readlink (argv[i], linkbuf, sizeof (linkbuf)); if ((linklen < 0) || (linklen >= 512)) { ftype = 0; fprintf (stderr, "Can't read link for %s, skipping\n", argv[i]); } break; default: fprintf (stderr, "Bad file type for %s, skipping\n", argv[i]); break; } if (ftype == 0) { free (headbuf); continue; } headbuf[0] = ftype; tmp = strlen (argv[i]); memcpy (headbuf+1, &tmp, sizeof (int)); if (ftype == REG) { tmp = statbuf.st_size; } else { assert (ftype == LINK); tmp = linklen; } memcpy (headbuf+1+sizeof (int), &tmp, sizeof (int)); memcpy (headbuf+1+2*sizeof (int), argv[i], strlen (argv[i])); if (write (fd, headbuf, headbuflen) != headbuflen) { perror ("write"); exit (1); } if (ftype == REG) { do_copy (infd, fd, statbuf.st_size); } else { assert (ftype == LINK); if (write (fd, linkbuf, linklen) != linklen) { perror ("write"); exit (1); } } free (headbuf); if (ftype == REG) { close (infd); } } close (fd); } int main (int argc, char ** argv) { char ch; int type = NONE; char * filename = NULL; int fd; while ((ch = getopt (argc, argv, "celf:")) > 0) { switch (ch) { case 'f': if (filename) usage (argv[0]); filename = optarg; break; case 'c': case 'e': case 'l': if (type != NONE) usage (argv[0]); type = ch; break; default: usage(argv[0]); } } if (type == NONE || filename == NULL) usage (argv[0]); switch (type) { case EXTRACT: case LIST: fd = open (filename, O_RDONLY); break; case CREATE: fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); break; } if (fd < 0) { perror ("open"); exit (1); } switch (type) { case LIST: case EXTRACT: do_list_extract (fd, type); break; case CREATE: do_create (fd, argv + optind); break; } return 0; }