/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2017 Peter Tribble. */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pkginfo.h" #include "pkgstrct.h" #include "pkgtrans.h" #include "pkgdev.h" #include "pkglib.h" #include "pkglibmsgs.h" #include "pkglocale.h" extern char *pkgdir; /* pkgparam.c */ /* libadm.a */ extern char *devattr(char *device, char *attribute); extern char *fpkginst(char *pkg, ...); extern int fpkginfo(struct pkginfo *info, char *pkginst); extern int getvol(char *device, char *label, int options, char *prompt); extern int _getvol(char *device, char *label, int options, char *prompt, char *norewind); /* dstream.c */ extern int ds_ginit(char *device); extern int ds_close(int pkgendflg); #define CPIOPROC "/usr/bin/cpio" #define CMDSIZE 512 /* command block size */ #define BLK_SIZE 512 /* size of logical block */ #define ENTRY_MAX 256 /* max size of entry for cpio cmd or header */ #define PKGINFO "pkginfo" #define PKGMAP "pkgmap" #define MAP_STAT_SIZE 60 /* 1st line of pkgmap (3 numbers & a : */ #define INSTALL "install" #define RELOC "reloc" #define ROOT "root" #define ARCHIVE "archive" static struct pkgdev srcdev, dstdev; static char *tmpdir; static char *tmppath; static char *tmpsymdir = NULL; static char dstinst[NON_ABI_NAMELNGTH]; static char *ids_name, *ods_name; static int ds_volcnt; static int ds_volno; static int compressedsize, has_comp_size; static void (*sigintHandler)(); static void (*sighupHandler)(); static void cleanup(void); static void sigtrap(int signo); static int rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize); static int cat_and_count(struct dm_buf *, char *); static int ckoverwrite(char *dir, char *inst, int options); static int pkgxfer(char *srcinst, int options); static int wdsheader(struct dm_buf *, char *device, char **pkg); static struct dm_buf *genheader(char *, char **); extern int ds_fd; /* open file descriptor for data stream WHERE? */ static char *root_names[] = { "root", "root.cpio", "root.Z", "root.cpio.Z", 0 }; static char *reloc_names[] = { "reloc", "reloc.cpio", "reloc.Z", "reloc.cpio.Z", 0 }; static int signal_received = 0; char **xpkg; /* array of transferred packages */ int nxpkg; static char *allpkg[] = { "all", NULL }; static struct dm_buf hdrbuf; static char *pinput, *nextpinput; int pkghead(char *device) { char *pt; int n; cleanup(); if (device == NULL) return (0); else if ((device[0] == '/') && !isdir(device)) { pkgdir = device; return (0); } else if ((pt = devattr(device, "pathname")) != NULL && !isdir(pt)) { pkgdir = pt; return (0); } /* check for datastream */ if (n = pkgtrans(device, (char *)0, allpkg, PT_SILENT|PT_INFO_ONLY)) { cleanup(); return (n); } /* pkgtrans has set pkgdir */ return (0); } static char * mgets(char *buf, int size) { nextpinput = strchr(pinput, '\n'); if (nextpinput == NULL) return (0); *nextpinput = '\0'; if ((int)strlen(pinput) > size) return (0); (void) strncpy(buf, pinput, strlen(pinput)); buf[strlen(pinput)] = '\0'; pinput = nextpinput + 1; return (buf); } /* * Here we construct the package size summaries for the headers. The * pkgmap file associated with fp must be rewound to the beginning of the * file. Note that we read three values from pkgmap first line in order * to get the *actual* size if this package is compressed. * This returns * 0 : error * 2 : not a compressed package * 3 : compressed package * and sets has_comp_size to indicate whether or not this is a compressed * package. */ static int rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize) { int n; char line_buffer[MAP_STAT_SIZE]; /* First read the null terminated first line */ if (fgets(line_buffer, MAP_STAT_SIZE, fp) == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOSIZE)); (void) fclose(fp); ecleanup(); return (0); } n = sscanf(line_buffer, ": %d %d %d", npts, maxpsz, cmpsize); if (n == 3) /* A valid compressed package entry */ has_comp_size = 1; else if (n == 2) /* A valid standard package entry */ has_comp_size = 0; else { /* invalid entry */ progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOSIZE)); (void) fclose(fp); ecleanup(); return (0); } return (n); } /* will return 0, 1, 3, or 99 */ static int _pkgtrans(char *device1, char *device2, char **pkg, int options) { char *src, *dst; int errflg, i, n; struct dm_buf *hdr; if (signal_received > 0) { return (1); } /* transfer spool to appropriate device */ if (devtype(device1, &srcdev)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_BADDEV), device1); return (1); } srcdev.rdonly++; /* check for datastream */ ids_name = NULL; if (srcdev.bdevice) { if (n = _getvol(srcdev.bdevice, NULL, 0, pkg_gt("Insert %v into %p."), srcdev.norewind)) { cleanup(); if (n == 3) return (3); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_GETVOL)); return (1); } if (ds_readbuf(srcdev.cdevice)) ids_name = srcdev.cdevice; } if (srcdev.cdevice && !srcdev.bdevice) ids_name = srcdev.cdevice; else if (srcdev.pathname) { ids_name = srcdev.pathname; if (access(ids_name, 0) == -1) { progerr(ERR_TRANSFER); logerr(pkg_gt(MSG_GETVOL)); return (1); } } if (!ids_name && device2 == (char *)0) { if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) { cleanup(); return (n); } if (srcdev.mount && *srcdev.mount) pkgdir = strdup(srcdev.mount); return (0); } if (ids_name && device2 == (char *)0) { tmppath = tmpnam(NULL); tmppath = strdup(tmppath); if (tmppath == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); return (1); } if (mkdir(tmppath, 0755)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MKDIR), tmppath); return (1); } device2 = tmppath; } if (devtype(device2, &dstdev)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_BADDEV), device2); return (1); } if ((srcdev.cdevice && dstdev.cdevice) && strcmp(srcdev.cdevice, dstdev.cdevice) == 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_SAMEDEV)); return (1); } ods_name = NULL; if (dstdev.cdevice && !dstdev.bdevice || dstdev.pathname) options |= PT_ODTSTREAM; if (options & PT_ODTSTREAM) { if (!((ods_name = dstdev.cdevice) != NULL || (ods_name = dstdev.pathname) != NULL)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_BADDEV), device2); return (1); } if (ids_name) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_TWODSTREAM)); return (1); } } if ((srcdev.dirname && dstdev.dirname) && strcmp(srcdev.dirname, dstdev.dirname) == 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_SAMEDEV)); return (1); } if ((srcdev.pathname && dstdev.pathname) && strcmp(srcdev.pathname, dstdev.pathname) == 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_SAMEDEV)); return (1); } if (signal_received > 0) { return (1); } if (ids_name) { if (srcdev.cdevice && !srcdev.bdevice && (n = _getvol(srcdev.cdevice, NULL, 0, NULL, srcdev.norewind))) { cleanup(); if (n == 3) return (3); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_GETVOL)); return (1); } if (srcdev.dirname = tmpnam(NULL)) tmpdir = srcdev.dirname = strdup(srcdev.dirname); if ((srcdev.dirname == NULL) || mkdir(srcdev.dirname, 0755) || chdir(srcdev.dirname)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOTEMP), srcdev.dirname); cleanup(); return (1); } if (ds_init(ids_name, pkg, srcdev.norewind)) { cleanup(); return (1); } } else if (srcdev.mount) { if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) { cleanup(); return (n); } } src = srcdev.dirname; dst = dstdev.dirname; if (chdir(src)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHDIR), src); cleanup(); return (1); } if (signal_received > 0) { return (1); } xpkg = pkg = gpkglist(src, pkg, NULL); if (!pkg) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOPKGS), src); cleanup(); return (1); } for (nxpkg = 0; pkg[nxpkg]; /* void */) { nxpkg++; /* count */ } if (ids_name) { ds_order(pkg); /* order requests */ } if (signal_received > 0) { return (1); } if (options & PT_ODTSTREAM) { char line[128]; if (!dstdev.pathname && (n = _getvol(ods_name, NULL, DM_FORMAT, NULL, dstdev.norewind))) { cleanup(); if (n == 3) return (3); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_GETVOL)); return (1); } if ((hdr = genheader(src, pkg)) == NULL) { cleanup(); return (1); } /* write out header to stream */ if (wdsheader(hdr, ods_name, pkg)) { cleanup(); return (1); } ds_volno = 1; /* number of volumes in datastream */ pinput = hdrbuf.text_buffer; /* skip past first line in header */ (void) mgets(line, 128); } if (signal_received > 0) { return (1); } errflg = 0; for (i = 0; pkg[i]; i++) { if (signal_received > 0) { return (1); } if (!(options & PT_ODTSTREAM) && dstdev.mount) { if (n = pkgmount(&dstdev, NULL, 0, 0, 1)) { cleanup(); return (n); } } if (errflg = pkgxfer(pkg[i], options)) { pkg[i] = NULL; if ((options & PT_ODTSTREAM) || (errflg != 2)) break; } else if (strcmp(dstinst, pkg[i])) pkg[i] = strdup(dstinst); } if (!(options & PT_ODTSTREAM) && dst) { pkgdir = strdup(dst); } /* * No cleanup of temporary directories created in this * function is done here. The calling function must do * the cleanup. */ return (signal_received > 0 ? 1 : errflg); } int pkgtrans(char *device1, char *device2, char **pkg, int options) { int r; struct sigaction nact; struct sigaction oact; /* * setup signal handlers for SIGINT and SIGHUP and release hold */ /* hold SIGINT/SIGHUP interrupts */ (void) sighold(SIGHUP); (void) sighold(SIGINT); /* hook SIGINT to sigtrap */ nact.sa_handler = sigtrap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGINT, &nact, &oact) < 0) { sigintHandler = SIG_DFL; } else { sigintHandler = oact.sa_handler; } /* hook SIGHUP to sigtrap */ nact.sa_handler = sigtrap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGHUP, &nact, &oact) < 0) { sighupHandler = SIG_DFL; } else { sighupHandler = oact.sa_handler; } /* reset signal received count */ signal_received = 0; /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* * perform the package translation */ r = _pkgtrans(device1, device2, pkg, options); /* * reset signal handlers */ /* hold SIGINT/SIGHUP interrupts */ (void) sighold(SIGHUP); (void) sighold(SIGINT); /* reset SIGINT */ nact.sa_handler = sigintHandler; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL); /* reset SIGHUP */ nact.sa_handler = sighupHandler; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL); /* if signal received and pkgtrans returned error, call cleanup */ if (signal_received > 0) { if (r != 0) { cleanup(); } (void) kill(getpid(), SIGINT); } /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (r); } /* * This function concatenates append to the text described in the buf_ctrl * structure. This code modifies data in this structure and handles all * allocation issues. It returns '0' if everything was successful and '1' * if not. */ static int cat_and_count(struct dm_buf *buf_ctrl, char *append) { /* keep allocating until we have enough room to hold string */ while ((buf_ctrl->offset + (int)strlen(append)) >= buf_ctrl->allocation) { /* reallocate (and maybe move) text buffer */ if ((buf_ctrl->text_buffer = (char *)realloc(buf_ctrl->text_buffer, buf_ctrl->allocation + BLK_SIZE)) == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); free(buf_ctrl->text_buffer); return (1); } /* clear the new memory */ (void) memset(buf_ctrl->text_buffer + buf_ctrl->allocation, '\0', BLK_SIZE); /* adjust total allocation */ buf_ctrl->allocation += BLK_SIZE; } /* append new string to end of buffer */ while (*append) { *(buf_ctrl->text_buffer + buf_ctrl->offset) = *append++; (buf_ctrl->offset)++; } return (0); } static struct dm_buf * genheader(char *src, char **pkg) { FILE *fp; char path[MAXPATHLEN], tmp_entry[ENTRY_MAX]; int i, n, nparts, maxpsize; int partcnt; long totsize; struct stat statbuf; if ((hdrbuf.text_buffer = (char *)malloc(BLK_SIZE)) == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); return (NULL); } /* clear the new memory */ (void) memset(hdrbuf.text_buffer, '\0', BLK_SIZE); /* set up the buffer control structure for the header */ hdrbuf.offset = 0; hdrbuf.allocation = BLK_SIZE; (void) cat_and_count(&hdrbuf, HDR_PREFIX); (void) cat_and_count(&hdrbuf, "\n"); nparts = maxpsize = 0; totsize = 0; for (i = 0; pkg[i]; i++) { (void) snprintf(path, MAXPATHLEN, "%s/%s/%s", src, pkg[i], PKGINFO); if (stat(path, &statbuf) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_BADPKGINFO)); ecleanup(); return (NULL); } totsize += statbuf.st_size/BLK_SIZE + 1; } /* * totsize contains number of blocks used by the pkginfo files */ totsize += i/4 + 1; if (dstdev.capacity && totsize > dstdev.capacity) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOSPACE), totsize, dstdev.capacity); ecleanup(); return (NULL); } ds_volcnt = 1; for (i = 0; pkg[i]; i++) { partcnt = 0; (void) snprintf(path, MAXPATHLEN, "%s/%s/%s", src, pkg[i], PKGMAP); if ((fp = fopen(path, "r")) == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOPKGMAP), pkg[i]); ecleanup(); return (NULL); } /* Evaluate the first entry in pkgmap */ n = rd_map_size(fp, &nparts, &maxpsize, &compressedsize); if (n == 3) /* It's a compressed package */ /* The header needs the *real* size */ maxpsize = compressedsize; else if (n == 0) /* pkgmap is corrupt */ return (NULL); if (dstdev.capacity && maxpsize > dstdev.capacity) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOSPACE), (long)maxpsize, dstdev.capacity); (void) fclose(fp); ecleanup(); return (NULL); } /* add pkg name, number of parts and the max part size */ if (snprintf(tmp_entry, ENTRY_MAX, "%s %d %d", pkg[i], nparts, maxpsize) >= ENTRY_MAX) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(ERR_MEM)); (void) fclose(fp); ecleanup(); return (NULL); } if (cat_and_count(&hdrbuf, tmp_entry)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); (void) fclose(fp); ecleanup(); return (NULL); } totsize += nparts * maxpsize; if (dstdev.capacity && dstdev.capacity < totsize) { int lastpartcnt = 0; if (totsize) totsize -= nparts * maxpsize; while (partcnt < nparts) { while (totsize <= dstdev.capacity && partcnt <= nparts) { totsize += maxpsize; partcnt++; } /* partcnt == 0 means skip to next volume */ if (partcnt) partcnt--; (void) snprintf(tmp_entry, ENTRY_MAX, " %d", partcnt - lastpartcnt); if (cat_and_count(&hdrbuf, tmp_entry)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); (void) fclose(fp); ecleanup(); return (NULL); } ds_volcnt++; totsize = 0; lastpartcnt = partcnt; } /* first parts/volume number does not count */ ds_volcnt--; } if (cat_and_count(&hdrbuf, "\n")) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); (void) fclose(fp); ecleanup(); return (NULL); } (void) fclose(fp); } if (cat_and_count(&hdrbuf, HDR_SUFFIX) || cat_and_count(&hdrbuf, "\n")) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MEM)); (void) fclose(fp); ecleanup(); return (NULL); } return (&hdrbuf); } static int wdsheader(struct dm_buf *hdr, char *device, char **pkg) { char tmp_entry[ENTRY_MAX], tmp_file[L_tmpnam+1]; int i, n; int list_fd; int block_cnt; (void) ds_close(0); if (dstdev.pathname) ds_fd = creat(device, 0644); else ds_fd = open(device, 1); if (ds_fd < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), device, errno); return (1); } if (ds_ginit(device) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), device, errno); (void) ds_close(0); return (1); } /* * The loop below assures compatibility with tapes that don't * have a block size (e.g.: Exabyte) by forcing EOR at the end * of each 512 bytes. */ for (block_cnt = 0; block_cnt < hdr->allocation; block_cnt += BLK_SIZE) { (void) write(ds_fd, (hdr->text_buffer + block_cnt), BLK_SIZE); } /* * write the first cpio() archive to the datastream * which should contain the pkginfo & pkgmap files * for all packages */ (void) tmpnam(tmp_file); /* temporary file name */ if ((list_fd = open(tmp_file, O_RDWR | O_CREAT, 0644)) == -1) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOTMPFIL), tmp_file); return (1); } /* * Create a cpio-compatible list of the requisite files in * the temporary file. */ for (i = 0; pkg[i]; i++) { register ssize_t entry_size; /* * Copy pkginfo and pkgmap filenames into the * temporary string allowing for the first line * as a special case. */ entry_size = sprintf(tmp_entry, (i == 0) ? "%s/%s\n%s/%s" : "\n%s/%s\n%s/%s", pkg[i], PKGINFO, pkg[i], PKGMAP); if (write(list_fd, tmp_entry, entry_size) != entry_size) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOTMPFIL), tmp_file); (void) close(list_fd); ecleanup(); return (1); } } (void) lseek(list_fd, 0, SEEK_SET); (void) snprintf(tmp_entry, sizeof (tmp_entry), "%s -ocD -C %d", CPIOPROC, (int)BLK_SIZE); if (n = esystem(tmp_entry, list_fd, ds_fd)) { rpterr(); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CMDFAIL), tmp_entry, n); (void) close(list_fd); (void) unlink(tmp_file); cleanup(); return (1); } (void) close(list_fd); (void) unlink(tmp_file); return (0); } static int ckoverwrite(char *dir, char *inst, int options) { char path[PATH_MAX]; (void) snprintf(path, sizeof (path), "%s/%s", dir, inst); if (access(path, 0) == 0) { if (options & PT_OVERWRITE) return (rrmdir(path)); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_EXISTS), path); return (1); } return (0); } static int pkgxfer(char *srcinst, int options) { int r; struct pkginfo info; FILE *fp, *pp; char *pt, *src, *dst; char dstdir[PATH_MAX], temp[PATH_MAX], srcdir[PATH_MAX]; char cmd[CMDSIZE], pkgname[NON_ABI_NAMELNGTH]; int i, n, part, nparts, maxpartsize, curpartcnt, iscomp; char volnos[128], tmpvol[128]; struct statvfs64 svfsb; longlong_t free_blocks; struct stat srcstat; info.pkginst = NULL; /* required initialization */ /* * when this routine is entered, the first part of * the package to transfer is already available in * the directory indicated by 'src' --- unless the * source device is a datstream, in which case only * the pkginfo and pkgmap files are available in 'src' */ src = srcdev.dirname; dst = dstdev.dirname; if (!(options & PT_SILENT)) (void) fprintf(stderr, pkg_gt(MSG_TRANSFER), srcinst); (void) strlcpy(dstinst, srcinst, sizeof (dstinst)); if (!(options & PT_ODTSTREAM)) { /* destination is a (possibly mounted) directory */ (void) snprintf(dstdir, sizeof (dstdir), "%s/%s", dst, dstinst); /* * need to check destination directory to assure * that we will not be duplicating a package which * already resides there (though we are allowed to * overwrite the same version) */ pkgdir = src; if (fpkginfo(&info, srcinst)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOEXISTS), srcinst); (void) fpkginfo(&info, NULL); return (1); } pkgdir = dst; (void) strlcpy(temp, srcinst, sizeof (temp)); if (pt = strchr(temp, '.')) *pt = '\0'; (void) strlcat(temp, ".*", sizeof (temp)); if (pt = fpkginst(temp, info.arch, info.version)) { /* * the same instance already exists, although * its pkgid might be different */ if (options & PT_OVERWRITE) { (void) strlcpy(dstinst, pt, sizeof (dstinst)); (void) snprintf(dstdir, sizeof (dstdir), "%s/%s", dst, dstinst); } else { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_DUPVERS), srcinst); (void) fpkginfo(&info, NULL); (void) fpkginst(NULL); return (2); } } else if (options & PT_RENAME) { /* * find next available instance by appending numbers * to the package abbreviation until the instance * does not exist in the destination directory */ if (pt = strchr(temp, '.')) *pt = '\0'; for (i = 2; (access(dstdir, 0) == 0); i++) { (void) snprintf(dstinst, sizeof (dstinst), "%s.%d", temp, i); (void) snprintf(dstdir, sizeof (dstdir), "%s/%s", dst, dstinst); } } else if (options & PT_OVERWRITE) { /* * we're allowed to overwrite, but there seems * to be no valid package to overwrite, and we are * not allowed to rename the destination, so act * as if we weren't given permission to overwrite * --- this keeps us from removing a destination * instance which is named the same as the source * instance, but really reflects a different pkg! */ options &= (~PT_OVERWRITE); } (void) fpkginfo(&info, NULL); (void) fpkginst(NULL); if (ckoverwrite(dst, dstinst, options)) return (2); if (isdir(dstdir) && mkdir(dstdir, 0755)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MKDIR), dstdir); return (1); } (void) snprintf(srcdir, sizeof (srcdir), "%s/%s", src, srcinst); if (stat(srcdir, &srcstat) != -1) { if (chmod(dstdir, (srcstat.st_mode & S_IAMB)) == -1) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHMODDIR), dstdir); return (1); } } else { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_STATDIR), srcdir); return (1); } } if (!(options & PT_SILENT) && strcmp(dstinst, srcinst)) (void) fprintf(stderr, pkg_gt(MSG_RENAME), dstinst); (void) snprintf(srcdir, sizeof (srcdir), "%s/%s", src, srcinst); if (chdir(srcdir)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHDIR), srcdir); return (1); } if (ids_name) { /* unpack the datatstream into a directory */ /* * transfer pkginfo & pkgmap first */ (void) snprintf(cmd, sizeof (cmd), "%s -pudm %s", CPIOPROC, dstdir); if ((pp = epopen(cmd, "w")) == NULL) { rpterr(); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_POPEN), cmd, errno); return (1); } (void) fprintf(pp, "%s\n%s\n", PKGINFO, PKGMAP); (void) sighold(SIGINT); (void) sighold(SIGHUP); r = epclose(pp); (void) sigrelse(SIGINT); (void) sigrelse(SIGHUP); if (r != 0) { rpterr(); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_PCLOSE), cmd, errno); return (1); } if (options & PT_INFO_ONLY) return (0); /* don't transfer objects */ if (chdir(dstdir)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHDIR), dstdir); return (1); } /* * for each part of the package, use cpio() to * unpack the archive into the destination directory */ nparts = ds_findpkg(srcdev.cdevice, srcinst); if (nparts < 0) { progerr(pkg_gt(ERR_TRANSFER)); return (1); } for (part = 1; part <= nparts; /* void */) { if (ds_getpkg(srcdev.cdevice, part, dstdir)) { progerr(pkg_gt(ERR_TRANSFER)); return (1); } part++; if (dstdev.mount) { (void) chdir("/"); if (pkgumount(&dstdev)) return (1); if (part <= nparts) { if (n = pkgmount(&dstdev, NULL, part+1, nparts, 1)) return (n); if (ckoverwrite(dst, dstinst, options)) return (1); if (isdir(dstdir) && mkdir(dstdir, 0755)) { progerr( pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MKDIR), dstdir); return (1); } /* * since volume is removable, each part * must contain a duplicate of the * pkginfo file to properly identify the * volume */ if (chdir(srcdir)) { progerr( pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHDIR), srcdir); return (1); } if ((pp = epopen(cmd, "w")) == NULL) { rpterr(); progerr( pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_POPEN), cmd, errno); return (1); } (void) fprintf(pp, "pkginfo"); (void) sighold(SIGINT); (void) sighold(SIGHUP); r = epclose(pp); (void) sigrelse(SIGINT); (void) sigrelse(SIGHUP); if (r != 0) { rpterr(); progerr( pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_PCLOSE), cmd, errno); return (1); } if (chdir(dstdir)) { progerr( pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CHDIR), dstdir); return (1); } } } } return (0); } if ((fp = fopen(PKGMAP, "r")) == NULL) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOPKGMAP), srcinst); return (1); } nparts = 1; if (!rd_map_size(fp, &nparts, &maxpartsize, &compressedsize)) return (1); else (void) fclose(fp); if (srcdev.mount) { if (ckvolseq(srcdir, 1, nparts)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_SEQUENCE)); return (1); } } /* write each part of this package */ if (options & PT_ODTSTREAM) { char line[128]; (void) mgets(line, 128); curpartcnt = -1; /* LINTED E_SEC_SCANF_UNBOUNDED_COPY */ if (sscanf(line, "%s %d %d %[ 0-9]", pkgname, &nparts, &maxpartsize, volnos) == 4) { (void) sscanf(volnos, "%d %[ 0-9]", &curpartcnt, tmpvol); (void) strlcpy(volnos, tmpvol, sizeof (volnos)); } } for (part = 1; part <= nparts; /* void */) { if (curpartcnt == 0 && (options & PT_ODTSTREAM)) { char prompt[128]; int index; ds_volno++; (void) ds_close(0); (void) sprintf(prompt, pkg_gt("Insert %%v %d of %d into %%p"), ds_volno, ds_volcnt); if (n = getvol(ods_name, NULL, DM_FORMAT, prompt)) return (n); if ((ds_fd = open(dstdev.cdevice, O_WRONLY)) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), dstdev.cdevice, errno); return (1); } if (ds_ginit(dstdev.cdevice) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), dstdev.cdevice, errno); (void) ds_close(0); return (1); } (void) sscanf(volnos, "%d %[ 0-9]", &index, tmpvol); (void) strlcpy(volnos, tmpvol, sizeof (volnos)); curpartcnt += index; } if (options & PT_INFO_ONLY) nparts = 0; if (part == 1) { (void) snprintf(cmd, sizeof (cmd), "find %s %s", PKGINFO, PKGMAP); if (nparts && (isdir(INSTALL) == 0)) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, INSTALL, sizeof (cmd)); } } else (void) snprintf(cmd, sizeof (cmd), "find %s", PKGINFO); if (nparts > 1) { (void) snprintf(temp, sizeof (temp), "%s.%d", RELOC, part); if (iscpio(temp, &iscomp) || isdir(temp) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, temp, sizeof (cmd)); } (void) snprintf(temp, sizeof (temp), "%s.%d", ROOT, part); if (iscpio(temp, &iscomp) || isdir(temp) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, temp, sizeof (cmd)); } (void) snprintf(temp, sizeof (temp), "%s.%d", ARCHIVE, part); if (isdir(temp) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, temp, sizeof (cmd)); } } else if (nparts) { for (i = 0; reloc_names[i] != NULL; i++) { if (iscpio(reloc_names[i], &iscomp) || isdir(reloc_names[i]) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, reloc_names[i], sizeof (cmd)); } } for (i = 0; root_names[i] != NULL; i++) { if (iscpio(root_names[i], &iscomp) || isdir(root_names[i]) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, root_names[i], sizeof (cmd)); } } if (isdir(ARCHIVE) == 0) { (void) strlcat(cmd, " ", sizeof (cmd)); (void) strlcat(cmd, ARCHIVE, sizeof (cmd)); } } if (options & PT_ODTSTREAM) { (void) snprintf(cmd + strlen(cmd), sizeof (cmd) - strlen(cmd), " -print | %s -ocD -C %d", CPIOPROC, (int)BLK_SIZE); } else { if (statvfs64(dstdir, &svfsb) == -1) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_STATVFS), dstdir, errno); return (1); } free_blocks = (((long)svfsb.f_frsize > 0) ? howmany(svfsb.f_frsize, DEV_BSIZE) : howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail; if ((has_comp_size ? compressedsize : maxpartsize) > free_blocks) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_NOSPACE), has_comp_size ? (long)compressedsize : (long)maxpartsize, free_blocks); return (1); } (void) snprintf(cmd + strlen(cmd), sizeof (cmd) - strlen(cmd), " -print | %s -pdum %s", CPIOPROC, dstdir); } n = esystem(cmd, -1, (options & PT_ODTSTREAM) ? ds_fd : -1); if (n) { rpterr(); progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CMDFAIL), cmd, n); return (1); } part++; if (srcdev.mount && (nparts > 1)) { /* unmount current source volume */ (void) chdir("/"); if (pkgumount(&srcdev)) return (1); /* loop until volume is mounted successfully */ while (part <= nparts) { /* read only */ n = pkgmount(&srcdev, NULL, part, nparts, 1); if (n) return (n); if (chdir(srcdir)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_CORRUPT)); (void) chdir("/"); (void) pkgumount(&srcdev); continue; } if (ckvolseq(srcdir, part, nparts)) { (void) chdir("/"); (void) pkgumount(&srcdev); continue; } break; } } if (!(options & PT_ODTSTREAM) && dstdev.mount) { /* unmount current volume */ if (pkgumount(&dstdev)) return (1); /* loop until next volume is mounted successfully */ while (part <= nparts) { /* writable */ n = pkgmount(&dstdev, NULL, part, nparts, 1); if (n) return (n); if (ckoverwrite(dst, dstinst, options)) continue; if (isdir(dstdir) && mkdir(dstdir, 0755)) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_MKDIR), dstdir); continue; } break; } } if ((options & PT_ODTSTREAM) && part <= nparts) { if (curpartcnt >= 0 && part > curpartcnt) { char prompt[128]; int index; ds_volno++; if (ds_close(0)) return (1); (void) sprintf(prompt, pkg_gt("Insert %%v %d of %d into %%p"), ds_volno, ds_volcnt); if (n = getvol(ods_name, NULL, DM_FORMAT, prompt)) return (n); if ((ds_fd = open(dstdev.cdevice, 1)) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), dstdev.cdevice, errno); return (1); } if (ds_ginit(dstdev.cdevice) < 0) { progerr(pkg_gt(ERR_TRANSFER)); logerr(pkg_gt(MSG_OPEN), dstdev.cdevice, errno); (void) ds_close(0); return (1); } (void) sscanf(volnos, "%d %[ 0-9]", &index, tmpvol); (void) strlcpy(volnos, tmpvol, sizeof (volnos)); curpartcnt += index; } } } return (0); } static void sigtrap(int signo) { _NOTE(ARGUNUSED(signo)); signal_received++; } static void cleanup(void) { (void) chdir("/"); if (tmpdir) { (void) rrmdir(tmpdir); free(tmpdir); tmpdir = NULL; } if (tmppath) { /* remove any previous tmppath stuff */ (void) rrmdir(tmppath); free(tmppath); tmppath = NULL; } if (tmpsymdir) { /* remove temp symbolic links made for signed pkg */ (void) rrmdir(tmpsymdir); free(tmpsymdir); tmpsymdir = NULL; } if (srcdev.mount && !ids_name) (void) pkgumount(&srcdev); if (dstdev.mount && !ods_name) (void) pkgumount(&dstdev); (void) ds_close(1); }