static void test_fdio_hash(void) { char hash[MD5HASHLEN + 1]; char *test_file; int fd; test_file = test_alloc(strdup("test.XXXXXX")); fd = mkstemp(test_file); test_pass(fd >= 0); test_pass(fd_md5(fd, hash, -1, NULL) >= 0); test_str(hash, ==, ref_hash_empty); test_pass(write(fd, str_test, strlen(str_test)) == strlen(str_test)); test_pass(lseek(fd, 0, SEEK_SET) == 0); test_pass(fd_md5(fd, hash, -1, NULL) >= 0); test_str(hash, ==, ref_hash_test); test_pass(unlink(test_file) == 0); }
static int mksplit(const char *file_src, const char *prefix, size_t partsize, size_t maxpartsize, bool msdos) { int fd_src; struct stat st; char hash[MD5HASHLEN + 1]; char *package, *version; int nparts, curpart; off_t startat; char *prefixdir = NULL, *msdos_prefix = NULL; struct varbuf file_dst = VARBUF_INIT; struct varbuf partmagic = VARBUF_INIT; struct varbuf partname = VARBUF_INIT; char *partdata; 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"); nparts = (st.st_size + partsize - 1) / partsize; setvbuf(stdout, NULL, _IONBF, 0); printf("Splitting package %s into %d parts: ", 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); } partdata = m_malloc(partsize); curpart = 1; for (startat = 0; startat < st.st_size; startat += partsize) { int fd_dst; ssize_t partrealsize; varbufreset(&file_dst); /* Generate output filename. */ if (msdos) { struct varbuf refname = VARBUF_INIT; int prefix_max; varbufprintf(&refname, "%dof%d", curpart, nparts); prefix_max = max(8 - strlen(refname.buf), 0); varbufprintf(&file_dst, "%s/%.*s%.8s.deb", prefixdir, prefix_max, prefix, refname.buf); varbuf_destroy(&refname); } else { varbufprintf(&file_dst, "%s.%dof%d.deb", prefix, curpart, nparts); } /* Read data from the original package. */ partrealsize = read(fd_src, partdata, partsize); if (partrealsize < 0) ohshite("mksplit: read"); if ((size_t)partrealsize > 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 = open(file_dst.buf, O_CREAT | O_WRONLY, 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. */ varbufprintf(&partmagic, "%s\n%s\n%s\n%s\n%zu\n%zu\n%d/%d\n", SPLITVERSION, package, version, hash, st.st_size, partsize, curpart, nparts); dpkg_ar_member_put_mem(file_dst.buf, fd_dst, PARTMAGIC, partmagic.buf, partmagic.used); varbufreset(&partmagic); /* Write the data part. */ varbufprintf(&partname, "data.%d", curpart); dpkg_ar_member_put_mem(file_dst.buf, fd_dst, partname.buf, partdata, (size_t)partrealsize); varbufreset(&partname); close(fd_dst); printf("%d ", curpart); curpart++; } varbuf_destroy(&file_dst); varbuf_destroy(&partname); varbuf_destroy(&partmagic); free(partdata); free(prefixdir); free(msdos_prefix); close(fd_src); printf("done\n"); return 0; }
int main( int ac, char *av[] ) { const char* const pqfname = getQueuePath(); const char* const progname = ubasename(av[0]); int useProductID = FALSE; int signatureFromId = FALSE; char *productID = NULL; int multipleFiles = FALSE; char identifier[KEYSIZE]; int status; int seq_start = 0; enum ExitCode { exit_success = 0, /* all files inserted successfully */ exit_system = 1, /* operating-system failure */ exit_pq_open = 2, /* couldn't open product-queue */ exit_infile = 3, /* couldn't process input file */ exit_dup = 4, /* input-file already in product-queue */ exit_md5 = 6 /* couldn't initialize MD5 processing */ } exitCode = exit_success; #if !USE_MMAP pqeIndex = PQE_NONE; #endif { extern int optind; extern int opterr; extern char *optarg; int ch; (void) openulog(progname, LOG_NOTIME, LOG_LDM, "-"); (void) setulogmask(LOG_UPTO(LOG_NOTICE)); opterr = 0; /* Suppress getopt(3) error messages */ while ((ch = getopt(ac, av, ":ivxl:q:f:s:p:")) != EOF) switch (ch) { case 'i': signatureFromId = 1; break; case 'v': (void) setulogmask(getulogmask() | LOG_MASK(LOG_INFO)); break; case 'x': (void) setulogmask(getulogmask() | LOG_MASK(LOG_DEBUG)); break; case 'l': openulog(progname, ulog_get_options(), LOG_LDM, optarg); break; case 'q': setQueuePath(optarg); break; case 's': seq_start = atoi(optarg); break; case 'f': feedtype = atofeedtypet(optarg); if(feedtype == NONE) { fprintf(stderr, "Unknown feedtype \"%s\"\n", optarg); usage(progname); } break; case 'p': useProductID = TRUE; productID = optarg; break; case ':': { LOG_ADD1("Option \"-%c\" requires an operand", optopt); usage(progname); } /* no break */ default: LOG_ADD1("Unknown option: \"%c\"", optopt); usage(progname); /* no break */ } ac -= optind; av += optind ; if(ac < 1) usage(progname); } /* * register exit handler */ if(atexit(cleanup) != 0) { serror("atexit"); exit(exit_system); } /* * set up signal handlers */ set_sigactions(); /* * who am i, anyway */ (void) strncpy(myname, ghostname(), sizeof(myname)); myname[sizeof(myname)-1] = 0; /* * open the product queue */ if(status = pq_open(pqfname, PQ_DEFAULT, &pq)) { if (PQ_CORRUPT == status) { uerror("The product-queue \"%s\" is inconsistent\n", pqfname); } else { uerror("pq_open: \"%s\" failed: %s", pqfname, status > 0 ? strerror(status) : "Internal error"); } exit(exit_pq_open); } { char *filename; int fd; struct stat statb; product prod; MD5_CTX *md5ctxp = NULL; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if(md5ctxp == NULL) { serror("new_md5_CTX failed"); exit(exit_md5); } /* These members are constant over the loop. */ prod.info.origin = myname; prod.info.feedtype = feedtype; if (ac > 1) { multipleFiles = TRUE; } for(prod.info.seqno = seq_start ; ac > 0 ; av++, ac--, prod.info.seqno++) { filename = *av; fd = open(filename, O_RDONLY, 0); if(fd == -1) { serror("open: %s", filename); exitCode = exit_infile; continue; } if( fstat(fd, &statb) == -1) { serror("fstat: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } /* Determine what to use for product identifier */ if (useProductID) { if (multipleFiles) { sprintf(identifier,"%s.%d", productID, prod.info.seqno); prod.info.ident = identifier; } else prod.info.ident = productID; } else prod.info.ident = filename; prod.info.sz = statb.st_size; prod.data = NULL; /* These members, and seqno, vary over the loop. */ status = set_timestamp(&prod.info.arrival); if(status != ENOERR) { serror("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } #if USE_MMAP prod.data = mmap(0, prod.info.sz, PROT_READ, MAP_PRIVATE, fd, 0); if(prod.data == NULL) { serror("mmap: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : mm_md5(md5ctxp, prod.data, prod.info.sz, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("mm_md5: %s", filename); (void) munmap(prod.data, prod.info.sz); (void) close(fd); exitCode = exit_infile; continue; } /* These members, and seqno, vary over the loop. */ status = set_timestamp(&prod.info.arrival); if(status != ENOERR) { serror("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } /* * Do the deed */ status = pq_insert(pq, &prod); switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case PQUEUE_BIG: uerror("Product too big for queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_infile; break; case ENOMEM: uerror("queue full?"); exitCode = exit_system; break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); break; } (void) munmap(prod.data, prod.info.sz); #else // USE_MMAP above; !USE_MMAP below status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : fd_md5(md5ctxp, fd, statb.st_size, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("xx_md5: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } if(lseek(fd, 0, SEEK_SET) == (off_t)-1) { serror("rewind: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } pqeIndex = PQE_NONE; status = pqe_new(pq, &prod.info, &prod.data, &pqeIndex); if(status != ENOERR) { serror("pqe_new: %s", filename); exitCode = exit_infile; } else { ssize_t nread = read(fd, prod.data, prod.info.sz); (void)exitIfDone(1); if (nread != prod.info.sz) { serror("read %s %u", filename, prod.info.sz); status = EIO; } else { status = pqe_insert(pq, pqeIndex); pqeIndex = PQE_NONE; switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); exitCode = exit_dup; break; case ENOMEM: uerror("queue full?"); break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); } } /* data read into `pqeIndex` region */ if (status != ENOERR) { (void)pqe_discard(pq, pqeIndex); pqeIndex = PQE_NONE; } } /* `pqeIndex` region allocated */ #endif (void) close(fd); } /* input-file loop */ free_MD5_CTX(md5ctxp); } /* code block */ exit(exitCode); }
/* * Sends a list of files to the LDM as data-products. * * Arguments: * ldmProxy The LDM proxy data-structure. * offer The description of the class of data-products that this * process is willing to send. * origin The identifier of the host that created the * data-products (typically the host running this program). * seq_start The starting value of the data-product sequence number. * nfiles The number of files to send. * filenames The pathnames of the files to send. * * Returns: * 0 Success. * SYSTEM_ERROR O/S failure. "log_add()" called. * CONNECTION_ABORTED The connection was aborted. "log_add()" * called. */ static int ldmsend( LdmProxy* ldmProxy, prod_class_t* offer, char* origin, int seq_start, int nfiles, char* filenames[]) { int status = 0; char* filename; int fd; struct stat statb; prod_info info; MD5_CTX* md5ctxp = NULL; prod_class_t* want; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if (md5ctxp == NULL) { log_syserr_q("new_md5_CTX failed"); return SYSTEM_ERROR; } status = lp_hiya(ldmProxy, offer, &want); if (status != 0) { status = CONNECTION_ABORTED; } else { /* These members are constant over the loop. */ info.origin = origin; info.feedtype = offer->psa.psa_val->feedtype; for (info.seqno = seq_start; exitIfDone(1) && nfiles > 0; filenames++, nfiles--, info.seqno++) { filename = *filenames; info.ident = filename; /* * ?? This could be the creation time of the file. */ (void) set_timestamp(&info.arrival); /* * Checks 'arrival', 'feedtype', and 'ident' * against what the other guy has said he wants. */ if (!prodInClass(offer, &info)) { log_info_q("Not going to send %s", filename); continue; } if (!prodInClass(want, &info)) { log_info_q("%s doesn't want %s", lp_host(ldmProxy), filename); continue; } fd = open(filename, O_RDONLY, 0); if (fd == -1) { log_syserr_q("open: %s", filename); continue; } if (fstat(fd, &statb) == -1) { log_syserr_q("fstat: %s", filename); (void) close(fd); continue; } log_info_q("Sending %s, %d bytes", filename, statb.st_size); /* These members, and seqno, vary over the loop. */ if (fd_md5(md5ctxp, fd, statb.st_size, info.signature) != 0) { (void) close(fd); continue; } if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { log_syserr_q("rewind: %s", filename); (void) close(fd); continue; } info.sz = (u_int)statb.st_size; (void)exitIfDone(1); status = send_product(ldmProxy, fd, &info); (void) close(fd); if (0 != status) { log_add("Couldn't send file \"%s\" to LDM", filename); break; } } /* file loop */ if (lp_flush(ldmProxy)) log_add("Couldn't flush connection"); free_prod_class(want); } /* HIYA succeeded */ free_MD5_CTX(md5ctxp); return status; }
int main( int ac, char *av[] ) { char *progname = av[0]; char *logfname; int useProductID = FALSE; int signatureFromId = FALSE; char *productID = NULL; int multipleFiles = FALSE; char identifier[KEYSIZE]; int status; int seq_start = 0; enum ExitCode { exit_success = 0, exit_system = 1, /* operating-system failure */ exit_pq_open = 2, /* couldn't open product-queue */ exit_infile = 3, /* couldn't process input file */ exit_md5 = 6 /* couldn't initialize MD5 processing */ } exitCode = exit_success; logfname = "-"; /* * Check the environment for some options. * May be overridden by command line switches below. */ { const char *ldmpqfname = getenv("LDMPQFNAME"); if(ldmpqfname != NULL) pqfname = ldmpqfname; } { extern int optind; extern int opterr; extern char *optarg; int ch; int logmask = (LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING) | LOG_MASK(LOG_NOTICE)); opterr = 1; while ((ch = getopt(ac, av, "ivxl:q:f:s:p:")) != EOF) switch (ch) { case 'i': signatureFromId = 1; break; case 'v': logmask |= LOG_MASK(LOG_INFO); break; case 'x': logmask |= LOG_MASK(LOG_DEBUG); break; case 'l': logfname = optarg; break; case 'q': pqfname = optarg; break; case 's': seq_start = atoi(optarg); break; case 'f': feedtype = atofeedtypet(optarg); if(feedtype == NONE) { fprintf(stderr, "Unknown feedtype \"%s\"\n", optarg); usage(progname); } break; case 'p': useProductID = TRUE; productID = optarg; break; case '?': usage(progname); break; } ac -= optind; av += optind ; if(ac < 1) usage(progname); (void) setulogmask(logmask); } /* * Set up error logging */ (void) openulog(ubasename(progname), LOG_NOTIME, LOG_LDM, logfname); /* * register exit handler */ if(atexit(cleanup) != 0) { serror("atexit"); exit(exit_system); } /* * set up signal handlers */ set_sigactions(); /* * who am i, anyway */ (void) strcpy(myname, ghostname()); /* * open the product queue */ if(status = pq_open(pqfname, PQ_DEFAULT, &pq)) { if (PQ_CORRUPT == status) { uerror("The product-queue \"%s\" is inconsistent\n", pqfname); } else { uerror("pq_open: \"%s\" failed: %s", pqfname, status > 0 ? strerror(status) : "Internal error"); } exit(exit_pq_open); } { char *filename; int fd; struct stat statb; product prod; MD5_CTX *md5ctxp = NULL; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if(md5ctxp == NULL) { serror("new_md5_CTX failed"); exit(exit_md5); } /* These members are constant over the loop. */ prod.info.origin = myname; prod.info.feedtype = feedtype; if (ac > 1) { multipleFiles = TRUE; } for(prod.info.seqno = seq_start ; ac > 0 ; av++, ac--, prod.info.seqno++) { filename = *av; fd = open(filename, O_RDONLY, 0); if(fd == -1) { serror("open: %s", filename); exitCode = exit_infile; continue; } if( fstat(fd, &statb) == -1) { serror("fstat: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } /* Determine what to use for product identifier */ if (useProductID) { if (multipleFiles) { sprintf(identifier,"%s.%d", productID, prod.info.seqno); prod.info.ident = identifier; } else prod.info.ident = productID; } else prod.info.ident = filename; prod.info.sz = statb.st_size; prod.data = NULL; #ifndef NO_MMAP prod.data = mmap(0, prod.info.sz, PROT_READ, MAP_PRIVATE, fd, 0); if(prod.data == NULL) { serror("mmap: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : mm_md5(md5ctxp, prod.data, prod.info.sz, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("mm_md5: %s", filename); (void) munmap(prod.data, prod.info.sz); (void) close(fd); exitCode = exit_infile; continue; } /* These members, and seqno, vary over the loop. */ status = set_timestamp(&prod.info.arrival); if(status != ENOERR) { serror("set_timestamp: %s, filename"); exitCode = exit_infile; continue; } /* * Do the deed */ status = pq_insert(pq, &prod); switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); break; case ENOMEM: uerror("queue full?"); break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); break; } (void) munmap(prod.data, prod.info.sz); #else /*!NO_MMAP*/ status = signatureFromId ? mm_md5(md5ctxp, prod.info.ident, strlen(prod.info.ident), prod.info.signature) : fd_md5(md5ctxp, fd, statb.st_size, prod.info.signature); (void)exitIfDone(1); if (status != 0) { serror("fd_md5: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } if(lseek(fd, 0, SEEK_SET) == (off_t)-1) { serror("rewind: %s", filename); (void) close(fd); exitCode = exit_infile; continue; } index = PQE_NONE; status = pqe_new(pq, &prod.info, &prod.data, &index); if(status != ENOERR) { serror("pqe_new: %s", filename); exitCode = exit_infile; } else { ssize_t nread = read(fd, prod.data, prod.info.sz); (void)exitIfDone(1); if (nread != prod.info.sz) { serror("read %s %u", filename, prod.info.sz); status = EIO; } else { status = pqe_insert(pq, index); index = PQE_NONE; switch (status) { case ENOERR: /* no error */ if(ulogIsVerbose()) uinfo("%s", s_prod_info(NULL, 0, &prod.info, ulogIsDebug())) ; break; case PQUEUE_DUP: uerror("Product already in queue: %s", s_prod_info(NULL, 0, &prod.info, 1)); break; case ENOMEM: uerror("queue full?"); break; case EINTR: #if defined(EDEADLOCK) && EDEADLOCK != EDEADLK case EDEADLOCK: /*FALLTHROUGH*/ #endif case EDEADLK: /* TODO: retry ? */ /*FALLTHROUGH*/ default: uerror("pq_insert: %s", status > 0 ? strerror(status) : "Internal error"); } } /* data read into "index" region */ if (status != ENOERR) { (void)pqe_discard(pq, index); index = PQE_NONE; } } /* "index" region allocated */ #endif /*!NO_MMAP*/ (void) close(fd); } /* input-file loop */ free_MD5_CTX(md5ctxp); } /* code block */ exit(exitCode); }
static int ldmsend(CLIENT *clnt, prod_class_t* clssp, char* origin, int seq_start, int nfiles, char* filenames[]) { int status = 0; char *filename; int fd; struct stat statb; prod_info info; MD5_CTX *md5ctxp = NULL; /* * Allocate an MD5 context */ md5ctxp = new_MD5_CTX(); if(md5ctxp == NULL) { status = errno; serror("new_md5_CTX failed"); return status; } status = (*hiya)(clnt, &clssp); if(status != 0) return status; /* These members are constant over the loop. */ info.origin = origin; info.feedtype = clssp->psa.psa_val->feedtype; for( info.seqno = seq_start; exitIfDone(1) && nfiles > 0; filenames++, nfiles--, info.seqno++) { filename = *filenames; info.ident = filename; /* * ?? This could be the creation time of the file. */ (void) set_timestamp(&info.arrival); /* * Checks 'arrival', 'feedtype', and 'ident' * against what the other guy has said he wants. */ if(!prodInClass(clssp, &info)) { uinfo("%s doesn't want %s", remote, filename); continue; } fd = open(filename, O_RDONLY, 0); if(fd == -1) { serror("open: %s", filename); continue; } if( fstat(fd, &statb) == -1) { serror("fstat: %s", filename); (void) close(fd); continue; } uinfo("Sending %s, %d bytes", filename, statb.st_size); /* These members, and seqno, vary over the loop. */ if(fd_md5(md5ctxp, fd, statb.st_size, info.signature) != 0) { (void) close(fd); continue; } if(lseek(fd, 0, SEEK_SET) == (off_t)-1) { serror("rewind: %s", filename); (void) close(fd); continue; } (void)exitIfDone(1); info.sz = (u_int)statb.st_size; (*send_product)(clnt, fd, &info); (void) close(fd); } if (exitIfDone(1) && NULL != nullproc && NULL == (*nullproc)(NULL, clnt)) { uerror("%s: NULLPROC failure: %s", remote, clnt_errmsg(clnt)); status = 1; } free_MD5_CTX(md5ctxp); return status; }
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; }