| /* |
| * 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 <sys/stat.h> |
| #include <fcntl.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <malloc.h> |
| #include <sched.h> |
| |
| #include "types.h" |
| #include "util.h" |
| #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 */ |
| intgen_t 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 intgen_t ds_match( int, char *[], drive_t * ); |
| static intgen_t ds_instantiate( int, char *[], drive_t * ); |
| |
| /* declare manager operators |
| */ |
| static bool_t do_init( drive_t * ); |
| static bool_t do_sync( drive_t * ); |
| static intgen_t do_begin_read( drive_t * ); |
| static char *do_read( drive_t *, size_t , size_t *, intgen_t * ); |
| static void do_return_read_buf( drive_t *, char *, size_t ); |
| static void do_get_mark( drive_t *, drive_mark_t * ); |
| static intgen_t do_seek_mark( drive_t *, drive_mark_t * ); |
| static intgen_t do_next_mark( drive_t * ); |
| static void do_get_mark( drive_t *, drive_mark_t * ); |
| static void do_end_read( drive_t * ); |
| static intgen_t 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 intgen_t do_write( drive_t *, char *, size_t ); |
| static size_t do_get_align_cnt( drive_t * ); |
| static intgen_t do_end_write( drive_t *, off64_t * ); |
| static intgen_t do_rewind( drive_t * ); |
| static intgen_t do_erase( drive_t * ); |
| static intgen_t 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 intgen_t |
| 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 ) { |
| intgen_t 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 { |
| intgen_t oflags = 0; |
| struct stat statbuf; |
| intgen_t 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 intgen_t |
| do_begin_read( drive_t *drivep ) |
| { |
| #ifdef DEBUG |
| intgen_t 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; |
| intgen_t nread; |
| intgen_t 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, |
| intgen_t *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 intgen_t |
| 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 */ |
| intgen_t nread; |
| off64_t nreadneeded64; |
| intgen_t nreadneeded; |
| intgen_t 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; |
| ASSERT( nreadneeded64 <= INTGENMAX ); |
| nreadneeded = ( intgen_t )nreadneeded64; |
| nread = read_buf( 0, |
| ( size_t )nreadneeded, |
| ( void * )drivep, |
| ( rfp_t )drivep->d_opsp->do_read, |
| ( rrbfp_t )drivep->d_opsp->do_return_read_buf, |
| &rval ); |
| if ( rval ) { |
| return rval; |
| } |
| ASSERT( nread == nreadneeded ); |
| |
| /* 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 intgen_t |
| do_next_mark( drive_t *drivep ) |
| { |
| #ifdef DEBUG |
| intgen_t dcaps = drivep->d_capabilities; |
| #endif |
| drive_context_t *contextp = ( drive_context_t * )drivep->d_contextp; |
| drive_mark_t mark = contextp->dc_firstmark; |
| intgen_t 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 intgen_t |
| do_begin_write( drive_t *drivep ) |
| { |
| intgen_t 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 */ |
| intgen_t 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 intgen_t |
| 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 ) { |
| intgen_t 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; |
| __psint_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 = ( __psint_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 intgen_t |
| 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 |
| */ |
| intgen_t |
| do_rewind( drive_t *drivep ) |
| { |
| #ifdef DEBUG |
| intgen_t 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 |
| */ |
| intgen_t |
| do_erase( drive_t *drivep ) |
| { |
| #ifdef DEBUG |
| intgen_t dcaps = drivep->d_capabilities; |
| #endif |
| drive_context_t *contextp = ( drive_context_t * )drivep->d_contextp; |
| off64_t newoff; |
| intgen_t 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 intgen_t |
| 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; |
| } |