| /* |
| * dpkg-split - splitting and joining of multipart *.deb archives |
| * split.c - splitting archives |
| * |
| * Copyright © 1995 Ian Jackson <ian@chiark.greenend.org.uk> |
| * Copyright © 2008-2011 Guillem Jover <guillem@debian.org> |
| * |
| * 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 <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <compat.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <libgen.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include <dpkg/i18n.h> |
| #include <dpkg/dpkg.h> |
| #include <dpkg/dpkg-db.h> |
| #include <dpkg/path.h> |
| #include <dpkg/subproc.h> |
| #include <dpkg/buffer.h> |
| #include <dpkg/ar.h> |
| #include <dpkg/options.h> |
| |
| #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); |
| |
| msdos_prefix = m_strdup(path_basename(prefix)); |
| 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; |
| } |