| /* |
| * Copyright (c) 2000-2001 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program 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. |
| * |
| * This program is distributed in the hope that it would 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, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <xfs/xfs.h> |
| #include <xfs/jdm.h> |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/param.h> |
| #include <fcntl.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <malloc.h> |
| #include <sched.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <uuid/uuid.h> |
| #include <xfs/xfs.h> |
| #include <xfs/jdm.h> /* needed only for util.h include */ |
| |
| #include "config.h" |
| |
| #include "types.h" |
| #include "util.h" /* needed onyl for I/O routines */ |
| #include "stream.h" |
| #include "mlog.h" |
| #include "global.h" |
| #include "drive.h" |
| #include "media.h" |
| #include "arch_xlate.h" |
| |
| /* this rmt junk is here because the rmt protocol supports writing ordinary |
| * (non-device) files in the remote /dev directory! yuck! |
| */ |
| #define open rmtopen |
| #define creat rmtcreat |
| #define close rmtclose |
| #define ioctl rmtioctl |
| #define read rmtread |
| #define write rmtwrite |
| |
| extern int rmtclose(int); |
| extern int rmtcreat (char *path, int mode); |
| extern int rmtioctl(int, int, ...); |
| extern int rmtopen(char *, int, ...); |
| extern int rmtread(int, void*, uint); |
| extern int rmtwrite(int, const void *, uint); |
| |
| |
| /* drive_simple.c - drive strategy for standard in or a file |
| */ |
| |
| |
| /* structure definitions used locally ****************************************/ |
| |
| /* drive context - drive-specific context |
| * buf must be page-aligned and at least 1 page in size |
| */ |
| #define PGPERBUF 64 /* private read buffer */ |
| #define BUFSZ (PGPERBUF * PGSZ) |
| |
| /* operational mode |
| */ |
| typedef enum { OM_NONE, OM_READ, OM_WRITE } om_t; |
| |
| struct drive_context { |
| char dc_buf[BUFSZ]; /* input/output buffer */ |
| om_t dc_mode; /* current mode of operation */ |
| ix_t dc_fmarkcnt; /* how many file marks to the left */ |
| char *dc_ownedp; /* first byte owned by caller */ |
| size_t dc_ownedsz; /* how much owned by caller (write only) */ |
| char *dc_nextp; /* next byte avail. to read/write */ |
| char *dc_emptyp; /* first empty slot in buffer */ |
| off64_t dc_bufstroff; /* offset in stream of top of buf */ |
| int dc_fd; /* input/output file descriptor */ |
| drive_mark_t dc_firstmark;/* first mark's offset within mfile (dump) */ |
| ix_t dc_markcnt; /* count of marks set (dump) */ |
| bool_t dc_rampr; /* can randomly access file (not a pipe) */ |
| bool_t dc_isrmtpr; /* is accessed via rmt */ |
| bool_t dc_israwdevpr; /* is a raw disk partition */ |
| }; |
| |
| typedef struct drive_context drive_context_t; |
| |
| |
| /* declarations of externally defined global variables ***********************/ |
| |
| extern size_t pgsz; |
| |
| |
| /* forward declarations of locally defined static functions ******************/ |
| |
| /* strategy functions |
| */ |
| static int ds_match(int, char *[], drive_t *); |
| static int ds_instantiate(int, char *[], drive_t *); |
| |
| /* declare manager operators |
| */ |
| static bool_t do_init(drive_t *); |
| static bool_t do_sync(drive_t *); |
| static int do_begin_read(drive_t *); |
| static char *do_read(drive_t *, size_t, size_t *, int *); |
| static void do_return_read_buf(drive_t *, char *, size_t); |
| static void do_get_mark(drive_t *, drive_mark_t *); |
| static int do_seek_mark(drive_t *, drive_mark_t *); |
| static int do_next_mark(drive_t *); |
| static void do_get_mark(drive_t *, drive_mark_t *); |
| static void do_end_read(drive_t *); |
| static int do_begin_write(drive_t *); |
| static void do_set_mark(drive_t *, drive_mcbfp_t, void *, drive_markrec_t *); |
| static char * do_get_write_buf(drive_t *, size_t, size_t *); |
| static int do_write(drive_t *, char *, size_t); |
| static size_t do_get_align_cnt(drive_t *); |
| static int do_end_write(drive_t *, off64_t *); |
| static int do_rewind(drive_t *); |
| static int do_erase(drive_t *); |
| static int do_get_device_class(drive_t *); |
| static void do_quit(drive_t *); |
| |
| |
| /* definition of locally defined global variables ****************************/ |
| |
| /* simple drive strategy for file or stdin. referenced by drive.c |
| */ |
| drive_strategy_t drive_strategy_simple = { |
| DRIVE_STRATEGY_SIMPLE, /* ds_id */ |
| "file dump (drive_simple)", /* ds_description */ |
| ds_match, /* ds_match */ |
| ds_instantiate, /* ds_instantiate */ |
| 0x1000000ll, /* ds_recmarksep */ |
| OFF64MAX /* ds_recmfilesz */ |
| }; |
| |
| |
| /* definition of locally defined static variables *****************************/ |
| |
| /* drive operators |
| */ |
| static drive_ops_t drive_ops = { |
| do_init, /* do_init */ |
| do_sync, /* do_sync */ |
| do_begin_read, /* do_begin_read */ |
| do_read, /* do_read */ |
| do_return_read_buf, /* do_return_read_buf */ |
| do_get_mark, /* do_get_mark */ |
| do_seek_mark, /* do_seek_mark */ |
| do_next_mark, /* do_next_mark */ |
| do_end_read, /* do_end_read */ |
| do_begin_write, /* do_begin_write */ |
| do_set_mark, /* do_set_mark */ |
| do_get_write_buf, /* do_get_write_buf */ |
| do_write, /* do_write */ |
| do_get_align_cnt, /* do_get_align_cnt */ |
| do_end_write, /* do_end_write */ |
| 0, /* do_fsf */ |
| 0, /* do_bsf */ |
| do_rewind, /* do_rewind */ |
| do_erase, /* do_erase */ |
| 0, /* do_eject_media */ |
| do_get_device_class, /* do_get_device_class */ |
| 0, /* do_display_metrics */ |
| do_quit, /* do_quit */ |
| }; |
| |
| /* definition of locally defined global functions ****************************/ |
| |
| |
| /* definition of locally defined static functions ****************************/ |
| |
| /* strategy match - determines if this is the right strategy |
| */ |
| /* ARGSUSED */ |
| static int |
| ds_match(int argc, char *argv[], drive_t *drivep) |
| { |
| bool_t isrmtpr; |
| struct stat64 statbuf; |
| |
| /* sanity checks |
| */ |
| assert(!(sizeofmember(drive_context_t, dc_buf) % PGSZ)); |
| |
| /* determine if this is an rmt file. if so, give a weak match: |
| * might be an ordinary file accessed via the rmt protocol. |
| */ |
| if (strchr(drivep->d_pathname, ':')) { |
| isrmtpr = BOOL_TRUE; |
| } else { |
| isrmtpr = BOOL_FALSE; |
| } |
| if (isrmtpr) { |
| return 1; |
| } |
| |
| /* willing to pick up anything not picked up by other strategies, |
| * as long as it exists and is not a directory |
| */ |
| if (!strcmp(drivep->d_pathname, "stdio")) { |
| return 1; |
| } |
| |
| if (stat64(drivep->d_pathname, &statbuf)) { |
| return -1; |
| } |
| |
| if (S_ISDIR(statbuf.st_mode)) { |
| return -1; |
| } |
| |
| return 1; |
| } |
| |
| /* strategy instantiate - initializes the pre-allocated drive descriptor |
| */ |
| /*ARGSUSED*/ |
| static bool_t |
| ds_instantiate(int argc, char *argv[], drive_t *drivep) |
| { |
| drive_context_t *contextp; |
| |
| /* hook up the drive ops |
| */ |
| drivep->d_opsp = &drive_ops; |
| |
| /* initialize the drive context - allocate a page-aligned |
| * structure, so the buffer is page-aligned. |
| */ |
| contextp = (drive_context_t *)memalign(PGSZ, |
| sizeof(drive_context_t)); |
| assert(contextp); |
| assert((void *)contextp->dc_buf == (void *)contextp); |
| memset((void *)contextp, 0, sizeof(*contextp)); |
| |
| /* scan drive device pathname to see if remote tape |
| */ |
| if (strchr(drivep->d_pathname, ':')) { |
| contextp->dc_isrmtpr = BOOL_TRUE; |
| } else { |
| contextp->dc_isrmtpr = BOOL_FALSE; |
| } |
| |
| /* determine drive capabilities of and open the named file. |
| */ |
| drivep->d_capabilities = 0; |
| drivep->d_capabilities |= DRIVE_CAP_AUTOREWIND; |
| if (!strcmp(drivep->d_pathname, "stdio")) { |
| #ifdef DUMP |
| contextp->dc_fd = 1; |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| contextp->dc_fd = 0; |
| #endif /* RESTORE */ |
| } else if (contextp->dc_isrmtpr) { |
| int oflags; |
| #ifdef DUMP |
| oflags = O_WRONLY | O_CREAT | O_TRUNC; |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| oflags = O_RDONLY; |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| #endif /* RESTORE */ |
| contextp->dc_rampr = BOOL_FALSE; |
| contextp->dc_fd = open(drivep->d_pathname, |
| oflags, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| if (contextp->dc_fd < 0) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("unable to open %s: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return BOOL_FALSE; |
| } |
| } else { |
| int oflags = 0; |
| struct stat statbuf; |
| int rval; |
| rval = stat(drivep->d_pathname, &statbuf); |
| #ifdef DUMP |
| if (rval) { |
| if (errno != ENOENT) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("stat of %s failed: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return BOOL_FALSE; |
| } |
| drivep->d_capabilities |= DRIVE_CAP_REWIND; |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| drivep->d_capabilities |= DRIVE_CAP_ERASE; |
| contextp->dc_rampr = BOOL_TRUE; |
| oflags = O_RDWR | O_CREAT; |
| |
| } else { |
| switch(statbuf.st_mode & S_IFMT) { |
| case S_IFREG: |
| drivep->d_capabilities |= DRIVE_CAP_ERASE; |
| drivep->d_capabilities |= DRIVE_CAP_REWIND; |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| contextp->dc_rampr = BOOL_TRUE; |
| oflags = O_RDWR; |
| break; |
| case S_IFCHR: |
| contextp->dc_israwdevpr = BOOL_TRUE; |
| /* intentional fall-through */ |
| case S_IFBLK: |
| /* intentional fall-through */ |
| case S_IFIFO: |
| oflags = O_WRONLY; |
| break; |
| default: |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("cannot dump to %s " |
| "file type %x\n"), |
| drivep->d_pathname, |
| statbuf.st_mode & S_IFMT); |
| return BOOL_FALSE; |
| } |
| } |
| #endif /* DUMP */ |
| #ifdef RESTORE |
| if (rval) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("stat of %s failed: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return BOOL_FALSE; |
| |
| } |
| oflags = O_RDONLY; |
| switch(statbuf.st_mode & S_IFMT) { |
| case S_IFREG: |
| case S_IFCHR: |
| case S_IFBLK: |
| drivep->d_capabilities |= DRIVE_CAP_REWIND; |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| break; |
| case S_IFIFO: |
| drivep->d_capabilities |= DRIVE_CAP_READ; |
| break; |
| default: |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("cannot restore from %s file type %x\n"), |
| drivep->d_pathname, |
| statbuf.st_mode & S_IFMT); |
| return BOOL_FALSE; |
| } |
| #endif /* RESTORE */ |
| contextp->dc_fd = open(drivep->d_pathname, |
| oflags, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
| if (contextp->dc_fd < 0) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("unable to open %s: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* initialize the operational mode. fmarkcnt is bumped on each |
| * end_read and end_write, set back to 0 on rewind. |
| */ |
| contextp->dc_mode = OM_NONE; |
| contextp->dc_fmarkcnt = 0; |
| |
| drivep->d_contextp = (void *)contextp; |
| |
| drivep->d_cap_est = -1; |
| drivep->d_rate_est = -1; |
| |
| return BOOL_TRUE; |
| } |
| |
| /* drive op init - second pass drive manager init - nothing to do, |
| * since async I/O not used. |
| */ |
| /* ARGSUSED */ |
| static bool_t |
| do_init(drive_t *drivep) |
| { |
| #ifdef DUMP |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| media_hdr_t *mwhdrp = (media_hdr_t *)dwhdrp->dh_upper; |
| #endif /* DUMP */ |
| |
| #ifdef DUMP |
| /* fill in media strategy id: artifact of first version of xfsdump |
| */ |
| mwhdrp->mh_strategyid = MEDIA_STRATEGY_SIMPLE; |
| #endif /* DUMP */ |
| |
| return BOOL_TRUE; |
| } |
| |
| /* drive op init - third pass drive manager init - nothing to do, |
| * since async I/O not used. |
| */ |
| /* ARGSUSED */ |
| static bool_t |
| do_sync(drive_t *drivep) |
| { |
| return BOOL_TRUE; |
| } |
| |
| /* drive op begin_read - prepare file for reading - main job is to |
| * read the media hdr |
| */ |
| static int |
| do_begin_read(drive_t *drivep) |
| { |
| #ifdef DEBUG |
| int dcaps = drivep->d_capabilities; |
| #endif |
| global_hdr_t *grhdrp = drivep->d_greadhdrp; |
| drive_hdr_t *drhdrp = drivep->d_readhdrp; |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| int nread; |
| int rval; |
| global_hdr_t *tmphdr = (global_hdr_t *)malloc(GLOBAL_HDR_SZ); |
| drive_hdr_t *tmpdh = (drive_hdr_t *)tmphdr->gh_upper; |
| media_hdr_t *tmpmh = (media_hdr_t *)tmpdh->dh_upper; |
| content_hdr_t *tmpch = (content_hdr_t *)tmpmh->mh_upper; |
| content_inode_hdr_t *tmpcih = (content_inode_hdr_t *)tmpch->ch_specific; |
| drive_hdr_t *dh = (drive_hdr_t *)grhdrp->gh_upper; |
| media_hdr_t *mh = (media_hdr_t *)dh->dh_upper; |
| content_hdr_t *ch = (content_hdr_t *)mh->mh_upper; |
| content_inode_hdr_t *cih = (content_inode_hdr_t *)ch->ch_specific; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple begin_read( )\n"); |
| |
| /* verify protocol being followed |
| */ |
| assert(dcaps & DRIVE_CAP_READ); |
| assert(contextp->dc_fd >= 0); |
| assert(contextp->dc_mode == OM_NONE); |
| |
| /* can only read one media file |
| */ |
| if (contextp->dc_fmarkcnt > 0) { |
| return DRIVE_ERROR_EOM; |
| } |
| |
| /* prepare the drive context |
| */ |
| contextp->dc_ownedp = 0; |
| contextp->dc_emptyp = &contextp->dc_buf[0]; |
| contextp->dc_nextp = contextp->dc_emptyp; |
| contextp->dc_bufstroff = 0; |
| |
| /* read the global header using the read_buf() utility function and |
| * my own read and return_read_buf operators. spoof the mode |
| */ |
| contextp->dc_mode = OM_READ; |
| |
| nread = read_buf((char *)tmphdr, |
| GLOBAL_HDR_SZ, |
| (void *)drivep, |
| (rfp_t)drivep->d_opsp->do_read, |
| (rrbfp_t)drivep->d_opsp->do_return_read_buf, |
| &rval); |
| contextp->dc_mode = OM_NONE; |
| |
| /* if EOD and nread is zero, there is no data after the file mark |
| */ |
| if (rval == DRIVE_ERROR_EOD) { |
| if (nread == 0) { |
| free(tmphdr); |
| return DRIVE_ERROR_BLANK; |
| } else { |
| free(tmphdr); |
| return DRIVE_ERROR_FORMAT; |
| } |
| } |
| if (rval) { |
| free(tmphdr); |
| return rval; |
| } |
| assert((size_t)nread == GLOBAL_HDR_SZ); |
| |
| mlog(MLOG_NITTY, "do_begin_read: global_hdr\n" |
| "\tgh_magic %.100s\n" |
| "\tgh_version %u\n" |
| "\tgh_checksum %u\n" |
| "\tgh_timestamp %u\n" |
| "\tgh_ipaddr %llu\n" |
| "\tgh_hostname %.100s\n" |
| "\tgh_dumplabel %.100s\n", |
| tmphdr->gh_magic, |
| tmphdr->gh_version, |
| tmphdr->gh_checksum, |
| tmphdr->gh_timestamp, |
| tmphdr->gh_ipaddr, |
| tmphdr->gh_hostname, |
| tmphdr->gh_dumplabel); |
| |
| /* check the checksum |
| */ |
| if (!global_hdr_checksum_check(tmphdr)) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("media file header checksum error\n")); |
| free(tmphdr); |
| return DRIVE_ERROR_CORRUPTION; |
| } |
| |
| xlate_global_hdr(tmphdr, grhdrp, 1); |
| xlate_drive_hdr(tmpdh, dh, 1); |
| *((drive_mark_t *)dh->dh_specific) = |
| INT_GET(*((drive_mark_t *)tmpdh->dh_specific), ARCH_CONVERT); |
| xlate_media_hdr(tmpmh, mh, 1); |
| xlate_content_hdr(tmpch, ch, 1); |
| xlate_content_inode_hdr(tmpcih, cih, 1); |
| free(tmphdr); |
| |
| /* check the magic number |
| */ |
| if (strncmp(grhdrp->gh_magic, GLOBAL_HDR_MAGIC, GLOBAL_HDR_MAGIC_SZ)) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("media file header magic number mismatch: %s, %s\n"), |
| grhdrp->gh_magic, |
| GLOBAL_HDR_MAGIC); |
| return DRIVE_ERROR_FORMAT; |
| } |
| |
| /* check the version |
| */ |
| if (global_version_check(grhdrp->gh_version) != BOOL_TRUE) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("unrecognized media file header version (%d)\n"), |
| grhdrp->gh_version); |
| return DRIVE_ERROR_VERSION; |
| } |
| |
| /* check the strategy id |
| */ |
| if (drhdrp->dh_strategyid != drive_strategy_simple.ds_id) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("unrecognized drive strategy ID " |
| "(media says %d, expected %d)\n"), |
| drhdrp->dh_strategyid, drive_strategy_simple.ds_id); |
| return DRIVE_ERROR_FORMAT; |
| } |
| |
| /* record the offset of the first mark |
| */ |
| contextp->dc_firstmark = *(drive_mark_t *)drhdrp->dh_specific; |
| |
| /* adjust the drive capabilities based on presence of first mark. |
| * this is a hack workaround for a bug in xfsdump which causes the |
| * first mark offset to not always be placed in the hdr. |
| */ |
| if (contextp->dc_firstmark) { |
| drivep->d_capabilities |= DRIVE_CAP_NEXTMARK; |
| } |
| |
| /* note that a successful begin_read ocurred |
| */ |
| contextp->dc_mode = OM_READ; |
| return 0; |
| } |
| |
| /* read - supply the caller with some filled buffer |
| */ |
| static char * |
| do_read(drive_t *drivep, |
| size_t wantedcnt, |
| size_t *actualcntp, |
| int *rvalp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| size_t remainingcnt; |
| size_t actualcnt; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple read( want %u )\n", |
| wantedcnt); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_READ); |
| assert(!contextp->dc_ownedp); |
| assert(wantedcnt > 0); |
| |
| /* pre-initialize reference return |
| */ |
| *rvalp = 0; |
| |
| /* count number of unread bytes in buffer |
| */ |
| assert(contextp->dc_emptyp >= contextp->dc_nextp); |
| remainingcnt = (size_t)(contextp->dc_emptyp - contextp->dc_nextp); |
| |
| /* if no unread bytes in buffer, refill |
| */ |
| if (remainingcnt == 0) { |
| size_t bufhowfullcnt; |
| int nread; |
| |
| /* calculate how many bytes were in the buffer. this will |
| * be used to adjust the recorded offset (relative to the |
| * beginning of the media file) of the top of the buffer |
| * after we refill the buffer. |
| */ |
| bufhowfullcnt = (size_t) |
| (contextp->dc_emptyp - contextp->dc_buf); |
| |
| /* attempt to fill the buffer. nread may be less if at EOF |
| */ |
| nread = read(contextp->dc_fd, contextp->dc_buf, BUFSZ); |
| if (nread < 0) { |
| *rvalp = DRIVE_ERROR_DEVICE; |
| return 0; |
| } |
| |
| /* adjust the recorded offset of the top of the buffer |
| * relative to the beginning of the media file |
| */ |
| contextp->dc_bufstroff += (off64_t)bufhowfullcnt; |
| |
| /* record the ptrs to the first empty byte and the next |
| * byte to be read |
| */ |
| assert(nread <= BUFSZ); |
| contextp->dc_emptyp = contextp->dc_buf + nread; |
| contextp->dc_nextp = contextp->dc_buf; |
| |
| /* if no bytes were read, the caller has seen all bytes. |
| */ |
| if (nread == 0) { |
| *rvalp = DRIVE_ERROR_EOD; |
| return 0; |
| } |
| |
| /* adjust the remaining count |
| */ |
| remainingcnt = (size_t)nread; |
| } |
| |
| /* the caller specified at most how many bytes he wants. if less |
| * than that remain unread in buffer, just return that many. |
| */ |
| actualcnt = min(wantedcnt, remainingcnt); |
| |
| /* set the owned ptr to the first byte to be supplied |
| */ |
| contextp->dc_ownedp = contextp->dc_nextp; |
| |
| /* advance the next ptr to the next byte to be supplied |
| */ |
| contextp->dc_nextp += actualcnt; |
| assert(contextp->dc_nextp <= contextp->dc_emptyp); |
| |
| /* return the actual number of bytes supplied, and a ptr to the first |
| */ |
| *actualcntp = actualcnt; |
| return contextp->dc_ownedp; |
| } |
| |
| /* return_read_buf - lets the caller give back all of the |
| * buffer obtained from a call to do_read(). |
| */ |
| /* ARGSUSED */ |
| static void |
| do_return_read_buf(drive_t *drivep, char *retp, size_t retcnt) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| /* REFERENCED */ |
| size_t ownedcnt; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple return_read_buf( returning %u )\n", |
| retcnt); |
| |
| /* verify protocol |
| */ |
| assert(contextp->dc_mode == OM_READ); |
| assert(contextp->dc_ownedp); |
| |
| /* verify returning right buffer |
| */ |
| assert(retp == contextp->dc_ownedp); |
| |
| /* verify all of buffer provided is being returned |
| */ |
| ownedcnt = (size_t)(contextp->dc_nextp - contextp->dc_ownedp); |
| assert(retcnt == ownedcnt); |
| |
| /* indicate nothing now owned by caller |
| */ |
| contextp->dc_ownedp = 0; |
| } |
| |
| /* the mark is simply the offset into the media file of the |
| * next byte to be read |
| */ |
| static void |
| do_get_mark(drive_t *drivep, drive_mark_t *markp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| off64_t nextoff; |
| off64_t strmoff; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple get_mark( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_READ); |
| assert(!contextp->dc_ownedp); |
| |
| /* calculate the offset of the next byte to be supplied relative to |
| * the beginning of the buffer and relative to the beginning of |
| * ther media file. |
| */ |
| nextoff = (off64_t)(contextp->dc_nextp - contextp->dc_buf); |
| strmoff = contextp->dc_bufstroff + nextoff; |
| *markp = (drive_mark_t)strmoff; |
| } |
| |
| /* seek forward to the specified mark. the caller must not have already read |
| * past that point. |
| */ |
| static int |
| do_seek_mark(drive_t *drivep, drive_mark_t *markp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| off64_t mark = *(off64_t *)markp; |
| off64_t nextoff; |
| off64_t strmoff; |
| /* REFERENCED */ |
| int nread; |
| off64_t nreadneeded64; |
| int nreadneeded; |
| int rval; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple seek_mark( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_READ); |
| assert(!contextp->dc_ownedp); |
| |
| /* calculate the current offset within the media file |
| * of the next byte to be read |
| */ |
| nextoff = (off64_t)(contextp->dc_nextp - contextp->dc_buf); |
| strmoff = contextp->dc_bufstroff + nextoff; |
| |
| /* if the caller attempts to seek past the current offset, |
| * this is really bad |
| */ |
| if (strmoff > mark) { |
| return DRIVE_ERROR_CORE; |
| } |
| |
| /* use read_buf util func to eat up difference |
| */ |
| nreadneeded64 = mark - strmoff; |
| while (nreadneeded64 > 0) { |
| if (nreadneeded64 > INTGENMAX) |
| nreadneeded = INTGENMAX; |
| else |
| nreadneeded = (int)nreadneeded64; |
| nread = read_buf(0, nreadneeded, drivep, |
| (rfp_t)drivep->d_opsp->do_read, |
| (rrbfp_t)drivep->d_opsp->do_return_read_buf, |
| &rval); |
| if (rval) { |
| return rval; |
| } |
| nreadneeded64 -= nread; |
| } |
| |
| /* verify we are on the mark |
| */ |
| nextoff = (off64_t)(contextp->dc_nextp - contextp->dc_buf); |
| strmoff = contextp->dc_bufstroff + nextoff; |
| assert(strmoff == mark); |
| |
| return 0; |
| } |
| |
| /* seek forward to the next mark. we only know of one mark, the first |
| * mark in the media file (recorded in the header). if the caller |
| * has already read past that mark, blow up. |
| */ |
| static int |
| do_next_mark(drive_t *drivep) |
| { |
| #ifdef DEBUG |
| int dcaps = drivep->d_capabilities; |
| #endif |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| drive_mark_t mark = contextp->dc_firstmark; |
| int rval; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple next_mark( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(dcaps & DRIVE_CAP_NEXTMARK); |
| assert(contextp->dc_mode == OM_READ); |
| assert(!contextp->dc_ownedp); |
| |
| if (!mark) { |
| return DRIVE_ERROR_EOF; |
| } |
| |
| rval = do_seek_mark(drivep, (drive_mark_t *)&mark); |
| if (rval) { |
| return rval; |
| } |
| |
| return 0; |
| } |
| |
| /* end_read - tell the drive we are done reading the media file |
| * just discards any buffered data |
| */ |
| void |
| do_end_read(drive_t *drivep) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple end_read( )\n"); |
| |
| /* be sure we are following protocol |
| */ |
| assert(contextp->dc_mode == OM_READ); |
| contextp->dc_mode = OM_NONE; |
| |
| /* bump the file mark cnt |
| */ |
| contextp->dc_fmarkcnt++; |
| assert(contextp->dc_fmarkcnt == 1); |
| } |
| |
| /* begin_write - prepare file for writing |
| */ |
| static int |
| do_begin_write(drive_t *drivep) |
| { |
| int dcaps = drivep->d_capabilities; |
| global_hdr_t *gwhdrp = drivep->d_gwritehdrp; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| int rval; |
| global_hdr_t *tmphdr; |
| drive_hdr_t *tmpdh; |
| media_hdr_t *tmpmh; |
| content_hdr_t *tmpch; |
| content_inode_hdr_t *tmpcih; |
| drive_hdr_t *dh = (drive_hdr_t *)gwhdrp->gh_upper; |
| media_hdr_t *mh = (media_hdr_t *)dh->dh_upper; |
| content_hdr_t *ch = (content_hdr_t *)mh->mh_upper; |
| content_inode_hdr_t *cih = (content_inode_hdr_t *)ch->ch_specific; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple begin_write( )\n"); |
| |
| /* sanity checks |
| */ |
| assert(dwhdrp->dh_strategyid == DRIVE_STRATEGY_SIMPLE); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_fd >= 0); |
| assert(contextp->dc_mode == OM_NONE); |
| |
| /* only one media file may be written |
| */ |
| if (contextp->dc_fmarkcnt > 0) { |
| return DRIVE_ERROR_EOM; |
| } |
| |
| /* indicate in the header that there is no recorded mark. |
| */ |
| *((off64_t *)dwhdrp->dh_specific) = 0; |
| |
| /* prepare the drive context. initially the caller does not own |
| * any of the write buffer, so the next portion of the buffer to |
| * be supplied is the top of the buffer. emptyp never changes; |
| * it always points to the byte after the end of the buffer. markcnt |
| * keeps track of the number marks the caller has set in the media file. |
| */ |
| contextp->dc_ownedp = 0; |
| contextp->dc_nextp = contextp->dc_buf; |
| contextp->dc_emptyp = contextp->dc_buf + sizeof(contextp->dc_buf); |
| contextp->dc_bufstroff = 0; |
| contextp->dc_markcnt = 0; |
| |
| /* truncate the destination if it supports read. |
| */ |
| if (dcaps & DRIVE_CAP_READ) { |
| rval = ftruncate(contextp->dc_fd, 0); |
| if (rval) { |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("attempt to truncate %s failed: %d (%s)\n"), |
| drivep->d_pathname, |
| errno, |
| strerror(errno)); |
| } |
| } |
| |
| /* set the mode |
| */ |
| contextp->dc_mode = OM_WRITE; |
| |
| tmphdr = (global_hdr_t *)malloc(GLOBAL_HDR_SZ); |
| assert(tmphdr); |
| memset(tmphdr, 0, GLOBAL_HDR_SZ); |
| tmpdh = (drive_hdr_t *)tmphdr->gh_upper; |
| tmpmh = (media_hdr_t *)tmpdh->dh_upper; |
| tmpch = (content_hdr_t *)tmpmh->mh_upper; |
| tmpcih = (content_inode_hdr_t *)tmpch->ch_specific; |
| |
| xlate_global_hdr(gwhdrp, tmphdr, 1); |
| xlate_drive_hdr(dh, tmpdh, 1); |
| INT_SET(*((drive_mark_t *)tmpdh->dh_specific), |
| ARCH_CONVERT, |
| *((drive_mark_t *)dh->dh_specific)); |
| xlate_media_hdr(mh, tmpmh, 1); |
| xlate_content_hdr(ch, tmpch, 1); |
| xlate_content_inode_hdr(cih, tmpcih, 1); |
| |
| /* checksum the header |
| */ |
| global_hdr_checksum_set(tmphdr); |
| |
| mlog(MLOG_NITTY, "do_begin_write: global_hdr\n" |
| "\tgh_magic %.100s\n" |
| "\tgh_version %u\n" |
| "\tgh_checksum %u\n" |
| "\tgh_timestamp %u\n" |
| "\tgh_ipaddr %llu\n" |
| "\tgh_hostname %.100s\n" |
| "\tgh_dumplabel %.100s\n", |
| tmphdr->gh_magic, |
| tmphdr->gh_version, |
| tmphdr->gh_checksum, |
| tmphdr->gh_timestamp, |
| tmphdr->gh_ipaddr, |
| tmphdr->gh_hostname, |
| tmphdr->gh_dumplabel); |
| |
| if (!global_hdr_checksum_check(tmphdr)) { |
| mlog(MLOG_NORMAL | MLOG_ERROR | MLOG_DRIVE, |
| _("media file header checksum error\n")); |
| } |
| else { |
| mlog(MLOG_NITTY, "media file header checksum OK!\n"); |
| } |
| |
| /* write the header using the write_buf() utility function and |
| * my own get_write_buf and write operators. |
| */ |
| rval = write_buf((char *)tmphdr, |
| GLOBAL_HDR_SZ, |
| (void *)drivep, |
| (gwbfp_t)drivep->d_opsp->do_get_write_buf, |
| (wfp_t)drivep->d_opsp->do_write); |
| |
| free(tmphdr); |
| |
| /* if error while writing hdr, undo mode |
| */ |
| if (rval) { |
| contextp->dc_mode = OM_NONE; |
| } |
| |
| return rval; |
| } |
| |
| /* do_set_mark - record a markrecord and callback |
| */ |
| static void |
| do_set_mark(drive_t *drivep, |
| drive_mcbfp_t cbfuncp, |
| void *cbcontextp, |
| drive_markrec_t *markrecp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| drive_mark_t mark; |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple set_mark( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_WRITE); |
| assert(!contextp->dc_ownedp); |
| assert(contextp->dc_nextp); |
| |
| /* calculate the mark offset |
| */ |
| mark = (drive_mark_t)(contextp->dc_bufstroff |
| + |
| (off64_t) |
| (contextp->dc_nextp - contextp->dc_buf)); |
| |
| /* fill in the mark field of the mark record |
| */ |
| markrecp->dm_log = mark; |
| |
| /* bump the mark count. if this is the first mark, record it |
| * in the drive strategy-specific header. this allows multiple |
| * media object restores to work. NOTE that the mark will not |
| * be recorded if the destination does not support random access |
| * and the write buffer has been flushed at least once. |
| * this is hidden by save_first_mark, and detected during restore |
| * by noting the first mark offset is NULL. to do this, must seek |
| * back to the header, rewrite and rechecksum the header, |
| * and seek back to the current position. HOWEVER, if the write |
| * buffer has not yet been flushed, we can just edit the buffer. |
| */ |
| contextp->dc_markcnt++; |
| if (contextp->dc_markcnt == 1) { |
| if (contextp->dc_bufstroff == 0) { |
| /* cast the write buffer into a media file hdr |
| */ |
| global_hdr_t *gwhdrp = |
| (global_hdr_t *)contextp->dc_buf; |
| drive_hdr_t *dwhdrp = (drive_hdr_t *)gwhdrp->gh_upper; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "re-writing media file header with first mark " |
| "(in buffer)\n"); |
| |
| /* record mark in hdr |
| */ |
| INT_SET(*((drive_mark_t *)dwhdrp->dh_specific), ARCH_CONVERT, mark); |
| |
| /* adjust header checksum |
| */ |
| global_hdr_checksum_set(gwhdrp); |
| |
| } else if (contextp->dc_rampr) { |
| global_hdr_t *gwhdrp = drivep->d_gwritehdrp; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| off64_t newoff; |
| /* REFERENCED */ |
| int nwritten; |
| |
| /* assert the header has been flushed |
| */ |
| assert(contextp->dc_bufstroff >= sizeof(*gwhdrp)); |
| |
| /* record mark in hdr |
| */ |
| INT_SET(*((drive_mark_t *)dwhdrp->dh_specific), ARCH_CONVERT, mark); |
| |
| /* adjust header checksum |
| */ |
| global_hdr_checksum_set(gwhdrp); |
| |
| /* seek to beginning |
| */ |
| newoff = lseek64(contextp->dc_fd, (off64_t)0, SEEK_SET); |
| if (newoff < 0) { |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("could not save first mark: %d (%s)\n"), |
| errno, |
| strerror(errno)); |
| } else { |
| global_hdr_t *tmphdr; |
| drive_hdr_t *tmpdh; |
| media_hdr_t *tmpmh; |
| content_hdr_t *tmpch; |
| content_inode_hdr_t *tmpcih; |
| drive_hdr_t *dh = (drive_hdr_t *)gwhdrp->gh_upper; |
| media_hdr_t *mh = (media_hdr_t *)dh->dh_upper; |
| content_hdr_t *ch = (content_hdr_t *)mh->mh_upper; |
| content_inode_hdr_t *cih = (content_inode_hdr_t *)ch->ch_specific; |
| |
| assert(newoff == 0); |
| |
| /* write and seek back to current offset |
| */ |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "re-writing media file header " |
| "with first mark " |
| "(on media)\n"); |
| |
| tmphdr = (global_hdr_t *)malloc(GLOBAL_HDR_SZ); |
| assert(tmphdr); |
| tmpdh = (drive_hdr_t *)tmphdr->gh_upper; |
| tmpmh = (media_hdr_t *)tmpdh->dh_upper; |
| tmpch = (content_hdr_t *)tmpmh->mh_upper; |
| tmpcih = (content_inode_hdr_t *)tmpch->ch_specific; |
| xlate_global_hdr(gwhdrp, tmphdr, 1); |
| xlate_drive_hdr(dh, tmpdh, 1); |
| INT_SET(*((drive_mark_t *)tmpdh->dh_specific), |
| ARCH_CONVERT, |
| *((drive_mark_t *)dh->dh_specific)); |
| xlate_media_hdr(mh, tmpmh, 1); |
| xlate_content_hdr(ch, tmpch, 1); |
| xlate_content_inode_hdr(cih, tmpcih, 1); |
| |
| /* adjust header checksum |
| */ |
| global_hdr_checksum_set(tmphdr); |
| |
| mlog(MLOG_NITTY, "do_set_mark: global_hdr\n" |
| "\tgh_magic %.100s\n" |
| "\tgh_version %u\n" |
| "\tgh_checksum %u\n" |
| "\tgh_timestamp %u\n" |
| "\tgh_ipaddr %llu\n" |
| "\tgh_hostname %.100s\n" |
| "\tgh_dumplabel %.100s\n", |
| tmphdr->gh_magic, |
| tmphdr->gh_version, |
| tmphdr->gh_checksum, |
| tmphdr->gh_timestamp, |
| tmphdr->gh_ipaddr, |
| tmphdr->gh_hostname, |
| tmphdr->gh_dumplabel); |
| |
| nwritten = write(contextp->dc_fd, |
| tmphdr, |
| sizeof(*tmphdr)); |
| assert((size_t)nwritten == sizeof(*tmphdr)); |
| free(tmphdr); |
| |
| newoff = lseek64(contextp->dc_fd, |
| contextp->dc_bufstroff, |
| SEEK_SET); |
| assert(newoff == contextp->dc_bufstroff); |
| } |
| } |
| } |
| |
| /* if all written are committed, send the mark back immediately. |
| * otherwise put the mark record on the tail of the queue. |
| */ |
| if (contextp->dc_nextp == contextp->dc_buf) { |
| assert(drivep->d_markrecheadp == 0); |
| (*cbfuncp)(cbcontextp, markrecp, BOOL_TRUE); |
| return; |
| } else { |
| markrecp->dm_cbfuncp = cbfuncp; |
| markrecp->dm_cbcontextp = cbcontextp; |
| markrecp->dm_nextp = 0; |
| if (drivep->d_markrecheadp == 0) { |
| drivep->d_markrecheadp = markrecp; |
| drivep->d_markrectailp = markrecp; |
| } else { |
| assert(drivep->d_markrectailp); |
| drivep->d_markrectailp->dm_nextp = markrecp; |
| drivep->d_markrectailp = markrecp; |
| } |
| } |
| } |
| |
| /* get_write_buf - supply the caller with buffer space. the caller |
| * will fill the space with data, then call write() to request that |
| * some or all of the buffer be written and to return the buffer space. |
| */ |
| /*ARGSUSED*/ |
| static char * |
| do_get_write_buf(drive_t *drivep, size_t wanted_bufsz, size_t *actual_bufszp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| size_t remaining_bufsz; |
| size_t actual_bufsz; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple get_write_buf( want %u )\n", |
| wanted_bufsz); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_WRITE); |
| assert(!contextp->dc_ownedp); |
| assert(contextp->dc_nextp); |
| assert(contextp->dc_nextp < contextp->dc_emptyp); |
| assert(contextp->dc_ownedsz == 0); |
| |
| /* calculate how much buffer remains |
| */ |
| remaining_bufsz =(size_t)(contextp->dc_emptyp - contextp->dc_nextp); |
| |
| /* give the caller the lesser of what he wants and what is available |
| */ |
| actual_bufsz = min(wanted_bufsz, remaining_bufsz); |
| |
| /* caller will own that portion of buffer |
| */ |
| contextp->dc_ownedp = contextp->dc_nextp; |
| contextp->dc_ownedsz = actual_bufsz; |
| |
| /* won't know nextp until write |
| */ |
| contextp->dc_nextp = 0; |
| |
| /* return the portion of the buffer to the caller |
| */ |
| *actual_bufszp = actual_bufsz; |
| return contextp->dc_ownedp; |
| } |
| |
| /* write - write and accept ownership of the portion of the buffer owned by the |
| * caller. |
| */ |
| /*ARGSUSED*/ |
| static int |
| do_write(drive_t *drivep, char *bufp, size_t writesz) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| off64_t ownedstroff = contextp->dc_bufstroff |
| + |
| (off64_t) |
| (contextp->dc_ownedp - contextp->dc_buf); |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple write( " |
| "offset %lld (0x%llx 0%llo) " |
| "size %u (0x%x 0%o) " |
| ")\n", |
| ownedstroff, |
| ownedstroff, |
| ownedstroff, |
| writesz, |
| writesz, |
| writesz, |
| 0); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_WRITE); |
| assert(contextp->dc_ownedp); |
| assert(bufp == contextp->dc_ownedp); |
| assert(!contextp->dc_nextp); |
| assert(contextp->dc_ownedp < contextp->dc_emptyp); |
| assert(writesz == contextp->dc_ownedsz); |
| |
| /* calculate next portion of buffer available for get_write_buf, |
| * and indicate no portion is owned. |
| */ |
| contextp->dc_nextp = contextp->dc_ownedp + writesz; |
| assert(contextp->dc_nextp <= contextp->dc_emptyp); |
| contextp->dc_ownedp = 0; |
| contextp->dc_ownedsz = 0; |
| |
| if (writesz == 0) { |
| return 0; /* returning unused buffer */ |
| } |
| |
| /* if buffer is full, flush it |
| */ |
| if (contextp->dc_nextp == contextp->dc_emptyp) { |
| int nwritten; |
| |
| mlog(MLOG_DEBUG | MLOG_DRIVE, |
| "flushing write buf addr 0x%x size 0x%x\n", |
| contextp->dc_buf, |
| sizeof(contextp->dc_buf)); |
| |
| contextp->dc_nextp = 0; |
| nwritten = write(contextp->dc_fd, |
| contextp->dc_buf, |
| sizeof(contextp->dc_buf)); |
| if (nwritten < 0) { |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("write to %s failed: %d (%s)\n"), |
| drivep->d_pathname, |
| errno, |
| strerror(errno)); |
| nwritten = 0; |
| } |
| contextp->dc_bufstroff += (off64_t)nwritten; |
| drive_mark_commit(drivep, contextp->dc_bufstroff); |
| contextp->dc_nextp = contextp->dc_buf; |
| if ((size_t)nwritten < sizeof(contextp->dc_buf)) { |
| return DRIVE_ERROR_EOM; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* get_align_cnt - returns the number of bytes which must be written to |
| * cause the next call to get_write_buf() to be page-aligned. |
| */ |
| static size_t |
| do_get_align_cnt(drive_t *drivep) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| intptr_t next_alignment_off; |
| char *next_alignment_point; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple get_align_cnt( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_WRITE); |
| assert(!contextp->dc_ownedp); |
| assert(contextp->dc_nextp); |
| assert(contextp->dc_nextp < contextp->dc_emptyp); |
| |
| /* calculate the next alignment point at or beyond the current nextp. |
| * the following algorithm works because dc_buf is page-aligned and |
| * a multiple of PGSZ. |
| */ |
| next_alignment_off = (intptr_t)contextp->dc_nextp; |
| next_alignment_off += PGMASK; |
| next_alignment_off &= ~PGMASK; |
| next_alignment_point = (char *)next_alignment_off; |
| assert(next_alignment_point <= contextp->dc_emptyp); |
| |
| /* return the number of bytes to the next alignment point |
| */ |
| return (size_t)(next_alignment_point - contextp->dc_nextp); |
| } |
| |
| /* end_write - flush any buffered data, and return by reference how many |
| * bytes were committed. |
| */ |
| static int |
| do_end_write(drive_t *drivep, off64_t *ncommittedp) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| size_t remaining_bufsz; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple end_write( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_WRITE); |
| assert(!contextp->dc_ownedp); |
| assert(contextp->dc_nextp); |
| assert(contextp->dc_nextp < contextp->dc_emptyp); |
| |
| /* calculate length of un-written portion of buffer |
| */ |
| assert(contextp->dc_nextp >= contextp->dc_buf); |
| remaining_bufsz = (size_t)(contextp->dc_nextp - contextp->dc_buf); |
| |
| if (remaining_bufsz) { |
| int nwritten; |
| if (contextp->dc_israwdevpr) { |
| remaining_bufsz = (remaining_bufsz + (BBSIZE - 1)) |
| & |
| ~(BBSIZE - 1); |
| } |
| |
| mlog(MLOG_DEBUG | MLOG_DRIVE, |
| "flushing write buf addr 0x%x size 0x%x\n", |
| contextp->dc_buf, |
| remaining_bufsz); |
| |
| nwritten = write(contextp->dc_fd, |
| contextp->dc_buf, |
| remaining_bufsz); |
| if (nwritten < 0) { |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("write to %s failed: %d (%s)\n"), |
| drivep->d_pathname, |
| errno, |
| strerror(errno)); |
| drive_mark_discard(drivep); |
| *ncommittedp = contextp->dc_bufstroff; |
| contextp->dc_mode = OM_NONE; |
| return DRIVE_ERROR_DEVICE; |
| } |
| contextp->dc_bufstroff += (off64_t)nwritten; |
| drive_mark_commit(drivep, contextp->dc_bufstroff); |
| if ((size_t)nwritten < remaining_bufsz) { |
| *ncommittedp = contextp->dc_bufstroff; |
| contextp->dc_mode = OM_NONE; |
| return DRIVE_ERROR_EOM; |
| } |
| } |
| |
| /* bump the file mark cnt |
| */ |
| contextp->dc_fmarkcnt++; |
| assert(contextp->dc_fmarkcnt == 1); |
| |
| *ncommittedp = contextp->dc_bufstroff; |
| contextp->dc_mode = OM_NONE; |
| return 0; |
| } |
| |
| /* rewind - return the current file offset to the beginning |
| */ |
| int |
| do_rewind(drive_t *drivep) |
| { |
| #ifdef DEBUG |
| int dcaps = drivep->d_capabilities; |
| #endif |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| off64_t newoff; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple rewind( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_NONE); |
| assert(dcaps & DRIVE_CAP_REWIND); |
| assert(contextp->dc_fd >= 0); |
| |
| /* seek to beginning of file |
| */ |
| newoff = lseek64(contextp->dc_fd, (off64_t)0, SEEK_SET); |
| if (newoff) { |
| assert(newoff < 0); |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("could not rewind %s: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return DRIVE_ERROR_DEVICE; |
| } |
| |
| return 0; |
| } |
| |
| /* erase - truncate to zero length |
| */ |
| int |
| do_erase(drive_t *drivep) |
| { |
| #ifdef DEBUG |
| int dcaps = drivep->d_capabilities; |
| #endif |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| off64_t newoff; |
| int rval; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple erase( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_NONE); |
| assert(dcaps & DRIVE_CAP_ERASE); |
| assert(contextp->dc_fd >= 0); |
| |
| /* seek to beginning of file |
| */ |
| newoff = lseek64(contextp->dc_fd, (off64_t)0, SEEK_SET); |
| if (newoff) { |
| assert(newoff < 0); |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("could not rewind %s in prep for erase: %s\n"), |
| drivep->d_pathname, |
| strerror(errno)); |
| return DRIVE_ERROR_DEVICE; |
| } |
| |
| /* erase to beginning of file |
| */ |
| rval = ftruncate64(contextp->dc_fd, (off64_t)0); |
| if (rval) { |
| mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_DRIVE, |
| _("could not erase %s: %s (%d)\n"), |
| drivep->d_pathname, |
| strerror(errno), |
| errno); |
| return DRIVE_ERROR_DEVICE; |
| } |
| contextp->dc_fmarkcnt = 0; |
| |
| return 0; |
| } |
| |
| /* get_media_class() |
| */ |
| /* ARGSUSED */ |
| static int |
| do_get_device_class(drive_t *drivep) |
| { |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple get_device_class( )\n"); |
| assert(drivep); |
| return DEVICE_NONREMOVABLE; |
| } |
| |
| static void |
| do_quit(drive_t *drivep) |
| { |
| drive_context_t *contextp = (drive_context_t *)drivep->d_contextp; |
| |
| mlog(MLOG_NITTY | MLOG_DRIVE, |
| "drive_simple quit( )\n"); |
| |
| /* assert protocol |
| */ |
| assert(contextp->dc_mode == OM_NONE); |
| assert(contextp); |
| |
| /* close file |
| */ |
| if (contextp->dc_fd > 1) { |
| close(contextp->dc_fd); |
| } |
| contextp->dc_fd = -1; |
| |
| /* free context |
| */ |
| free((void *)contextp); |
| drivep->d_contextp = 0; |
| } |