/* * dpkg-split - splitting and joining of multipart *.deb archives * split.c - splitting archives * * Copyright © 1995 Ian Jackson * Copyright © 2010 Guillem Jover * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dpkg-split.h" static char * deb_field(const char *filename, const char *field) { pid_t pid; int p[2]; struct varbuf buf = VARBUF_INIT; char *end; m_pipe(p); pid = subproc_fork(); if (pid == 0) { /* Child writes to pipe. */ m_dup2(p[1], 1); close(p[0]); close(p[1]); execlp(BACKEND, BACKEND, "--field", filename, field, NULL); ohshite(_("unable to execute %s (%s)"), _("package field value extraction"), BACKEND); } close(p[1]); /* Parant reads from pipe. */ varbuf_reset(&buf); fd_vbuf_copy(p[0], &buf, -1, _("package field value extraction")); varbuf_end_str(&buf); close(p[0]); subproc_wait_check(pid, _("package field value extraction"), PROCPIPE); /* Trim down trailing junk. */ for (end = buf.buf + strlen(buf.buf) - 1; end - buf.buf >= 1; end--) if (isspace(*end)) *end = '\0'; else break; return varbuf_detach(&buf); } /* Cleanup filename for use in crippled msdos systems. */ static char * clean_msdos_filename(char *filename) { char *d, *s; for (s = d = filename; *s; d++, s++) { if (*s == '+') *d = 'x'; else if (isupper(*s)) *d = tolower(*s); else if (islower(*s) || isdigit(*s)) *d = *s; else s++; } return filename; } static int mksplit(const char *file_src, const char *prefix, off_t maxpartsize, bool msdos) { int fd_src; struct stat st; char hash[MD5HASHLEN + 1]; char *package, *version, *arch; int nparts, curpart; off_t partsize; off_t cur_partsize, last_partsize; char *prefixdir = NULL, *msdos_prefix = NULL; struct varbuf file_dst = VARBUF_INIT; struct varbuf partmagic = VARBUF_INIT; struct varbuf partname = VARBUF_INIT; fd_src = open(file_src, O_RDONLY); if (fd_src < 0) ohshite(_("unable to open source file `%.250s'"), file_src); if (fstat(fd_src, &st)) ohshite(_("unable to fstat source file")); if (!S_ISREG(st.st_mode)) ohshit(_("source file `%.250s' not a plain file"), file_src); fd_md5(fd_src, hash, -1, "md5hash"); lseek(fd_src, 0, SEEK_SET); /* FIXME: Use libdpkg-deb. */ package = deb_field(file_src, "Package"); version = deb_field(file_src, "Version"); arch = deb_field(file_src, "Architecture"); partsize = maxpartsize - HEADERALLOWANCE; last_partsize = st.st_size % partsize; if (last_partsize == 0) last_partsize = partsize; nparts = (st.st_size + partsize - 1) / partsize; setvbuf(stdout, NULL, _IONBF, 0); printf(P_("Splitting package %s into %d part: ", "Splitting package %s into %d parts: ", nparts), package, nparts); if (msdos) { char *t; t = m_strdup(prefix); prefixdir = m_strdup(dirname(t)); free(t); t = m_strdup(prefix); msdos_prefix = m_strdup(basename(t)); free(t); prefix = clean_msdos_filename(msdos_prefix); } for (curpart = 1; curpart <= nparts; curpart++) { int fd_dst; varbuf_reset(&file_dst); /* Generate output filename. */ if (msdos) { char *refname; int prefix_max; m_asprintf(&refname, "%dof%d", curpart, nparts); prefix_max = max(8 - strlen(refname), 0); varbuf_printf(&file_dst, "%s/%.*s%.8s.deb", prefixdir, prefix_max, prefix, refname); free(refname); } else { varbuf_printf(&file_dst, "%s.%dof%d.deb", prefix, curpart, nparts); } if (curpart == nparts) cur_partsize = last_partsize; else cur_partsize = partsize; if (cur_partsize > maxpartsize) { ohshit(_("Header is too long, making part too long. " "Your package name or version\n" "numbers must be extraordinarily long, " "or something. Giving up.\n")); } /* Split the data. */ fd_dst = creat(file_dst.buf, 0644); if (fd_dst < 0) ohshite(_("unable to open file '%s'"), file_dst.buf); /* Write the ar header. */ dpkg_ar_put_magic(file_dst.buf, fd_dst); /* Write the debian-split part. */ varbuf_printf(&partmagic, "%s\n%s\n%s\n%s\n%jd\n%jd\n%d/%d\n%s\n", SPLITVERSION, package, version, hash, (intmax_t)st.st_size, (intmax_t)partsize, curpart, nparts, arch); dpkg_ar_member_put_mem(file_dst.buf, fd_dst, 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, fd_src, cur_partsize); varbuf_reset(&partname); close(fd_dst); printf("%d ", curpart); } varbuf_destroy(&file_dst); varbuf_destroy(&partname); varbuf_destroy(&partmagic); free(prefixdir); free(msdos_prefix); close(fd_src); printf(_("done\n")); return 0; } int do_split(const char *const *argv) { const char *sourcefile, *prefix; sourcefile = *argv++; if (!sourcefile) badusage(_("--split needs a source filename argument")); prefix = *argv++; if (prefix && *argv) badusage(_("--split takes at most a source filename and destination prefix")); if (!prefix) { char *palloc; int l; l = strlen(sourcefile); palloc = nfmalloc(l + 1); strcpy(palloc, sourcefile); if (!strcmp(palloc + l - (sizeof(DEBEXT) - 1), DEBEXT)) { l -= (sizeof(DEBEXT) - 1); palloc[l] = '\0'; } prefix = palloc; } mksplit(sourcefile, prefix, opt_maxpartsize, opt_msdos); return 0; }