Browse Source

libdpkg: Add new struct dpkg_ar and basic operations

Switch current code to use dpkg_ar instead of taking a filename and a
file descriptor arguments.
Guillem Jover 12 years ago
parent
commit
a15e095201
11 changed files with 237 additions and 144 deletions
  1. 1 0
      debian/changelog
  2. 15 16
      dpkg-deb/build.c
  3. 20 28
      dpkg-deb/extract.c
  4. 3 2
      dpkg-split/dpkg-split.h
  5. 65 52
      dpkg-split/info.c
  6. 7 3
      dpkg-split/main.c
  7. 4 4
      dpkg-split/queue.c
  8. 6 8
      dpkg-split/split.c
  9. 88 26
      lib/dpkg/ar.c
  10. 23 5
      lib/dpkg/ar.h
  11. 5 0
      lib/dpkg/libdpkg.map

+ 1 - 0
debian/changelog

@@ -17,6 +17,7 @@ dpkg (1.18.8) UNRELEASED; urgency=medium
     start-stop-daemon.
   * Set return buffer length for sysctl(2) calls on */kFreeBSD in
     start-stop-daemon.
+  * Abstract ar archive handling behind a new struct dpkg_ar and functions.
   * Perl modules:
     - Use warnings::warnif() instead of carp() for deprecated warnings.
     - Add new format_range() method and deprecate dpkg() and rfc822() methods

+ 15 - 16
dpkg-deb/build.c

@@ -474,11 +474,11 @@ do_build(const char *const *argv)
 {
   struct compress_params control_compress_params;
   struct dpkg_error err;
+  struct dpkg_ar *ar;
   const char *dir, *dest;
   char *ctrldir;
   char *debar;
   char *tfbuf;
-  int arfd;
   int gzfd;
 
   /* Decode our arguments. */
@@ -511,9 +511,8 @@ do_build(const char *const *argv)
 
   /* Now that we have verified everything its time to actually
    * build something. Let's start by making the ar-wrapper. */
-  arfd = creat(debar, 0644);
-  if (arfd < 0)
-    ohshite(_("unable to create '%.255s'"), debar);
+  ar = dpkg_ar_create(debar, 0644);
+
   unsetenv("TAR_OPTIONS");
 
   /* Create a temporary file to store the control data in. Immediately
@@ -557,11 +556,11 @@ do_build(const char *const *argv)
       ohshite(_("failed to stat temporary file (%s)"), _("control member"));
     sprintf(versionbuf, "%-8s\n%jd\n", OLDARCHIVEVERSION,
             (intmax_t)controlstab.st_size);
-    if (fd_write(arfd, versionbuf, strlen(versionbuf)) < 0)
+    if (fd_write(ar->fd, versionbuf, strlen(versionbuf)) < 0)
       ohshite(_("error writing '%s'"), debar);
-    if (fd_fd_copy(gzfd, arfd, -1, &err) < 0)
+    if (fd_fd_copy(gzfd, ar->fd, -1, &err) < 0)
       ohshit(_("cannot copy '%s' into archive '%s': %s"), _("control member"),
-             debar, err.str);
+             ar->name, err.str);
   } else if (deb_format.major == 2) {
     const char deb_magic[] = ARCHIVEVERSION "\n";
     char adminmember[16 + 1];
@@ -569,9 +568,9 @@ do_build(const char *const *argv)
     sprintf(adminmember, "%s%s", ADMINMEMBER,
             compressor_get_extension(control_compress_params.type));
 
-    dpkg_ar_put_magic(debar, arfd);
-    dpkg_ar_member_put_mem(debar, arfd, DEBMAGIC, deb_magic, strlen(deb_magic));
-    dpkg_ar_member_put_file(debar, arfd, adminmember, gzfd, -1);
+    dpkg_ar_put_magic(ar);
+    dpkg_ar_member_put_mem(ar, DEBMAGIC, deb_magic, strlen(deb_magic));
+    dpkg_ar_member_put_file(ar, adminmember, gzfd, -1);
   } else {
     internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor);
   }
@@ -583,7 +582,7 @@ do_build(const char *const *argv)
     /* In old format, the data member is just concatenated after the
      * control member, so we do not need a temporary file and can use
      * the compression file descriptor. */
-    gzfd = arfd;
+    gzfd = ar->fd;
   } else if (deb_format.major == 2) {
     /* Start by creating a new temporary file. Immediately unlink the
      * temporary file so others can't mess with it. */
@@ -613,14 +612,14 @@ do_build(const char *const *argv)
     if (lseek(gzfd, 0, SEEK_SET))
       ohshite(_("failed to rewind temporary file (%s)"), _("data member"));
 
-    dpkg_ar_member_put_file(debar, arfd, datamember, gzfd, -1);
+    dpkg_ar_member_put_file(ar, datamember, gzfd, -1);
 
     close(gzfd);
   }
-  if (fsync(arfd))
-    ohshite(_("unable to sync file '%s'"), debar);
-  if (close(arfd))
-    ohshite(_("unable to close file '%s'"), debar);
+  if (fsync(ar->fd))
+    ohshite(_("unable to sync file '%s'"), ar->name);
+
+  dpkg_ar_close(ar);
 
   free(debar);
 

+ 20 - 28
dpkg-deb/extract.c

@@ -107,6 +107,7 @@ extracthalf(const char *debar, const char *dir,
 {
   struct dpkg_error err;
   const char *errstr;
+  struct dpkg_ar *ar;
   char versionbuf[40];
   struct deb_version version;
   off_t ctrllennum, memberlen = 0;
@@ -115,23 +116,14 @@ extracthalf(const char *debar, const char *dir,
   pid_t c1=0,c2,c3;
   int p1[2], p2[2];
   int p2_out;
-  int arfd;
-  struct stat stab;
   char nlc;
   int adminmember = -1;
   bool header_done;
   enum compressor_type decompressor = COMPRESSOR_TYPE_GZIP;
 
-  if (strcmp(debar, "-") == 0)
-    arfd = STDIN_FILENO;
-  else
-    arfd = open(debar, O_RDONLY);
-  if (arfd < 0)
-    ohshite(_("failed to read archive '%.255s'"), debar);
-  if (fstat(arfd, &stab))
-    ohshite(_("failed to fstat archive"));
+  ar = dpkg_ar_open(debar);
 
-  r = read_line(arfd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1);
+  r = read_line(ar->fd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1);
   if (r < 0)
     read_fail(r, debar, _("archive magic version number"));
 
@@ -141,7 +133,7 @@ extracthalf(const char *debar, const char *dir,
     for (;;) {
       struct ar_hdr arh;
 
-      r = fd_read(arfd, &arh, sizeof(arh));
+      r = fd_read(ar->fd, &arh, sizeof(arh));
       if (r != sizeof(arh))
         read_fail(r, debar, _("archive member header"));
 
@@ -149,7 +141,7 @@ extracthalf(const char *debar, const char *dir,
 
       if (dpkg_ar_member_is_illegal(&arh))
         ohshit(_("file '%.250s' is corrupt - bad archive header magic"), debar);
-      memberlen = dpkg_ar_member_get_size(debar, &arh);
+      memberlen = dpkg_ar_member_get_size(ar, &arh);
       if (!header_done) {
         char *infobuf;
 
@@ -157,7 +149,7 @@ extracthalf(const char *debar, const char *dir,
           ohshit(_("file '%.250s' is not a debian binary archive (try dpkg-split?)"),
                  debar);
         infobuf= m_malloc(memberlen+1);
-        r = fd_read(arfd, infobuf, memberlen + (memberlen & 1));
+        r = fd_read(ar->fd, infobuf, memberlen + (memberlen & 1));
         if (r != (memberlen + (memberlen & 1)))
           read_fail(r, debar, _("archive information header member"));
         infobuf[memberlen] = '\0';
@@ -177,8 +169,8 @@ extracthalf(const char *debar, const char *dir,
       } else if (arh.ar_name[0] == '_') {
         /* Members with ‘_’ are noncritical, and if we don't understand
          * them we skip them. */
-        if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0)
-          ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str);
+        if (fd_skip(ar->fd, memberlen + (memberlen & 1), &err) < 0)
+          ohshit(_("cannot skip archive member from '%s': %s"), ar->name, err.str);
       } else {
         if (strncmp(arh.ar_name, ADMINMEMBER, strlen(ADMINMEMBER)) == 0) {
           const char *extension = arh.ar_name + strlen(ADMINMEMBER);
@@ -219,8 +211,8 @@ extracthalf(const char *debar, const char *dir,
           ctrllennum= memberlen;
         }
         if (!adminmember != !admininfo) {
-          if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0)
-            ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str);
+          if (fd_skip(ar->fd, memberlen + (memberlen & 1), &err) < 0)
+            ohshit(_("cannot skip archive member from '%s': %s"), ar->name, err.str);
         } else {
           /* Yes! - found it. */
           break;
@@ -232,7 +224,7 @@ extracthalf(const char *debar, const char *dir,
       printf(_(" new debian package, version %d.%d.\n"
                " size %jd bytes: control archive=%jd bytes.\n"),
              version.major, version.minor,
-             (intmax_t)stab.st_size, (intmax_t)ctrllennum);
+             (intmax_t)ar->size, (intmax_t)ctrllennum);
       m_output(stdout, _("<standard output>"));
     }
   } else if (strncmp(versionbuf, "0.93", 4) == 0) {
@@ -247,7 +239,7 @@ extracthalf(const char *debar, const char *dir,
     if (errstr)
       ohshit(_("archive has invalid format version: %s"), errstr);
 
-    r = read_line(arfd, ctrllenbuf, 1, sizeof(ctrllenbuf) - 1);
+    r = read_line(ar->fd, ctrllenbuf, 1, sizeof(ctrllenbuf) - 1);
     if (r < 0)
       read_fail(r, debar, _("archive control member size"));
     if (sscanf(ctrllenbuf, "%jd%c%d", &ctrllennum, &nlc, &dummy) != 2 ||
@@ -257,9 +249,9 @@ extracthalf(const char *debar, const char *dir,
     if (admininfo) {
       memberlen = ctrllennum;
     } else {
-      memberlen = stab.st_size - ctrllennum - strlen(ctrllenbuf) - l;
-      if (fd_skip(arfd, ctrllennum, &err) < 0)
-        ohshit(_("cannot skip archive control member from '%s': %s"), debar,
+      memberlen = ar->size - ctrllennum - strlen(ctrllenbuf) - l;
+      if (fd_skip(ar->fd, ctrllennum, &err) < 0)
+        ohshit(_("cannot skip archive control member from '%s': %s"), ar->name,
                err.str);
     }
 
@@ -267,8 +259,8 @@ extracthalf(const char *debar, const char *dir,
       printf(_(" old debian package, version %d.%d.\n"
                " size %jd bytes: control archive=%jd, main archive=%jd.\n"),
              version.major, version.minor,
-             (intmax_t)stab.st_size, (intmax_t)ctrllennum,
-             (intmax_t)(stab.st_size - ctrllennum - strlen(ctrllenbuf) - l));
+             (intmax_t)ar->size, (intmax_t)ctrllennum,
+             (intmax_t)(ar->size - ctrllennum - strlen(ctrllenbuf) - l));
       m_output(stdout, _("<standard output>"));
     }
   } else {
@@ -284,9 +276,9 @@ extracthalf(const char *debar, const char *dir,
   c1 = subproc_fork();
   if (!c1) {
     close(p1[0]);
-    if (fd_fd_copy(arfd, p1[1], memberlen, &err) < 0)
+    if (fd_fd_copy(ar->fd, p1[1], memberlen, &err) < 0)
       ohshit(_("cannot copy archive member from '%s' to decompressor pipe: %s"),
-             debar, err.str);
+             ar->name, err.str);
     if (close(p1[1]))
       ohshite(_("cannot close decompressor pipe"));
     exit(0);
@@ -309,7 +301,7 @@ extracthalf(const char *debar, const char *dir,
     exit(0);
   }
   close(p1[0]);
-  close(arfd);
+  dpkg_ar_close(ar);
   if (taroption) close(p2[1]);
 
   if (taroption) {

+ 3 - 2
dpkg-split/dpkg-split.h

@@ -22,6 +22,7 @@
 #ifndef DPKG_SPLIT_H
 #define DPKG_SPLIT_H
 
+#include <dpkg/ar.h>
 #include <dpkg/deb-version.h>
 
 action_func do_split;
@@ -63,9 +64,9 @@ extern const char *opt_outputfile;
 extern int opt_npquiet;
 extern int opt_msdos;
 
-void rerreof(FILE *f, const char *fn) DPKG_ATTR_NORET;
+void read_fail(int rc, const char *filename, const char *what) DPKG_ATTR_NORET;
 void print_info(const struct partinfo *pi);
-struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir);
+struct partinfo *read_info(struct dpkg_ar *ar, struct partinfo *ir);
 
 void reassemble(struct partinfo **partlist, const char *outputfile);
 void mustgetpartinfo(const char *filename, struct partinfo *ri);

+ 65 - 52
dpkg-split/info.c

@@ -38,6 +38,7 @@
 #include <dpkg/c-ctype.h>
 #include <dpkg/dpkg.h>
 #include <dpkg/dpkg-db.h>
+#include <dpkg/fdio.h>
 #include <dpkg/ar.h>
 #include <dpkg/options.h>
 
@@ -82,7 +83,9 @@ static char *nextline(char **ripp, const char *fn, const char *what) {
  * @return Part info (nfmalloc'd) if was an archive part and we read it,
  *         NULL if it wasn't.
  */
-struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir) {
+struct partinfo *
+read_info(struct dpkg_ar *ar, struct partinfo *ir)
+{
   static char *readinfobuf= NULL;
   static size_t readinfobuflen= 0;
 
@@ -91,118 +94,128 @@ struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir)
   char magicbuf[sizeof(DPKG_AR_MAGIC) - 1], *rip, *partnums, *slash;
   const char *err;
   struct ar_hdr arh;
-  int c;
-  struct stat stab;
+  ssize_t rc;
 
-  if (fread(magicbuf, 1, sizeof(magicbuf), partfile) != sizeof(magicbuf)) {
-    if (ferror(partfile))
-      ohshite(_("error reading %.250s"), fn);
+  rc = fd_read(ar->fd, magicbuf, sizeof(magicbuf));
+  if (rc != sizeof(magicbuf)) {
+    if (rc < 0)
+      ohshite(_("error reading %.250s"), ar->name);
     else
       return NULL;
   }
   if (memcmp(magicbuf, DPKG_AR_MAGIC, sizeof(magicbuf)))
     return NULL;
 
-  if (fread(&arh,1,sizeof(arh),partfile) != sizeof(arh)) rerreof(partfile,fn);
+  rc = fd_read(ar->fd, &arh, sizeof(arh));
+  if (rc != sizeof(arh))
+    read_fail(rc, ar->name, "ar header");
 
   dpkg_ar_normalize_name(&arh);
 
   if (strncmp(arh.ar_name, PARTMAGIC, sizeof(arh.ar_name)) != 0)
     return NULL;
   if (dpkg_ar_member_is_illegal(&arh))
-    ohshit(_("file '%.250s' is corrupt - bad magic at end of first header"), fn);
-  thisilen = dpkg_ar_member_get_size(fn, &arh);
+    ohshit(_("file '%.250s' is corrupt - bad magic at end of first header"),
+           ar->name);
+  thisilen = dpkg_ar_member_get_size(ar, &arh);
   if (thisilen >= readinfobuflen) {
-    readinfobuflen= thisilen+1;
+    readinfobuflen = thisilen + 2;
     readinfobuf= m_realloc(readinfobuf,readinfobuflen);
   }
-  if (fread(readinfobuf,1,thisilen,partfile) != thisilen) rerreof(partfile,fn);
+  rc = fd_read(ar->fd, readinfobuf, thisilen + (thisilen & 1));
+  if (rc != (ssize_t)(thisilen + (thisilen & 1)))
+    read_fail(rc, ar->name, "reading header member");
   if (thisilen & 1) {
-    c= getc(partfile);  if (c==EOF) rerreof(partfile,fn);
+    int c = readinfobuf[thisilen + 1];
+
     if (c != '\n')
       ohshit(_("file '%.250s' is corrupt - bad padding character (code %d)"),
-             fn, c);
+             ar->name, c);
   }
   readinfobuf[thisilen] = '\0';
   if (memchr(readinfobuf,0,thisilen))
-    ohshit(_("file '%.250s' is corrupt - nulls in info section"), fn);
+    ohshit(_("file '%.250s' is corrupt - nulls in info section"), ar->name);
 
-  ir->filename= fn;
+  ir->filename = ar->name;
 
   rip= readinfobuf;
   err = deb_version_parse(&ir->fmtversion,
-                          nextline(&rip, fn, _("format version number")));
+                          nextline(&rip, ar->name, _("format version number")));
   if (err)
-    ohshit(_("file '%.250s' has invalid format version: %s"), fn, err);
+    ohshit(_("file '%.250s' has invalid format version: %s"), ar->name, err);
   if (ir->fmtversion.major != 2)
     ohshit(_("file '%.250s' is format version %d.%d; get a newer dpkg-split"),
-           fn, ir->fmtversion.major, ir->fmtversion.minor);
+           ar->name, ir->fmtversion.major, ir->fmtversion.minor);
 
-  ir->package = nfstrsave(nextline(&rip, fn, _("package name")));
-  ir->version = nfstrsave(nextline(&rip, fn, _("package version number")));
-  ir->md5sum = nfstrsave(nextline(&rip, fn, _("package file MD5 checksum")));
+  ir->package = nfstrsave(nextline(&rip, ar->name, _("package name")));
+  ir->version = nfstrsave(nextline(&rip, ar->name, _("package version number")));
+  ir->md5sum = nfstrsave(nextline(&rip, ar->name, _("package file MD5 checksum")));
   if (strlen(ir->md5sum) != MD5HASHLEN ||
       strspn(ir->md5sum, "0123456789abcdef") != MD5HASHLEN)
     ohshit(_("file '%.250s' is corrupt - bad MD5 checksum '%.250s'"),
-           fn, ir->md5sum);
+           ar->name, ir->md5sum);
 
-  ir->orglength = parse_intmax(nextline(&rip, fn, _("archive total size")),
-                               fn, _("archive total size"));
-  ir->maxpartlen = parse_intmax(nextline(&rip, fn, _("archive part offset")),
-                                fn, _("archive part offset"));
+  ir->orglength = parse_intmax(nextline(&rip, ar->name, _("archive total size")),
+                               ar->name, _("archive total size"));
+  ir->maxpartlen = parse_intmax(nextline(&rip, ar->name, _("archive part offset")),
+                                ar->name, _("archive part offset"));
 
-  partnums = nextline(&rip, fn, _("archive part numbers"));
+  partnums = nextline(&rip, ar->name, _("archive part numbers"));
   slash= strchr(partnums,'/');
   if (!slash)
-    ohshit(_("file '%.250s' is corrupt - no slash between archive part numbers"), fn);
+    ohshit(_("file '%.250s' is corrupt - no slash between archive part numbers"), ar->name);
   *slash++ = '\0';
 
-  templong = parse_intmax(slash, fn, _("number of archive parts"));
+  templong = parse_intmax(slash, ar->name, _("number of archive parts"));
   if (templong <= 0 || templong > INT_MAX)
-    ohshit(_("file '%.250s' is corrupt - bad number of archive parts"), fn);
+    ohshit(_("file '%.250s' is corrupt - bad number of archive parts"), ar->name);
   ir->maxpartn= templong;
-  templong = parse_intmax(partnums, fn, _("archive parts number"));
+  templong = parse_intmax(partnums, ar->name, _("archive parts number"));
   if (templong <= 0 || templong > ir->maxpartn)
-    ohshit(_("file '%.250s' is corrupt - bad archive part number"), fn);
+    ohshit(_("file '%.250s' is corrupt - bad archive part number"), ar->name);
   ir->thispartn= templong;
 
   /* If the package was created with dpkg 1.16.1 or later it will include
    * the architecture. */
   if (*rip != '\0')
-    ir->arch = nfstrsave(nextline(&rip, fn, _("package architecture")));
+    ir->arch = nfstrsave(nextline(&rip, ar->name, _("package architecture")));
   else
     ir->arch = NULL;
 
-  if (fread(&arh,1,sizeof(arh),partfile) != sizeof(arh)) rerreof(partfile,fn);
+  rc = fd_read(ar->fd, &arh, sizeof(arh));
+  if (rc != sizeof(arh))
+    read_fail(rc, ar->name, "reading data part member ar header");
 
   dpkg_ar_normalize_name(&arh);
 
   if (dpkg_ar_member_is_illegal(&arh))
-    ohshit(_("file '%.250s' is corrupt - bad magic at end of second header"), fn);
+    ohshit(_("file '%.250s' is corrupt - bad magic at end of second header"),
+           ar->name);
   if (strncmp(arh.ar_name,"data",4))
-    ohshit(_("file '%.250s' is corrupt - second member is not data member"), fn);
+    ohshit(_("file '%.250s' is corrupt - second member is not data member"),
+           ar->name);
 
-  ir->thispartlen = dpkg_ar_member_get_size(fn, &arh);
+  ir->thispartlen = dpkg_ar_member_get_size(ar, &arh);
   ir->thispartoffset= (ir->thispartn-1)*ir->maxpartlen;
 
   if (ir->maxpartn != (ir->orglength+ir->maxpartlen-1)/ir->maxpartlen)
-    ohshit(_("file '%.250s' is corrupt - wrong number of parts for quoted sizes"), fn);
+    ohshit(_("file '%.250s' is corrupt - wrong number of parts for quoted sizes"),
+           ar->name);
   if (ir->thispartlen !=
       (ir->thispartn == ir->maxpartn
        ? ir->orglength - ir->thispartoffset : ir->maxpartlen))
-    ohshit(_("file '%.250s' is corrupt - size is wrong for quoted part number"), fn);
+    ohshit(_("file '%.250s' is corrupt - size is wrong for quoted part number"),
+           ar->name);
 
   ir->filesize = (strlen(DPKG_AR_MAGIC) +
                   sizeof(arh) + thisilen + (thisilen & 1) +
                   sizeof(arh) + ir->thispartlen + (ir->thispartlen & 1));
 
-  if (fstat(fileno(partfile), &stab))
-    ohshite(_("unable to fstat part file '%.250s'"), fn);
-  if (S_ISREG(stab.st_mode)) {
+  if (S_ISREG(ar->mode)) {
     /* Don't do this check if it's coming from a pipe or something.  It's
      * only an extra sanity check anyway. */
-    if (stab.st_size < ir->filesize)
-      ohshit(_("file '%.250s' is corrupt - too short"), fn);
+    if (ar->size < ir->filesize)
+      ohshit(_("file '%.250s' is corrupt - too short"), ar->name);
   }
 
   ir->headerlen = strlen(DPKG_AR_MAGIC) +
@@ -212,14 +225,14 @@ struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir)
 }
 
 void mustgetpartinfo(const char *filename, struct partinfo *ri) {
-  FILE *part;
+  struct dpkg_ar *part;
 
-  part= fopen(filename,"r");
+  part = dpkg_ar_open(filename);
   if (!part)
     ohshite(_("cannot open archive part file '%.250s'"), filename);
-  if (!read_info(part,filename,ri))
+  if (!read_info(part, ri))
     ohshite(_("file '%.250s' is not an archive part"), filename);
-  fclose(part);
+  dpkg_ar_close(part);
 }
 
 void print_info(const struct partinfo *pi) {
@@ -255,18 +268,18 @@ do_info(const char *const *argv)
 {
   const char *thisarg;
   struct partinfo *pi, ps;
-  FILE *part;
+  struct dpkg_ar *part;
 
   if (!*argv)
     badusage(_("--%s requires one or more part file arguments"),
              cipaction->olong);
 
   while ((thisarg= *argv++)) {
-    part= fopen(thisarg,"r");
+    part = dpkg_ar_open(thisarg);
     if (!part)
       ohshite(_("cannot open archive part file '%.250s'"), thisarg);
-    pi= read_info(part,thisarg,&ps);
-    fclose(part);
+    pi = read_info(part, &ps);
+    dpkg_ar_close(part);
     if (pi) {
       print_info(pi);
     } else {

+ 7 - 3
dpkg-split/main.c

@@ -109,9 +109,13 @@ const char *opt_outputfile = NULL;
 int opt_npquiet = 0;
 int opt_msdos = 0;
 
-void rerreof(FILE *f, const char *fn) {
-  if (ferror(f)) ohshite(_("error reading %.250s"),fn);
-  ohshit(_("unexpected end of file in %.250s"),fn);
+void DPKG_ATTR_NORET
+read_fail(int rc, const char *filename, const char *what)
+{
+  if (rc >= 0)
+    ohshit(_("unexpected end of file in %s in %.255s"), what, filename);
+  else
+    ohshite(_("error reading %s from file %.255s"), what, filename);
 }
 
 static void

+ 4 - 4
dpkg-split/queue.c

@@ -136,9 +136,9 @@ do_auto(const char *const *argv)
   struct partinfo *refi, **partlist, *otherthispart;
   struct partqueue *queue;
   struct partqueue *pq;
+  struct dpkg_ar *part;
   unsigned int i;
   int j;
-  FILE *part;
 
   if (!opt_outputfile)
     badusage(_("--auto requires the use of the --output option"));
@@ -147,16 +147,16 @@ do_auto(const char *const *argv)
     badusage(_("--auto requires exactly one part file argument"));
 
   refi= nfmalloc(sizeof(struct partqueue));
-  part= fopen(partfile,"r");
+  part = dpkg_ar_open(partfile);
   if (!part)
     ohshite(_("unable to read part file '%.250s'"), partfile);
-  if (!read_info(part,partfile,refi)) {
+  if (!read_info(part, refi)) {
     if (!opt_npquiet)
       printf(_("File '%.250s' is not part of a multipart archive.\n"), partfile);
     m_output(stdout, _("<standard output>"));
     return 1;
   }
-  fclose(part);
+  dpkg_ar_close(part);
 
   queue = scandepot();
   partlist= nfmalloc(sizeof(struct partinfo*)*refi->maxpartn);

+ 6 - 8
dpkg-split/split.c

@@ -165,7 +165,7 @@ mksplit(const char *file_src, const char *prefix, off_t maxpartsize,
 	}
 
 	for (curpart = 1; curpart <= nparts; curpart++) {
-		int fd_dst;
+		struct dpkg_ar *ar;
 
 		varbuf_reset(&file_dst);
 		/* Generate output filename. */
@@ -196,12 +196,10 @@ mksplit(const char *file_src, const char *prefix, off_t maxpartsize,
 		}
 
 		/* Split the data. */
-		fd_dst = creat(file_dst.buf, 0644);
-		if (fd_dst < 0)
-			ohshite(_("unable to open file '%s'"), file_dst.buf);
+		ar = dpkg_ar_create(file_dst.buf, 0644);
 
 		/* Write the ar header. */
-		dpkg_ar_put_magic(file_dst.buf, fd_dst);
+		dpkg_ar_put_magic(ar);
 
 		/* Write the debian-split part. */
 		varbuf_printf(&partmagic,
@@ -209,17 +207,17 @@ mksplit(const char *file_src, const char *prefix, off_t maxpartsize,
 		              SPLITVERSION, pkg->set->name, version, hash,
 		              (intmax_t)st.st_size, (intmax_t)partsize,
 		              curpart, nparts, pkg->available.arch->name);
-		dpkg_ar_member_put_mem(file_dst.buf, fd_dst, PARTMAGIC,
+		dpkg_ar_member_put_mem(ar, PARTMAGIC,
 		                       partmagic.buf, partmagic.used);
 		varbuf_reset(&partmagic);
 
 		/* Write the data part. */
 		varbuf_printf(&partname, "data.%d", curpart);
-		dpkg_ar_member_put_file(file_dst.buf, fd_dst, partname.buf,
+		dpkg_ar_member_put_file(ar, partname.buf,
 		                        fd_src, cur_partsize);
 		varbuf_reset(&partname);
 
-		close(fd_dst);
+		dpkg_ar_close(ar);
 
 		printf("%d ", curpart);
 	}

+ 88 - 26
lib/dpkg/ar.c

@@ -25,7 +25,9 @@
 #include <sys/stat.h>
 
 #include <time.h>
+#include <fcntl.h>
 #include <stdint.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -35,12 +37,73 @@
 #include <dpkg/buffer.h>
 #include <dpkg/ar.h>
 
+struct dpkg_ar *
+dpkg_ar_fdopen(const char *filename, int fd)
+{
+	struct dpkg_ar *ar;
+	struct stat st;
+
+	if (fstat(fd, &st) != 0)
+		ohshite(_("failed to fstat archive"));
+
+	ar = m_malloc(sizeof(*ar));
+	ar->name = filename;
+	ar->mode = st.st_mode;
+	ar->size = st.st_size;
+	ar->time = st.st_mtime;
+	ar->fd = fd;
+
+	return ar;
+}
+
+struct dpkg_ar *
+dpkg_ar_open(const char *filename)
+{
+	int fd;
+
+	if (strcmp(filename, "-") == 0)
+		fd = STDIN_FILENO;
+	else
+		fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		ohshite(_("failed to read archive '%.255s'"), filename);
+
+	return dpkg_ar_fdopen(filename, fd);
+}
+
+struct dpkg_ar *
+dpkg_ar_create(const char *filename, mode_t mode)
+{
+	int fd;
+
+	fd = creat(filename, mode);
+	if (fd < 0)
+		ohshite(_("unable to create '%.255s'"), filename);
+
+	return dpkg_ar_fdopen(filename, fd);
+}
+
+void
+dpkg_ar_set_mtime(struct dpkg_ar *ar, time_t mtime)
+{
+	ar->time = mtime;
+}
+
+void
+dpkg_ar_close(struct dpkg_ar *ar)
+{
+	if (close(ar->fd))
+		ohshite(_("unable to close file '%s'"), ar->name);
+	free(ar);
+}
+
 static void
-dpkg_ar_member_init(struct dpkg_ar_member *member, const char *name, off_t size)
+dpkg_ar_member_init(struct dpkg_ar *ar, struct dpkg_ar_member *member,
+                    const char *name, off_t size)
 {
 	member->name = name;
 	member->size = size;
-	member->time = time(NULL);
+	member->time = ar->time;
 	member->mode = 0100644;
 	member->uid = 0;
 	member->gid = 0;
@@ -62,7 +125,7 @@ dpkg_ar_normalize_name(struct ar_hdr *arh)
 }
 
 off_t
-dpkg_ar_member_get_size(const char *ar_name, struct ar_hdr *arh)
+dpkg_ar_member_get_size(struct dpkg_ar *ar, struct ar_hdr *arh)
 {
 	const char *str = arh->ar_size;
 	int len = sizeof(arh->ar_size);
@@ -77,7 +140,7 @@ dpkg_ar_member_get_size(const char *ar_name, struct ar_hdr *arh)
 		if (*str < '0' || *str > '9')
 			ohshit(_("invalid character '%c' in archive '%.250s' "
 			         "member '%.16s' size"),
-			       *str, ar_name, arh->ar_name);
+			       *str, ar->name, arh->ar_name);
 
 		size *= 10;
 		size += *str++ - '0';
@@ -93,15 +156,14 @@ dpkg_ar_member_is_illegal(struct ar_hdr *arh)
 }
 
 void
-dpkg_ar_put_magic(const char *ar_name, int ar_fd)
+dpkg_ar_put_magic(struct dpkg_ar *ar)
 {
-	if (fd_write(ar_fd, DPKG_AR_MAGIC, strlen(DPKG_AR_MAGIC)) < 0)
-		ohshite(_("unable to write file '%s'"), ar_name);
+	if (fd_write(ar->fd, DPKG_AR_MAGIC, strlen(DPKG_AR_MAGIC)) < 0)
+		ohshite(_("unable to write file '%s'"), ar->name);
 }
 
 void
-dpkg_ar_member_put_header(const char *ar_name, int ar_fd,
-                          struct dpkg_ar_member *member)
+dpkg_ar_member_put_header(struct dpkg_ar *ar, struct dpkg_ar_member *member)
 {
 	char header[sizeof(struct ar_hdr) + 1];
 	int n;
@@ -116,32 +178,32 @@ dpkg_ar_member_put_header(const char *ar_name, int ar_fd,
 	            (unsigned long)member->uid, (unsigned long)member->gid,
 	            (unsigned long)member->mode, (intmax_t)member->size);
 	if (n != sizeof(struct ar_hdr))
-		ohshit(_("generated corrupt ar header for '%s'"), ar_name);
+		ohshit(_("generated corrupt ar header for '%s'"), ar->name);
 
-	if (fd_write(ar_fd, header, n) < 0)
-		ohshite(_("unable to write file '%s'"), ar_name);
+	if (fd_write(ar->fd, header, n) < 0)
+		ohshite(_("unable to write file '%s'"), ar->name);
 }
 
 void
-dpkg_ar_member_put_mem(const char *ar_name, int ar_fd,
+dpkg_ar_member_put_mem(struct dpkg_ar *ar,
                        const char *name, const void *data, size_t size)
 {
 	struct dpkg_ar_member member;
 
-	dpkg_ar_member_init(&member, name, size);
-	dpkg_ar_member_put_header(ar_name, ar_fd, &member);
+	dpkg_ar_member_init(ar, &member, name, size);
+	dpkg_ar_member_put_header(ar, &member);
 
 	/* Copy data contents. */
-	if (fd_write(ar_fd, data, size) < 0)
-		ohshite(_("unable to write file '%s'"), ar_name);
+	if (fd_write(ar->fd, data, size) < 0)
+		ohshite(_("unable to write file '%s'"), ar->name);
 
 	if (size & 1)
-		if (fd_write(ar_fd, "\n", 1) < 0)
-			ohshite(_("unable to write file '%s'"), ar_name);
+		if (fd_write(ar->fd, "\n", 1) < 0)
+			ohshite(_("unable to write file '%s'"), ar->name);
 }
 
 void
-dpkg_ar_member_put_file(const char *ar_name, int ar_fd,
+dpkg_ar_member_put_file(struct dpkg_ar *ar,
                         const char *name, int fd, off_t size)
 {
 	struct dpkg_error err;
@@ -155,15 +217,15 @@ dpkg_ar_member_put_file(const char *ar_name, int ar_fd,
 		size = st.st_size;
 	}
 
-	dpkg_ar_member_init(&member, name, size);
-	dpkg_ar_member_put_header(ar_name, ar_fd, &member);
+	dpkg_ar_member_init(ar, &member, name, size);
+	dpkg_ar_member_put_header(ar, &member);
 
 	/* Copy data contents. */
-	if (fd_fd_copy(fd, ar_fd, size, &err) < 0)
+	if (fd_fd_copy(fd, ar->fd, size, &err) < 0)
 		ohshit(_("cannot append ar member file (%s) to '%s': %s"),
-		       name, ar_name, err.str);
+		       name, ar->name, err.str);
 
 	if (size & 1)
-		if (fd_write(ar_fd, "\n", 1) < 0)
-			ohshite(_("unable to write file '%s'"), ar_name);
+		if (fd_write(ar->fd, "\n", 1) < 0)
+			ohshite(_("unable to write file '%s'"), ar->name);
 }

+ 23 - 5
lib/dpkg/ar.h

@@ -38,6 +38,17 @@ DPKG_BEGIN_DECLS
 
 #define DPKG_AR_MAGIC "!<arch>\n"
 
+/**
+ * An archive (Unix ar) file.
+ */
+struct dpkg_ar {
+	const char *name;
+	mode_t mode;
+	time_t time;
+	off_t size;
+	int fd;
+};
+
 /**
  * In-memory archive member information.
  */
@@ -52,17 +63,24 @@ struct dpkg_ar_member {
 	gid_t gid;
 };
 
+struct dpkg_ar *
+dpkg_ar_fdopen(const char *filename, int fd);
+struct dpkg_ar *dpkg_ar_open(const char *filename);
+struct dpkg_ar *dpkg_ar_create(const char *filename, mode_t mode);
+void dpkg_ar_set_mtime(struct dpkg_ar *ar, time_t mtime);
+void dpkg_ar_close(struct dpkg_ar *ar);
+
 void dpkg_ar_normalize_name(struct ar_hdr *arh);
 bool dpkg_ar_member_is_illegal(struct ar_hdr *arh);
 
-void dpkg_ar_put_magic(const char *ar_name, int ar_fd);
-void dpkg_ar_member_put_header(const char *ar_name, int ar_fd,
+void dpkg_ar_put_magic(struct dpkg_ar *ar);
+void dpkg_ar_member_put_header(struct dpkg_ar *ar,
                                struct dpkg_ar_member *member);
-void dpkg_ar_member_put_file(const char *ar_name, int ar_fd, const char *name,
+void dpkg_ar_member_put_file(struct dpkg_ar *ar, const char *name,
                              int fd, off_t size);
-void dpkg_ar_member_put_mem(const char *ar_name, int ar_fd, const char *name,
+void dpkg_ar_member_put_mem(struct dpkg_ar *ar, const char *name,
                             const void *data, size_t size);
-off_t dpkg_ar_member_get_size(const char *ar_name, struct ar_hdr *arh);
+off_t dpkg_ar_member_get_size(struct dpkg_ar *ar, struct ar_hdr *arh);
 
 /** @} */
 

+ 5 - 0
lib/dpkg/libdpkg.map

@@ -19,6 +19,11 @@ global:
 	dpkg_program_done;
 
 	# Ar support
+	dpkg_ar_fdopen;
+	dpkg_ar_create;
+	dpkg_ar_open;
+	dpkg_ar_set_mtime;
+	dpkg_ar_close;
 	dpkg_ar_normalize_name;
 	dpkg_ar_member_is_illegal;