diff options
author | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
---|---|---|
committer | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
commit | 5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch) | |
tree | 0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/lib/libpkg/common/dstream.c | |
parent | 2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff) | |
download | illumos-joyent-5c51f1241dbbdf2656d0e10011981411ed0c9673.tar.gz |
6739234 move SVR4 packaging to ONNV gate
Diffstat (limited to 'usr/src/lib/libpkg/common/dstream.c')
-rw-r--r-- | usr/src/lib/libpkg/common/dstream.c | 1036 |
1 files changed, 1036 insertions, 0 deletions
diff --git a/usr/src/lib/libpkg/common/dstream.c b/usr/src/lib/libpkg/common/dstream.c new file mode 100644 index 0000000000..68ff540393 --- /dev/null +++ b/usr/src/lib/libpkg/common/dstream.c @@ -0,0 +1,1036 @@ +/* + * 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 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 <stdio.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysmacros.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <fcntl.h> +#ifdef u3b2 +#include <sys/sys3b.h> +#endif /* u3b2 */ +#include <openssl/err.h> +#include "pkglib.h" +#include "pkglibmsgs.h" +#include "pkglocale.h" +#ifdef u3b2 +static +struct stat orig_st_buf; /* Stat structure of original file (3B2/CTC) */ +static char ds_ctcflg; +#endif /* u3b2 */ + +/* libadm.a */ +extern char *devattr(char *device, char *attribute); +extern int pkgnmchk(register char *pkg, register char *spec, + int presvr4flg); +extern int getvol(char *device, char *label, int options, char *prompt); + +#define CMDSIZ 512 +#define LSIZE 128 +#define DDPROC "/usr/bin/dd" +#define CPIOPROC "/usr/bin/cpio" + +/* device types */ + +#define G_TM_TAPE 1 /* Tapemaster controller */ +#define G_XY_DISK 3 /* xy disks */ +#define G_SD_DISK 7 /* scsi sd disk */ +#define G_XT_TAPE 8 /* xt tapes */ +#define G_SF_FLOPPY 9 /* sf floppy */ +#define G_XD_DISK 10 /* xd disks */ +#define G_ST_TAPE 11 /* scsi tape */ +#define G_NS 12 /* noswap pseudo-dev */ +#define G_RAM 13 /* ram pseudo-dev */ +#define G_FT 14 /* tftp */ +#define G_HD 15 /* 386 network disk */ +#define G_FD 16 /* 386 AT disk */ +#define G_FILE 28 /* file, not a device */ +#define G_NO_DEV 29 /* device does not require special treatment */ +#define G_DEV_MAX 30 /* last valid device type */ + +struct dstoc { + int cnt; + char pkg[NON_ABI_NAMELNGTH]; + int nparts; + long maxsiz; + char volnos[128]; + struct dstoc *next; +} *ds_head, *ds_toc; + +#define ds_nparts ds_toc->nparts +#define ds_maxsiz ds_toc->maxsiz + +int ds_totread; /* total number of parts read */ +int ds_fd = -1; +int ds_curpartcnt = -1; + +int ds_next(char *device, char *instdir); +int ds_ginit(char *device); +int ds_close(int pkgendflg); + +static FILE *ds_pp; +static int ds_realfd = -1; /* file descriptor for real device */ +static int ds_read; /* number of parts read for current package */ +static int ds_volno; /* volume number of current volume */ +static int ds_volcnt; /* total number of volumes */ +static char ds_volnos[128]; /* parts/volume info */ +static char *ds_device; +static int ds_volpart; /* number of parts read in current volume, */ + /* including skipped parts */ +static int ds_bufsize; +static int ds_skippart; /* number of parts skipped in current volume */ + +static int ds_getnextvol(char *device); +static int ds_skip(char *device, int nskip); + +void +ds_order(char *list[]) +{ + struct dstoc *toc_pt; + register int j, n; + char *pt; + + toc_pt = ds_head; + n = 0; + while (toc_pt) { + for (j = n; list[j]; j++) { + if (strcmp(list[j], toc_pt->pkg) == 0) { + /* just swap places in the array */ + pt = list[n]; + list[n++] = list[j]; + list[j] = pt; + } + } + toc_pt = toc_pt->next; + } +} + +static char *pds_header; +static char *ds_header; +static char *ds_header_raw; +static int ds_headsize; + +static char * +ds_gets(char *buf, int size) +{ + int length; + char *nextp; + + nextp = strchr(pds_header, '\n'); + if (nextp == NULL) { + length = strlen(pds_header); + if (length > size) + return (0); + if ((ds_header = (char *)realloc(ds_header, + ds_headsize + BLK_SIZE)) == NULL) + return (0); + if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE) + return (0); + ds_headsize += BLK_SIZE; + nextp = strchr(pds_header, '\n'); + if (nextp == NULL) + return (0); + *nextp = '\0'; + if (length + (int)strlen(pds_header) > size) + return (0); + (void) strncpy(buf + length, pds_header, strlen(pds_header)); + buf[length + strlen(pds_header)] = '\0'; + pds_header = nextp + 1; + return (buf); + } + *nextp = '\0'; + if ((int)strlen(pds_header) > size) + return (0); + (void) strncpy(buf, pds_header, strlen(pds_header)); + buf[strlen(pds_header)] = '\0'; + pds_header = nextp + 1; + return (buf); +} + +/* + * function to determine if media is datastream or mounted + * floppy + */ +int +ds_readbuf(char *device) +{ + char buf[BLK_SIZE]; + + if (ds_fd >= 0) + (void) close(ds_fd); + if ((ds_fd = open(device, O_RDONLY)) >= 0 && + read(ds_fd, buf, BLK_SIZE) == BLK_SIZE && + strncmp(buf, HDR_PREFIX, 20) == 0) { + if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_MEM)); + (void) ds_close(0); + return (0); + } + memcpy(ds_header, buf, BLK_SIZE); + ds_headsize = BLK_SIZE; + + if (ds_ginit(device) < 0) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_OPEN), device, errno); + (void) ds_close(0); + return (0); + } + return (1); + } else if (ds_fd >= 0) { + (void) close(ds_fd); + ds_fd = -1; + } + return (0); +} + +/* + * Determine how many additional volumes are needed for current package. + * Note: a 0 will occur as first volume number when the package begins + * on the next volume. + */ +static int +ds_volsum(struct dstoc *toc) +{ + int curpartcnt, volcnt; + char volnos[128], tmpvol[128]; + if (toc->volnos[0]) { + int index, sum; + sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos); + volcnt = 0; + sum = curpartcnt; + while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]", + &index, tmpvol) >= 1) { + (void) strcpy(volnos, tmpvol); + volcnt++; + sum += index; + } + /* side effect - set number of parts read on current volume */ + ds_volpart = index; + return (volcnt); + } + ds_volpart += toc->nparts; + return (0); +} + +/* initialize ds_curpartcnt and ds_volnos */ +static void +ds_pkginit(void) +{ + if (ds_toc->volnos[0]) + sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt, ds_volnos); + else + ds_curpartcnt = -1; +} + +/* + * functions to pass current package info to exec'ed program + */ +void +ds_putinfo(char *buf) +{ + (void) sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %s", + ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart, + ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz, + ds_toc->volnos); +} + +int +ds_getinfo(char *string) +{ + ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc)); + (void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]", + &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread, + &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts, + &ds_toc->maxsiz, ds_toc->volnos); + ds_pkginit(); + return (ds_toc->nparts); +} + +/* + * Return true if the file descriptor (ds_fd) is open on the package stream. + */ +boolean_t +ds_fd_open(void) +{ + return (ds_fd >= 0 ? B_TRUE : B_FALSE); +} + +/* + * Read the source device. Acquire the header data and check it for validity. + */ +int +ds_init(char *device, char **pkg, char *norewind) +{ + struct dstoc *tail, *toc_pt; + char *ret; + char cmd[CMDSIZ]; + char line[LSIZE+1]; + int i, n, count = 0, header_size = BLK_SIZE; + + if (!ds_header) { /* If the header hasn't been read yet */ + if (ds_fd >= 0) + (void) ds_close(0); + + /* always start with rewind device */ + if ((ds_fd = open(device, O_RDONLY)) < 0) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_OPEN), device, errno); + return (-1); + } + + /* allocate room for the header equivalent to a block */ + if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_MEM)); + return (-1); + } + + /* initialize the device */ + if (ds_ginit(device) < 0) { + (void) ds_close(0); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_OPEN), device, errno); + return (-1); + } + + /* read a logical block from the source device */ + if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + (void) ds_close(0); + return (-1); + } + + /* + * This loop scans the medium for the start of the header. + * If the above read worked, we skip this. If it did't, this + * loop will retry the read ten times looking for the header + * marker string. + */ + while (strncmp(ds_header, HDR_PREFIX, 20) != 0) { + /* only ten tries iff the device rewinds */ + if (!norewind || count++ > 10) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + (void) ds_close(0); + return (-1); + } + + /* read through to the last block */ + if (count > 1) + while (read(ds_fd, ds_header, BLK_SIZE) > 0) + ; + + /* then close the device */ + (void) ds_close(0); + + /* and reopen it */ + if ((ds_fd = open(norewind, O_RDONLY)) < 0) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_OPEN), device, errno); + (void) free(ds_header); + return (-1); + } + + /* initialize the device */ + if (ds_ginit(device) < 0) { + (void) ds_close(0); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_OPEN), device, errno); + return (-1); + } + + /* read the block again */ + if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + (void) ds_close(0); + return (-1); + } + } + + /* Now keep scanning until the whole header is in place. */ + while (strstr(ds_header, HDR_SUFFIX) == NULL) { + /* We need a bigger buffer */ + if ((ds_header = (char *)realloc(ds_header, + header_size + BLK_SIZE)) == NULL) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_MEM)); + (void) ds_close(0); + return (1); + } + + /* clear the new memory */ + (void) memset(ds_header + header_size, '\0', + BLK_SIZE); + + + /* read a logical block from the source device */ + if (read(ds_fd, ds_header + header_size, BLK_SIZE) != + BLK_SIZE) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + (void) ds_close(0); + return (-1); + } else + header_size += BLK_SIZE; /* new size */ + } + + /* + * remember rewind device for ds_close to rewind at + * close + */ + if (count >= 1) + ds_device = device; + ds_headsize = header_size; + + } + + pds_header = ds_header; + + /* save raw copy of header for later use in BIO_dump_header */ + if ((ds_header_raw = (char *)malloc(header_size)) == NULL) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_MEM)); + (void) ds_close(0); + return (1); + } + memcpy(ds_header_raw, ds_header, header_size); + + /* read datastream table of contents */ + ds_head = tail = (struct dstoc *)0; + ds_volcnt = 1; + + while (ret = ds_gets(line, LSIZE)) { + if (strcmp(line, HDR_SUFFIX) == 0) + break; + if (!line[0] || line[0] == '#') + continue; + toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc)); + if (!toc_pt) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_MEM)); + ecleanup(); + (void) free(ds_header); + return (-1); + } + if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg, + &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + free(toc_pt); + (void) free(ds_header); + ecleanup(); + return (-1); + } + if (tail) { + tail->next = toc_pt; + tail = toc_pt; + } else + ds_head = tail = toc_pt; + ds_volcnt += ds_volsum(toc_pt); + } + if (!ret) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_TOC)); + (void) free(ds_header); + return (-1); + } + sighold(SIGINT); + sigrelse(SIGINT); + if (!ds_head) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_EMPTY)); + (void) free(ds_header); + return (-1); + } + /* this could break, thanks to cpio command limit */ +#ifndef SUNOS41 + (void) sprintf(cmd, "%s -icdumD -C %d", CPIOPROC, (int)BLK_SIZE); +#else + (void) sprintf(cmd, "%s -icdum -C %d", CPIOPROC, (int)BLK_SIZE); +#endif + n = 0; + for (i = 0; pkg[i]; i++) { + if (strcmp(pkg[i], "all") == 0) + continue; + if (n == 0) { + strcat(cmd, " "); + n = 1; + } + strlcat(cmd, pkg[i], CMDSIZ); + strlcat(cmd, "'/*' ", CMDSIZ); + + /* extract signature too, if present. */ + strlcat(cmd, SIGNATURE_FILENAME, CMDSIZ); + strlcat(cmd, " ", CMDSIZ); + } + + /* + * if we are extracting all packages (pkgs == NULL), + * signature will automatically be extracted + */ + if (n = esystem(cmd, ds_fd, -1)) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_CMDFAIL), cmd, n); + (void) free(ds_header); + return (-1); + } + + ds_toc = ds_head; + ds_totread = 0; + ds_volno = 1; + return (0); +} + +int +ds_findpkg(char *device, char *pkg) +{ + char *pkglist[2]; + int nskip, ods_volpart; + + if (ds_head == NULL) { + pkglist[0] = pkg; + pkglist[1] = NULL; + if (ds_init(device, pkglist, NULL)) + return (-1); + } + + if (!pkg || pkgnmchk(pkg, "all", 0)) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_PKGNAME)); + return (-1); + } + + nskip = 0; + ds_volno = 1; + ds_volpart = 0; + ds_toc = ds_head; + while (ds_toc) { + if (strcmp(ds_toc->pkg, pkg) == 0) + break; + nskip += ds_toc->nparts; + ds_volno += ds_volsum(ds_toc); + ds_toc = ds_toc->next; + } + if (!ds_toc) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_NOPKG), pkg); + return (-1); + } + + ds_pkginit(); + ds_skippart = 0; + if (ds_curpartcnt > 0) { + ods_volpart = ds_volpart; + /* + * skip past archives belonging to last package on current + * volume + */ + if (ds_volpart > 0 && ds_getnextvol(device)) + return (-1); + ds_totread = nskip - ods_volpart; + if (ds_skip(device, ods_volpart)) + return (-1); + } else if (ds_curpartcnt < 0) { + if (ds_skip(device, nskip - ds_totread)) + return (-1); + } else + ds_totread = nskip; + ds_read = 0; + return (ds_nparts); +} + +/* + * Get datastream part + * Call for first part should be preceded by + * call to ds_findpkg + */ + +int +ds_getpkg(char *device, int n, char *dstdir) +{ + struct statvfs64 svfsb; + u_longlong_t free_blocks; + + if (ds_read >= ds_nparts) + return (2); + + if (ds_read == n) + return (0); + else if ((ds_read > n) || (n > ds_nparts)) + return (2); + + if (ds_maxsiz > 0) { + if (statvfs64(".", &svfsb)) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_STATFS), errno); + return (-1); + } +#ifdef SUNOS41 + free_blocks = svfsb.f_bfree * howmany(svfsb.f_bsize, DEV_BSIZE); +#else /* !SUNOS41 */ + free_blocks = (((long)svfsb.f_frsize > 0) ? + howmany(svfsb.f_frsize, DEV_BSIZE) : + howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree; +#endif /* SUNOS41 */ + if ((ds_maxsiz + 50) > free_blocks) { + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks); + return (-1); + } + } + return (ds_next(device, dstdir)); +} + +static int +ds_getnextvol(char *device) +{ + char prompt[128]; + int n; + + 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(device, NULL, NULL, prompt)) + return (n); + if ((ds_fd = open(device, O_RDONLY)) < 0) + return (-1); + if (ds_ginit(device) < 0) { + (void) ds_close(0); + return (-1); + } + ds_volpart = 0; + return (0); +} + +/* + * called by ds_findpkg to skip past archives for unwanted packages + * in current volume + */ +static int +ds_skip(char *device, int nskip) +{ + char cmd[CMDSIZ]; + int n, onskip = nskip; + + while (nskip--) { + /* skip this one */ +#ifndef SUNOS41 + (void) sprintf(cmd, "%s -ictD -C %d > /dev/null", +#else + (void) sprintf(cmd, "%s -ict -C %d > /dev/null", +#endif + CPIOPROC, (int)BLK_SIZE); + if (n = esystem(cmd, ds_fd, -1)) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_CMDFAIL), cmd, n); + nskip = onskip; + if (ds_volno == 1 || ds_volpart > 0) + return (n); + if (n = ds_getnextvol(device)) + return (n); + } + } + ds_totread += onskip; + ds_volpart = onskip; + ds_skippart = onskip; + return (0); +} + +/* skip to end of package if necessary */ +void +ds_skiptoend(char *device) +{ + if (ds_read < ds_nparts && ds_curpartcnt < 0) + (void) ds_skip(device, ds_nparts - ds_read); +} + +int +ds_next(char *device, char *instdir) +{ + char cmd[CMDSIZ], tmpvol[128]; + int nparts, n, index; + + /*CONSTCOND*/ + while (1) { + if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) { + ds_volno++; + if (n = ds_getnextvol(device)) + return (n); + (void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol); + (void) strcpy(ds_volnos, tmpvol); + ds_curpartcnt += index; + } +#ifndef SUNOS41 + (void) sprintf(cmd, "%s -icdumD -C %d", +#else + (void) sprintf(cmd, "%s -icdum -C %d", +#endif + CPIOPROC, (int)BLK_SIZE); + if (n = esystem(cmd, ds_fd, -1)) { + rpterr(); + progerr(pkg_gt(ERR_UNPACK)); + logerr(pkg_gt(MSG_CMDFAIL), cmd, n); + } + if (ds_read == 0) + nparts = 0; + else + nparts = ds_toc->nparts; + if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) { + if (ds_volno == 1 || ds_volpart > ds_skippart) + return (-1); + + if (n = ds_getnextvol(device)) + return (n); + continue; + } + ds_read++; + ds_totread++; + ds_volpart++; + + return (0); + } + /*NOTREACHED*/ +} + +/* + * Name: BIO_ds_dump + * Description: Dumps all data from the static 'ds_fd' file handle into + * the supplied BIO. + * + * Arguments: err - where to record any errors. + * device - Description of device being dumped into, + * for error reporting + * bio - BIO object to dump data into + * + * Returns : zero - successfully dumped all data to EOF + * non-zero - some failure occurred. + */ +int +BIO_ds_dump(PKG_ERR *err, char *device, BIO *bio) +{ + int amtread; + char readbuf[BLK_SIZE]; + + /* + * note this will read to the end of the device, so it won't + * work for character devices since we don't know when the + * end of the CPIO archive is + */ + while ((amtread = read(ds_fd, readbuf, BLK_SIZE)) != 0) { + if (BIO_write(bio, readbuf, amtread) != amtread) { + pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, device, + ERR_error_string(ERR_get_error(), NULL)); + return (1); + } + } + + return (0); + /*NOTREACHED*/ +} + + +/* + * Name: BIO_ds_dump_header + * Description: Dumps all ds_headsize bytes from the + * static 'ds_header_raw' character array + * to the supplied BIO. + * + * Arguments: err - where to record any errors. + * bio - BIO object to dump data into + * + * Returns : zero - successfully dumped all raw + * header characters + * non-zero - some failure occurred. + */ +int +BIO_ds_dump_header(PKG_ERR *err, BIO *bio) +{ + + char zeros[BLK_SIZE]; + + memset(zeros, 0, BLK_SIZE); + + if (BIO_write(bio, ds_header_raw, ds_headsize) != ds_headsize) { + pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, "bio", + ERR_error_string(ERR_get_error(), NULL)); + return (1); + } + + return (0); +} + +/* + * ds_ginit: Determine the device being accessed, set the buffer size, + * and perform any device specific initialization. For the 3B2, + * a device with major number of 17 (0x11) is an internal hard disk, + * unless the minor number is 128 (0x80) in which case it is an internal + * floppy disk. Otherwise, get the system configuration + * table and check it by comparing slot numbers to major numbers. + * For the special case of the 3B2 CTC several unusual things must be done. + * To enable + * streaming mode on the CTC, the file descriptor must be closed, re-opened + * (with O_RDWR and O_CTSPECIAL flags set), the STREAMON ioctl(2) command + * issued, and the file descriptor re-re-opened either read-only or write_only. + */ + +int +ds_ginit(char *device) +{ +#ifdef u3b2 + major_t maj; + minor_t min; + int nflag, i, count, size; + struct s3bconf *buffer; + struct s3bc *table; + struct stat st_buf; + int devtype; + char buf[BLK_SIZE]; + int fd2, fd; +#endif /* u3b2 */ + int oflag; + char *pbufsize, cmd[CMDSIZ]; + int fd2, fd; + + if ((pbufsize = devattr(device, "bufsize")) != NULL) { + ds_bufsize = atoi(pbufsize); + (void) free(pbufsize); + } else + ds_bufsize = BLK_SIZE; + oflag = fcntl(ds_fd, F_GETFL, 0); +#ifdef u3b2 + devtype = G_NO_DEV; + if (fstat(ds_fd, &st_buf) == -1) + return (-1); + if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode)) + goto lab; + + /* + * We'll have to add a remote attribute to stat but this should + * work for now. + */ + else if (st_buf.st_dev & 0x8000) /* if remote rdev */ + goto lab; + + maj = major(st_buf.st_rdev); + min = minor(st_buf.st_rdev); + if (maj == 0x11) { /* internal hard or floppy disk */ + if (min & 0x80) + devtype = G_3B2_FD; /* internal floppy disk */ + else + devtype = G_3B2_HD; /* internal hard disk */ + } else { + if (sys3b(S3BCONF, (struct s3bconf *)&count, sizeof (count)) == + -1) + return (-1); + size = sizeof (int) + (count * sizeof (struct s3bconf)); + buffer = (struct s3bconf *)malloc((unsigned)size); + if (sys3b(S3BCONF, buffer, size) == -1) + return (-1); + table = (struct s3bc *)((char *)buffer + sizeof (int)); + for (i = 0; i < count; i++) { + if (maj == (int)table->board) { + if (strncmp(table->name, "CTC", 3) == 0) { + devtype = G_3B2_CTC; + break; + } else if (strncmp(table->name, "TAPE", 4) + == 0) { + devtype = G_TAPE; + break; + } + /* other possible devices can go here */ + } + table++; + } + } + switch (devtype) { + case G_3B2_CTC: /* do special CTC initialization */ + ds_bufsize = pbufsize ? ds_bufsize : 15872; + if (fstat(ds_fd, &orig_st_buf) < 0) { + ds_bufsize = -1; + break; + } + nflag = (O_RDWR | O_CTSPECIAL); + (void) close(ds_fd); + if ((ds_fd = open(device, nflag, 0666)) != -1) { + if (ioctl(ds_fd, STREAMON) != -1) { + (void) close(ds_fd); + nflag = (oflag == O_WRONLY) ? + O_WRONLY : O_RDONLY; + if ((ds_fd = + open(device, nflag, 0666)) == -1) { + rpterr(); + progerr( + pkg_gt(ERR_TRANSFER)); + logerr(pkg_gt(MSG_OPEN), + device, errno); + return (-1); + } + ds_bufsize = 15872; + } + } else + ds_bufsize = -1; + if (oflag == O_RDONLY && ds_header && ds_totread == 0) + /* Have already read in first block of header */ + read(ds_fd, buf, BLK_SIZE); + ds_ctcflg = 1; + + break; + case G_NO_DEV: + case G_3B2_HD: + case G_3B2_FD: + case G_TAPE: + case G_SCSI_HD: /* not developed yet */ + case G_SCSI_FD: + case G_SCSI_9T: + case G_SCSI_Q24: + case G_SCSI_Q120: + case G_386_HD: + case G_386_FD: + case G_386_Q24: + ds_bufsize = pbufsize ? ds_bufsize : BLK_SIZE; + break; + default: + ds_bufsize = -1; + errno = ENODEV; + } /* devtype */ +lab: +#endif /* u3b2 */ + if (ds_bufsize > BLK_SIZE) { + if (oflag & O_WRONLY) + fd = 1; + else + fd = 0; + fd2 = fcntl(fd, F_DUPFD, fd); + (void) close(fd); + fcntl(ds_fd, F_DUPFD, fd); + if (fd) + sprintf(cmd, "%s obs=%d 2>/dev/null", DDPROC, + ds_bufsize); + else + sprintf(cmd, "%s ibs=%d 2>/dev/null", DDPROC, + ds_bufsize); + if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) { + progerr(pkg_gt(ERR_TRANSFER)); + logerr(pkg_gt(MSG_POPEN), cmd, errno); + return (-1); + } + (void) close(fd); + fcntl(fd2, F_DUPFD, fd); + (void) close(fd2); + ds_realfd = ds_fd; + ds_fd = fileno(ds_pp); + } + return (ds_bufsize); +} + +int +ds_close(int pkgendflg) +{ +#ifdef u3b2 + int cnt, mode; + char *ptr; + struct stat statbuf; +#endif /* u3b2 */ + int n, ret = 0; + +#ifdef u3b2 + if (ds_pp && ds_ctcflg) { + ds_ctcflg = 0; + if ((mode = fcntl(ds_realfd, F_GETFL, 0)) < 0) { + ret = -1; + } else if (mode & O_WRONLY) { + /* + * pipe to dd write process, + * make sure one more buffer + * gets written out + */ + if ((ptr = calloc(BLK_SIZE, 1)) == NULL) { + ret = -1; + /* pad to bufsize */ + } else { + cnt = ds_bufsize; + while (cnt > 0) { + if ((n = write(ds_fd, ptr, + BLK_SIZE)) < 0) { + ret = -1; + break; + } + cnt -= n; + } + (void) free(ptr); + } + } + } +#endif + if (pkgendflg) { + if (ds_header) + (void) free(ds_header); + ds_header = (char *)NULL; + ds_totread = 0; + } + + if (ds_pp) { + (void) pclose(ds_pp); + ds_pp = 0; + (void) close(ds_realfd); + ds_realfd = -1; + ds_fd = -1; + } else if (ds_fd >= 0) { + (void) close(ds_fd); + ds_fd = -1; + } + + if (ds_device) { + /* rewind device */ + if ((n = open(ds_device, 0)) >= 0) + (void) close(n); + ds_device = NULL; + } + return (ret); +} |