| /* |
| * Copyright (c) 2000-2004 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 <sys/prctl.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <signal.h> |
| #include <sys/statvfs.h> |
| #include <dirent.h> |
| #include <sys/ioctl.h> |
| #include <sys/quota.h> |
| #include <malloc.h> |
| |
| #ifdef linux |
| #include <xfs/xqm.h> |
| #endif |
| |
| #include <attr/attributes.h> |
| |
| #include "hsmapi.h" |
| |
| #include "exit.h" |
| #include "types.h" |
| #include "path.h" |
| #include "timeutil.h" |
| #include "util.h" |
| #include "lock.h" |
| #include "qlock.h" |
| #include "mlog.h" |
| #include "dlog.h" |
| #include "getopt.h" |
| #include "stream.h" |
| #include "cldmgr.h" |
| #include "global.h" |
| #include "drive.h" |
| #include "media.h" |
| #include "content.h" |
| #include "content_common.h" |
| #include "content_inode.h" |
| #include "fs.h" |
| #include "inomap.h" |
| #include "var.h" |
| #include "inventory.h" |
| #include "getdents.h" |
| #include "arch_xlate.h" |
| |
| /* max "unsigned long long int" |
| */ |
| #define ULONGLONG_MAX 18446744073709551615LLU |
| |
| /* legal range of dump levels |
| */ |
| #define LEVEL_DEFAULT 0 |
| #define LEVEL_MAX 9 |
| |
| /* ordinary files as big or bigger than this many pages will be |
| * preceeded in the dump by enough padding to align the first byte |
| * of that file's data to a page boundary |
| */ |
| #define PGALIGNTHRESH 8 |
| |
| |
| /* structure definitions used locally ****************************************/ |
| |
| /* number of bstats bstat_iter fetches at a time |
| */ |
| #define BSTATBUFLEN 4096 |
| |
| /* if the source file system type can't be determined, assume it is this |
| */ |
| #define FS_DEFAULT "xfs" |
| |
| /* marks consist of a opaque drive layer cookie and a startpoint. |
| * the drive layer requires that it be passed a pointer to a drive_markrec_t. |
| * we tack on content-specific baggage (startpt_t). this works because we |
| * allocate and free mark_t's here. |
| */ |
| struct mark { |
| drive_markrec_t dm; |
| startpt_t startpt; |
| }; |
| |
| typedef struct mark mark_t; |
| |
| /* Media_mfile_begin( ) entry state. |
| */ |
| enum bes { BES_INIT, /* in the beginning */ |
| BES_ENDOK, /* last media file successfully flushed to media */ |
| BES_ENDEOM, /* hit EOM while writing last media file */ |
| BES_INVAL }; /* to assert protocol being followed */ |
| |
| typedef enum bes bes_t; |
| |
| /* per-stream context |
| */ |
| struct context { |
| filehdr_t *cc_filehdrp; |
| /* pre-allocated buffer: heads each dumped file |
| */ |
| extenthdr_t *cc_extenthdrp; |
| /* pre-allocated buffer: heads each dumped file extent |
| */ |
| void *cc_inomap_contextp; |
| /* pre-allocated context to speed inomap iteration |
| */ |
| char *cc_getdentsbufp; |
| size_t cc_getdentsbufsz; |
| /* pre-allocated buffer for getdents() syscall |
| */ |
| char *cc_mdirentbufp; |
| size_t cc_mdirentbufsz; |
| /* pre-allocated buffer for on-media dirent |
| */ |
| char *cc_extattrlistbufp; |
| size_t cc_extattrlistbufsz; |
| /* pre-allocated buffer for retrieving a |
| * list of extended file attributes |
| */ |
| attr_multiop_t *cc_extattrrtrvarrayp; |
| size_t cc_extattrrtrvarraylen; |
| /* pre-allocated array of ops for retrieving the |
| * values for a list of extended file attributes |
| */ |
| char *cc_extattrdumpbufp; |
| size_t cc_extattrdumpbufsz; |
| /* pre-allocated buffer for dumping the names and |
| * values for a list of extended file attributes |
| */ |
| hsm_f_ctxt_t *cc_hsm_f_ctxtp; |
| /* pre-allocated HSM context used for holding HSM |
| state information about a file across subroutine |
| calls. |
| */ |
| char *cc_readlinkbufp; |
| size_t cc_readlinkbufsz; |
| /* pre-allocated buffer for readlink() |
| */ |
| off64_t cc_mfilesz; |
| /* total bytes dumped to media file |
| */ |
| size_t cc_markscommitted; |
| /* number of marks committed in mfile. only useful |
| * info is if greater than zero. |
| */ |
| xfs_ino_t cc_stat_lastino; |
| /* monotonic strm nondir ino dumped |
| */ |
| bool_t cc_completepr; |
| /* set if stream completely dumped. useful for |
| * determining if dump was interrupted |
| */ |
| bool_t cc_Media_useterminatorpr; |
| /* true if stream terminators are expected and |
| * will be used |
| */ |
| char *cc_Media_firstlabel; |
| /* optional command line media label. only used |
| * for first media object in stream, and only if |
| * media object does not already have a label |
| */ |
| bes_t cc_Media_begin_entrystate; |
| /* Media_mfile_begin context entry state |
| */ |
| }; |
| |
| typedef struct context context_t; |
| |
| /* extent group context, used by dump_file() |
| */ |
| #define BMAP_LEN 512 |
| |
| struct extent_group_context { |
| getbmapx_t eg_bmap[ BMAP_LEN ]; |
| getbmapx_t *eg_nextbmapp; /* ptr to the next extent to dump */ |
| getbmapx_t *eg_endbmapp; /* to detect extent exhaustion */ |
| intgen_t eg_fd; /* file desc. */ |
| intgen_t eg_bmapix; /* debug info only, not used */ |
| intgen_t eg_gbmcnt; /* debug, counts getbmapx calls for ino*/ |
| }; |
| |
| typedef struct extent_group_context extent_group_context_t; |
| |
| |
| /* minimum getdents( ) buffer size |
| */ |
| #define GETDENTSBUF_SZ_MIN ( 2 * pgsz ) |
| |
| |
| /* minimum sizes for extended attributes buffers |
| */ |
| #define EXTATTR_LISTBUF_SZ ( XATTR_LIST_MAX ) |
| #define EXTATTR_RTRVARRAY_LEN ( 1 * pgsz ) |
| #define EXTATTR_DUMPBUF_SZ ( 4 * pgsz ) |
| |
| /* for printing ext attr namespace |
| */ |
| #define EXTATTR_NAMESPACE(flag) ( ((flag) & ATTR_ROOT) ? _("root") : \ |
| ( ((flag) & ATTR_SECURE) ? _("secure") : \ |
| _("non-root") ) ) |
| |
| /* for printing file type |
| */ |
| #define FILETYPE(statp) ( ( (statp)->bs_mode & S_IFMT ) == S_IFDIR \ |
| ? _("dir") : _("nondir") ) |
| |
| /* per-drive status descriptor |
| */ |
| struct pds { |
| enum { PDS_NULL, /* per-drive activity not begun */ |
| PDS_INOMAP, /* dumping inomap */ |
| PDS_DIRDUMP, /* dumping dirs */ |
| PDS_NONDIR, /* dumping nondirs */ |
| PDS_INVSYNC, /* waiting for inventory */ |
| PDS_INVDUMP, /* dumping session inventory */ |
| PDS_TERMDUMP /* writing stream terminator */ |
| } pds_phase; |
| size64_t pds_dirdone; /* number of directories done */ |
| }; |
| |
| typedef struct pds pds_t; |
| |
| /* declarations of externally defined global symbols *************************/ |
| |
| extern void usage( void ); |
| extern bool_t preemptchk( int ); |
| extern char *homedir; |
| extern bool_t pipeline; |
| extern bool_t stdoutpiped; |
| extern char *sistr; |
| extern size_t pgsz; |
| |
| |
| /* forward declarations of locally defined static functions ******************/ |
| |
| /* file dumpers |
| */ |
| static rv_t dump_dirs( ix_t strmix, |
| xfs_bstat_t *bstatbufp, |
| size_t bstatbuflen, |
| void *inomap_contextp ); |
| static rv_t dump_dir( ix_t strmix, |
| jdm_fshandle_t *, |
| intgen_t, |
| xfs_bstat_t * ); |
| static rv_t dump_file( void *, |
| jdm_fshandle_t *, |
| intgen_t, |
| xfs_bstat_t * ); |
| static rv_t dump_file_reg( drive_t *drivep, |
| context_t *contextp, |
| content_inode_hdr_t *scwhdrp, |
| jdm_fshandle_t *, |
| xfs_bstat_t *, |
| bool_t *); |
| static rv_t dump_file_spec( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *, |
| xfs_bstat_t * ); |
| static rv_t dump_filehdr( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *, |
| off64_t, |
| intgen_t ); |
| static rv_t dump_extenthdr( drive_t *drivep, |
| context_t *contextp, |
| int32_t, |
| int32_t, |
| off64_t, |
| off64_t ); |
| static rv_t dump_dirent( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *, |
| xfs_ino_t, |
| gen_t, |
| char *, |
| size_t ); |
| static rv_t init_extent_group_context( jdm_fshandle_t *, |
| xfs_bstat_t *, |
| extent_group_context_t * ); |
| static void cleanup_extent_group_context( extent_group_context_t * ); |
| static rv_t dump_extent_group( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *, |
| extent_group_context_t *, |
| off64_t, |
| off64_t, |
| bool_t, |
| off64_t *, |
| off64_t *, |
| bool_t * ); |
| static bool_t dump_session_inv( drive_t *drivep, |
| context_t *contextp, |
| media_hdr_t *mwhdrp, |
| content_inode_hdr_t *scwhdrp ); |
| static rv_t write_pad( drive_t *drivep, size_t ); |
| |
| static void mark_callback( void *, drive_markrec_t *, bool_t ); |
| |
| static void inv_cleanup( void ); |
| static void dump_terminator( drive_t *drivep, |
| context_t *contextp, |
| media_hdr_t *mwhdrp ); |
| static rv_t Media_mfile_begin( drive_t *drivep, |
| context_t *contextp, |
| bool_t intr_allowed ); |
| static rv_t Media_mfile_end( drive_t *drivep, |
| context_t *contextp, |
| media_hdr_t *mwhdrp, |
| off64_t *ncommittedp, |
| bool_t hit_eom ); |
| static bool_t Media_prompt_overwrite( drive_t *drivep ); |
| static rv_t Media_erasechk( drive_t *drivep, |
| intgen_t dcaps, |
| bool_t intr_allowed, |
| bool_t prevmediapresentpr ); |
| static bool_t Media_prompt_erase( drive_t *drivep ); |
| static char *Media_prompt_label( drive_t *drivep, char *bufp, size_t bufsz ); |
| static void update_cc_Media_useterminatorpr( drive_t *drivep, |
| context_t *contextp ); |
| static void set_mcflag( ix_t thrdix ); |
| static void clr_mcflag( ix_t thrdix ); |
| |
| static bool_t check_complete_flags( void ); |
| |
| static rv_t dump_extattrs( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp); |
| static rv_t dump_extattr_list( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp, |
| attrlist_t *listp, |
| int flag, |
| bool_t *abortprp ); |
| static char *dump_extattr_buildrecord( xfs_bstat_t *statp, |
| char *dumpbufp, |
| char *dumpbufendp, |
| char *namesrcp, |
| u_int32_t valuesz, |
| int flag, |
| char **valuepp ); |
| static rv_t dump_extattrhdr( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *statp, |
| size_t recsz, |
| size_t valoff, |
| ix_t flags, |
| u_int32_t valsz ); |
| |
| static bool_t save_quotas( char *mntpnt, |
| quota_info_t *quotainfo ); |
| static int getxfsqstat( char *fsdev ); |
| |
| |
| |
| /* definition of locally defined global variables ****************************/ |
| |
| bool_t content_media_change_needed; |
| char *media_change_alert_program = NULL; |
| hsm_fs_ctxt_t *hsm_fs_ctxtp = NULL; |
| u_int64_t hdr_mfilesz = 0; |
| u_int64_t maxdumpfilesize = 0; |
| bool_t allowexcludefiles_pr = BOOL_FALSE; |
| |
| /* definition of locally defined static variables *****************************/ |
| |
| static bool_t sc_preerasepr = BOOL_FALSE; |
| /* pre-erase media |
| */ |
| static inv_idbtoken_t sc_inv_idbtoken = INV_TOKEN_NULL; |
| /* handle to inventory |
| */ |
| static inv_sestoken_t sc_inv_sestoken = INV_TOKEN_NULL; |
| /* handle to inventory session |
| */ |
| static inv_stmtoken_t *sc_inv_stmtokenp = 0; |
| /* array of inventory session stream handles |
| */ |
| static bool_t sc_inv_updatepr = BOOL_TRUE; |
| /* set if ok to update online inventory with stats of this dump |
| */ |
| static ix_t sc_level = LEVEL_DEFAULT; |
| /* dump level requested |
| */ |
| static bool_t sc_incrpr = BOOL_FALSE; |
| static time32_t sc_incrbasetime; |
| static ix_t sc_incrbaselevel; |
| static uuid_t sc_incrbaseid; |
| /* if an incremental dump, the base, level and time of the incremental |
| * base dump. TRICKY: if resuming an incremental dump, this is the |
| * base of the original incremental. |
| */ |
| static bool_t sc_resumepr = BOOL_FALSE; |
| static time32_t sc_resumebasetime = 0; |
| static uuid_t sc_resumebaseid; |
| static size_t sc_resumerangecnt = 0; |
| static drange_t *sc_resumerangep = 0; |
| /* if a resumed dump, the id, time and undumped ino/offset ranges |
| * of the interrupted dump being resumed. |
| */ |
| static jdm_fshandle_t *sc_fshandlep = 0; |
| /* dmi file system handle |
| */ |
| static int sc_fsfd = -1; |
| /* open file descriptor for root directory |
| */ |
| static xfs_bstat_t *sc_rootxfsstatp = 0; |
| /* pointer to loaded bulkstat for root directory |
| */ |
| static startpt_t *sc_startptp = 0; |
| /* an array of stream ino/offset start points |
| */ |
| static time32_t sc_stat_starttime = 0; |
| /* for cacluating elapsed time |
| */ |
| static ix_t sc_stat_inomapphase = 0; |
| static ix_t sc_stat_inomappass = 0; |
| static size64_t sc_stat_inomapcnt; |
| static size64_t sc_stat_inomapdone; |
| static size64_t sc_stat_dircnt = 0; |
| /* total number of directory inodes to be dumped (strm 0) |
| */ |
| static pds_t sc_stat_pds[ STREAM_SIMMAX ]; |
| /* per-drive stream status |
| */ |
| static size64_t sc_stat_nondircnt = 0; |
| /* total number of non-directory inodes to be dumped (all strms) |
| */ |
| static size64_t sc_stat_nondirdone = 0; |
| /* total number of non-directory inodes dumped (all strms) |
| */ |
| static size64_t sc_stat_datasz = 0; |
| /* total size in bytes of non-dirs to be dumped (all strms) |
| */ |
| static size64_t sc_stat_datadone = 0; |
| /* total size in bytes of non-dirs dumped (all strms) |
| */ |
| static size_t sc_thrdsarrivedcnt = 0; |
| /* each thread checks in by bumping this count under lock. |
| * used to decide when its ok to begin waiting for all threads |
| * to arrive at sync pt for session inventory dump. |
| */ |
| static size_t sc_thrdsdonecnt = 0; |
| /* number of threads which are ready to dump the session inventory. |
| * when equal to the number of streams remaining (stream_cnt( )), |
| * can proceed with inventory dumps |
| */ |
| static context_t *sc_contextp; |
| /* an array of per-stream context descriptors |
| */ |
| static bool_t sc_mcflag[ STREAM_SIMMAX ]; |
| /* media change flag |
| */ |
| static bool_t sc_dumpextattrpr = BOOL_TRUE; |
| /* dump extended attributes |
| */ |
| static bool_t sc_dumpasoffline = BOOL_FALSE; |
| /* dump dual-residency HSM files as offline |
| */ |
| static bool_t sc_use_old_direntpr = BOOL_FALSE; |
| /* dump dirents as dirent_v1_t instead of dirent_t |
| * (for compat with dump format 2) |
| */ |
| |
| static bool_t sc_savequotas = BOOL_TRUE; |
| /* save quota information in dump |
| */ |
| static quota_info_t quotas[] = { |
| { "user quota", BOOL_TRUE, CONTENT_QUOTAFILE, "", "-uf", XFS_QUOTA_UDQ_ACCT, 0 }, |
| { "project quota", BOOL_TRUE, CONTENT_PQUOTAFILE, "", "-pf", XFS_QUOTA_PDQ_ACCT, 0 }, |
| { "group quota", BOOL_TRUE, CONTENT_GQUOTAFILE, "", "-gf", XFS_QUOTA_GDQ_ACCT, 0 } |
| }; |
| |
| /* definition of locally defined global functions ****************************/ |
| |
| |
| /* definition of locally defined static functions ****************************/ |
| static bool_t create_inv_session( |
| global_hdr_t *gwhdrtemplatep, |
| uuid_t *fsidp, |
| const char *mntpnt, |
| const char *fsdevice, |
| ix_t subtreecnt, |
| size_t strmix); |
| |
| bool_t |
| content_init( intgen_t argc, |
| char *argv[ ], |
| global_hdr_t *gwhdrtemplatep ) |
| { |
| |
| inv_idbtoken_t inv_idbt; |
| inv_session_t *sessp = 0; |
| drive_hdr_t *dwhdrtemplatep; |
| media_hdr_t *mwhdrtemplatep; |
| content_hdr_t *cwhdrtemplatep; |
| content_inode_hdr_t *scwhdrtemplatep; |
| ix_t subtreecnt; |
| char **subtreep; |
| ix_t subtreeix; |
| bool_t resumereqpr = BOOL_FALSE; |
| char *srcname; |
| char mntpnt[ GLOBAL_HDR_STRING_SZ ]; |
| char fsdevice[ GLOBAL_HDR_STRING_SZ ]; |
| char fstype[ CONTENT_HDR_FSTYPE_SZ ]; |
| bool_t skip_unchanged_dirs = BOOL_FALSE; |
| uuid_t fsid; |
| bool_t underfoundpr; |
| ix_t underlevel = ( ix_t )( -1 ); |
| time32_t undertime = 0; |
| uuid_t underid; |
| bool_t underpartialpr = BOOL_FALSE; |
| bool_t underinterruptedpr = BOOL_FALSE; |
| bool_t samefoundpr; |
| time32_t sametime = 0; |
| uuid_t sameid; |
| bool_t samepartialpr = BOOL_FALSE; |
| bool_t sameinterruptedpr = BOOL_FALSE; |
| size_t strmix; |
| intgen_t c; |
| intgen_t i; |
| intgen_t qstat; |
| intgen_t rval; |
| bool_t ok; |
| extern char *optarg; |
| extern int optind, opterr, optopt; |
| char *baseuuidstr = NULL; |
| uuid_t baseuuid; |
| bool_t baseuuidvalpr; |
| u_int64_t dircnt; |
| u_int64_t nondircnt; |
| u_int64_t datasz; |
| u_int64_t inocnt; |
| u_int64_t inomapsz; |
| u_int64_t direntsz; |
| u_int64_t filesz; |
| u_int64_t size_estimate; |
| |
| /* basic sanity checks |
| */ |
| ASSERT( sizeof( mode_t ) == MODE_SZ ); |
| ASSERT( sizeof( timestruct_t ) == TIMESTRUCT_SZ ); |
| ASSERT( sizeof( bstat_t ) == BSTAT_SZ ); |
| ASSERT( sizeof( filehdr_t ) == FILEHDR_SZ ); |
| ASSERT( sizeof( extenthdr_t ) == EXTENTHDR_SZ ); |
| ASSERT( sizeof( direnthdr_t ) == DIRENTHDR_SZ ); |
| ASSERT( sizeof( direnthdr_v1_t ) == DIRENTHDR_SZ ); |
| ASSERT( DIRENTHDR_SZ % DIRENTHDR_ALIGN == 0 ); |
| ASSERT( sizeofmember( content_hdr_t, ch_specific ) |
| >= |
| sizeof( content_inode_hdr_t )); |
| ASSERT( sizeof( extattrhdr_t ) == EXTATTRHDR_SZ ); |
| |
| /* calculate offsets of portions of the write hdr template |
| */ |
| dwhdrtemplatep = ( drive_hdr_t * )gwhdrtemplatep->gh_upper; |
| mwhdrtemplatep = ( media_hdr_t * )dwhdrtemplatep->dh_upper; |
| cwhdrtemplatep = ( content_hdr_t * )mwhdrtemplatep->mh_upper; |
| scwhdrtemplatep = ( content_inode_hdr_t * ) cwhdrtemplatep->ch_specific; |
| |
| if ( gwhdrtemplatep->gh_version < GLOBAL_HDR_VERSION_3 ) { |
| sc_use_old_direntpr = BOOL_TRUE; |
| } |
| |
| /* process command line args |
| */ |
| optind = 1; |
| opterr = 0; |
| subtreecnt = 0; |
| baseuuidvalpr = BOOL_FALSE; |
| while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) { |
| switch ( c ) { |
| case GETOPT_LEVEL: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| sc_level = ( ix_t )atoi( optarg ); |
| if ( sc_level > LEVEL_MAX ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument must be " |
| "between 0 and %d\n"), |
| c, |
| LEVEL_MAX ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| break; |
| case GETOPT_SUBTREE: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( optarg[ 0 ] == '/' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument (subtree) " |
| "must be a relative pathname\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| subtreecnt++; |
| break; |
| case GETOPT_MAXDUMPFILESIZE: |
| if ( ! optarg || optarg [ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| maxdumpfilesize = strtoull(optarg, NULL, 0); |
| if ( maxdumpfilesize == 0 || |
| maxdumpfilesize > ULONGLONG_MAX / 1024 || |
| ( maxdumpfilesize == ULONGLONG_MAX && errno == ERANGE ) ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument is not a valid file size\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| maxdumpfilesize *= 1024; |
| break; |
| case GETOPT_NOUNCHANGEDDIRS: |
| skip_unchanged_dirs = BOOL_TRUE; |
| break; |
| case GETOPT_EXCLUDEFILES: |
| allowexcludefiles_pr = BOOL_TRUE; |
| break; |
| case GETOPT_RESUME: |
| resumereqpr = BOOL_TRUE; |
| break; |
| case GETOPT_NOINVUPDATE: |
| sc_inv_updatepr = BOOL_FALSE; |
| break; |
| case GETOPT_ERASE: |
| sc_preerasepr = BOOL_TRUE; |
| break; |
| case GETOPT_ALERTPROG: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| media_change_alert_program = optarg; |
| break; |
| case GETOPT_NOEXTATTR: |
| sc_dumpextattrpr = BOOL_FALSE; |
| break; |
| case GETOPT_DUMPASOFFLINE: |
| sc_dumpasoffline = BOOL_TRUE; |
| break; |
| case GETOPT_BASED: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| baseuuidstr = optarg; |
| |
| if ( uuid_parse( baseuuidstr, baseuuid ) < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument not a valid " |
| "dump session id\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| baseuuidvalpr = BOOL_TRUE; |
| } |
| } |
| |
| if ( resumereqpr && baseuuidvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "may not specify both -%c and -%c\n"), |
| GETOPT_BASED, |
| GETOPT_RESUME ); |
| return BOOL_FALSE; |
| } |
| |
| /* the user may specify stdout as the destination, by a single |
| * dash ('-') with no option letter. This must appear between |
| * all lettered arguments and the source file system pathname. |
| */ |
| if ( optind < argc && ! strcmp( argv[ optind ], "-" )) { |
| optind++; |
| } |
| |
| /* the last argument must be either the mount point or a |
| * device pathname of the file system to be dumped. |
| */ |
| if ( optind >= argc ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "source file system " |
| "not specified\n") ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| srcname = argv[ optind ]; |
| |
| if ( preemptchk( PREEMPT_FULL )) { |
| return BOOL_FALSE; |
| } |
| |
| /* allocate space for the subtree pointer array and load it |
| */ |
| if ( subtreecnt ) { |
| subtreep = ( char ** )calloc( subtreecnt, sizeof( char * )); |
| ASSERT( subtreep ); |
| optind = 1; |
| opterr = 0; |
| subtreeix = 0; |
| while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) { |
| switch ( c ) { |
| case GETOPT_SUBTREE: |
| ASSERT( subtreeix < subtreecnt ); |
| ASSERT( optarg && optarg[ 0 ] != '-' ); |
| subtreep[ subtreeix++ ] = optarg; |
| break; |
| } |
| } |
| ASSERT( subtreeix == subtreecnt ); |
| } else { |
| subtreep = 0; |
| } |
| |
| /* call a magic function to figure out if the last argument is |
| * a mount point or a device pathname, and retrieve the file |
| * system type, full pathname of the character special device |
| * containing the file system, the latest mount point, and the file |
| * system ID (uuid). returns BOOL_FALSE if the last |
| * argument doesn't look like a file system. |
| */ |
| if ( ! fs_info( fstype, |
| sizeof( fstype ), |
| FS_DEFAULT, |
| fsdevice, |
| sizeof( fsdevice ), |
| mntpnt, |
| sizeof( mntpnt ), |
| &fsid, |
| srcname )) { |
| |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "%s does not identify a file system\n"), |
| srcname ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| |
| /* verify that the file system is mounted. This must be enhanced |
| * to mount an unmounted file system on a temporary mount point, |
| * if it is not currently mounted. |
| */ |
| if ( ! fs_mounted( fstype, fsdevice, mntpnt, &fsid )) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "%s must be mounted to be dumped\n"), |
| srcname ); |
| return BOOL_FALSE; |
| } |
| |
| /* place the fs info in the write hdr template |
| */ |
| ( void )strncpyterm( cwhdrtemplatep->ch_mntpnt, |
| mntpnt, |
| sizeof( cwhdrtemplatep->ch_mntpnt )); |
| ( void )strncpyterm( cwhdrtemplatep->ch_fsdevice, |
| fsdevice, |
| sizeof( cwhdrtemplatep->ch_fsdevice )); |
| ( void )strncpyterm( cwhdrtemplatep->ch_fstype, |
| fstype, |
| sizeof( cwhdrtemplatep->ch_fstype )); |
| uuid_copy( cwhdrtemplatep->ch_fsid, fsid ); |
| |
| /* write quota information */ |
| if( sc_savequotas ) { |
| |
| sc_savequotas = BOOL_FALSE; |
| for(i = 0; i < (sizeof(quotas) / sizeof(quotas[0])); i++) { |
| quotas[i].savequotas = BOOL_FALSE; |
| qstat = getxfsqstat( fsdevice ); |
| if (qstat > 0 && (qstat & quotas[i].statflag) ) { |
| sprintf( quotas[i].quotapath, "%s/%s", mntpnt, quotas[i].quotafile ); |
| if( save_quotas( mntpnt, "as[i] )) { |
| if( subtreecnt ) { |
| subtreecnt++; |
| subtreep = (char **) realloc( subtreep, |
| subtreecnt * sizeof(char *)); |
| assert( subtreep ); |
| subtreep[ subtreecnt - 1 ] = quotas[i].quotafile; |
| } |
| sc_savequotas = BOOL_TRUE; |
| quotas[i].savequotas = BOOL_TRUE; |
| } else { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "failed to save %s information, continuing\n"), |
| quotas[i].desc ); |
| } |
| } |
| } |
| } |
| |
| |
| /* create my /var directory if it doesn't already exist. |
| */ |
| var_create( ); |
| |
| /* get two session descriptors from the inventory: one for the last |
| * dump at this level, and one for the last dump at a lower level. |
| * the former will be used to check if the last dump at this level |
| * was prematurely terminated; if so, for those inos already dumped |
| * then, we will look only for changes since that dump. the latter |
| * will give us a change date for all other inos. |
| */ |
| |
| if ( preemptchk( PREEMPT_FULL )) { |
| return BOOL_FALSE; |
| } |
| |
| /* briefly open the online dump inventory, so it can be used |
| * to calculate incremental and resumed dumps. |
| */ |
| inv_idbt = inv_open( ( inv_predicate_t )INV_BY_UUID, |
| INV_SEARCH_ONLY, |
| ( void * )&fsid ); |
| |
| /* if a based request, look for the indicated session. |
| * if found, and not interrupted, this will be used as an |
| * incremental base. if interrupted, will be used as |
| * resume base. |
| */ |
| if ( baseuuidvalpr ) { |
| ix_t strix; |
| ix_t strcnt; |
| inv_stream_t *bsp; |
| bool_t interruptedpr; |
| |
| underfoundpr = BOOL_FALSE; |
| samefoundpr = BOOL_FALSE; |
| underinterruptedpr = BOOL_FALSE; |
| sameinterruptedpr = BOOL_FALSE; |
| interruptedpr = BOOL_FALSE; |
| |
| ok = inv_get_session_byuuid( &baseuuid, &sessp ); |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "could not find specified base dump (%s) " |
| "in inventory\n"), |
| baseuuidstr ); |
| return BOOL_FALSE; |
| } |
| strcnt = ( ix_t )sessp->s_nstreams; |
| for ( strix = 0 ; strix < strcnt ; strix++ ) { |
| bsp = &sessp->s_streams[ strix ]; |
| if ( bsp->st_interrupted ) { |
| interruptedpr = BOOL_TRUE; |
| break; |
| } |
| } |
| |
| if ( interruptedpr ) { |
| sc_level = ( ix_t )sessp->s_level; |
| resumereqpr = BOOL_TRUE; |
| samefoundpr = BOOL_TRUE; |
| sametime = sessp->s_time; |
| uuid_copy (sameid, sessp->s_sesid); |
| samepartialpr = sessp->s_ispartial; |
| sameinterruptedpr = BOOL_TRUE; |
| sc_resumerangecnt = ( size_t )sessp->s_nstreams; |
| sc_resumerangep = ( drange_t * )calloc( sc_resumerangecnt, |
| sizeof( drange_t )); |
| ASSERT( sc_resumerangep ); |
| for ( strmix = 0 ; strmix < sc_resumerangecnt ; strmix++ ) { |
| inv_stream_t *bsp; |
| inv_stream_t *esp; |
| drange_t *p = &sc_resumerangep[ strmix ]; |
| bsp = &sessp->s_streams[ strmix ]; |
| esp = ( strmix < sc_resumerangecnt - 1 ) |
| ? |
| bsp + 1 |
| : |
| 0; |
| if ( bsp->st_interrupted ) { |
| sameinterruptedpr = BOOL_TRUE; |
| p->dr_begin.sp_ino = bsp->st_endino; |
| p->dr_begin.sp_offset = bsp->st_endino_off; |
| if ( esp ) { |
| p->dr_end.sp_ino = esp->st_startino; |
| p->dr_end.sp_offset = |
| esp->st_startino_off; |
| mlog( MLOG_DEBUG, |
| "resume range stream %u " |
| "ino %llu:%lld to " |
| "%llu:%lld\n", |
| strmix, |
| p->dr_begin.sp_ino, |
| p->dr_begin.sp_offset, |
| p->dr_end.sp_ino, |
| p->dr_end.sp_offset ); |
| } else { |
| p->dr_end.sp_flags = STARTPT_FLAGS_END; |
| mlog( MLOG_DEBUG, |
| "resume range stream %u " |
| "ino %llu:%lld to " |
| "end\n", |
| strmix, |
| p->dr_begin.sp_ino, |
| p->dr_begin.sp_offset ); |
| } |
| } else { |
| /* set the range start pt's END flag to |
| * indicate the range was not interrupted. |
| */ |
| p->dr_begin.sp_flags = STARTPT_FLAGS_END; |
| } |
| } |
| } else { |
| if ( sessp->s_level >= LEVEL_MAX ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cannot select dump session %d as base " |
| "for incremental dump: " |
| "level must be less than %d\n"), |
| sessp->s_level, |
| LEVEL_MAX ); |
| return BOOL_FALSE; |
| } |
| sc_level = ( ix_t )sessp->s_level + 1; |
| undertime = sessp->s_time; |
| underlevel = ( ix_t )sessp->s_level; |
| uuid_copy (underid, sessp->s_sesid); |
| underpartialpr = sessp->s_ispartial; |
| underinterruptedpr = BOOL_FALSE; |
| underfoundpr = BOOL_TRUE; |
| } |
| inv_free_session( &sessp ); |
| sessp = 0; |
| ok = inv_close( inv_idbt ); |
| ASSERT( ok ); |
| inv_idbt = INV_TOKEN_NULL; |
| goto baseuuidbypass; |
| } |
| |
| /* look for the most recent dump at a level less than the level |
| * of this dump. extract the time, level, id, and predicates partial |
| * and interrupted. |
| */ |
| underfoundpr = BOOL_FALSE; |
| if ( sc_level > 0 ) { |
| if ( inv_idbt == INV_TOKEN_NULL ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cannot calculate incremental dump: " |
| "online inventory not available\n") ); |
| return BOOL_FALSE; |
| } |
| ok = inv_lastsession_level_lessthan( inv_idbt, |
| ( u_char_t )sc_level, |
| &sessp ); |
| if ( ! ok ) { |
| sessp = 0; |
| } |
| |
| if ( sessp ) { |
| ix_t strix; |
| ix_t strcnt; |
| inv_stream_t *bsp; |
| |
| undertime = sessp->s_time; |
| underlevel = ( ix_t )sessp->s_level; |
| uuid_copy (underid, sessp->s_sesid); |
| underpartialpr = sessp->s_ispartial; |
| underinterruptedpr = BOOL_FALSE; |
| strcnt = ( ix_t )sessp->s_nstreams; |
| for ( strix = 0 ; strix < strcnt ; strix++ ) { |
| bsp = &sessp->s_streams[ strix ]; |
| if ( bsp->st_interrupted ) { |
| underinterruptedpr = BOOL_TRUE; |
| break; |
| } |
| } |
| underfoundpr = BOOL_TRUE; |
| inv_free_session( & sessp ); |
| sessp = 0; |
| } |
| } |
| |
| /* look for the most recent dump at a level equal to the level |
| * of this dump. extract the time, level, id, and predicates partial |
| * and interrupted, and for each stream the range of ino/offset |
| * values not dumped. |
| */ |
| if ( inv_idbt != INV_TOKEN_NULL ) { |
| /* REFERENCED */ |
| bool_t ok1; |
| ok = inv_lastsession_level_equalto( inv_idbt, |
| ( u_char_t )sc_level, |
| &sessp ); |
| ok1 = inv_close( inv_idbt ); |
| ASSERT( ok1 ); |
| if ( ! ok ) { |
| sessp = 0; |
| } |
| inv_idbt = INV_TOKEN_NULL; |
| } else { |
| sessp = 0; |
| } |
| |
| samefoundpr = BOOL_FALSE; |
| if ( sessp ) { |
| sametime = sessp->s_time; |
| uuid_copy(sameid, sessp->s_sesid); |
| samepartialpr = sessp->s_ispartial; |
| sameinterruptedpr = BOOL_FALSE; |
| sc_resumerangecnt = ( size_t )sessp->s_nstreams; |
| sc_resumerangep = ( drange_t * )calloc( sc_resumerangecnt, |
| sizeof( drange_t )); |
| ASSERT( sc_resumerangep ); |
| for ( strmix = 0 ; strmix < sc_resumerangecnt ; strmix++ ) { |
| inv_stream_t *bsp; |
| inv_stream_t *esp; |
| drange_t *p = &sc_resumerangep[ strmix ]; |
| bsp = &sessp->s_streams[ strmix ]; |
| esp = ( strmix < sc_resumerangecnt - 1 ) |
| ? |
| bsp + 1 |
| : |
| 0; |
| if ( bsp->st_interrupted ) { |
| sameinterruptedpr = BOOL_TRUE; |
| p->dr_begin.sp_ino = bsp->st_endino; |
| p->dr_begin.sp_offset = bsp->st_endino_off; |
| if ( esp ) { |
| p->dr_end.sp_ino = esp->st_startino; |
| p->dr_end.sp_offset = |
| esp->st_startino_off; |
| mlog( MLOG_DEBUG, |
| "resume range stream %u " |
| "ino %llu:%lld to " |
| "%llu:%lld\n", |
| strmix, |
| p->dr_begin.sp_ino, |
| p->dr_begin.sp_offset, |
| p->dr_end.sp_ino, |
| p->dr_end.sp_offset ); |
| } else { |
| p->dr_end.sp_flags = STARTPT_FLAGS_END; |
| mlog( MLOG_DEBUG, |
| "resume range stream %u " |
| "ino %llu:%lld to " |
| "end\n", |
| strmix, |
| p->dr_begin.sp_ino, |
| p->dr_begin.sp_offset ); |
| } |
| } else { |
| /* set the range start pt's END flag to |
| * indicate the range was not interrupted. |
| */ |
| p->dr_begin.sp_flags = STARTPT_FLAGS_END; |
| } |
| } |
| inv_free_session( & sessp ); |
| sessp = 0; |
| samefoundpr = BOOL_TRUE; |
| } |
| |
| baseuuidbypass: |
| |
| /* now determine the incremental and resume bases, if any. |
| */ |
| if ( samefoundpr && ! sameinterruptedpr ) { |
| free( ( void * )sc_resumerangep ); |
| sc_resumerangep = 0; |
| samefoundpr = BOOL_FALSE; |
| } |
| if ( samefoundpr && ! resumereqpr ) { |
| if ( ! underfoundpr || undertime <= sametime ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "most recent level %d dump " |
| "was interrupted, " |
| "but not resuming that dump since " |
| "resume (-R) option not specified\n"), |
| sc_level ); |
| } |
| free( ( void * )sc_resumerangep ); |
| sc_resumerangep = 0; |
| samefoundpr = BOOL_FALSE; |
| } |
| if ( underfoundpr ) { |
| ASSERT( underlevel <= LEVEL_MAX ); |
| ASSERT( undertime ); |
| if ( samefoundpr ) { |
| if ( undertime >= sametime ) { |
| if ( underinterruptedpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "most recent base for " |
| "incremental dump was " |
| "interrupted (level %u): " |
| "must resume or redump " |
| "at or below level %d\n"), |
| underlevel, |
| sc_level ); |
| return BOOL_FALSE; |
| } |
| if ( subtreecnt && ! underpartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "subtree dump " |
| "will be based on non-subtree " |
| "level %u dump\n"), |
| sc_level, |
| underlevel ); |
| } |
| if ( ! subtreecnt && underpartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "non-subtree dump " |
| "will be based on subtree " |
| "level %u dump\n"), |
| sc_level, |
| underlevel ); |
| } |
| sc_incrpr = BOOL_TRUE; |
| sc_incrbasetime = undertime; |
| sc_incrbaselevel = underlevel; |
| uuid_copy(sc_incrbaseid, underid); |
| sc_resumepr = BOOL_FALSE; |
| ASSERT( sc_resumerangep ); |
| free( ( void * )sc_resumerangep ); |
| sc_resumerangep = 0; |
| } else { |
| if ( subtreecnt && ! samepartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "subtree dump " |
| "will be based on non-subtree " |
| "level %u resumed dump\n"), |
| sc_level, |
| sc_level ); |
| } |
| if ( ! subtreecnt && samepartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "non-subtree dump " |
| "will be based on subtree " |
| "level %u resumed dump\n"), |
| sc_level, |
| sc_level ); |
| } |
| ASSERT( sametime ); |
| sc_incrpr = BOOL_TRUE; |
| sc_incrbasetime = undertime; |
| sc_incrbaselevel = underlevel; |
| sc_resumepr = BOOL_TRUE; |
| sc_resumebasetime = sametime; |
| uuid_copy(sc_resumebaseid, sameid); |
| ASSERT( sc_resumerangep ); |
| } |
| } else { |
| if ( underinterruptedpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "most recent base for " |
| "incremental dump was " |
| "interrupted (level %u): " |
| "must resume or redump " |
| "at or below level %d\n"), |
| underlevel, |
| sc_level ); |
| return BOOL_FALSE; |
| } |
| if ( subtreecnt && ! underpartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "subtree dump " |
| "will be based on non-subtree " |
| "level %u dump\n"), |
| sc_level, |
| underlevel ); |
| } |
| if ( ! subtreecnt && underpartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u incremental " |
| "non-subtree dump " |
| "will be based on subtree " |
| "level %u dump\n"), |
| sc_level, |
| underlevel ); |
| } |
| sc_incrpr = BOOL_TRUE; |
| sc_incrbasetime = undertime; |
| sc_incrbaselevel = underlevel; |
| uuid_copy(sc_incrbaseid, underid); |
| sc_resumepr = BOOL_FALSE; |
| ASSERT( ! sc_resumerangep ); |
| } |
| } else { |
| if ( samefoundpr ) { |
| ASSERT( sametime ); |
| if ( subtreecnt && ! samepartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u " |
| "subtree dump " |
| "will be based on non-subtree " |
| "level %u resumed dump\n"), |
| sc_level, |
| sc_level ); |
| } |
| if ( ! subtreecnt && samepartialpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "level %u " |
| "non-subtree dump " |
| "will be based on subtree " |
| "level %u resumed dump\n"), |
| sc_level, |
| sc_level ); |
| } |
| sc_incrpr = BOOL_FALSE; |
| sc_resumepr = BOOL_TRUE; |
| sc_resumebasetime = sametime; |
| uuid_copy(sc_resumebaseid, sameid); |
| ASSERT( sc_resumerangep ); |
| } else { |
| sc_incrpr = BOOL_FALSE; |
| sc_resumepr = BOOL_FALSE; |
| ASSERT( ! sc_resumerangep ); |
| if ( sc_level > 0 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cannot find earlier dump " |
| "to base level %d increment upon\n"), |
| sc_level ); |
| return BOOL_FALSE; |
| } |
| } |
| } |
| |
| /* don't allow interrupted dumps of a lesser level to be bases |
| */ |
| if ( sc_incrpr && underinterruptedpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "most recent base dump (level %d begun %s) " |
| "was interrupted: aborting\n"), |
| sc_incrbaselevel, |
| ctimennl( &sc_incrbasetime )); |
| return BOOL_FALSE; |
| } |
| |
| /* reject if resume (-R) specified, but base was not interrupted |
| */ |
| if ( ! sc_resumepr && resumereqpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "resume (-R) option inappropriate: " |
| "no interrupted level %d dump to resume\n"), |
| sc_level ); |
| return BOOL_FALSE; |
| } |
| |
| /* announce the dump characteristics |
| */ |
| if ( sc_incrpr ) { |
| if ( sc_resumepr ) { |
| char restimestr[ 30 ]; |
| char incrtimestr[ 30 ]; |
| |
| strcpy( restimestr, ctimennl( &sc_resumebasetime )); |
| ASSERT( strlen( restimestr ) < sizeof( restimestr )); |
| strcpy( incrtimestr, ctimennl( &sc_incrbasetime )); |
| ASSERT( strlen( incrtimestr ) < sizeof( incrtimestr )); |
| |
| mlog( MLOG_VERBOSE, _( |
| "resuming level %d incremental dump of %s:%s " |
| "begun %s " |
| "(incremental base level %d begun %s)\n"), |
| sc_level, |
| gwhdrtemplatep->gh_hostname, |
| mntpnt, |
| restimestr, |
| sc_incrbaselevel, |
| incrtimestr ); |
| } else { |
| mlog( MLOG_VERBOSE, _( |
| "level %d incremental dump of %s:%s " |
| "based on level %d dump begun %s\n"), |
| sc_level, |
| gwhdrtemplatep->gh_hostname, |
| mntpnt, |
| sc_incrbaselevel, |
| ctimennl( &sc_incrbasetime )); |
| } |
| } else { |
| if ( sc_resumepr ) { |
| mlog( MLOG_VERBOSE, _( |
| "resuming level %d dump of %s:%s begun %s\n"), |
| sc_level, |
| gwhdrtemplatep->gh_hostname, |
| mntpnt, |
| ctimennl( &sc_resumebasetime )); |
| } else { |
| mlog( MLOG_VERBOSE, _( |
| "level %d dump of %s:%s\n"), |
| sc_level, |
| gwhdrtemplatep->gh_hostname, |
| mntpnt ); |
| } |
| } |
| |
| if ( preemptchk( PREEMPT_FULL )) { |
| return BOOL_FALSE; |
| } |
| |
| /* announce the dump time |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "dump date: %s\n"), |
| ctimennl( &gwhdrtemplatep->gh_timestamp )); |
| |
| /* display the session UUID |
| */ |
| { |
| char string_uuid[UUID_STR_LEN + 1]; |
| uuid_unparse( gwhdrtemplatep->gh_dumpid, string_uuid ); |
| mlog( MLOG_VERBOSE, _( |
| "session id: %s\n"), |
| string_uuid ); |
| } |
| |
| /* display the session label |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "session label: \"%s\"\n"), |
| gwhdrtemplatep->gh_dumplabel ); |
| |
| /* get a file descriptor for the file system. any file |
| * contained in the file system will do; use the mntpnt. |
| * needed by bigstat. |
| */ |
| sc_fsfd = open( mntpnt, O_RDONLY ); |
| if ( sc_fsfd < 0 ) { |
| mlog( MLOG_NORMAL, _( |
| "unable to open %s: %s\n"), |
| mntpnt, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| /* figure out the ino for the root directory of the fs |
| * and get its xfs_bstat_t for inomap_build() |
| */ |
| { |
| stat64_t rootstat; |
| rval = fstat64( sc_fsfd, &rootstat ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL, _( |
| "could not stat %s\n"), |
| mntpnt ); |
| return BOOL_FALSE; |
| } |
| sc_rootxfsstatp = |
| ( xfs_bstat_t * )calloc( 1, sizeof( xfs_bstat_t )); |
| ASSERT( sc_rootxfsstatp ); |
| |
| if ( bigstat_one( sc_fsfd, rootstat.st_ino, sc_rootxfsstatp) < 0 ) { |
| mlog( MLOG_ERROR, |
| _("failed to get bulkstat information for root inode\n")); |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* alloc a file system handle, to be used with the jdm_open() |
| * functions. |
| */ |
| sc_fshandlep = jdm_getfshandle( mntpnt ); |
| if ( ! sc_fshandlep ) { |
| mlog( MLOG_NORMAL, _( |
| "unable to construct a file system handle for %s: %s\n"), |
| mntpnt, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| if ( preemptchk( PREEMPT_FULL )) { |
| return BOOL_FALSE; |
| } |
| |
| /* If GETOPT_DUMPASOFFLINE was specified, allocate a filesystem context |
| * for use by the HSM routines. |
| */ |
| |
| if (sc_dumpasoffline) { |
| hsm_fs_ctxtp = HsmInitFsysContext(mntpnt, HSM_API_VERSION_1); |
| } |
| |
| /* set now so statline can be displayed |
| */ |
| sc_stat_starttime = gwhdrtemplatep->gh_timestamp; |
| |
| /* allocate storage for the stream startpoints, and build inomap. |
| * inomap_build() also fills in the start points. storage only needed |
| * until the startpoints are copied into each streams header. will |
| * be freed at the end of this function. |
| */ |
| sc_stat_inomapcnt = ( size64_t )fs_getinocnt( mntpnt ); |
| |
| sc_startptp = ( startpt_t * )calloc( drivecnt, sizeof( startpt_t )); |
| ASSERT( sc_startptp ); |
| ok = inomap_build( sc_fshandlep, |
| sc_fsfd, |
| sc_rootxfsstatp, |
| sc_incrpr, |
| sc_incrbasetime, |
| sc_resumepr, |
| sc_resumebasetime, |
| sc_resumerangecnt, |
| sc_resumerangep, |
| subtreep, |
| subtreecnt, |
| skip_unchanged_dirs, |
| sc_startptp, |
| drivecnt, |
| &sc_stat_inomapphase, |
| &sc_stat_inomappass, |
| sc_stat_inomapcnt, |
| &sc_stat_inomapdone ); |
| free( ( void * )subtreep ); |
| subtreep = 0; |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| |
| /* ask var to ask inomap to skip files under var if var is in |
| * the fs being dumped |
| */ |
| var_skip( &fsid, inomap_skip ); |
| |
| /* fill in write header template content info. always produce |
| * an inomap for each media file. the dirdump flag will be set |
| * in content_stream_dump() for streams which dump the directories. |
| */ |
| ASSERT( sizeof( cwhdrtemplatep->ch_specific ) >= sizeof( *scwhdrtemplatep )); |
| scwhdrtemplatep->cih_mediafiletype = CIH_MEDIAFILETYPE_DATA; |
| scwhdrtemplatep->cih_level = ( int32_t )sc_level; |
| scwhdrtemplatep->cih_dumpattr = CIH_DUMPATTR_INOMAP; |
| if ( subtreecnt ) { |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_SUBTREE; |
| } |
| if ( sc_inv_updatepr ) { |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_INVENTORY; |
| } |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_FILEHDR_CHECKSUM; |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_EXTENTHDR_CHECKSUM; |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_DIRENTHDR_CHECKSUM; |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_DIRENTHDR_GEN; |
| if ( sc_incrpr ) { |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_INCREMENTAL; |
| scwhdrtemplatep->cih_last_time = sc_incrbasetime; |
| uuid_copy(scwhdrtemplatep->cih_last_id, sc_incrbaseid); |
| if ( skip_unchanged_dirs ) { |
| scwhdrtemplatep->cih_dumpattr |= |
| CIH_DUMPATTR_NOTSELFCONTAINED; |
| } |
| } |
| if ( sc_resumepr ) { |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_RESUME; |
| scwhdrtemplatep->cih_resume_time = sc_resumebasetime; |
| uuid_copy(scwhdrtemplatep->cih_resume_id, sc_resumebaseid); |
| } |
| if ( sc_dumpextattrpr ) { |
| scwhdrtemplatep->cih_dumpattr |= CIH_DUMPATTR_EXTATTR; |
| scwhdrtemplatep->cih_dumpattr |= |
| CIH_DUMPATTR_EXTATTRHDR_CHECKSUM; |
| } |
| |
| scwhdrtemplatep->cih_rootino = sc_rootxfsstatp->bs_ino; |
| inomap_writehdr( scwhdrtemplatep ); |
| |
| /* log the dump size. just a rough approx. |
| */ |
| dircnt = scwhdrtemplatep->cih_inomap_dircnt; |
| nondircnt = scwhdrtemplatep->cih_inomap_nondircnt; |
| datasz = scwhdrtemplatep->cih_inomap_datasz; |
| inocnt = dircnt + nondircnt; |
| inomapsz = inomap_getsz( ); |
| direntsz = inocnt * ( u_int64_t )( DIRENTHDR_SZ + 8 ); |
| filesz = inocnt * ( u_int64_t )( FILEHDR_SZ + EXTENTHDR_SZ ); |
| |
| hdr_mfilesz = GLOBAL_HDR_SZ |
| + |
| inomapsz |
| + |
| direntsz; |
| size_estimate = hdr_mfilesz |
| + |
| filesz |
| + |
| datasz; |
| mlog( MLOG_VERBOSE, _( |
| "estimated dump size: %llu bytes\n"), |
| size_estimate ); |
| |
| if (drivecnt > 1) { |
| mlog( MLOG_VERBOSE, _( |
| "estimated dump size per stream: %llu bytes\n"), |
| hdr_mfilesz + (filesz + datasz) / drivecnt); |
| } |
| mlog( MLOG_DEBUG, |
| "estimated dump header size: %llu bytes\n", |
| hdr_mfilesz ); |
| mlog( MLOG_DEBUG, |
| "estimated component sizes: global hdr: %llu bytes, " |
| "inomap: %llu bytes, dir entries: %llu bytes, " |
| "file hdrs: %llu bytes, datasz: %llu bytes\n", |
| GLOBAL_HDR_SZ, inomapsz, direntsz, |
| filesz, datasz ); |
| |
| /* extract the progress stat denominators from the write hdr |
| * template. placed there by inomap_writehdr( ) |
| */ |
| sc_stat_dircnt = scwhdrtemplatep->cih_inomap_dircnt; |
| sc_stat_nondircnt = scwhdrtemplatep->cih_inomap_nondircnt; |
| sc_stat_datasz = scwhdrtemplatep->cih_inomap_datasz; |
| |
| /* allocate and populate per-stream context descriptors |
| */ |
| sc_contextp = ( context_t * )calloc( drivecnt, sizeof( context_t )); |
| ASSERT( sc_contextp ); |
| for ( strmix = 0 ; strmix < drivecnt ; strmix++ ) { |
| context_t *contextp = &sc_contextp[ strmix ]; |
| |
| contextp->cc_filehdrp = |
| ( filehdr_t * )calloc( 1, sizeof( filehdr_t )); |
| ASSERT( contextp->cc_filehdrp ); |
| |
| contextp->cc_extenthdrp = |
| ( extenthdr_t * )calloc( 1, sizeof( extenthdr_t )); |
| ASSERT( contextp->cc_extenthdrp ); |
| |
| contextp->cc_getdentsbufsz = sizeof( struct dirent ) |
| + |
| NAME_MAX + 1; |
| if ( contextp->cc_getdentsbufsz < GETDENTSBUF_SZ_MIN ) { |
| contextp->cc_getdentsbufsz = GETDENTSBUF_SZ_MIN; |
| } |
| contextp->cc_getdentsbufp = |
| ( char * ) calloc( 1, contextp->cc_getdentsbufsz ); |
| ASSERT( contextp->cc_getdentsbufp ); |
| |
| contextp->cc_mdirentbufsz = sizeof( direnthdr_t ) |
| + |
| NAME_MAX + 1 |
| + |
| DIRENTHDR_ALIGN; |
| contextp->cc_mdirentbufp = |
| ( char * ) calloc( 1, contextp->cc_mdirentbufsz ); |
| ASSERT( contextp->cc_mdirentbufp ); |
| |
| contextp->cc_extattrlistbufsz = EXTATTR_LISTBUF_SZ; |
| contextp->cc_extattrrtrvarraylen = EXTATTR_RTRVARRAY_LEN; |
| contextp->cc_extattrdumpbufsz = 2 * ATTR_MAX_VALUELEN; |
| if ( contextp->cc_extattrdumpbufsz < EXTATTR_DUMPBUF_SZ ) { |
| contextp->cc_extattrdumpbufsz = EXTATTR_DUMPBUF_SZ; |
| } |
| contextp->cc_extattrlistbufp = |
| ( char * )calloc( 1, contextp->cc_extattrlistbufsz ); |
| ASSERT( contextp->cc_extattrlistbufp ); |
| contextp->cc_extattrrtrvarrayp = |
| ( attr_multiop_t * )calloc( contextp->cc_extattrrtrvarraylen, |
| sizeof( attr_multiop_t )); |
| ASSERT( contextp->cc_extattrrtrvarrayp ); |
| contextp->cc_extattrdumpbufp = |
| ( char * )memalign( sizeof( extattrhdr_t ), |
| contextp->cc_extattrdumpbufsz ); |
| ASSERT( contextp->cc_extattrdumpbufp ); |
| if (hsm_fs_ctxtp) { |
| contextp->cc_hsm_f_ctxtp = HsmAllocateFileContext( |
| hsm_fs_ctxtp); |
| } else { |
| contextp->cc_hsm_f_ctxtp = NULL; |
| } |
| |
| contextp->cc_readlinkbufsz = MAXPATHLEN + SYMLINK_ALIGN; |
| contextp->cc_readlinkbufp = |
| ( char * ) calloc( 1, contextp->cc_readlinkbufsz ); |
| ASSERT( contextp->cc_readlinkbufp ); |
| |
| contextp->cc_inomap_contextp = inomap_alloc_context( ); |
| } |
| |
| /* look for command line media labels. these will be assigned |
| * to each stream as found. this label is only for the media |
| * object currently in the drive. subsequently inserted media |
| * objects must get a label via prompting. |
| */ |
| |
| { |
| context_t *cp = sc_contextp; |
| context_t *ep = sc_contextp + drivecnt; |
| |
| optind = 1; |
| opterr = 0; |
| while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) { |
| switch ( c ) { |
| case GETOPT_MEDIALABEL: |
| if ( cp >= ep ) { |
| mlog( MLOG_NORMAL, _( |
| "more -%c arguments " |
| "than number of drives\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| cp->cc_Media_firstlabel = optarg; |
| cp++; |
| break; |
| } |
| } |
| |
| if ( cp > sc_contextp && cp < ep ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "media labels given for only %d out of %d " |
| "drives\n"), |
| cp - sc_contextp, |
| drivecnt ); |
| } |
| } |
| |
| if ( preemptchk( PREEMPT_FULL )) { |
| return BOOL_FALSE; |
| } |
| |
| /* open the dump inventory and a dump inventory write session |
| * if an inventory update is to be done. |
| */ |
| if ( sc_inv_updatepr ) { |
| bool_t result; |
| sigset_t tty_set, orig_set; |
| |
| /* hold tty signals while creating a new inventory session */ |
| sigemptyset( &tty_set ); |
| sigaddset( &tty_set, SIGINT ); |
| sigaddset( &tty_set, SIGQUIT ); |
| sigaddset( &tty_set, SIGHUP ); |
| pthread_sigmask( SIG_BLOCK, &tty_set, &orig_set ); |
| |
| result = create_inv_session( gwhdrtemplatep, &fsid, mntpnt, |
| fsdevice, subtreecnt, strmix ); |
| |
| pthread_sigmask( SIG_SETMASK, &orig_set, NULL ); |
| |
| if ( !result ) { |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* set media change flags to FALSE; |
| */ |
| { |
| ix_t ix; |
| ix_t endix = sizeof( sc_mcflag ) |
| / |
| sizeof( sc_mcflag[ 0 ] ); |
| for ( ix = 0 ; ix < endix ; ix++ ) { |
| sc_mcflag[ ix ] = BOOL_FALSE; |
| } |
| } |
| content_media_change_needed = BOOL_FALSE; |
| |
| /* initialize the per-drive status |
| */ |
| { |
| ix_t driveix; |
| for ( driveix = 0 ; driveix < STREAM_SIMMAX ; driveix++ ) { |
| sc_stat_pds[ driveix ].pds_phase = PDS_NULL; |
| } |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| #define STATLINESZ 160 |
| |
| size_t |
| content_statline( char **linespp[ ] ) |
| { |
| static char statlinebuf[ STREAM_SIMMAX + 1 ][ STATLINESZ ]; |
| static char *statline[ STREAM_SIMMAX + 1 ]; |
| size_t statlinecnt; |
| size64_t nondirdone; |
| size64_t datadone; |
| double percent; |
| time_t elapsed; |
| time_t now; |
| struct tm *tmp; |
| ix_t i; |
| |
| /* build and supply the line array |
| */ |
| for ( i = 0 ; i < STREAM_SIMMAX + 1 ; i++ ) { |
| statline[ i ] = &statlinebuf[ i ][ 0 ]; |
| } |
| *linespp = statline; |
| statlinecnt = 0; |
| |
| /* if start time not initialized, return no strings |
| */ |
| if ( ! sc_stat_starttime ) { |
| return 0; |
| } |
| |
| /* calculate the elapsed time |
| */ |
| now = time( 0 ); |
| elapsed = now - sc_stat_starttime; |
| |
| /* get local time |
| */ |
| tmp = localtime( &now ); |
| |
| /* if inomap phase indicated, report on that |
| */ |
| if ( sc_stat_inomapphase && sc_stat_inomapcnt ) { |
| if ( sc_stat_inomappass ) { |
| sprintf( statline[ 0 ], |
| "status at %02d:%02d:%02d: " |
| "inomap phase %u pass %u " |
| "%llu/%llu inos scanned, " |
| "%ld seconds elapsed\n", |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| (unsigned int)sc_stat_inomapphase, |
| (unsigned int)sc_stat_inomappass, |
| (unsigned long long)sc_stat_inomapdone, |
| (unsigned long long)sc_stat_inomapcnt, |
| elapsed ); |
| ASSERT( strlen( statline[ 0 ] ) < STATLINESZ ); |
| } else { |
| sprintf( statline[ 0 ], |
| "status at %02d:%02d:%02d: " |
| "inomap phase %u " |
| "%llu/%llu inos scanned, " |
| "%ld seconds elapsed\n", |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| (unsigned int)sc_stat_inomapphase, |
| (unsigned long long)sc_stat_inomapdone, |
| (unsigned long long)sc_stat_inomapcnt, |
| elapsed ); |
| ASSERT( strlen( statline[ 0 ] ) < STATLINESZ ); |
| } |
| return 1; |
| } |
| |
| /* get the accumulated totals for non-dir inos and data bytes dumped |
| */ |
| lock( ); |
| nondirdone = sc_stat_nondirdone; |
| datadone = sc_stat_datadone; |
| unlock( ); |
| |
| /* non-dir dump phase */ |
| if ( nondirdone || datadone ) { |
| /* calculate percentage of data dumped |
| */ |
| if ( sc_stat_datasz ) { |
| percent = ( double )datadone |
| / |
| ( double )sc_stat_datasz; |
| percent *= 100.0; |
| } else { |
| percent = 100.0; |
| } |
| if ( percent > 100.0 ) { |
| percent = 100.0; |
| } |
| |
| /* format the status line in a local static buffer (non-re-entrant!) |
| */ |
| sprintf( statline[ 0 ], |
| "status at %02d:%02d:%02d: %llu/%llu files dumped, " |
| "%.1lf%%%% data dumped, " |
| "%ld seconds elapsed\n", |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| (unsigned long long) nondirdone, |
| (unsigned long long) sc_stat_nondircnt, |
| percent, |
| elapsed ); |
| } else { |
| sprintf( statline[ 0 ], |
| "status at %02d:%02d:%02d: " |
| "%ld seconds elapsed\n", |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| elapsed ); |
| } |
| |
| ASSERT( strlen( statline[ 0 ] ) < STATLINESZ ); |
| |
| /* optionally create stat lines for each drive |
| */ |
| statlinecnt = 1; |
| for ( i = 0 ; i < drivecnt ; i++ ) { |
| pds_t *pdsp = &sc_stat_pds[ i ]; |
| if ( pdsp->pds_phase == PDS_NULL |
| || |
| pdsp->pds_phase == PDS_NONDIR ) { |
| continue; |
| } |
| statline[ statlinecnt ][ 0 ] = 0; |
| if ( drivecnt > 1 ) { |
| sprintf( statline[ statlinecnt ], |
| "drive %u: ", |
| (unsigned int)i ); |
| } |
| switch( pdsp->pds_phase ) { |
| case PDS_INOMAP: |
| strcat( statline[ statlinecnt ], |
| "dumping inomap" ); |
| break; |
| case PDS_DIRDUMP: |
| sprintf( &statline[ statlinecnt ] |
| [ strlen( statline[ statlinecnt ] ) ], |
| "%llu/%llu directories dumped", |
| (unsigned long long)pdsp->pds_dirdone, |
| (unsigned long long)sc_stat_dircnt ); |
| break; |
| case PDS_INVSYNC: |
| strcat( statline[ statlinecnt ], |
| "waiting to dump inventory" ); |
| break; |
| case PDS_INVDUMP: |
| strcat( statline[ statlinecnt ], |
| "dumping inventory" ); |
| break; |
| case PDS_TERMDUMP: |
| strcat( statline[ statlinecnt ], |
| "dumping stream terminator" ); |
| break; |
| default: |
| break; |
| } |
| sprintf( &statline[ statlinecnt ] |
| [ strlen( statline[ statlinecnt ] ) ], |
| "\n" ); |
| ASSERT( strlen( statline[ statlinecnt ] ) < STATLINESZ ); |
| statlinecnt++; |
| } |
| |
| return statlinecnt; |
| } |
| |
| static bool_t |
| create_inv_session( |
| global_hdr_t *gwhdrtemplatep, |
| uuid_t *fsidp, |
| const char *mntpnt, |
| const char *fsdevice, |
| ix_t subtreecnt, |
| size_t strmix) |
| { |
| intgen_t rval; |
| char *qmntpnt; |
| char *qfsdevice; |
| |
| /* create a cleanup handler to close the inventory on exit. */ |
| rval = atexit( inv_cleanup ); |
| ASSERT( ! rval ); |
| |
| sc_inv_idbtoken = inv_open( ( inv_predicate_t )INV_BY_UUID, |
| INV_SEARCH_N_MOD, |
| ( void * )fsidp ); |
| if ( sc_inv_idbtoken == INV_TOKEN_NULL ) { |
| return BOOL_FALSE; |
| } |
| qmntpnt = ( char * )calloc( 1, strlen( gwhdrtemplatep->gh_hostname ) |
| + 1 + strlen( mntpnt ) + 1 ); |
| ASSERT( qmntpnt ); |
| ASSERT( strlen( gwhdrtemplatep->gh_hostname )); |
| sprintf( qmntpnt, "%s:%s", gwhdrtemplatep->gh_hostname, mntpnt ); |
| qfsdevice = ( char * )calloc( 1, strlen( gwhdrtemplatep->gh_hostname ) |
| + 1 + strlen( fsdevice ) + 1 ); |
| ASSERT( qfsdevice ); |
| sprintf( qfsdevice, "%s:%s", gwhdrtemplatep->gh_hostname, fsdevice ); |
| |
| sc_inv_sestoken = inv_writesession_open( sc_inv_idbtoken, |
| fsidp, |
| &gwhdrtemplatep->gh_dumpid, |
| gwhdrtemplatep->gh_dumplabel, |
| subtreecnt ? BOOL_TRUE |
| : BOOL_FALSE, |
| sc_resumepr, |
| ( u_char_t )sc_level, |
| drivecnt, |
| gwhdrtemplatep->gh_timestamp, |
| qmntpnt, |
| qfsdevice ); |
| if ( sc_inv_sestoken == INV_TOKEN_NULL ) { |
| return BOOL_FALSE; |
| } |
| |
| /* open an inventory stream for each stream |
| */ |
| sc_inv_stmtokenp = ( inv_stmtoken_t * ) |
| calloc( drivecnt, sizeof( inv_stmtoken_t )); |
| ASSERT( sc_inv_stmtokenp ); |
| for ( strmix = 0 ; strmix < drivecnt ; strmix++ ) { |
| drive_t *drivep = drivepp[ strmix ]; |
| char *drvpath; |
| |
| if ( strcmp( drivep->d_pathname, "stdio" )) { |
| drvpath = path_reltoabs( drivep->d_pathname, homedir ); |
| } else { |
| drvpath = drivep->d_pathname; |
| } |
| sc_inv_stmtokenp[ strmix ] = inv_stream_open( sc_inv_sestoken, |
| drvpath ); |
| if ( strcmp( drivep->d_pathname, "stdio" )) { |
| free( ( void * )drvpath ); |
| } |
| if ( sc_inv_stmtokenp[ strmix ] == INV_TOKEN_NULL ) { |
| return BOOL_FALSE; |
| } |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| static void |
| mark_set( drive_t *drivep, xfs_ino_t ino, off64_t offset, int32_t flags ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| mark_t *markp = ( mark_t * )calloc( 1, sizeof( mark_t )); |
| ASSERT( markp ); |
| |
| if ( flags & STARTPT_FLAGS_NULL ) { |
| mlog( MLOG_DEBUG, |
| "setting media NULL mark\n" ); |
| } else if ( flags & STARTPT_FLAGS_END ) { |
| mlog( MLOG_DEBUG, |
| "setting media END mark\n" ); |
| } else { |
| mlog( MLOG_DEBUG, |
| "setting media mark" |
| " for ino %llu offset %lld\n", |
| ino, |
| offset ); |
| } |
| |
| markp->startpt.sp_ino = ino; |
| markp->startpt.sp_offset = offset; |
| markp->startpt.sp_flags = flags; |
| ( * dop->do_set_mark )( drivep, |
| mark_callback, |
| ( void * )drivep->d_index, |
| ( drive_markrec_t * )markp ); |
| } |
| |
| static void |
| mark_callback( void *p, drive_markrec_t *dmp, bool_t committed ) |
| { |
| /* get context |
| */ |
| ix_t strmix = ( ix_t )p; |
| context_t *contextp = &sc_contextp[ strmix ]; |
| drive_t *drivep = drivepp[ strmix ]; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| media_hdr_t *mwhdrp = ( media_hdr_t * )dwhdrp->dh_upper; |
| content_hdr_t *cwhdrp = ( content_hdr_t * )mwhdrp->mh_upper; |
| content_inode_hdr_t *scwhdrp = ( content_inode_hdr_t * ) |
| ( void * ) |
| cwhdrp->ch_specific; |
| |
| /* this is really a mark_t, allocated by mark_set() |
| */ |
| mark_t *markp = ( mark_t * )dmp; |
| |
| if ( committed ) { |
| /* bump the per-mfile mark committed count |
| */ |
| contextp->cc_markscommitted++; |
| |
| /* copy the mark into the write header: this establishes the |
| * starting point should we need to retry the non-dir portion |
| * of the dump |
| */ |
| |
| /* log the mark commit |
| */ |
| if ( markp->startpt.sp_flags & STARTPT_FLAGS_NULL ) { |
| mlog( MLOG_DEBUG, |
| "media NULL mark committed" |
| " in media file %d\n", |
| mwhdrp->mh_dumpfileix ); |
| scwhdrp->cih_startpt.sp_flags |= STARTPT_FLAGS_NULL; |
| } else if ( markp->startpt.sp_flags & STARTPT_FLAGS_END ) { |
| mlog( MLOG_DEBUG, |
| "media END mark committed" |
| " in media file %d\n", |
| mwhdrp->mh_dumpfileix ); |
| if ( scwhdrp->cih_endpt.sp_flags & STARTPT_FLAGS_END ) { |
| scwhdrp->cih_startpt.sp_ino++; |
| scwhdrp->cih_startpt.sp_offset = 0; |
| } else { |
| scwhdrp->cih_startpt = scwhdrp->cih_endpt; |
| } |
| scwhdrp->cih_startpt.sp_flags |= STARTPT_FLAGS_END; |
| } else { |
| mlog( MLOG_DEBUG, |
| "media mark committed" |
| " for ino %llu offset %lld" |
| " in media file %d\n", |
| markp->startpt.sp_ino, |
| markp->startpt.sp_offset, |
| mwhdrp->mh_dumpfileix ); |
| scwhdrp->cih_startpt = markp->startpt; |
| } |
| } else { |
| /* note the mark was not committed |
| */ |
| if ( markp->startpt.sp_flags & STARTPT_FLAGS_NULL ) { |
| mlog( MLOG_DEBUG, |
| "media NULL mark -NOT- committed\n" ); |
| } else if ( markp->startpt.sp_flags & STARTPT_FLAGS_END ) { |
| mlog( MLOG_DEBUG, |
| "media END mark -NOT- committed\n" ); |
| } else { |
| mlog( MLOG_DEBUG, |
| "media mark -NOT- committed" |
| " for ino %llu offset %lld\n", |
| markp->startpt.sp_ino, |
| markp->startpt.sp_offset ); |
| } |
| } |
| |
| /* get rid of this mark (it was allocated by mark_set()) |
| */ |
| free( ( void * )markp ); |
| } |
| |
| /* begin - called by stream process to invoke the dump stream |
| */ |
| intgen_t |
| content_stream_dump( ix_t strmix ) |
| { |
| context_t *contextp = &sc_contextp[ strmix ]; |
| drive_t *drivep = drivepp[ strmix ]; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| media_hdr_t *mwhdrp = ( media_hdr_t * )dwhdrp->dh_upper; |
| content_hdr_t *cwhdrp = ( content_hdr_t * )mwhdrp->mh_upper; |
| content_inode_hdr_t *scwhdrp = ( content_inode_hdr_t * ) |
| cwhdrp->ch_specific; |
| void *inomap_contextp; |
| bool_t all_nondirs_committed; |
| bool_t empty_mediafile; |
| time_t elapsed; |
| inv_stmtoken_t inv_stmt; |
| xfs_bstat_t *bstatbufp; |
| const size_t bstatbuflen = BSTATBUFLEN; |
| intgen_t rval; |
| rv_t rv; |
| |
| /* sanity checks |
| */ |
| ASSERT( RV_OK == 0 ); /* bigstat_iter depends on this */ |
| |
| /* allocate a buffer for use by bstat_iter |
| */ |
| bstatbufp = ( xfs_bstat_t * )calloc( bstatbuflen, |
| sizeof( xfs_bstat_t )); |
| ASSERT( bstatbufp ); |
| |
| /* allocate an inomap context */ |
| inomap_contextp = inomap_alloc_context(); |
| ASSERT( inomap_contextp ); |
| |
| /* determine if stream terminators will be used and are expected. |
| * this will be revised each time a new media file is begun. |
| */ |
| update_cc_Media_useterminatorpr( drivep, contextp ); |
| |
| /* check in |
| */ |
| lock( ); |
| sc_thrdsarrivedcnt++; |
| unlock( ); |
| |
| /* fill in write hdr stream start and end points |
| */ |
| scwhdrp->cih_startpt = sc_startptp[ strmix ]; |
| if ( strmix < drivecnt - 1 ) { |
| scwhdrp->cih_endpt = sc_startptp[ strmix + 1 ]; |
| } else { |
| scwhdrp->cih_endpt.sp_flags = STARTPT_FLAGS_END; |
| } |
| |
| // the first stream dumps the directories |
| if ( strmix == 0 ) { |
| scwhdrp->cih_dumpattr |= CIH_DUMPATTR_DIRDUMP; |
| } |
| |
| /* fill in inomap fields of write hdr |
| */ |
| inomap_writehdr( scwhdrp ); |
| |
| /* used to decide if any non-dirs not yet on media |
| */ |
| all_nondirs_committed = BOOL_FALSE; |
| |
| /* used to guarantee we don't count the same ino more than once |
| * in the progress stats |
| */ |
| contextp->cc_stat_lastino = 0; |
| |
| /* used to detect generation of an empty media file; |
| * contains at most an inomap and dirdump and null file hdr. |
| */ |
| empty_mediafile = BOOL_FALSE; |
| |
| /* get the inventory stream token |
| */ |
| if ( sc_inv_stmtokenp ) { |
| inv_stmt = sc_inv_stmtokenp[ strmix ]; |
| } else { |
| inv_stmt = INV_TOKEN_NULL; |
| } |
| |
| /* loop, dumping media files, until the entire stream is dumped. |
| * each time we hit EOM/EOF, repeat the inomap and directory dump. |
| * dump the non-dirs beginning with the current startpoint. |
| * The current startpoint will be updated each time a media mark |
| * is committed. |
| */ |
| for ( ; ; ) { |
| xfs_ino_t startino; |
| bool_t stop_requested; |
| bool_t hit_eom; |
| bool_t all_dirs_committed; |
| bool_t all_nondirs_sent; |
| off64_t startoffset; |
| off64_t ncommitted; |
| bool_t done; |
| |
| /* used to decide whether or not to go back for more. |
| */ |
| stop_requested = BOOL_FALSE; |
| |
| /* TRUE if hit EOM while dumping |
| */ |
| hit_eom = BOOL_FALSE; |
| |
| /* used to decide if the media file contains all |
| * of the inomap and dirdump. |
| */ |
| all_dirs_committed = BOOL_FALSE; |
| |
| /* used to decide if all non-dirs were sent (not necessarily |
| * committed) |
| */ |
| all_nondirs_sent = BOOL_FALSE; |
| |
| /* always clear the NULL flag from the stream startpoint |
| * before beginning the media file. allows detection |
| * of null file hdr commit. |
| */ |
| scwhdrp->cih_startpt.sp_flags &= ~STARTPT_FLAGS_NULL; |
| |
| /* save the original start points, to be given to |
| * the inventory at the end of each media file. |
| */ |
| startino = scwhdrp->cih_startpt.sp_ino; |
| startoffset = scwhdrp->cih_startpt.sp_offset; |
| |
| /* set the accumulated file size to zero. |
| * this will be monitored by dump_file() to decide |
| * if the current dump file is too long. if so, |
| * it will set a startpoint and spoof an EOF. |
| * this will cause a new dump file to be started, |
| * beginning at the startpoint. |
| */ |
| contextp->cc_mfilesz = 0; |
| |
| /* tell the Media abstraction to position a media object |
| * and begin a new media file. This will dump the media |
| * file header if successful. |
| */ |
| rv = Media_mfile_begin( drivep, contextp, BOOL_TRUE ); |
| if ( rv == RV_INTR ) { |
| return mlog_exit(EXIT_INTERRUPT, rv); |
| } |
| if ( rv == RV_TIMEOUT ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change timeout will be treated as " |
| "a request to stop using drive: " |
| "can resume later\n") ); |
| mlog_exit_hint(RV_QUIT); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_QUIT ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change decline will be treated as " |
| "a request to stop using drive: " |
| "can resume later\n") ); |
| mlog_exit_hint(RV_QUIT); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_DRIVE ) { |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_ERROR ) { |
| return mlog_exit(EXIT_ERROR, rv); |
| } |
| if ( rv == RV_CORE ) { |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| ASSERT( rv == RV_OK ); |
| if ( rv != RV_OK ) { |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| |
| /* sync up here with other streams if reasonable |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "creating dump session media file %u " |
| "(media %u, file %u)\n"), |
| mwhdrp->mh_dumpfileix, |
| mwhdrp->mh_mediaix, |
| mwhdrp->mh_mediafileix ); |
| |
| /* initialize the count of marks committed in the media file. |
| * will be bumped by mark_callback(). |
| */ |
| contextp->cc_markscommitted = 0; |
| |
| /* first dump the inomap |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "dumping ino map\n") ); |
| sc_stat_pds[ strmix ].pds_phase = PDS_INOMAP; |
| rv = inomap_dump( drivep ); |
| if ( rv == RV_INTR ) { |
| stop_requested = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_EOM ) { |
| hit_eom = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_DRIVE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_ERROR ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_ERROR, rv); |
| } |
| if ( rv == RV_CORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| ASSERT( rv == RV_OK ); |
| if ( rv != RV_OK ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| |
| /* now dump the directories, if this is a stream that dumps |
| * directories. use the bigstat iterator capability to call |
| * my dump_dir function for each directory in the bitmap. |
| */ |
| if ( scwhdrp->cih_dumpattr & CIH_DUMPATTR_DIRDUMP ) { |
| sc_stat_pds[ strmix ].pds_dirdone = 0; |
| rv = dump_dirs( strmix, |
| bstatbufp, |
| bstatbuflen, |
| inomap_contextp ); |
| if ( rv == RV_INTR ) { |
| stop_requested = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_EOM ) { |
| hit_eom = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_DRIVE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_ERROR ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_ERROR, rv); |
| } |
| if ( rv == RV_CORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| ASSERT( rv == RV_OK ); |
| if ( rv != RV_OK ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| } |
| |
| /* finally, dump the non-directory files beginning with this |
| * stream's startpoint. Note that dump_file will set one or |
| * more media marks; the callback will update the hdr's |
| * startpoint; thus each time a demarcated portion of a |
| * non-directory file is fully committed to media, |
| * the starting point for the next media file will be advanced. |
| */ |
| if ( ! all_nondirs_committed ) { |
| mlog( MLOG_VERBOSE, _( |
| "dumping non-directory files\n") ); |
| sc_stat_pds[ strmix ].pds_phase = PDS_NONDIR; |
| rv = RV_OK; |
| inomap_reset_context(inomap_contextp); |
| rval = bigstat_iter( sc_fshandlep, |
| sc_fsfd, |
| BIGSTAT_ITER_NONDIR, |
| scwhdrp->cih_startpt.sp_ino, |
| ( bstat_cbfp_t )dump_file, |
| ( void * )strmix, |
| inomap_next_nondir, |
| inomap_contextp, |
| ( intgen_t * )&rv, |
| pipeline ? |
| (bool_t (*)(int))preemptchk : 0, |
| bstatbufp, |
| bstatbuflen ); |
| if ( rval ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, RV_CORE); |
| } |
| if ( rv == RV_INTR ) { |
| stop_requested = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_EOM ) { |
| hit_eom = BOOL_TRUE; |
| goto decision_more; |
| } |
| if ( rv == RV_EOF ) { |
| goto decision_more; |
| } |
| if ( rv == RV_DRIVE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_ERROR ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_ERROR, rv); |
| } |
| if ( rv == RV_CORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| ASSERT( rv == RV_OK || rv == RV_NOMORE ); |
| if ( rv != RV_OK && rv != RV_NOMORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| } |
| |
| /* if we got here, all files were sent without hitting |
| * the end of the current media object, or hitting the |
| * media file size limit. send the special END mark. |
| * this is only send at the end of the last media file in the |
| * dump session. if it actually gets committed to media, |
| * we know the last data written to media all made it. |
| * we won't know if this mark is committed to media until |
| * we attempt to end the write stream. |
| */ |
| all_nondirs_sent = BOOL_TRUE; |
| mark_set( drivep, |
| INO64MAX, |
| OFF64MAX, |
| STARTPT_FLAGS_END ); |
| |
| decision_more: |
| /* write a null file hdr, to let restore recognize |
| * the end of the media file. the flags indicate |
| * whether or not this is intended to be the last |
| * media file in the stream. don't bother if we hit |
| * EOM. |
| */ |
| if ( ! hit_eom ) { |
| rv = dump_filehdr( drivep, |
| contextp, |
| 0, |
| 0, |
| all_nondirs_sent |
| ? |
| ( FILEHDR_FLAGS_NULL |
| | |
| FILEHDR_FLAGS_END ) |
| : |
| FILEHDR_FLAGS_NULL ); |
| if ( rv == RV_DRIVE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_CORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| if ( rv == RV_ERROR ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_ERROR, rv); |
| } |
| |
| /* send a mark to detect if the null file header made |
| * it. mark callback will adjust start pt before this |
| * call returns if the null file header made it. |
| */ |
| mark_set( drivep, |
| INO64MAX, |
| OFF64MAX, |
| all_nondirs_sent |
| ? |
| STARTPT_FLAGS_NULL | STARTPT_FLAGS_END |
| : |
| STARTPT_FLAGS_NULL ); |
| } |
| |
| /* tell the Media abstraction to end the media file. |
| * this is done before the inventory update, to |
| * see how much was actually committed to media. |
| * will invoke drive end_write, which will flush |
| * all pending marks. |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "ending media file\n") ); |
| ncommitted = 0; |
| rv = Media_mfile_end( drivep, |
| contextp, |
| mwhdrp, |
| &ncommitted, |
| hit_eom ); |
| if ( rv == RV_DRIVE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| if ( rv == RV_CORE ) { |
| free( ( void * )bstatbufp ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| mlog( MLOG_VERBOSE, _( |
| "media file size %lld bytes\n"), |
| ncommitted ); |
| |
| /* if at least one mark committed, we know all of |
| * the inomap and dirdump was committed. |
| */ |
| all_dirs_committed = ( contextp->cc_markscommitted > 0 ); |
| |
| /* at this point we can check the new start point |
| * to determine if all nondirs have been committed. |
| * if this flag was already set, then this is a |
| * inomap and dirdump-only media file. |
| */ |
| if ( scwhdrp->cih_startpt.sp_flags & STARTPT_FLAGS_END ) { |
| if ( all_nondirs_committed ) { |
| empty_mediafile = BOOL_TRUE; |
| } |
| all_nondirs_committed = BOOL_TRUE; |
| } |
| |
| /* we are done if all nondirs have been committed. |
| * it is not necessary for the null file header to have |
| * been committed. |
| */ |
| done = all_nondirs_committed; |
| |
| /* tell the inventory about the media file |
| */ |
| if ( inv_stmt != INV_TOKEN_NULL ) { |
| bool_t ok; |
| |
| if ( ! all_dirs_committed ) { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "partial dirdump media file\n" ); |
| } else if ( done && empty_mediafile ) { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "empty last media file: " |
| "%llu:%lld\n", |
| startino, |
| startoffset ); |
| } else if ( empty_mediafile ) { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "empty media file: " |
| "%llu:%lld\n", |
| startino, |
| startoffset ); |
| } else if ( done ) { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "last media file: " |
| "%llu:%lld\n", |
| startino, |
| startoffset ); |
| } else { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "media file: " |
| "%llu:%lld - %llu:%lld\n", |
| startino, |
| startoffset, |
| scwhdrp->cih_startpt.sp_ino, |
| scwhdrp->cih_startpt.sp_offset ); |
| } |
| |
| /* already thread-safe, don't need to lock |
| */ |
| ok = inv_put_mediafile( inv_stmt, |
| &mwhdrp->mh_mediaid, |
| mwhdrp->mh_medialabel, |
| ( u_intgen_t )mwhdrp->mh_mediafileix, |
| startino, |
| startoffset, |
| scwhdrp->cih_startpt.sp_ino, |
| scwhdrp->cih_startpt.sp_offset, |
| ncommitted, |
| all_dirs_committed |
| && |
| ! empty_mediafile, |
| BOOL_FALSE ); |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL, _( |
| "inventory media file put failed\n") ); |
| } |
| } |
| if ( done ) { |
| contextp->cc_completepr = BOOL_TRUE; |
| /* so inv_end_stream and main will know |
| */ |
| } |
| |
| /* don't go back for more if done or stop was requested |
| */ |
| if ( done || stop_requested ) { |
| break; |
| } |
| } /* end main dump loop */ |
| |
| /* check in |
| */ |
| lock( ); |
| sc_thrdsdonecnt++; |
| unlock( ); |
| |
| /* dump the session inventory and terminator here, if the drive |
| * supports multiple media files. must wait until all |
| * streams have completed or given up, so all media files |
| * from all streams have been registered. |
| */ |
| if ( drivep->d_capabilities & DRIVE_CAP_FILES ) { |
| if ( stream_cnt( ) > 1 ) { |
| mlog( MLOG_VERBOSE, _( |
| "waiting for synchronized " |
| "session inventory dump\n") ); |
| sc_stat_pds[ strmix ].pds_phase = PDS_INVSYNC; |
| } |
| |
| /* first be sure all threads have begun |
| */ |
| while ( sc_thrdsarrivedcnt < drivecnt ) { |
| sleep( 1 ); |
| } |
| /* now wait for survivors to checkin |
| */ |
| while ( sc_thrdsdonecnt < stream_cnt( )) { |
| sleep( 1 ); |
| } |
| /* proceeed |
| */ |
| sc_stat_pds[ strmix ].pds_phase = PDS_INVDUMP; |
| if ( dump_session_inv( drivep, contextp, mwhdrp, scwhdrp )) { |
| sc_stat_pds[ strmix ].pds_phase = PDS_TERMDUMP; |
| dump_terminator( drivep, contextp, mwhdrp ); |
| } |
| } |
| |
| sc_stat_pds[ strmix ].pds_phase = PDS_NULL; |
| |
| free( ( void * )bstatbufp ); |
| |
| elapsed = time( 0 ) - sc_stat_starttime; |
| |
| mlog( MLOG_TRACE, _( |
| "ending stream: %ld seconds elapsed\n"), |
| elapsed ); |
| |
| return mlog_exit(EXIT_NORMAL, rv); |
| } |
| |
| /* indicates if the dump was complete. |
| * easy to tell: initially contextp->cc_completepr is false for each stream. |
| * only set true if stream complete. if any stream NOT complete, |
| * dump is not complete. |
| */ |
| bool_t |
| content_complete( void ) |
| { |
| time_t elapsed; |
| bool_t completepr; |
| intgen_t i; |
| |
| completepr = check_complete_flags( ); |
| |
| elapsed = time( 0 ) - sc_stat_starttime; |
| |
| mlog( MLOG_VERBOSE, _( |
| "dump size (non-dir files) : %llu bytes\n"), |
| sc_stat_datadone ); |
| |
| if ( completepr ) { |
| if( sc_savequotas ) { |
| for(i = 0; i < (sizeof(quotas) / sizeof(quotas[0])); i++) { |
| if( quotas[i].savequotas && unlink( quotas[i].quotapath ) < 0 ) { |
| mlog( MLOG_ERROR, _( |
| "unable to remove %s: %s\n"), |
| quotas[i].quotapath, |
| strerror ( errno )); |
| } |
| } |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "dump complete" |
| ": %ld seconds elapsed" |
| "\n"), |
| elapsed ); |
| } else { |
| if ( sc_inv_updatepr ) { |
| mlog( MLOG_VERBOSE | MLOG_NOTE, _( |
| "dump interrupted" |
| ": %ld seconds elapsed" |
| ": may resume later using -%c option" |
| "\n"), |
| elapsed, |
| GETOPT_RESUME ); |
| mlog_exit_hint(RV_INTR); |
| } else { |
| mlog( MLOG_VERBOSE | MLOG_NOTE, _( |
| "dump interrupted" |
| ": %ld seconds elapsed" |
| "\n"), |
| elapsed ); |
| mlog_exit_hint(RV_INTR); |
| } |
| } |
| return completepr; |
| } |
| |
| #define PREAMBLEMAX 3 |
| #define QUERYMAX 1 |
| #define CHOICEMAX 30 |
| #define ACKMAX 3 |
| #define POSTAMBLEMAX 3 |
| #define DLOG_TIMEOUT 300 |
| #define DLOG_TIMEOUT_MEDIA 3600 |
| |
| #define CHOICESTRSZ 10 |
| typedef struct { ix_t thrdix; char choicestr[ CHOICESTRSZ ]; } cttm_t; |
| |
| char * |
| content_mediachange_query( void ) |
| { |
| cttm_t choicetothrdmap[ STREAM_SIMMAX ]; |
| char *querystr[ QUERYMAX ]; |
| size_t querycnt; |
| char *choicestr[ CHOICEMAX ]; |
| size_t choicecnt; |
| size_t maxdrvchoiceix; |
| size_t nochangeix; |
| size_t responseix; |
| ix_t thrdix; |
| |
| querycnt = 0; |
| querystr[ querycnt++ ] = "select a drive to acknowledge media change\n"; |
| choicecnt = 0; |
| maxdrvchoiceix = 0; |
| for ( thrdix = 0 ; thrdix < STREAM_SIMMAX ; thrdix++ ) { |
| if ( sc_mcflag[ thrdix ] ) { |
| choicetothrdmap[ choicecnt ].thrdix = thrdix; |
| sprintf( choicetothrdmap[ choicecnt ].choicestr, |
| "drive %u", |
| (unsigned int)thrdix ); |
| choicestr[ choicecnt ] = |
| choicetothrdmap[ choicecnt ].choicestr; |
| maxdrvchoiceix = choicecnt; |
| choicecnt++; |
| } |
| } |
| nochangeix = choicecnt; |
| choicestr[ choicecnt++ ] = "continue"; |
| ASSERT( choicecnt <= CHOICEMAX ); |
| responseix = dlog_multi_query( querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| nochangeix, /* defaultix */ |
| DLOG_TIMEOUT_MEDIA, |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| if ( responseix <= maxdrvchoiceix ) { |
| clr_mcflag( choicetothrdmap[ responseix ].thrdix ); |
| return "media change acknowledged\n"; |
| } |
| ASSERT( responseix == nochangeix ); |
| return "continuing\n"; |
| } |
| |
| |
| static void |
| update_cc_Media_useterminatorpr( drive_t *drivep, context_t *contextp ) |
| { |
| intgen_t dcaps = drivep->d_capabilities; |
| |
| contextp->cc_Media_useterminatorpr = BOOL_TRUE; |
| if ( ! ( dcaps & DRIVE_CAP_FILES )) { |
| contextp->cc_Media_useterminatorpr = BOOL_FALSE; |
| } |
| if ( ! ( dcaps & DRIVE_CAP_OVERWRITE )) { |
| contextp->cc_Media_useterminatorpr = BOOL_FALSE; |
| } |
| if ( ! ( dcaps & DRIVE_CAP_BSF )) { |
| contextp->cc_Media_useterminatorpr = BOOL_FALSE; |
| } |
| if ( ! ( dcaps & DRIVE_CAP_APPEND )) { |
| contextp->cc_Media_useterminatorpr = BOOL_FALSE; |
| } |
| } |
| |
| static rv_t |
| dump_dirs( ix_t strmix, |
| xfs_bstat_t *bstatbufp, |
| size_t bstatbuflen, |
| void *inomap_contextp ) |
| { |
| xfs_ino_t lastino; |
| size_t bulkstatcallcnt; |
| xfs_fsop_bulkreq_t bulkreq; |
| |
| inomap_reset_context(inomap_contextp); |
| |
| /* begin iteration at ino zero |
| */ |
| lastino = 0; |
| for ( bulkstatcallcnt = 0 ; ; bulkstatcallcnt++ ) { |
| xfs_bstat_t *p; |
| xfs_bstat_t *endp; |
| __s32 buflenout; |
| intgen_t rval; |
| |
| if ( bulkstatcallcnt == 0 ) { |
| mlog( MLOG_VERBOSE, _( |
| "dumping directories\n") ); |
| } |
| sc_stat_pds[ strmix ].pds_phase = PDS_DIRDUMP; |
| |
| /* check for interruption |
| */ |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* get a bunch of bulkstats |
| */ |
| mlog( MLOG_NITTY, |
| "dump_dirs SGI_FS_BULKSTAT %u buf len %u\n", |
| bulkstatcallcnt, |
| bstatbuflen ); |
| |
| bulkreq.lastip = (__u64 *)&lastino; |
| bulkreq.icount = bstatbuflen; |
| bulkreq.ubuffer = bstatbufp; |
| bulkreq.ocount = &buflenout; |
| |
| rval = ioctl(sc_fsfd, XFS_IOC_FSBULKSTAT, &bulkreq); |
| |
| if ( rval ) { |
| mlog( MLOG_NORMAL, _( |
| "SGI_FS_BULKSTAT failed: " |
| "%s (%d)\n"), |
| strerror( errno ), |
| errno ); |
| return RV_ERROR; |
| } |
| mlog( MLOG_NITTY, |
| "dump_dirs SGI_FS_BULKSTAT returns %d entries\n", |
| buflenout ); |
| |
| /* check if done |
| */ |
| if ( buflenout == 0 ) { |
| return RV_OK; |
| } |
| |
| /* step through each node, dumping if |
| * appropriate |
| */ |
| for ( p = bstatbufp, endp = bstatbufp + buflenout |
| ; |
| p < endp |
| ; |
| p++ ) { |
| rv_t rv; |
| |
| if ( p->bs_ino == 0 ) |
| continue; |
| |
| if ( !p->bs_nlink || !p->bs_mode ) { |
| /* inode being modified, get synced data */ |
| mlog( MLOG_NITTY, |
| "ino %llu needs second bulkstat\n", |
| p->bs_ino ); |
| |
| if ( bigstat_one( sc_fsfd, p->bs_ino, p ) < 0 ) { |
| mlog( MLOG_WARNING, _( |
| "failed to get bulkstat information for inode %llu\n"), |
| p->bs_ino ); |
| continue; |
| } |
| if ( !p->bs_nlink || !p->bs_mode || !p->bs_ino ) { |
| mlog( MLOG_TRACE, |
| "failed to get valid bulkstat information for inode %llu\n", |
| p->bs_ino ); |
| continue; |
| } |
| } |
| if ( ( p->bs_mode & S_IFMT ) != S_IFDIR ) { |
| continue; |
| } |
| |
| rv = dump_dir( strmix, sc_fshandlep, sc_fsfd, p ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| } |
| |
| lastino = inomap_next_dir(inomap_contextp, lastino); |
| if (lastino == INO64MAX) { |
| mlog( MLOG_DEBUG, "bulkstat seeked to EOS\n" ); |
| return 0; |
| } |
| |
| mlog( MLOG_DEBUG, "bulkstat seeked to %llu\n", lastino ); |
| |
| lastino = (lastino > 0) ? lastino - 1 : 0; |
| } |
| /* NOTREACHED */ |
| } |
| |
| static rv_t |
| dump_dir( ix_t strmix, |
| jdm_fshandle_t *fshandlep, |
| intgen_t fsfd, |
| xfs_bstat_t *statp ) |
| { |
| context_t *contextp = &sc_contextp[ strmix ]; |
| drive_t *drivep = drivepp[ strmix ]; |
| void *inomap_contextp = contextp->cc_inomap_contextp; |
| intgen_t state; |
| intgen_t fd; |
| struct dirent *gdp = ( struct dirent *)contextp->cc_getdentsbufp; |
| size_t gdsz = contextp->cc_getdentsbufsz; |
| intgen_t gdcnt; |
| gen_t gen; |
| rv_t rv; |
| |
| /* no way this can be non-dir, but check anyway |
| */ |
| ASSERT( ( statp->bs_mode & S_IFMT ) == S_IFDIR ); |
| if ( ( statp->bs_mode & S_IFMT ) != S_IFDIR ) { |
| return RV_OK; |
| } |
| |
| /* skip if no links |
| */ |
| if ( statp->bs_nlink < 1 ) { |
| return RV_OK; |
| } |
| |
| /* see what the inomap says about this ino |
| */ |
| state = inomap_get_state( inomap_contextp, statp->bs_ino ); |
| |
| /* skip if not in inomap |
| */ |
| if ( state == MAP_INO_UNUSED |
| || |
| state == MAP_DIR_NOCHNG |
| || |
| state == MAP_NDR_NOCHNG ) { |
| if ( state == MAP_NDR_NOCHNG ) { |
| mlog( MLOG_DEBUG, |
| "inomap inconsistency ino %llu: " |
| "map says is non-dir but is dir: skipping\n", |
| statp->bs_ino ); |
| } |
| return RV_OK; |
| } |
| |
| /* note if map says a non-dir |
| */ |
| if ( state == MAP_NDR_CHANGE ) { |
| mlog( MLOG_DEBUG, |
| "inomap inconsistency ino %llu: " |
| "map says non-dir but is dir: skipping\n", |
| statp->bs_ino ); |
| return RV_OK; |
| } |
| |
| /* bump the stats now. a bit early, but fewer lines of code |
| */ |
| sc_stat_pds[ strmix ].pds_dirdone++; |
| |
| /* if bulkstat ino# occupied more than 32 bits and |
| * linux ino# for getdents is 32 bits then |
| * warn and skip. |
| */ |
| if ( statp->bs_ino > ( xfs_ino_t )INOMAX ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to dump directory: ino %llu too large\n"), |
| statp->bs_ino ); |
| return RV_OK; /* continue anyway */ |
| } |
| |
| mlog( MLOG_TRACE, |
| "dumping directory ino %llu\n", |
| statp->bs_ino ); |
| |
| /* open the directory named by statp |
| */ |
| fd = jdm_open( fshandlep, statp, O_RDONLY ); |
| if ( fd < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to open directory: ino %llu: %s\n"), |
| statp->bs_ino, strerror( errno ) ); |
| return RV_OK; /* continue anyway */ |
| } |
| |
| /* dump the file header. |
| */ |
| rv = dump_filehdr( drivep, contextp, statp, 0, 0 ); |
| if ( rv != RV_OK ) { |
| close( fd ); |
| return rv; |
| } |
| |
| /* dump dirents - lots of buffering done here, to achieve OS- |
| * independence. if proves to be to much overhead, can streamline. |
| */ |
| for ( gdcnt = 1, rv = RV_OK ; rv == RV_OK ; gdcnt++ ) { |
| struct dirent *p; |
| intgen_t nread; |
| register size_t reclen; |
| |
| nread = getdents_wrap( fd, (char *)gdp, gdsz ); |
| |
| /* negative count indicates something very bad happened; |
| * try to gracefully end this dir. |
| */ |
| if ( nread < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to read dirents (%d) for " |
| "directory ino %llu: %s\n"), |
| gdcnt, |
| statp->bs_ino, |
| strerror( errno )); |
| /* !!! curtis looked at this, and pointed out that |
| * we could take some recovery action here. if the |
| * errno is appropriate, lseek64 to the value of |
| * doff field of the last dirent successfully |
| * obtained, and contiue the loop. |
| */ |
| nread = 0; /* pretend we are done */ |
| } |
| |
| /* no more directory entries: break; |
| */ |
| if ( nread == 0 ) { |
| break; |
| } |
| |
| /* translate and dump each entry: skip "." and ".." |
| * and null entries. |
| */ |
| for ( p = gdp, |
| reclen = ( size_t )p->d_reclen |
| ; |
| nread > 0 |
| ; |
| nread -= ( intgen_t )reclen, |
| ASSERT( nread >= 0 ), |
| p = ( struct dirent * )( ( char * )p + reclen ), |
| reclen = ( size_t )p->d_reclen ) { |
| xfs_ino_t ino; |
| register size_t namelen = strlen( p->d_name ); |
| #ifdef DEBUG |
| register size_t nameszmax = ( size_t )reclen |
| - |
| offsetofmember( struct dirent, |
| d_name ); |
| |
| /* getdents(2) guarantees that the string will |
| * be null-terminated, but the record may have |
| * padding after the null-termination. |
| */ |
| ASSERT( namelen < nameszmax ); |
| #endif |
| |
| /* skip "." and ".." |
| */ |
| if ( *( p->d_name + 0 ) == '.' |
| && |
| ( *( p->d_name + 1 ) == 0 |
| || |
| ( *( p->d_name + 1 ) == '.' |
| && |
| *( p->d_name + 2 ) == 0 ))) { |
| continue; |
| } |
| |
| ino = (xfs_ino_t)p->d_ino; |
| |
| if ( ino == 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "encountered 0 ino (%s) in " |
| "directory ino %llu: NOT dumping\n"), |
| p->d_name, |
| statp->bs_ino ); |
| continue; |
| } |
| |
| /* lookup the gen number in the ino-to-gen map. |
| * if it's not there, we have to get it the slow way. |
| */ |
| if ( inomap_get_gen( NULL, p->d_ino, &gen) ) { |
| xfs_bstat_t statbuf; |
| intgen_t scrval; |
| |
| scrval = bigstat_one( fsfd, |
| p->d_ino, |
| &statbuf ); |
| if ( scrval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "could not stat " |
| "dirent %s ino %llu: %s: " |
| "using null generation count " |
| "in directory entry\n"), |
| p->d_name, |
| ( xfs_ino_t )p->d_ino, |
| strerror( errno )); |
| gen = 0; |
| } else { |
| gen = statbuf.bs_gen; |
| } |
| } |
| |
| rv = dump_dirent( drivep, |
| contextp, |
| statp, |
| ino, |
| gen, |
| p->d_name, |
| namelen ); |
| if ( rv != RV_OK ) { |
| break; |
| } |
| } |
| } |
| |
| /* write a null dirent hdr, unless trouble encountered in the loop |
| */ |
| if ( rv == RV_OK ) { |
| rv = dump_dirent( drivep, contextp, statp, 0, 0, 0, 0 ); |
| } |
| |
| if ( rv == RV_OK |
| && |
| sc_dumpextattrpr |
| && |
| ( statp->bs_xflags & XFS_XFLAG_HASATTR )) { |
| rv = dump_extattrs( drivep, contextp, fshandlep, statp); |
| } |
| |
| close( fd ); |
| |
| /* if an error occurred, just return the error |
| */ |
| return rv; |
| } |
| |
| static rv_t |
| dump_extattrs( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp) |
| { |
| ix_t pass; |
| int flag; |
| attrlist_cursor_t cursor; |
| rv_t rv; |
| bool_t abort; |
| |
| /* dump a file header specially marked as heading extended attributes |
| */ |
| mlog( MLOG_NITTY, |
| "dumping %s ino %llu extended attributes filehdr\n", |
| FILETYPE(statp), |
| statp->bs_ino ); |
| |
| rv = dump_filehdr( drivep, contextp, statp, 0, FILEHDR_FLAGS_EXTATTR ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| /* loop three times: once for the non-root, once for root, and |
| * again for the secure attributes. |
| */ |
| for ( pass = 0; pass < 3; pass++ ) { |
| bool_t more; |
| |
| if ( pass == 0 ) |
| flag = 0; |
| else if ( pass == 1) |
| flag = ATTR_ROOT; |
| else |
| flag = ATTR_SECURE; |
| |
| mlog( MLOG_NITTY, |
| "dumping %s extended attributes for %s ino %llu\n", |
| EXTATTR_NAMESPACE(flag), |
| FILETYPE(statp), |
| statp->bs_ino ); |
| |
| /* loop dumping the extended attributes from the namespace |
| * selected by the outer loop |
| */ |
| memset( &cursor, 0, sizeof( cursor )); |
| more = BOOL_FALSE; |
| do { |
| attrlist_t *listp; |
| int rval = 0; |
| |
| rval = jdm_attr_list(fshandlep, statp, |
| contextp->cc_extattrlistbufp, |
| ( int )contextp->cc_extattrlistbufsz, |
| flag, &cursor ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "could not get list of %s attributes for " |
| "%s ino %llu: %s (%d)\n"), |
| EXTATTR_NAMESPACE(flag), |
| FILETYPE(statp), |
| statp->bs_ino, |
| strerror( errno ), |
| errno ); |
| break; |
| } |
| |
| listp = ( attrlist_t * )contextp->cc_extattrlistbufp; |
| more = listp->al_more; |
| |
| abort = BOOL_FALSE; |
| rv = dump_extattr_list( drivep, |
| contextp, |
| fshandlep, |
| statp, |
| listp, |
| flag, |
| &abort ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| } while ( more && !abort ); |
| } |
| |
| /* finally, dump a dummy extattr hdr so restore will know |
| * we're done. |
| */ |
| /*DBG*/mlog( MLOG_NITTY, |
| "dumping NULL extattrhdr\n" ); |
| rv = dump_extattrhdr( drivep, |
| contextp, |
| statp, |
| EXTATTRHDR_SZ, |
| 0, |
| EXTATTRHDR_FLAGS_NULL, |
| 0 ); |
| return rv; |
| } |
| |
| static rv_t |
| dump_extattr_list( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp, |
| attrlist_t *listp, |
| int flag, |
| bool_t *abortprp ) |
| { |
| size_t listlen = ( size_t )listp->al_count; |
| ix_t nameix; |
| char *dumpbufp; |
| char *endp; |
| size_t bufsz; |
| intgen_t rval = 0; |
| rv_t rv; |
| char *dumpbufendp = contextp->cc_extattrdumpbufp |
| + |
| contextp->cc_extattrdumpbufsz; |
| |
| /* sanity checks |
| */ |
| ASSERT( listp->al_count >= 0 ); |
| |
| /* fill up a retrieve array and build a dump buffer; |
| * can run out of entries in the name list, space in the |
| * retrieve buffer, or space in the dump buffer |
| */ |
| dumpbufp = contextp->cc_extattrdumpbufp; |
| endp = dumpbufp; |
| for ( nameix = 0 ; nameix < listlen ; ) { |
| ix_t rtrvix; |
| size_t rtrvcnt; |
| |
| rtrvix = 0; |
| while (nameix < listlen && rtrvix < EXTATTR_RTRVARRAY_LEN) { |
| attrlist_ent_t *entp; |
| char *valuep; |
| attr_multiop_t *opp; |
| |
| entp = ATTR_ENTRY( listp, nameix ); |
| opp = &contextp->cc_extattrrtrvarrayp[ rtrvix ]; |
| |
| /* Offer the HSM a chance to avoid dumping certain |
| * attributes. |
| */ |
| |
| if (hsm_fs_ctxtp) { |
| int skip_entry; |
| |
| if (!HsmFilterExistingAttribute( |
| contextp->cc_hsm_f_ctxtp, entp->a_name, |
| entp->a_valuelen, flag, |
| &skip_entry)) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "HSM could not filter %s " |
| "attribute %s for %s ino %llu\n"), |
| EXTATTR_NAMESPACE(flag), |
| entp->a_name, |
| FILETYPE(statp), |
| statp->bs_ino); |
| *abortprp = BOOL_TRUE; |
| return RV_OK; |
| } |
| if (skip_entry) { |
| nameix++; |
| continue; |
| } |
| } |
| |
| dumpbufp = dump_extattr_buildrecord( statp, |
| dumpbufp, |
| dumpbufendp, |
| entp->a_name, |
| entp->a_valuelen, |
| flag, |
| &valuep ); |
| if ( dumpbufp > dumpbufendp ) { |
| break; /* won't fit in buffer */ |
| } |
| if (valuep != NULL) { /* if added to dump buffer */ |
| endp = dumpbufp; |
| opp->am_attrname = entp->a_name; |
| opp->am_attrvalue = valuep; |
| opp->am_length = ( int )entp->a_valuelen; |
| opp->am_flags = flag; |
| opp->am_error = 0; |
| opp->am_opcode = ATTR_OP_GET; |
| rtrvix++; |
| } |
| nameix++; |
| } |
| |
| /* Either the retrieve buffer is full, the dump buffer is full, |
| * or we just put the last attribute into the dump buffer. In |
| * any case, fill in the values for any attributes added so far. |
| */ |
| |
| rtrvcnt = rtrvix; |
| if (rtrvcnt > 0) { |
| rval = jdm_attr_multi( fshandlep, statp, |
| (void *)contextp->cc_extattrrtrvarrayp, |
| ( int )rtrvcnt, |
| 0 ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "could not retrieve %s attributes for " |
| "%s ino %llu: %s (%d)\n"), |
| EXTATTR_NAMESPACE(flag), |
| FILETYPE(statp), |
| statp->bs_ino, |
| strerror( errno ), |
| errno ); |
| *abortprp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| for ( rtrvix = 0 ; rtrvix < rtrvcnt ; rtrvix++ ) { |
| attr_multiop_t *opp; |
| opp = &contextp->cc_extattrrtrvarrayp[ rtrvix ]; |
| if ( opp->am_error ) { |
| if ( opp->am_error == ENOATTR && |
| flag & ATTR_SECURE ) { |
| /* Security attributes are supported by |
| * the kernel but jdm_attr_multi() returns |
| * ENOATTR for every 'user' space attribute |
| * during the 'security' pass of the extended |
| * attribute loop (pass==3). Suppress the |
| * following error message with a no-op. The |
| * jdm_attr_multi() problem is fixed in mod |
| * xfs-linux:xfs-kern:167038a (PV 907903). |
| */ |
| continue; |
| } |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "attr_multi indicates error while " |
| "retrieving %s attribute [%s] for " |
| "%s ino %llu: %s (%d)\n"), |
| EXTATTR_NAMESPACE(flag), |
| opp->am_attrname, |
| FILETYPE(statp), |
| statp->bs_ino, |
| strerror( opp->am_error ), |
| opp->am_error ); |
| } |
| } |
| } |
| |
| /* The values for all attributes in the dump buffer have been |
| * filled in. If the dump buffer isn't full yet, let's wait |
| * and put some more attributes in. |
| */ |
| |
| if (dumpbufp <= dumpbufendp) |
| continue; /* no buffer overflow yet */ |
| |
| ASSERT( endp > contextp->cc_extattrdumpbufp ); |
| bufsz = ( size_t )( endp - contextp->cc_extattrdumpbufp ); |
| |
| rval = write_buf( contextp->cc_extattrdumpbufp, |
| bufsz, |
| ( void * )drivep, |
| ( gwbfp_t )drivep->d_opsp->do_get_write_buf, |
| ( wfp_t )drivep->d_opsp->do_write ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| if ( rv != RV_OK ) { |
| *abortprp = BOOL_FALSE; |
| return rv; |
| } |
| dumpbufp = contextp->cc_extattrdumpbufp; |
| endp = dumpbufp; |
| } |
| |
| /* All existing attributes are in the dump buffer. See if the HSM |
| * needs to add any addtional attributes. |
| */ |
| |
| if (!listp->al_more && hsm_fs_ctxtp) { |
| int hsmcursor = 0; |
| for (;;) { |
| char *hsmnamep; |
| char *hsmvaluep; |
| char *valuep; |
| u_int32_t hsmvaluesz; |
| |
| if (!HsmAddNewAttribute(contextp->cc_hsm_f_ctxtp, |
| hsmcursor, |
| flag, |
| &hsmnamep, |
| &hsmvaluep, |
| &hsmvaluesz)) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "HSM could not add new %s attribute " |
| "#%d for %s ino %llu\n"), |
| EXTATTR_NAMESPACE(flag), |
| hsmcursor, |
| FILETYPE(statp), |
| statp->bs_ino); |
| *abortprp = BOOL_TRUE; |
| return RV_OK; |
| } |
| if (hsmnamep == NULL) { |
| break; /* No more attributes to add */ |
| } |
| |
| dumpbufp = dump_extattr_buildrecord( statp, |
| dumpbufp, |
| dumpbufendp, |
| hsmnamep, |
| hsmvaluesz, |
| flag, |
| &valuep ); |
| |
| if ( dumpbufp < dumpbufendp ) { /* if fits in buffer */ |
| endp = dumpbufp; |
| (void)memcpy(valuep, hsmvaluep, hsmvaluesz); |
| hsmcursor++; |
| continue; |
| } |
| |
| ASSERT( endp > contextp->cc_extattrdumpbufp ); |
| bufsz = ( size_t )( endp - contextp->cc_extattrdumpbufp ); |
| |
| rval = write_buf( contextp->cc_extattrdumpbufp, |
| bufsz, |
| ( void * )drivep, |
| ( gwbfp_t )drivep->d_opsp->do_get_write_buf, |
| ( wfp_t )drivep->d_opsp->do_write ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| if ( rv != RV_OK ) { |
| *abortprp = BOOL_FALSE; |
| return rv; |
| } |
| dumpbufp = contextp->cc_extattrdumpbufp; |
| endp = dumpbufp; |
| } |
| } |
| |
| /* If any attributes remain unwritten in the dump buffer, write them |
| * now. |
| */ |
| |
| if (endp > contextp->cc_extattrdumpbufp) { |
| bufsz = ( size_t )( endp - contextp->cc_extattrdumpbufp ); |
| |
| rval = write_buf( contextp->cc_extattrdumpbufp, |
| bufsz, |
| ( void * )drivep, |
| ( gwbfp_t )drivep->d_opsp->do_get_write_buf, |
| ( wfp_t )drivep->d_opsp->do_write ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| if ( rv != RV_OK ) { |
| *abortprp = BOOL_FALSE; |
| return rv; |
| } |
| } |
| |
| *abortprp = BOOL_FALSE; |
| return RV_OK; |
| } |
| |
| static char * |
| dump_extattr_buildrecord( xfs_bstat_t *statp, |
| char *dumpbufp, |
| char *dumpbufendp, |
| char *namesrcp, |
| u_int32_t valuesz, |
| int flag, |
| char **valuepp ) |
| { |
| extattrhdr_t *ahdrp = ( extattrhdr_t * )dumpbufp; |
| char *namep = dumpbufp + EXTATTRHDR_SZ; |
| u_int32_t namelen = strlen( namesrcp ); |
| u_int32_t namesz = namelen + 1; |
| char *valuep = namep + namesz; |
| u_int32_t recsz = EXTATTRHDR_SZ + namesz + valuesz; |
| extattrhdr_t tmpah; |
| |
| recsz = ( recsz + ( EXTATTRHDR_ALIGN - 1 )) |
| & |
| ~( EXTATTRHDR_ALIGN - 1 ); |
| |
| if ( dumpbufp + recsz > dumpbufendp ) { |
| *valuepp = 0; |
| return dumpbufp + recsz; |
| } |
| |
| if ( namelen > NAME_MAX ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "%s extended attribute name for %s ino %llu too long: " |
| "%u, max is %u: skipping\n"), |
| EXTATTR_NAMESPACE(flag), |
| FILETYPE(statp), |
| statp->bs_ino, |
| namelen, |
| NAME_MAX ); |
| *valuepp = 0; |
| return dumpbufp; |
| } |
| |
| if ( valuesz > ATTR_MAX_VALUELEN ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "%s extended attribute value for %s ino %llu too long: " |
| "%u, max is %u: skipping\n"), |
| EXTATTR_NAMESPACE(flag), |
| FILETYPE(statp), |
| statp->bs_ino, |
| valuesz, |
| ATTR_MAX_VALUELEN ); |
| *valuepp = 0; |
| return dumpbufp; |
| } |
| |
| /*DBG*/mlog( MLOG_NITTY, |
| "building extattr " |
| "record sz %u " |
| "hdrsz %u " |
| "namesz %u (%s) " |
| "valsz %u\n", |
| recsz, |
| EXTATTRHDR_SZ, |
| namesz, namesrcp, |
| valuesz ); |
| ( void )strcpy( namep, namesrcp ); |
| |
| memset( ( void * )&tmpah, 0, sizeof( tmpah )); |
| tmpah.ah_sz = recsz; |
| ASSERT( EXTATTRHDR_SZ + namesz < UINT16MAX ); |
| tmpah.ah_valoff = ( u_int16_t )( EXTATTRHDR_SZ + namesz ); |
| tmpah.ah_flags = ( u_int16_t ) |
| (( flag & ATTR_ROOT ) ? EXTATTRHDR_FLAGS_ROOT : |
| (( flag & ATTR_SECURE ) ? EXTATTRHDR_FLAGS_SECURE : 0)); |
| tmpah.ah_valsz = valuesz; |
| tmpah.ah_flags |= EXTATTRHDR_FLAGS_CHECKSUM; |
| tmpah.ah_checksum = calc_checksum( &tmpah, EXTATTRHDR_SZ ); |
| |
| xlate_extattrhdr(ahdrp, &tmpah, -1); |
| *valuepp = valuep; |
| return dumpbufp + recsz; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| dump_extattrhdr( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *statp, |
| size_t recsz, |
| size_t valoff, |
| ix_t flags, |
| u_int32_t valsz ) |
| { |
| extattrhdr_t ahdr; |
| extattrhdr_t tmpahdr; |
| intgen_t rval; |
| rv_t rv; |
| |
| memset( ( void * )&ahdr, 0, sizeof( ahdr )); |
| ahdr.ah_sz = recsz; |
| ASSERT( valoff < UINT16MAX ); |
| ahdr.ah_valoff = ( u_int16_t )valoff; |
| ahdr.ah_flags = ( u_int16_t )flags | EXTATTRHDR_FLAGS_CHECKSUM; |
| ahdr.ah_valsz = valsz; |
| ahdr.ah_checksum = calc_checksum( &ahdr, EXTATTRHDR_SZ ); |
| |
| xlate_extattrhdr(&ahdr, &tmpahdr, 1); |
| rval = write_buf( ( char * )&tmpahdr, |
| EXTATTRHDR_SZ, |
| ( void * )drivep, |
| ( gwbfp_t )drivep->d_opsp->do_get_write_buf, |
| ( wfp_t )drivep->d_opsp->do_write ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| |
| return rv; |
| } |
| |
| /* this function is called by the bigstat iterator for all non-directory |
| * files. it passes the buck to file type-specific dump functions. |
| * return value is RV_EOF if the media file is getting too big, |
| * RV_... if trouble encountered with the media/drive, |
| * 0 if file completely dumped, RV_NOMORE if we've encountered the stream |
| * endpt, RV_INTR if operator interrupted the dump. |
| */ |
| /* ARGSUSED */ |
| static rv_t |
| dump_file( void *arg1, |
| jdm_fshandle_t *fshandlep, |
| intgen_t fsfd, |
| xfs_bstat_t *statp ) |
| { |
| ix_t strmix = ( ix_t )arg1; |
| context_t *contextp = &sc_contextp[ strmix ]; |
| drive_t *drivep = drivepp[ strmix ]; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| media_hdr_t *mwhdrp = ( media_hdr_t * )dwhdrp->dh_upper; |
| content_hdr_t *cwhdrp = ( content_hdr_t * )mwhdrp->mh_upper; |
| content_inode_hdr_t *scwhdrp = ( content_inode_hdr_t * ) |
| ( void * ) |
| cwhdrp->ch_specific; |
| startpt_t *startptp = &scwhdrp->cih_startpt; |
| startpt_t *endptp = &scwhdrp->cih_endpt; |
| bool_t file_skipped = BOOL_FALSE; |
| intgen_t state; |
| rv_t rv; |
| |
| /* skip if no links |
| */ |
| if ( statp->bs_nlink < 1 ) { |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| mlog( MLOG_DEBUG, "skip as no links for ino %llu\n", |
| statp->bs_ino); |
| return RV_OK; |
| } |
| |
| /* skip if prior to startpoint |
| */ |
| if ( statp->bs_ino < startptp->sp_ino ) { |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| mlog( MLOG_DEBUG, "skip as ino %llu is prior to starpoint\n", |
| statp->bs_ino); |
| return RV_OK; |
| } |
| |
| /* skip if at or beyond next startpoint. return non-zero to |
| * abort iteration. |
| */ |
| if ( ! ( endptp->sp_flags & STARTPT_FLAGS_END )) { |
| if ( endptp->sp_offset == 0 ) { |
| if ( statp->bs_ino >= endptp->sp_ino ) { |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| mlog( MLOG_DEBUG, "skip as ino %llu is at/beyond starpoint\n", |
| statp->bs_ino); |
| return RV_NOMORE; |
| } |
| } else { |
| if ( statp->bs_ino > endptp->sp_ino ) { |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| mlog( MLOG_DEBUG, "skip as ino %llu is at/beyond starpoint\n", |
| statp->bs_ino); |
| return RV_NOMORE; |
| } |
| } |
| } |
| |
| /* see what the inomap says about this ino |
| */ |
| state = inomap_get_state( contextp->cc_inomap_contextp, statp->bs_ino ); |
| |
| /* skip if not in inomap |
| */ |
| if ( state == MAP_INO_UNUSED |
| || |
| state == MAP_DIR_NOCHNG |
| || |
| state == MAP_NDR_NOCHNG ) { |
| if ( state == MAP_DIR_NOCHNG ) { |
| mlog( MLOG_DEBUG, |
| "inomap inconsistency ino %llu: " |
| "MAP_DIR_NOCHNG but is non-dir: skipping\n", |
| statp->bs_ino ); |
| } |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| mlog( MLOG_DEBUG, "skip as ino %llu is not marked as changed in inomap\n", |
| statp->bs_ino); |
| mlog( MLOG_DEBUG, "ino %llu is in state %d\n", |
| statp->bs_ino, state); |
| return RV_OK; |
| } |
| |
| /* note if map says a dir |
| */ |
| if ( state == MAP_DIR_CHANGE || state == MAP_DIR_SUPPRT ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "inomap inconsistency ino %llu: " |
| "%s but is now non-dir: NOT dumping\n"), |
| statp->bs_ino, |
| state == MAP_DIR_CHANGE |
| ? |
| "map says changed dir" |
| : |
| "map says unchanged dir" ); |
| } |
| |
| /* if GETOPT_DUMPASOFFLINE was specified, initialize the HSM's file |
| * context for use in other routines. If the context can't be |
| * initialized, don't dump the file. |
| */ |
| |
| if (hsm_fs_ctxtp) { |
| if (HsmInitFileContext(contextp->cc_hsm_f_ctxtp, statp)) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "inomap inconsistency ino %llu: " |
| "hsm detected error: NOT dumping\n"), |
| statp->bs_ino); |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| return RV_OK; |
| } |
| } |
| |
| /* pass on to specific dump function |
| */ |
| switch( statp->bs_mode & S_IFMT ) { |
| case S_IFREG: |
| /* ordinary file |
| */ |
| |
| /* filter out any files that have grown beyond the |
| * max file size since the initial scan. |
| */ |
| if (maxdumpfilesize) { |
| off64_t estimated_size = statp->bs_blocks * |
| ( off64_t )statp->bs_blksize; |
| |
| if (hsm_fs_ctxtp) { |
| HsmEstimateFileSpace(hsm_fs_ctxtp, |
| contextp->cc_hsm_f_ctxtp, |
| statp, |
| &estimated_size, |
| 1); |
| } |
| |
| /* quota files are exempt from max size check */ |
| if (estimated_size > maxdumpfilesize && |
| !is_quota_file(statp->bs_ino)) { |
| mlog( MLOG_DEBUG | MLOG_NOTE, |
| "ino %llu increased beyond maximum size: " |
| "NOT dumping\n", |
| statp->bs_ino); |
| return RV_OK; |
| } |
| } |
| rv = dump_file_reg( drivep, |
| contextp, |
| scwhdrp, |
| fshandlep, |
| statp, |
| &file_skipped ); |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| lock( ); |
| sc_stat_nondirdone++; |
| unlock( ); |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| break; /* drop out of switch to extattr dump */ |
| case S_IFCHR: |
| case S_IFBLK: |
| case S_IFIFO: |
| #ifdef S_IFNAM |
| case S_IFNAM: |
| #endif |
| case S_IFLNK: |
| case S_IFSOCK: |
| /* only need a filehdr_t; no data |
| */ |
| rv = dump_file_spec( drivep, contextp, fshandlep, statp ); |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| lock( ); |
| sc_stat_nondirdone++; |
| unlock( ); |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| break; /* drop out of switch to extattr dump */ |
| case S_IFDIR: |
| default: |
| /* don't know how to dump these |
| */ |
| mlog( MLOG_VERBOSE, _( |
| "don't know how to dump ino %llu: mode %08x\n"), |
| statp->bs_ino, |
| statp->bs_mode ); |
| if ( statp->bs_ino > contextp->cc_stat_lastino ) { |
| lock( ); |
| sc_stat_nondirdone++; |
| unlock( ); |
| contextp->cc_stat_lastino = statp->bs_ino; |
| } |
| return RV_OK; |
| /* not yet implemented |
| case S_IFMNT: |
| */ |
| } |
| |
| if ( rv == RV_OK |
| && |
| file_skipped == BOOL_FALSE |
| && |
| sc_dumpextattrpr |
| && |
| ( statp->bs_xflags & XFS_XFLAG_HASATTR )) { |
| rv = dump_extattrs( drivep, contextp, fshandlep, statp); |
| } |
| |
| return rv; |
| } |
| |
| /* a regular file may be broken into several portions if its size |
| * is large. Each portion begins with a filehdr_t and is followed by |
| * several extents. each extent begins with an extenthdr_t. returns RV_OK |
| * if all extents dumped, RV_... on drive errors, or RV_INTR if |
| * operator requested stop. |
| */ |
| static rv_t |
| dump_file_reg( drive_t *drivep, |
| context_t *contextp, |
| content_inode_hdr_t *scwhdrp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp, |
| bool_t *file_skippedp ) |
| { |
| startpt_t *startptp = &scwhdrp->cih_startpt; |
| startpt_t *endptp = &scwhdrp->cih_endpt; |
| extent_group_context_t extent_group_context; |
| bool_t cmpltflg; |
| off64_t offset; |
| off64_t stopoffset; |
| bool_t sosig; /* stop offset is significant */ |
| off64_t maxextentcnt; |
| rv_t rv; |
| |
| /* determine the offset within the file where the dump should begin. |
| * it must have been aligned to the basic fs block size by the |
| * startpoint calculations done during strategy initialization. |
| */ |
| if ( statp->bs_ino == startptp->sp_ino ) { |
| offset = startptp->sp_offset; |
| ASSERT( ( offset & ( off64_t )( BBSIZE - 1 )) == 0 ); |
| } else { |
| offset = 0; |
| } |
| |
| /* if this is a resumed dump and the resumption begins somewhere |
| * within this file, and that point is greater than offset set |
| * above, and that file hasn't changed since the resumed dump, |
| * modify offset. |
| */ |
| if ( sc_resumepr ) { |
| drange_t *drangep = sc_resumerangep; |
| size_t drangecnt = sc_resumerangecnt; |
| size_t drangeix; |
| |
| for ( drangeix = 0 ; drangeix < drangecnt ; drangeix++ ) { |
| drange_t *rp = &drangep[ drangeix ]; |
| if ( statp->bs_ino == rp->dr_begin.sp_ino ) { |
| register time32_t mtime = statp->bs_mtime.tv_sec; |
| register time32_t ctime = statp->bs_ctime.tv_sec; |
| register time32_t ltime = max( mtime, ctime ); |
| if ( ltime < sc_resumebasetime ) { |
| if ( rp->dr_begin.sp_offset > offset ){ |
| offset =rp->dr_begin.sp_offset; |
| } |
| } |
| break; |
| } |
| } |
| ASSERT( ( offset & ( off64_t )( BBSIZE - 1 )) == 0 ); |
| } |
| |
| /* determine the offset within the file where the dump should end. |
| * only significant if this is an inode spanning a startpoint. |
| */ |
| if ( endptp->sp_flags & STARTPT_FLAGS_END ) { |
| sosig = BOOL_FALSE; |
| stopoffset = 0; |
| } else if ( statp->bs_ino == endptp->sp_ino ) { |
| sosig = BOOL_TRUE; |
| stopoffset = endptp->sp_offset; |
| } else { |
| sosig = BOOL_FALSE; |
| stopoffset = 0; |
| } |
| |
| mlog( MLOG_TRACE, |
| "dumping regular file ino %llu " |
| "offset %lld " |
| "to offset %lld " |
| "(size %lld)\n", |
| statp->bs_ino, |
| offset, |
| sosig ? stopoffset : statp->bs_size, |
| statp->bs_size ); |
| |
| /* calculate the maximum extent group size. files larger than this |
| * will be broken into multiple extent groups, each with its own |
| * filehdr_t. |
| */ |
| maxextentcnt = drivep->d_recmarksep; |
| |
| /* initialize the extent group context. if fails, just return, |
| * pretending the dump succeeded. |
| */ |
| rv = init_extent_group_context( fshandlep, |
| statp, |
| &extent_group_context ); |
| if ( rv != RV_OK ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "could not open regular file ino %llu mode 0x%08x: " |
| "%s: not dumped\n"), |
| statp->bs_ino, |
| statp->bs_mode, |
| strerror( errno )); |
| *file_skippedp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* loop here, dumping marked groups of extents. each extent group |
| * is preceeded by a filehdr_t. this is required so that the |
| * recovery side can identify the fs file at each marked point |
| * in the stream. it sets by reference offset, bytecnt, and cmpltflg. |
| * it is important to understand that if a fs file is real big, |
| * it will be dumped in parts (extent groups), each beginning with |
| * an identical filehdr_t. |
| */ |
| cmpltflg = BOOL_FALSE; |
| |
| rv = RV_OK; |
| for ( ; ; ) { |
| off64_t bytecnt = 0; |
| off64_t bc; |
| |
| /* see if we are done. |
| */ |
| if ( cmpltflg ) { |
| ASSERT( rv == RV_OK ); |
| break; |
| } |
| |
| /* set a mark - important to do this now, before deciding |
| * the media file is to big or the operator asked to |
| * interrupt the dump. this mark, if committed, indicates |
| * the previous fs file / extent group was completely dumped. |
| */ |
| mark_set( drivep, statp->bs_ino, offset, 0 ); |
| |
| /* spoof EOF if the media file size is getting too big. |
| * note that the most we can go over is d_recmarksep. |
| */ |
| if ( contextp->cc_mfilesz >= drivep->d_recmfilesz ){ |
| rv = RV_EOF; |
| break; |
| } |
| |
| /* check if the operator has requested to interrupt the dump. |
| */ |
| if ( cldmgr_stop_requested( )) { |
| mlog( MLOG_NORMAL, _( |
| "dump interrupted prior to ino %llu offset %lld\n"), |
| statp->bs_ino, |
| offset ); |
| mlog_exit_hint(RV_INTR); |
| rv = RV_INTR; |
| break; |
| } |
| |
| /* dump the file header |
| */ |
| mlog( MLOG_DEBUG, |
| "dumping extent group ino %llu offset %lld\n", |
| statp->bs_ino, |
| offset ); |
| rv = dump_filehdr( drivep, contextp, statp, offset, 0 ); |
| if ( rv != RV_OK ) { |
| break; |
| } |
| bytecnt += sizeof( filehdr_t ); |
| |
| /* dump a group of extents. returns by reference |
| * the offset of the next extent group (to be placed |
| * in the next mark), the total number of bytes written |
| * to media (headers and all), and a flag indicating |
| * all extents have been dumped. |
| */ |
| bc = 0; /* for lint */ |
| rv = dump_extent_group( drivep, |
| contextp, |
| statp, |
| &extent_group_context, |
| maxextentcnt, |
| stopoffset, |
| sosig, |
| &offset, |
| &bc, |
| &cmpltflg ); |
| ASSERT( bc >= 0 ); |
| bytecnt += bc; |
| if ( rv != RV_OK ) { |
| break; |
| } |
| |
| /* update global stat |
| */ |
| lock( ); |
| sc_stat_datadone += ( size64_t )bc; |
| unlock( ); |
| |
| /* dump LAST extent hdr. one of these is placed at the |
| * end of each dumped file. necessary to detect the |
| * end of the file. |
| */ |
| rv = dump_extenthdr( drivep, |
| contextp, |
| EXTENTHDR_TYPE_LAST, |
| 0, |
| 0, |
| 0 ); |
| if ( rv != RV_OK ) { |
| break; |
| } |
| bytecnt += sizeof( extenthdr_t ); |
| |
| /* update the media file size |
| */ |
| contextp->cc_mfilesz += bytecnt; |
| |
| } |
| |
| cleanup_extent_group_context( &extent_group_context ); |
| return rv; |
| } |
| |
| /* dumps character, block, and fifo - special files. no data, just meta-data, |
| * all contained within the filehdr_t. also handles symbolic link files: |
| * appends a variable-length string after the filehdr_t. |
| */ |
| static rv_t |
| dump_file_spec( drive_t *drivep, |
| context_t *contextp, |
| jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp ) |
| { |
| intgen_t rval; |
| rv_t rv; |
| |
| mlog( MLOG_TRACE, |
| "dumping special file ino %llu mode 0x%04x\n", |
| statp->bs_ino, |
| statp->bs_mode ); |
| |
| /* set a mark - important to do this now, before deciding |
| * the media file is to big. this mark, if committed, |
| * indicates the previous fs file was completely dumped. |
| */ |
| mark_set( drivep, statp->bs_ino, 0, 0 ); |
| |
| /* dump the file header |
| */ |
| rv = dump_filehdr( drivep, contextp, statp, 0, 0 ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| /* update the media file size |
| */ |
| contextp->cc_mfilesz += sizeof( filehdr_t ); |
| |
| /* if a symbolic link, also dump the link pathname. |
| * use an extent header to represent the pathname. the |
| * extent sz will always be a multiple of SYMLINK_ALIGN. |
| * the symlink pathname char string will always be NULL-terminated. |
| */ |
| if ( ( statp->bs_mode & S_IFMT ) == S_IFLNK ) { |
| intgen_t nread; |
| size_t extentsz; |
| |
| /* read the link path. if error, dump a zero-length |
| * extent. in any case, nread will contain the number of |
| * bytes to dump, and contextp->cc_direntbufp will contain |
| * the bytes. |
| */ |
| nread = jdm_readlink( fshandlep, |
| statp, |
| contextp->cc_readlinkbufp, |
| contextp->cc_readlinkbufsz ); |
| if ( nread < 0 ) { |
| mlog( MLOG_DEBUG, |
| "could not read symlink ino %llu\n", |
| statp->bs_ino ); |
| nread = 0; |
| } |
| |
| /* null-terminate the string |
| */ |
| ASSERT( ( size_t )nread < contextp->cc_readlinkbufsz ); |
| contextp->cc_readlinkbufp[ nread ] = 0; |
| |
| /* calculate the extent size - be sure to include room |
| * for the null-termination. |
| */ |
| extentsz = ( ( size_t )nread + 1 + ( SYMLINK_ALIGN - 1 )) |
| & |
| ~ ( SYMLINK_ALIGN - 1 ); |
| ASSERT( extentsz <= contextp->cc_readlinkbufsz ); |
| |
| /* dump an extent header |
| */ |
| rv = dump_extenthdr( drivep, |
| contextp, |
| EXTENTHDR_TYPE_DATA, |
| 0, |
| 0, |
| ( off64_t )extentsz ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| /* dump the link path extent |
| */ |
| rval = write_buf( contextp->cc_readlinkbufp, |
| extentsz, |
| ( void * )drivep, |
| ( gwbfp_t )drivep->d_opsp->do_get_write_buf, |
| ( wfp_t )drivep->d_opsp->do_write ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| } |
| |
| return RV_OK; |
| } |
| |
| /* contrives the initial state of the extent group context such that |
| * dump_extent_group() will fetch some extents from the kernel before it |
| * does anything else. |
| */ |
| static rv_t |
| init_extent_group_context( jdm_fshandle_t *fshandlep, |
| xfs_bstat_t *statp, |
| extent_group_context_t *gcp ) |
| { |
| bool_t isrealtime; |
| intgen_t oflags; |
| struct flock fl; |
| |
| isrealtime = ( bool_t )(statp->bs_xflags & XFS_XFLAG_REALTIME ); |
| oflags = O_RDONLY; |
| if ( isrealtime ) { |
| oflags |= O_DIRECT; |
| } |
| ( void )memset( ( void * )gcp, 0, sizeof( *gcp )); |
| gcp->eg_bmap[ 0 ].bmv_offset = 0; |
| gcp->eg_bmap[ 0 ].bmv_length = -1; |
| gcp->eg_bmap[ 0 ].bmv_count = BMAP_LEN; |
| gcp->eg_bmap[ 0 ].bmv_iflags = BMV_IF_NO_DMAPI_READ; |
| gcp->eg_nextbmapp = &gcp->eg_bmap[ 1 ]; |
| gcp->eg_endbmapp = &gcp->eg_bmap[ 1 ]; |
| gcp->eg_bmapix = 0; |
| gcp->eg_gbmcnt = 0; |
| gcp->eg_fd = jdm_open( fshandlep, statp, oflags ); |
| if ( gcp->eg_fd < 0 ) { |
| return RV_ERROR; |
| } |
| |
| /* Check if a mandatory lock is set on the file to try and |
| * avoid blocking indefinitely on the reads later. Note that |
| * someone could still set a mandatory lock and hose xfsdump |
| * after this check but before all reads have completed. |
| * This change just closes the window a bit. |
| */ |
| if ( (statp->bs_mode & S_ISGID) && ( ! (statp->bs_mode&S_IXOTH) ) ) { |
| fl.l_type = F_RDLCK; |
| fl.l_whence = SEEK_SET; |
| fl.l_start = (off_t)0; |
| fl.l_len = 0; |
| if ((fcntl(gcp->eg_fd, F_GETLK, &fl)) < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "locking check failed ino %llu\n"), |
| statp->bs_ino ); |
| close(gcp->eg_fd); |
| return RV_ERROR; |
| } |
| if (fl.l_type != F_UNLCK) { |
| /* Mandatory lock is set */ |
| close(gcp->eg_fd); |
| errno = EBUSY; |
| return RV_ERROR; |
| } |
| } |
| return RV_OK; |
| } |
| |
| static void |
| cleanup_extent_group_context( extent_group_context_t *gcp ) |
| { |
| ( void )close( gcp->eg_fd ); |
| } |
| |
| static rv_t |
| dump_extent_group( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *statp, |
| extent_group_context_t *gcp, |
| off64_t maxcnt, |
| off64_t stopoffset, |
| bool_t sosig, |
| off64_t *nextoffsetp, |
| off64_t *bytecntp, |
| bool_t *cmpltflgp ) |
| { |
| struct dioattr da; |
| drive_ops_t *dop = drivep->d_opsp; |
| bool_t isrealtime = ( bool_t )( statp->bs_xflags |
| & |
| XFS_XFLAG_REALTIME ); |
| off64_t nextoffset; |
| off64_t bytecnt; /* accumulates total bytes sent to media */ |
| intgen_t rval; |
| rv_t rv; |
| |
| /* |
| * Setup realtime I/O size. |
| */ |
| if ( isrealtime ) { |
| if ( (ioctl(gcp->eg_fd, XFS_IOC_DIOINFO, &da) < 0) ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "dioinfo failed ino %llu\n"), |
| statp->bs_ino ); |
| da.d_miniosz = PGSZ; |
| } |
| } |
| |
| /* dump extents until the recommended extent length is achieved |
| */ |
| nextoffset = *nextoffsetp; |
| bytecnt = 0; |
| ASSERT( ( nextoffset & ( BBSIZE - 1 )) == 0 ); |
| |
| for ( ; ; ) { |
| off64_t offset; |
| off64_t extsz; |
| |
| /* if we've dumped to the stop point return. |
| */ |
| if ( sosig && nextoffset >= stopoffset ) { |
| mlog( MLOG_NITTY, |
| "dumped to stop offset\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* if we've dumped the entire file, return |
| */ |
| if ( nextoffset >= statp->bs_size ) { |
| mlog( MLOG_NITTY, |
| "dumped to end of file\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* if we've exceeded the desired per-extent group byte count, |
| * call it quits. we'll be called back for more because the |
| * completion flag is set FALSE. |
| */ |
| if ( bytecnt >= maxcnt ) { |
| mlog( MLOG_NITTY, |
| "reached per-extent group byte count\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_FALSE; |
| return RV_OK; |
| } |
| |
| /* if we are not looking at a valid bmap entry, |
| * get one. |
| */ |
| if ( gcp->eg_nextbmapp >= gcp->eg_endbmapp ) { |
| intgen_t entrycnt; /* entries in new bmap */ |
| |
| ASSERT( gcp->eg_nextbmapp == gcp->eg_endbmapp ); |
| |
| /* get a new extent block |
| */ |
| mlog( MLOG_NITTY, |
| "calling getbmapx for ino %llu\n", |
| statp->bs_ino ); |
| rval = ioctl( gcp->eg_fd, XFS_IOC_GETBMAPX, gcp->eg_bmap ); |
| gcp->eg_gbmcnt++; |
| entrycnt = gcp->eg_bmap[ 0 ].bmv_entries; |
| if ( entrycnt < 0 ) { /* workaround for getbmap bug */ |
| mlog( MLOG_DEBUG | MLOG_WARNING, _( |
| "getbmapx %d ino %lld mode 0x%08x " |
| "offset %lld ix %d " |
| "returns negative entry count\n"), |
| gcp->eg_gbmcnt, |
| statp->bs_ino, |
| statp->bs_mode, |
| nextoffset, |
| gcp->eg_bmapix ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "getbmapx %d ino %lld mode 0x%08x " |
| "offset %lld failed: %s\n"), |
| gcp->eg_gbmcnt, |
| statp->bs_ino, |
| statp->bs_mode, |
| nextoffset, |
| strerror( errno )); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* The F_GETBMAPX call succeeded. Give the HSM a chance |
| * to massage the extents. (It can change the number |
| * of extents remaining, even setting them to zero. |
| */ |
| |
| if (hsm_fs_ctxtp) { |
| if (!HsmModifyExtentMap(contextp->cc_hsm_f_ctxtp, |
| &gcp->eg_bmap[0])) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "hsm detected an extent map " |
| "error in ino %lld, skipping\n"), |
| statp->bs_ino); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| entrycnt = gcp->eg_bmap[ 0 ].bmv_entries; |
| } |
| |
| if ( entrycnt <= 0 ) { |
| mlog( MLOG_NITTY, |
| "all extent groups dumped\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| gcp->eg_nextbmapp = &gcp->eg_bmap[ 1 ]; |
| gcp->eg_endbmapp = &gcp->eg_bmap[ entrycnt + 1 ]; |
| mlog( MLOG_NITTY, |
| "getbmapx supplied %d extent entries\n", |
| entrycnt ); |
| } |
| |
| mlog( MLOG_NITTY, |
| "bmap entry %d ix %d block %lld offset %lld length %lld\n", |
| gcp->eg_nextbmapp - &gcp->eg_bmap[ 0 ], |
| gcp->eg_bmapix, |
| gcp->eg_nextbmapp->bmv_block, |
| gcp->eg_nextbmapp->bmv_offset, |
| gcp->eg_nextbmapp->bmv_length ); |
| |
| /* if the next bmap entry represents a hole, go to the next |
| * one in the bmap, and rescan to check above assumptions. |
| * bump nextoffset to after the hole, if beyond current value. |
| */ |
| if ( gcp->eg_nextbmapp->bmv_block == -1 ) { |
| off64_t tmpoffset; |
| |
| /* extract the offset and extent size from this |
| * entry |
| */ |
| offset = gcp->eg_nextbmapp->bmv_offset |
| * ( off64_t )BBSIZE; |
| extsz = gcp->eg_nextbmapp->bmv_length |
| * ( off64_t )BBSIZE; |
| |
| mlog( MLOG_NITTY, |
| "hole extent offset = %lld size = %lld\n", |
| offset, extsz ); |
| |
| /* Encode the hole - dump the extent header |
| * with the right extent type. |
| */ |
| rv = dump_extenthdr( drivep, |
| contextp, |
| EXTENTHDR_TYPE_HOLE, |
| 0, |
| offset, |
| extsz ); |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; /*moot since rv != OK */ |
| return rv; |
| } |
| bytecnt += sizeof( extenthdr_t ); |
| |
| tmpoffset = ( gcp->eg_nextbmapp->bmv_offset |
| + |
| gcp->eg_nextbmapp->bmv_length ) |
| * |
| ( off64_t )BBSIZE; |
| if ( tmpoffset > nextoffset ) { |
| nextoffset = tmpoffset; |
| } |
| gcp->eg_nextbmapp++; |
| gcp->eg_bmapix++; |
| continue; |
| } |
| |
| /* if the next bmap entry has a zero size, go to the next |
| * one in the bmap, and rescan to check above assumptions. |
| */ |
| if ( gcp->eg_nextbmapp->bmv_length <= 0 ) { |
| off64_t tmpoffset; |
| |
| mlog( MLOG_NITTY, |
| "non-positive extent\n" ); |
| tmpoffset = gcp->eg_nextbmapp->bmv_offset |
| * |
| ( off64_t )BBSIZE; |
| if ( tmpoffset > nextoffset ) { |
| nextoffset = tmpoffset; |
| } |
| gcp->eg_nextbmapp++; |
| gcp->eg_bmapix++; |
| continue; |
| } |
| |
| /* extract the offset and extent size from this |
| * entry |
| */ |
| offset = gcp->eg_nextbmapp->bmv_offset * ( off64_t )BBSIZE; |
| extsz = gcp->eg_nextbmapp->bmv_length * ( off64_t )BBSIZE; |
| mlog( MLOG_NITTY, |
| "extent offset %lld sz %lld; nextoffset %lld\n", |
| offset, |
| extsz, |
| nextoffset ); |
| |
| /* if the new bmap entry begins below the stop offset |
| * but does not contain any data above the current |
| * offset, go to the next one and rescan. |
| */ |
| if ( ! sosig || offset < stopoffset ) { |
| if ( offset + extsz <= nextoffset ) { |
| mlog( MLOG_NITTY, |
| "extent ends before nextoffset\n" ); |
| gcp->eg_nextbmapp++; |
| gcp->eg_bmapix++; |
| continue; |
| } |
| } |
| |
| /* if the new bmap entry begins beyond the end of the file, |
| * we are done. |
| */ |
| if ( offset >= statp->bs_size ) { |
| mlog( MLOG_NITTY, |
| "extent beyond end of file\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* if the new bmap entry begins at or above the stop offset, |
| * stop. we are done. |
| */ |
| if ( sosig && offset >= stopoffset ) { |
| mlog( MLOG_NITTY, |
| "extent beyond stop offset\n" ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return RV_OK; |
| } |
| |
| /* if the new entry begins below the range of |
| * interest, modify offset to begin at the |
| * beginning of the range of interest, and shorten |
| * extsz accordingly. |
| */ |
| if ( offset < nextoffset ) { |
| extsz -= nextoffset - offset; |
| offset = nextoffset; |
| mlog( MLOG_NITTY, |
| "adjusted bottom of extent to nextoffset: " |
| "offset %lld, sz %lld; nextoffset %lld\n", |
| offset, |
| extsz, |
| nextoffset ); |
| } |
| ASSERT( extsz > 0 ); |
| |
| /* if the resultant extent would put us over maxcnt, |
| * shorten it, and round up to the next BBSIZE (round |
| * upto d_miniosz for realtime). |
| */ |
| if ( extsz > maxcnt - ( bytecnt + sizeof( extenthdr_t ))) { |
| int iosz; |
| |
| if (isrealtime) |
| iosz = da.d_miniosz; |
| else |
| iosz = BBSIZE; |
| extsz = maxcnt - ( bytecnt + sizeof( extenthdr_t )); |
| extsz = ( extsz + ( off64_t )( iosz - 1 )) |
| & |
| ~( off64_t )( iosz - 1 ); |
| mlog( MLOG_NITTY, |
| "adjusted top of extent to honor maxcnt " |
| "(rounded up %d): " |
| "offset %lld, sz %lld; maxcnt %lld\n", |
| iosz, |
| offset, |
| extsz, |
| maxcnt ); |
| } |
| |
| /* if the shortened extent is too small, return; we'll |
| * pick it up next time around. exception: if the file |
| * size is zero, indicate we are done. |
| * !!! I don't believe this rule can ever fire! |
| */ |
| if ( extsz <= 0 ) { |
| mlog( MLOG_NITTY, |
| "adjusted extent size is non-positive: " |
| "%lld (bs_size %lld)\n", |
| extsz, |
| statp->bs_size ); |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| if ( statp->bs_size == 0 ) { |
| *cmpltflgp = BOOL_TRUE; |
| } else { |
| *cmpltflgp = BOOL_FALSE; |
| } |
| return RV_OK; |
| } |
| |
| /* if the resultant extent extends beyond the end of the |
| * file, shorten the extent to the nearest BBSIZE alignment |
| * at or beyond EOF. (Shorten to d_miniosz for realtime |
| * files). |
| */ |
| if ( extsz > statp->bs_size - offset ) { |
| int iosz; |
| |
| if (isrealtime) |
| iosz = da.d_miniosz; |
| else |
| iosz = BBSIZE; |
| extsz = statp->bs_size - offset; |
| extsz = ( extsz + ( off64_t )( iosz - 1 )) |
| & |
| ~( off64_t )( iosz - 1 ); |
| mlog( MLOG_NITTY, |
| "adjusted top of extent to match file size " |
| "(rounded up %d): " |
| "offset %lld, sz %lld; bs_size %lld\n", |
| iosz, |
| offset, |
| extsz, |
| statp->bs_size ); |
| } |
| |
| /* if the extent extends beyond the stop offset, |
| * shorten it to the stop offset. |
| */ |
| if ( sosig && ( extsz > stopoffset - offset )) { |
| extsz = stopoffset - offset; |
| ASSERT( extsz >= 0 ); |
| ASSERT( ! ( extsz & ( off64_t )( BBSIZE - 1 ))); |
| mlog( MLOG_NITTY, |
| "adjusted top of extent " |
| "to adhere to stop offset: " |
| "offset %lld, sz %lld; bs_size %lld\n", |
| offset, |
| extsz, |
| statp->bs_size ); |
| } |
| |
| /* I/O performance is better if we align the media write |
| * buffer to a page boundary. do this if the extent is |
| * at least a page in length. Also, necessary for real time |
| * files |
| */ |
| if ( isrealtime || extsz >= PGALIGNTHRESH * PGSZ ) { |
| size_t cnt_to_align; |
| cnt_to_align = ( * dop->do_get_align_cnt )( drivep ); |
| if ( ( size_t )cnt_to_align < 2*sizeof( extenthdr_t )) { |
| cnt_to_align += PGSZ; |
| } |
| |
| /* account for the DATA header following the alignment |
| */ |
| cnt_to_align -= sizeof( extenthdr_t ); |
| |
| rv = dump_extenthdr( drivep, |
| contextp, |
| EXTENTHDR_TYPE_ALIGN, |
| 0, |
| 0, |
| ( off64_t ) |
| ( ( size_t )cnt_to_align |
| - |
| sizeof(extenthdr_t))); |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return rv; |
| } |
| bytecnt += sizeof( extenthdr_t ); |
| cnt_to_align -= sizeof( extenthdr_t ); |
| rv = write_pad( drivep, cnt_to_align ); |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; /* moot: rv != OK */ |
| return rv; |
| } |
| bytecnt += ( off64_t )cnt_to_align; |
| } |
| /* adjust the next offset |
| */ |
| ASSERT( ( offset & ( off64_t )( BBSIZE - 1 )) == 0 ); |
| ASSERT( ( extsz & ( off64_t )( BBSIZE - 1 )) == 0 ); |
| nextoffset = offset + extsz; |
| |
| /* dump the extent header |
| */ |
| rv = dump_extenthdr( drivep, |
| contextp, |
| EXTENTHDR_TYPE_DATA, |
| 0, |
| offset, |
| extsz ); |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; /* moot since rv != OK */ |
| return rv; |
| } |
| bytecnt += sizeof( extenthdr_t ); |
| |
| /* dump the extent. if read fails to return all |
| * asked for, pad out the extent with zeros. necessary |
| * because the extent hdr is already out there! |
| */ |
| while ( extsz ) { |
| off64_t new_off; |
| intgen_t nread; |
| size_t reqsz; |
| size_t actualsz; |
| char *bufp; |
| |
| reqsz = extsz > ( off64_t )INTGENMAX |
| ? |
| INTGENMAX |
| : |
| ( size_t )extsz; |
| bufp = ( * dop->do_get_write_buf )( drivep, |
| reqsz, |
| &actualsz ); |
| ASSERT( actualsz <= reqsz ); |
| new_off = lseek64( gcp->eg_fd, offset, SEEK_SET ); |
| if ( new_off == ( off64_t )( -1 )) { |
| mlog( MLOG_NORMAL, _( |
| "can't lseek ino %llu\n"), |
| statp->bs_ino ); |
| nread = 0; |
| } else { |
| nread = read( gcp->eg_fd, bufp, actualsz); |
| } |
| if ( nread < 0 ) { |
| #ifdef HIDDEN |
| struct statvfs64 s; |
| |
| /* Be quiet if this is a swap file - #510197 */ |
| if ( (fstatvfs64(gcp->eg_fd, &s) < 0 ) || |
| ((s.f_flag & ST_LOCAL) != 0) ) |
| mlog( MLOG_NORMAL, _( |
| "can't read ino %llu at offset %d (act=%d req=%d) rt=%d\n"), |
| statp->bs_ino, new_off, actualsz , reqsz, isrealtime ); |
| #endif /* HIDDEN */ |
| |
| nread = 0; |
| } |
| ASSERT( ( size_t )nread <= actualsz ); |
| mlog( MLOG_NITTY, |
| "read ino %llu offset %lld sz %d actual %d\n", |
| statp->bs_ino, |
| offset, |
| actualsz, |
| nread ); |
| |
| /* must return entire buffer supplied by call to |
| * do_get_write_buf; so pad end with zeros. below |
| * we assume the short read implies EOF, so will |
| * then pad out remainder of extent as well. |
| */ |
| if ( ( size_t )nread < actualsz ) { |
| memset( ( void * )( bufp + nread ), |
| 0, |
| actualsz - ( size_t )nread ); |
| } |
| |
| rval = ( * dop->do_write )( drivep, |
| bufp, |
| actualsz ); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; /* moot: rv != OK */ |
| return rv; |
| } |
| bytecnt += ( off64_t )actualsz; |
| extsz -= ( off64_t )actualsz; |
| offset += ( off64_t )actualsz; |
| |
| /* if we got a short read, assume we are at the |
| * end of the file; pad out the remainder of the |
| * extent to match the header. |
| */ |
| if ( ( size_t )nread < actualsz ) { |
| mlog( MLOG_NITTY, |
| "padding remaind %lld bytes of extent\n", |
| extsz ); |
| while ( extsz ) { |
| reqsz = extsz > ( off64_t )INTGENMAX |
| ? |
| INTGENMAX |
| : |
| ( size_t )extsz; |
| rv = write_pad( drivep, |
| ( size_t )reqsz ); |
| if ( rv != RV_OK ) { |
| *nextoffsetp = nextoffset; |
| *bytecntp = bytecnt; |
| *cmpltflgp = BOOL_TRUE; |
| return rv; |
| } |
| bytecnt += ( off64_t )reqsz; |
| extsz -= ( off64_t )reqsz; |
| offset += ( off64_t )reqsz; |
| } |
| } |
| } |
| |
| /* made it! advance to the next extent if the current |
| * extent is completely dumped. |
| * !!! not be necessary, taken care of near the |
| * !!! top of the loop. |
| */ |
| if ( nextoffset |
| >= |
| gcp->eg_nextbmapp->bmv_offset * ( off64_t )BBSIZE |
| + |
| gcp->eg_nextbmapp->bmv_length * ( off64_t )BBSIZE ) { |
| mlog( MLOG_NITTY, |
| "advancing to next extent in bmap\n" ); |
| gcp->eg_nextbmapp++; |
| gcp->eg_bmapix++; |
| } |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* Note: assumes the pad fields in dst have been zeroed. */ |
| static void |
| copy_xfs_bstat(bstat_t *dst, xfs_bstat_t *src) |
| { |
| dst->bs_ino = src->bs_ino; |
| dst->bs_mode = src->bs_mode; |
| dst->bs_nlink = src->bs_nlink; |
| dst->bs_uid = src->bs_uid; |
| dst->bs_gid = src->bs_gid; |
| dst->bs_rdev = src->bs_rdev; |
| dst->bs_blksize = src->bs_blksize; |
| dst->bs_size = src->bs_size; |
| dst->bs_atime.tv_sec = src->bs_atime.tv_sec; |
| dst->bs_atime.tv_nsec = src->bs_atime.tv_nsec; |
| dst->bs_mtime.tv_sec = src->bs_mtime.tv_sec; |
| dst->bs_mtime.tv_nsec = src->bs_mtime.tv_nsec; |
| dst->bs_ctime.tv_sec = src->bs_ctime.tv_sec; |
| dst->bs_ctime.tv_nsec = src->bs_ctime.tv_nsec; |
| dst->bs_blocks = src->bs_blocks; |
| dst->bs_xflags = src->bs_xflags; |
| dst->bs_extsize = src->bs_extsize; |
| dst->bs_extents = src->bs_extents; |
| dst->bs_gen = src->bs_gen; |
| dst->bs_projid_lo = src->bs_projid_lo; |
| dst->bs_forkoff = 0; |
| dst->bs_projid_hi = src->bs_projid_hi; |
| dst->bs_dmevmask = src->bs_dmevmask; |
| dst->bs_dmstate = src->bs_dmstate; |
| } |
| |
| static rv_t |
| dump_filehdr( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *statp, |
| off64_t offset, |
| intgen_t flags ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| register filehdr_t *fhdrp = contextp->cc_filehdrp; |
| filehdr_t tmpfhdrp; |
| intgen_t rval; |
| rv_t rv; |
| |
| ( void )memset( ( void * )fhdrp, 0, sizeof( *fhdrp )); |
| if ( statp ) { |
| if (hsm_fs_ctxtp) { |
| HsmModifyInode(contextp->cc_hsm_f_ctxtp, statp); |
| } |
| copy_xfs_bstat(&fhdrp->fh_stat, statp); |
| } |
| fhdrp->fh_offset = offset; |
| fhdrp->fh_flags = flags | FILEHDR_FLAGS_CHECKSUM; |
| fhdrp->fh_checksum = calc_checksum( fhdrp, FILEHDR_SZ ); |
| |
| xlate_filehdr(fhdrp, &tmpfhdrp, 1); |
| rval = write_buf( ( char * )&tmpfhdrp, |
| sizeof( tmpfhdrp ), |
| ( void * )drivep, |
| ( gwbfp_t )dop->do_get_write_buf, |
| ( wfp_t )dop->do_write ); |
| |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| |
| return rv; |
| } |
| |
| static rv_t |
| dump_extenthdr( drive_t *drivep, |
| context_t *contextp, |
| int32_t type, |
| int32_t flags, |
| off64_t offset, |
| off64_t sz ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| register extenthdr_t *ehdrp = contextp->cc_extenthdrp; |
| extenthdr_t tmpehdrp; |
| intgen_t rval; |
| rv_t rv; |
| char typestr[20]; |
| |
| switch( type ) { |
| case EXTENTHDR_TYPE_LAST: |
| strcpy( typestr, "LAST" ); |
| break; |
| case EXTENTHDR_TYPE_ALIGN: |
| strcpy( typestr, "ALIGN" ); |
| break; |
| case EXTENTHDR_TYPE_DATA: |
| strcpy( typestr, "DATA" ); |
| break; |
| case EXTENTHDR_TYPE_HOLE: |
| strcpy( typestr, "HOLE" ); |
| break; |
| default: |
| strcpy( typestr, "UNKNOWN" ); |
| } |
| |
| mlog( MLOG_DEBUG, |
| "dumping extent type = %s offset = %lld size = %lld\n", |
| typestr, |
| offset, |
| sz ); |
| |
| ( void )memset( ( void * )ehdrp, 0, sizeof( *ehdrp )); |
| ehdrp->eh_type = type; |
| ehdrp->eh_flags = flags | EXTENTHDR_FLAGS_CHECKSUM; |
| ehdrp->eh_offset = offset; |
| ehdrp->eh_sz = sz; |
| ehdrp->eh_checksum = calc_checksum( ehdrp, EXTENTHDR_SZ ); |
| |
| xlate_extenthdr(ehdrp, &tmpehdrp, 1); |
| rval = write_buf( ( char * )&tmpehdrp, |
| sizeof( tmpehdrp ), |
| ( void * )drivep, |
| ( gwbfp_t )dop->do_get_write_buf, |
| ( wfp_t )dop->do_write ); |
| |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| |
| return rv; |
| } |
| |
| static rv_t |
| dump_dirent( drive_t *drivep, |
| context_t *contextp, |
| xfs_bstat_t *statp, |
| xfs_ino_t ino, |
| gen_t gen, |
| char *name, |
| size_t namelen ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| char *outbufp; |
| size_t direntbufsz = contextp->cc_mdirentbufsz; |
| size_t sz; |
| size_t name_offset; |
| intgen_t rval; |
| rv_t rv; |
| |
| if ( sc_use_old_direntpr ) { |
| name_offset = offsetofmember( direnthdr_v1_t, dh_name ); |
| } else { |
| name_offset = offsetofmember( direnthdr_t, dh_name ); |
| } |
| |
| sz = name_offset |
| + |
| namelen |
| + |
| 1; |
| sz = ( sz + DIRENTHDR_ALIGN - 1 ) |
| & |
| ~( DIRENTHDR_ALIGN - 1 ); |
| |
| if ( sz > direntbufsz ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to dump " |
| "directory %llu entry %s (%llu): " |
| "name too long\n"), |
| statp->bs_ino, |
| name, |
| ino ); |
| mlog_exit_hint(RV_INCOMPLETE); |
| return RV_OK; |
| } |
| |
| ASSERT( sz <= UINT16MAX ); |
| ASSERT( sz >= DIRENTHDR_SZ ); |
| |
| outbufp = malloc(sz); |
| |
| if ( sc_use_old_direntpr ) { |
| direnthdr_v1_t *dhdrp = ( direnthdr_v1_t * )contextp->cc_mdirentbufp; |
| direnthdr_v1_t *tmpdhdrp = ( direnthdr_v1_t * )outbufp; |
| |
| memset( ( void * )dhdrp, 0, sz ); |
| dhdrp->dh_ino = ino; |
| dhdrp->dh_sz = ( u_int16_t )sz; |
| dhdrp->dh_gen = ( u_int16_t )( gen & DENTGENMASK ); |
| if ( name ) { |
| strcpy( dhdrp->dh_name, name ); |
| } |
| |
| dhdrp->dh_checksum = calc_checksum( dhdrp, DIRENTHDR_SZ ); |
| |
| xlate_direnthdr_v1( dhdrp, tmpdhdrp, 1 ); |
| if ( name ) { |
| strcpy( tmpdhdrp->dh_name, name ); |
| } |
| } else { |
| direnthdr_t *dhdrp = ( direnthdr_t * )contextp->cc_mdirentbufp; |
| direnthdr_t *tmpdhdrp = ( direnthdr_t * )outbufp; |
| |
| memset( ( void * )dhdrp, 0, sz ); |
| dhdrp->dh_ino = ino; |
| dhdrp->dh_gen = gen; |
| dhdrp->dh_sz = ( u_int16_t )sz; |
| if ( name ) { |
| strcpy( dhdrp->dh_name, name ); |
| } |
| |
| dhdrp->dh_checksum = calc_checksum( dhdrp, DIRENTHDR_SZ ); |
| |
| xlate_direnthdr( dhdrp, tmpdhdrp, 1 ); |
| if ( name ) { |
| strcpy( tmpdhdrp->dh_name, name ); |
| } |
| } |
| |
| rval = write_buf( outbufp, |
| sz, |
| ( void * )drivep, |
| ( gwbfp_t )dop->do_get_write_buf, |
| ( wfp_t )dop->do_write ); |
| free(outbufp); |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| |
| return rv; |
| } |
| |
| static bool_t |
| dump_session_inv( drive_t *drivep, |
| context_t *contextp, |
| media_hdr_t *mwhdrp, |
| content_inode_hdr_t *scwhdrp ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| ix_t strmix = drivep->d_index; |
| inv_stmtoken_t inv_stmt; |
| char *inv_sbufp; |
| size_t inv_sbufsz; |
| off64_t ncommitted; |
| bool_t ok; |
| bool_t done; |
| |
| /* if the inventory session token is null, skip |
| */ |
| if ( sc_inv_sestoken == INV_TOKEN_NULL ) { |
| return BOOL_TRUE; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "dumping session inventory\n") ); |
| |
| /* get a buffer from the inventory manager |
| */ |
| inv_sbufp = 0; |
| inv_sbufsz = 0; |
| ok = inv_get_sessioninfo( sc_inv_sestoken, ( void * )&inv_sbufp, &inv_sbufsz ); |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to get session inventory to dump\n") ); |
| return BOOL_TRUE; |
| } |
| ASSERT( inv_sbufp ); |
| |
| /* modify the write header to indicate the media file type. |
| */ |
| scwhdrp->cih_mediafiletype = CIH_MEDIAFILETYPE_INVENTORY; |
| |
| /* loop attempting to write a complete media file, |
| * until we are successful or until the media layer |
| * tells us to give up. |
| */ |
| for ( done = BOOL_FALSE ; ! done ; ) { |
| uuid_t mediaid; |
| char medialabel[ GLOBAL_HDR_STRING_SZ ]; |
| bool_t partial; |
| intgen_t mediafileix; |
| intgen_t rval; |
| rv_t rv; |
| |
| mlog( MLOG_VERBOSE, _( |
| "beginning inventory media file\n") ); |
| |
| partial = BOOL_FALSE; |
| rv = Media_mfile_begin( drivep, contextp, BOOL_FALSE ); |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_TIMEOUT: |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change timeout: " |
| "session inventory not dumped\n") ); |
| mlog_exit_hint(RV_INV); |
| return BOOL_FALSE; |
| case RV_QUIT: |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change declined: " |
| "session inventory not dumped\n") ); |
| mlog_exit_hint(RV_INV); |
| return BOOL_FALSE; |
| case RV_DRIVE: |
| case RV_ERROR: |
| case RV_CORE: |
| default: |
| return BOOL_FALSE; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "media file %u (media %u, file %u)\n"), |
| mwhdrp->mh_dumpfileix, |
| mwhdrp->mh_mediaix, |
| mwhdrp->mh_mediafileix ); |
| |
| uuid_copy(mediaid, mwhdrp->mh_mediaid); |
| strcpy( medialabel, mwhdrp->mh_medialabel ); |
| mediafileix = ( intgen_t )mwhdrp->mh_mediafileix; |
| |
| rval = write_buf( inv_sbufp, |
| inv_sbufsz, |
| ( void * )drivep, |
| ( gwbfp_t )dop->do_get_write_buf, |
| ( wfp_t )dop->do_write ); |
| switch ( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| partial = BOOL_TRUE; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| case DRIVE_ERROR_CORE: |
| default: |
| return BOOL_FALSE; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "ending inventory media file\n") ); |
| ncommitted = 0; |
| rv = Media_mfile_end( drivep, |
| contextp, |
| mwhdrp, |
| &ncommitted, |
| partial ); |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_EOM: |
| partial = BOOL_TRUE; |
| break; |
| default: |
| return BOOL_FALSE; |
| } |
| |
| if ( partial ) { |
| mlog( MLOG_VERBOSE, _( |
| "encountered EOM while writing " |
| "inventory media file size %lld bytes\n"), |
| ncommitted ); |
| mlog_exit_hint(RV_INV); |
| } else { |
| mlog( MLOG_VERBOSE, _( |
| "inventory media file size %lld bytes\n"), |
| ncommitted ); |
| } |
| if ( sc_inv_stmtokenp ) { |
| inv_stmt = sc_inv_stmtokenp[ strmix ]; |
| } else { |
| inv_stmt = INV_TOKEN_NULL; |
| } |
| |
| if ( inv_stmt != INV_TOKEN_NULL ) { |
| mlog( MLOG_DEBUG, |
| "giving inventory " |
| "session dump media file\n" ); |
| ok = inv_put_mediafile( inv_stmt, |
| &mediaid, |
| medialabel, |
| ( u_intgen_t )mediafileix, |
| (xfs_ino_t )0, |
| ( off64_t )0, |
| (xfs_ino_t )0, |
| ( off64_t )0, |
| ncommitted, |
| ! partial, |
| BOOL_TRUE ); |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL, _( |
| "inventory session media file " |
| "put failed\n") ); |
| return BOOL_FALSE; |
| } |
| } |
| |
| done = ! partial; |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| static void |
| dump_terminator( drive_t *drivep, context_t *contextp, media_hdr_t *mwhdrp ) |
| { |
| off64_t ncommitted; |
| bool_t done; |
| |
| /* if the drive doesn't support use of stream terminators, don't bother |
| */ |
| if ( ! contextp->cc_Media_useterminatorpr ) { |
| return; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "writing stream terminator\n") ); |
| |
| /* modify the write header to indicate a terminator |
| */ |
| MEDIA_TERMINATOR_SET( mwhdrp ); |
| |
| /* loop attempting to write a complete media file header |
| * until we are successful or until the media layer |
| * tells us to give up. |
| */ |
| for ( done = BOOL_FALSE ; ! done ; ) { |
| bool_t partial; |
| rv_t rv; |
| |
| mlog( MLOG_VERBOSE, _( |
| "beginning media stream terminator\n") ); |
| |
| partial = BOOL_FALSE; |
| rv = Media_mfile_begin( drivep, contextp, BOOL_FALSE ); |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_TIMEOUT: |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change timeout: " |
| "media stream terminator not written\n") ); |
| return; |
| case RV_QUIT: |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "media change declined: " |
| "media stream terminator not written\n") ); |
| return; |
| case RV_DRIVE: |
| case RV_ERROR: |
| case RV_CORE: |
| default: |
| return; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "media file %u (media %u, file %u)\n"), |
| mwhdrp->mh_dumpfileix, |
| mwhdrp->mh_mediaix, |
| mwhdrp->mh_mediafileix ); |
| |
| mlog( MLOG_VERBOSE, _( |
| "ending media stream terminator\n") ); |
| ncommitted = 0; |
| rv = Media_mfile_end( drivep, |
| contextp, |
| mwhdrp, |
| &ncommitted, |
| BOOL_FALSE ); |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_EOM: |
| partial = BOOL_TRUE; |
| break; |
| default: |
| return; |
| } |
| |
| if ( partial ) { |
| mlog( MLOG_VERBOSE, _( |
| "encountered EOM while writing " |
| "media stream terminator size %lld bytes\n"), |
| ncommitted ); |
| } else { |
| mlog( MLOG_VERBOSE, _( |
| "media stream terminator size %lld bytes\n"), |
| ncommitted ); |
| } |
| |
| done = ! partial; |
| } |
| } |
| |
| static rv_t |
| write_pad( drive_t *drivep, size_t sz ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| intgen_t rval; |
| rv_t rv; |
| |
| rval = write_buf( 0, |
| sz, |
| ( void * )drivep, |
| ( gwbfp_t )dop->do_get_write_buf, |
| ( wfp_t )dop->do_write ); |
| |
| switch ( rval ) { |
| case 0: |
| rv = RV_OK; |
| break; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOM; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| break; |
| } |
| |
| return rv; |
| } |
| |
| static void |
| inv_cleanup( void ) |
| { |
| /* REFERENCED */ |
| bool_t ok; |
| |
| if ( sc_inv_stmtokenp && sc_contextp ) { |
| size_t strmix; |
| inv_stmtoken_t *inv_stmtp; |
| context_t *contextp; |
| for ( strmix = 0, |
| inv_stmtp = sc_inv_stmtokenp, |
| contextp = sc_contextp |
| ; |
| strmix < drivecnt |
| ; |
| strmix++, |
| inv_stmtp++, |
| contextp++ ) { |
| bool_t interrupted; |
| interrupted = ! contextp->cc_completepr; |
| if ( *inv_stmtp == INV_TOKEN_NULL ) { |
| continue; |
| } |
| mlog( MLOG_DEBUG, |
| "closing inventory stream %d%s\n", |
| strmix, |
| interrupted ? ": interrupted" : "" ); |
| if (interrupted) mlog_exit_hint(RV_INTR); |
| ok = inv_stream_close( *inv_stmtp, interrupted ); |
| ASSERT( ok ); |
| } |
| } |
| |
| if ( sc_inv_sestoken != INV_TOKEN_NULL ) { |
| ok = inv_writesession_close( sc_inv_sestoken ); |
| ASSERT( ok ); |
| } |
| |
| if ( sc_inv_idbtoken != INV_TOKEN_NULL ) { |
| ok = inv_close( sc_inv_idbtoken ); |
| ASSERT( ok ); |
| } |
| } |
| |
| /* This function returns with the proper media positioned at the proper place |
| * in the specified drive, with a write header layed down. The caller can |
| * immediately dump. The caller is expected to call Media_mfile_end when |
| * the media file is complete or EOM is encountered. |
| * |
| * Media_mfile_begin is partitioned into 4 coroutines, between which it |
| * readily jumps back and forth using gotos. So be careful about the scope |
| * of automatic variables. |
| */ |
| static rv_t |
| Media_mfile_begin( drive_t *drivep, context_t *contextp, bool_t intr_allowed ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| intgen_t dcaps = drivep->d_capabilities; |
| global_hdr_t *gwhdrp = drivep->d_gwritehdrp; |
| drive_hdr_t *dwhdrp = drivep->d_writehdrp; |
| media_hdr_t *mwhdrp = ( media_hdr_t * )dwhdrp->dh_upper; |
| drive_hdr_t *drhdrp = drivep->d_readhdrp; |
| media_hdr_t *mrhdrp = ( media_hdr_t * )drhdrp->dh_upper; |
| char *cmdlinemedialabel; |
| bool_t virginmediapr; |
| bool_t mediapresentpr; |
| bool_t prevmediapresentpr; |
| bool_t mediawrittentopr; |
| global_hdr_t saved_gwhdr; |
| intgen_t rval; |
| bool_t ok; |
| |
| /* sanity checks |
| */ |
| ASSERT( BES_INIT == 0 ); |
| |
| mlog( MLOG_DEBUG | MLOG_MEDIA, |
| "Media op: begin media file\n" ); |
| |
| /* the command line-specified media label is good only for the |
| * first media object written to. after that, the operator will |
| * be prompted for a label. To enforce this, cc_Media_firstlabel |
| * is saved in a temp var and NULLed. |
| */ |
| cmdlinemedialabel = contextp->cc_Media_firstlabel; |
| contextp->cc_Media_firstlabel = 0; |
| |
| /* dispatch based on entry state. invalidate entry state to assert |
| * each Media_mfile_begin is followed by and Media_mfile_end. |
| */ |
| prevmediapresentpr = BOOL_UNKNOWN; |
| { |
| bes_t entrystate; |
| entrystate = contextp->cc_Media_begin_entrystate; |
| contextp->cc_Media_begin_entrystate = BES_INVAL; |
| switch ( entrystate ) { |
| case BES_INIT: |
| mediawrittentopr = BOOL_FALSE; |
| mwhdrp->mh_mediaix = ( u_int32_t )( -1 ); |
| mwhdrp->mh_dumpfileix = ( u_int32_t )( -1 ); |
| if ( dcaps & DRIVE_CAP_READ ) { |
| mediapresentpr = BOOL_UNKNOWN; |
| virginmediapr = BOOL_UNKNOWN; |
| goto position; |
| } else { |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_TRUE; |
| goto write; |
| } |
| case BES_ENDOK: |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| mediawrittentopr = BOOL_TRUE; |
| goto write; |
| case BES_ENDEOM: |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| mediawrittentopr = BOOL_TRUE; |
| goto changemedia; |
| default: |
| ASSERT( 0 ); |
| return RV_CORE; |
| } |
| } |
| |
| position: |
| /* loop until we are positioned either at end of recorded data |
| * or at a terminator, as appropriate, of some media object, or hit EOM. |
| * goto write or changemedia to get out of loop (or return on |
| * catastrophic condition). ensure that all but the first media file |
| * of a stream begins on virgin media. that is, dump streams may |
| * be concatenated but not jumbled. a dump stream must be virtually |
| * contiguous. |
| */ |
| for ( ; ; ) { |
| /* check if a stop has been requested |
| */ |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* do a begin_read to see the disposition of the drive/media. |
| */ |
| rval = ( * dop->do_begin_read )( drivep ); |
| |
| /* update cc_Media_useterminatorpr after every begin_read, |
| * since begin_read will cause some unknown drive params |
| * to be resolved. |
| */ |
| update_cc_Media_useterminatorpr( drivep, contextp ); |
| |
| switch( rval ) { |
| case 0: |
| mlog_lock( ); |
| mlog( MLOG_VERBOSE | MLOG_NOLOCK | MLOG_MEDIA, _( |
| "positioned at media file %u: " |
| "dump %u, " |
| "stream %u\n"), |
| mrhdrp->mh_mediafileix, |
| mrhdrp->mh_dumpmediaix, |
| drhdrp->dh_driveix ); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "stream media file %u (%u in object), " |
| "stream media object %d\n", |
| mrhdrp->mh_dumpfileix, |
| mrhdrp->mh_dumpmediafileix, |
| mrhdrp->mh_mediaix ); |
| mlog_unlock( ); |
| |
| /* successfully read media file header. |
| * we know media must be present in drive, and |
| * contains at least one valid xfsdump, hence |
| * is not virgin. |
| */ |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| |
| /* do an end_read. the next begin_read will |
| * position in preparation for appending. |
| * if terminator, back up, we'll overwrite it. |
| * also be sure we can append dumps. |
| * if we back over a terminator which is the |
| * first media file on the media object, make the |
| * media object a virgin. |
| * also, check for erase option. |
| */ |
| ( * dop->do_end_read )( drivep ); |
| |
| switch( Media_erasechk( drivep, |
| dcaps, |
| intr_allowed, |
| prevmediapresentpr )) { |
| case RV_OK: |
| goto erasemedia; |
| case RV_INTR: |
| return RV_INTR; |
| default: |
| break; |
| } |
| |
| if ( ( int32_t )mwhdrp->mh_mediaix >= 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA,_( |
| "cannot interleave dump streams: " |
| "must supply a blank media object\n") ); |
| goto changemedia; |
| } |
| if ( ! ( dcaps & DRIVE_CAP_APPEND )) { |
| mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_MEDIA, _( |
| "media contains valid xfsdump " |
| "but does not support append\n") ); |
| goto changemedia; |
| } |
| if ( MEDIA_TERMINATOR_CHK( mrhdrp )) { |
| intgen_t status; |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "stream terminator found\n") ); |
| ASSERT( contextp->cc_Media_useterminatorpr ); |
| ASSERT( dcaps & DRIVE_CAP_BSF ); /* redundant */ |
| status = 0; |
| rval = ( * dop->do_bsf )( drivep, 0, &status ); |
| ASSERT( rval == 0 ); |
| if ( status == DRIVE_ERROR_DEVICE ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_MEDIA, _( |
| "encountered media error " |
| "attempting BSF\n") ); |
| goto changemedia; |
| } |
| if ( mrhdrp->mh_mediafileix == 0 ) { |
| virginmediapr = BOOL_TRUE; |
| } |
| goto write; |
| } |
| continue; |
| case DRIVE_ERROR_FOREIGN: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "media contains non-xfsdump data " |
| "or a corrupt xfsdump media file header " |
| "at beginning of media\n") ); |
| mlog_exit_hint(RV_CORRUPT); |
| |
| switch( Media_erasechk( drivep, |
| dcaps, |
| intr_allowed, |
| prevmediapresentpr )) { |
| case RV_OK: |
| goto erasemedia; |
| case RV_INTR: |
| return RV_INTR; |
| default: |
| break; |
| } |
| |
| if ( dlog_allowed( )) { |
| bool_t ok; |
| ok = Media_prompt_overwrite( drivep ); |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( ! ok ) { |
| goto changemedia; |
| } |
| } |
| |
| if ( ! ( dcaps & DRIVE_CAP_OVERWRITE )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "unable to overwrite\n") ); |
| goto changemedia; |
| } else { |
| intgen_t status; |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "repositioning to overwrite\n") ); |
| ASSERT( dcaps & DRIVE_CAP_BSF ); |
| status = 0; |
| rval = ( * dop->do_bsf )( drivep, 0, &status ); |
| ASSERT( rval == 0 ); |
| if ( status == DRIVE_ERROR_DEVICE ) { |
| return RV_DRIVE; |
| } |
| |
| virginmediapr = BOOL_TRUE; |
| |
| goto write; |
| } |
| |
| case DRIVE_ERROR_OVERWRITE: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "media may contain data. " |
| "Overwrite option specified\n") ); |
| |
| if ( dlog_allowed( )) { |
| bool_t ok; |
| ok = Media_prompt_overwrite( drivep ); |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( ! ok ) { |
| goto changemedia; |
| } |
| } |
| virginmediapr = BOOL_TRUE; /* like a virgin */ |
| goto write; |
| case DRIVE_ERROR_BLANK: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_TRUE; |
| goto write; |
| case DRIVE_ERROR_MEDIA: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| goto changemedia; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_EOD: |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "at end of data\n") ); |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| |
| switch( Media_erasechk( drivep, |
| dcaps, |
| intr_allowed, |
| prevmediapresentpr )) { |
| case RV_OK: |
| goto erasemedia; |
| case RV_INTR: |
| return RV_INTR; |
| default: |
| break; |
| } |
| |
| if ( contextp->cc_Media_useterminatorpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA,_( |
| "encountered EOD but expecting a media " |
| "stream terminator: " |
| "assuming full media\n") ); |
| goto changemedia; |
| } else { |
| goto write; |
| } |
| case DRIVE_ERROR_EOM: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "encountered EOM: media is full\n") ); |
| |
| switch( Media_erasechk( drivep, |
| dcaps, |
| intr_allowed, |
| prevmediapresentpr )) { |
| case RV_OK: |
| goto erasemedia; |
| case RV_INTR: |
| return RV_INTR; |
| default: |
| break; |
| } |
| |
| goto changemedia; |
| case DRIVE_ERROR_CORRUPTION: |
| case DRIVE_ERROR_VERSION: |
| case DRIVE_ERROR_FORMAT: |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_TRUE; |
| virginmediapr = BOOL_FALSE; |
| |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "encountered corrupt or foreign data: " |
| "assuming corrupted media\n") ); |
| mlog_exit_hint(RV_CORRUPT); |
| |
| switch( Media_erasechk( drivep, |
| dcaps, |
| intr_allowed, |
| prevmediapresentpr )) { |
| case RV_OK: |
| goto erasemedia; |
| case RV_INTR: |
| return RV_INTR; |
| default: |
| break; |
| } |
| |
| if ( contextp->cc_Media_useterminatorpr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA,_( |
| "encountered corrupt or foreign data " |
| "but expecting a media " |
| "stream terminator: " |
| "assuming corrupted media\n") ); |
| mlog_exit_hint(RV_CORRUPT); |
| goto changemedia; |
| } else if ( ! ( dcaps & DRIVE_CAP_OVERWRITE )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA,_( |
| "encountered corrupt or foreign data: " |
| "unable to overwrite: " |
| "assuming corrupted media\n") ); |
| mlog_exit_hint(RV_CORRUPT); |
| goto changemedia; |
| } else { |
| intgen_t status; |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA,_( |
| "encountered corrupt or foreign data: " |
| "repositioning to overwrite\n") ); |
| mlog_exit_hint(RV_CORRUPT); |
| assert( dcaps & DRIVE_CAP_BSF ); |
| status = 0; |
| rval = ( * dop->do_bsf )( drivep, 0, &status ); |
| ASSERT( rval == 0 ); |
| if ( status == DRIVE_ERROR_DEVICE ) { |
| return RV_DRIVE; |
| } |
| goto write; |
| } |
| case DRIVE_ERROR_STOP: |
| return RV_INTR; |
| case DRIVE_ERROR_INVAL: |
| return RV_ERROR; |
| case DRIVE_ERROR_EOF: |
| default: |
| mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_MEDIA, _( |
| "unexpected error from do_begin_read: %d\n"), |
| rval ); |
| return RV_CORE; |
| } |
| } |
| /* NOTREACHED */ |
| |
| erasemedia: |
| mlog( MLOG_VERBOSE | MLOG_WARNING | MLOG_MEDIA, _( |
| "erasing media\n") ); |
| rval = ( * dop->do_erase )( drivep ); |
| if ( rval ) { |
| return RV_DRIVE; |
| } |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_UNKNOWN; |
| virginmediapr = BOOL_UNKNOWN; |
| mediawrittentopr = BOOL_FALSE; |
| goto position; |
| |
| changemedia: |
| /* if the drive does not support media change, quit. |
| */ |
| if ( ! ( dcaps & DRIVE_CAP_REMOVABLE )) { |
| return RV_ERROR; |
| } |
| |
| /* first eject the current media object if capability supported |
| */ |
| ASSERT( mediapresentpr != BOOL_UNKNOWN ); |
| if ( mediapresentpr == BOOL_TRUE ) { |
| if ( dcaps & DRIVE_CAP_EJECT ) { |
| rval = ( * dop->do_eject_media )( drivep ); |
| if ( rval ) { |
| return RV_DRIVE; |
| } |
| } |
| } |
| |
| /* if dialogs not allowed, we are done. |
| */ |
| if ( ! dlog_allowed( )) { |
| return RV_QUIT; /* this return value will cause approp. msg */ |
| } |
| |
| /* If an alert program has been specified , run it |
| */ |
| if (media_change_alert_program != NULL) |
| system(media_change_alert_program); |
| |
| /* if media change prompt declined or times out, |
| * we are done |
| */ |
| if ( drivecnt > 1 && ! stdoutpiped ) { |
| ix_t thrdix = drivep->d_index; |
| ASSERT( sistr ); |
| mlog( MLOG_NORMAL | MLOG_NOTE | MLOG_MEDIA, _( |
| "please change media: " |
| "type %s to confirm media change\n"), |
| sistr ); |
| set_mcflag( thrdix ); |
| while ( sc_mcflag[ thrdix ] ) { |
| sleep( 2 ); |
| if ( cldmgr_stop_requested( )) { |
| clr_mcflag( thrdix ); |
| return RV_INTR; |
| } |
| } |
| ok = BOOL_TRUE; |
| } else { |
| ok = Media_prompt_change( drivep ); |
| } |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( ! ok ) { |
| return RV_QUIT; |
| } |
| |
| /* we know nothing about the media after a media change |
| */ |
| prevmediapresentpr = mediapresentpr; |
| mediapresentpr = BOOL_UNKNOWN; |
| virginmediapr = BOOL_UNKNOWN; |
| mediawrittentopr = BOOL_FALSE; |
| |
| goto position; |
| |
| write: |
| ASSERT( mediapresentpr == BOOL_TRUE ); |
| ASSERT( virginmediapr != BOOL_UNKNOWN ); |
| |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* bump the media header indices here. NOTE: will rescind these |
| * if the subsequent do_begin_write fails. this will be done by |
| * making a copy of the global write header, and copying it |
| * back on failure. |
| */ |
| saved_gwhdr = *gwhdrp; |
| |
| if ( mediawrittentopr ) { |
| mwhdrp->mh_dumpmediafileix++; |
| } else { |
| mwhdrp->mh_dumpmediafileix = 0; |
| } |
| mwhdrp->mh_dumpfileix++; /* pre-initialized to -1 */ |
| if ( virginmediapr ) { |
| mwhdrp->mh_mediafileix = 0; |
| mwhdrp->mh_dumpmediaix = 0; |
| } else { |
| if ( mwhdrp->mh_dumpmediafileix == 0 ) { |
| mwhdrp->mh_dumpmediaix = mrhdrp->mh_dumpmediaix + 1; |
| } |
| if ( mediawrittentopr ) { |
| mwhdrp->mh_mediafileix++; |
| } else { |
| mwhdrp->mh_mediafileix = mrhdrp->mh_mediafileix; |
| if ( ! MEDIA_TERMINATOR_CHK( mrhdrp )) { |
| mwhdrp->mh_mediafileix++; |
| } |
| } |
| } |
| |
| if ( ! mediawrittentopr ) { |
| mwhdrp->mh_mediaix++; /* pre-initialized to -1 */ |
| } |
| |
| ASSERT( mwhdrp->mh_mediaix != ( u_int32_t )( -1 )); |
| ASSERT( mwhdrp->mh_dumpfileix != ( u_int32_t )( -1 )); |
| |
| /* do not allow interleaving of media files from different xfsdumps. |
| */ |
| if ( mwhdrp->mh_mediaix != 0 |
| && |
| mwhdrp->mh_dumpmediafileix == 0 |
| && |
| ! virginmediapr ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "cannot interleave dump streams: must supply a blank " |
| "media object\n") ); |
| *gwhdrp = saved_gwhdr; |
| goto changemedia; |
| } |
| |
| /* update the media object previous id and label |
| */ |
| if ( ! mediawrittentopr && mwhdrp->mh_dumpfileix != 0 ) { |
| uuid_copy(mwhdrp->mh_prevmediaid, mwhdrp->mh_mediaid); |
| ( void )strncpyterm( mwhdrp->mh_prevmedialabel, |
| mwhdrp->mh_medialabel, |
| sizeof( mwhdrp->mh_medialabel )); |
| } |
| |
| /* update the media object current id and label |
| */ |
| if ( ! mediawrittentopr ) { |
| if ( mwhdrp->mh_mediafileix == 0 ) { |
| char labelbuf[ GLOBAL_HDR_STRING_SZ ]; |
| |
| uuid_generate( mwhdrp->mh_mediaid ); |
| |
| if ( ! cmdlinemedialabel |
| && |
| ! drivep->d_isnamedpipepr |
| && |
| ! drivep->d_isunnamedpipepr |
| && |
| dlog_allowed( )) { |
| cmdlinemedialabel = Media_prompt_label( drivep, |
| labelbuf, |
| sizeof( labelbuf )); |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| } |
| if ( cmdlinemedialabel && strlen( cmdlinemedialabel )) { |
| ( void )strncpyterm( mwhdrp->mh_medialabel, |
| cmdlinemedialabel, |
| sizeof( mwhdrp->mh_medialabel )); |
| } else { |
| ( void )memset( ( void * )mwhdrp->mh_medialabel, |
| 0, |
| sizeof( mwhdrp->mh_medialabel )); |
| if ( ! pipeline ) { |
| mlog( MLOG_VERBOSE |
| | |
| MLOG_WARNING |
| | |
| MLOG_MEDIA, _( |
| "no media label specified\n") ); |
| } |
| } |
| } else { |
| ASSERT( ! virginmediapr ); |
| uuid_copy(mwhdrp->mh_mediaid, mrhdrp->mh_mediaid); |
| ( void )strncpyterm( mwhdrp->mh_medialabel, |
| mrhdrp->mh_medialabel, |
| sizeof( mwhdrp->mh_medialabel )); |
| } |
| } |
| |
| mediawrittentopr = BOOL_TRUE; |
| |
| /* write hdr is prepared. place it on media |
| */ |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| rval = ( * dop->do_begin_write )( drivep ); |
| switch( rval ) { |
| case 0: |
| return RV_OK; |
| case DRIVE_ERROR_EOM: |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "encountered end of media " |
| "while attempting to begin new media file\n") ); |
| *gwhdrp = saved_gwhdr; |
| goto changemedia; |
| case DRIVE_ERROR_MEDIA: |
| *gwhdrp = saved_gwhdr; |
| goto changemedia; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| default: |
| return RV_CORE; |
| } |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| Media_mfile_end( drive_t *drivep, |
| context_t *contextp, |
| media_hdr_t *mwhdrp, |
| off64_t *ncommittedp, |
| bool_t hit_eom ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| intgen_t rval; |
| |
| mlog( MLOG_DEBUG | MLOG_MEDIA, |
| "Media op: end media file\n" ); |
| |
| ASSERT( contextp->cc_Media_begin_entrystate == BES_INVAL ); |
| |
| /* call drive's end_write op to flush the tail of the media file |
| * if has previously hit EOM, this is moot. |
| */ |
| rval = ( dop->do_end_write )( drivep, ncommittedp ); |
| if ( hit_eom ) { |
| ASSERT( ! rval ); |
| contextp->cc_Media_begin_entrystate = BES_ENDEOM; |
| return RV_EOM; |
| } |
| switch( rval ) { |
| case 0: |
| contextp->cc_Media_begin_entrystate = BES_ENDOK; |
| return RV_OK; |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_EOM: |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "encountered end of media " |
| "while ending media file\n") ); |
| mlog_exit_hint(RV_EOM); |
| contextp->cc_Media_begin_entrystate = BES_ENDEOM; |
| return RV_EOM; |
| case DRIVE_ERROR_DEVICE: |
| contextp->cc_Media_begin_entrystate = BES_INVAL; |
| return RV_DRIVE; |
| default: |
| contextp->cc_Media_begin_entrystate = BES_INVAL; |
| return RV_CORE; |
| } |
| |
| } |
| |
| static bool_t |
| Media_prompt_overwrite( drive_t *drivep ) |
| { |
| fold_t fold; |
| char question[ 100 ]; |
| char *preamblestr[ PREAMBLEMAX ]; |
| size_t preamblecnt; |
| char *querystr[ QUERYMAX ]; |
| size_t querycnt; |
| char *choicestr[ CHOICEMAX ]; |
| size_t choicecnt; |
| char *ackstr[ ACKMAX ]; |
| size_t ackcnt; |
| char *postamblestr[ POSTAMBLEMAX ]; |
| size_t postamblecnt; |
| ix_t doix; |
| ix_t dontix; |
| ix_t responseix; |
| ix_t sigintix; |
| |
| retry: |
| preamblecnt = 0; |
| fold_init( fold, "media overwrite dialog", '=' ); |
| preamblestr[ preamblecnt++ ] = "\n"; |
| preamblestr[ preamblecnt++ ] = fold; |
| preamblestr[ preamblecnt++ ] = "\n\n"; |
| ASSERT( preamblecnt <= PREAMBLEMAX ); |
| dlog_begin( preamblestr, preamblecnt ); |
| |
| /* query: ask if overwrite ok |
| */ |
| sprintf( question, |
| "overwrite data on media in " |
| "drive %u?\n", |
| (unsigned int)drivep->d_index ); |
| querycnt = 0; |
| querystr[ querycnt++ ] = question; |
| ASSERT( querycnt <= QUERYMAX ); |
| choicecnt = 0; |
| dontix = choicecnt; |
| choicestr[ choicecnt++ ] = "don't overwrite"; |
| doix = choicecnt; |
| choicestr[ choicecnt++ ] = "overwrite"; |
| ASSERT( choicecnt <= CHOICEMAX ); |
| sigintix = IXMAX - 1; |
| |
| responseix = dlog_multi_query( querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| doix, /* defaultix */ |
| DLOG_TIMEOUT_MEDIA, |
| dontix, /* timeout ix */ |
| sigintix, /* sigint ix */ |
| dontix, /* sighup ix */ |
| dontix ); /* sigquit ix */ |
| ackcnt = 0; |
| if ( responseix == doix ) { |
| ackstr[ ackcnt++ ] = "media will be overwritten\n"; |
| } else if ( responseix == dontix ) { |
| ackstr[ ackcnt++ ] = "media will NOT be overwritten\n"; |
| } else { |
| ackstr[ ackcnt++ ] = "keyboard interrupt\n"; |
| } |
| ASSERT( ackcnt <= ACKMAX ); |
| dlog_multi_ack( ackstr, |
| ackcnt ); |
| |
| postamblecnt = 0; |
| fold_init( fold, "end dialog", '-' ); |
| postamblestr[ postamblecnt++ ] = "\n"; |
| postamblestr[ postamblecnt++ ] = fold; |
| postamblestr[ postamblecnt++ ] = "\n\n"; |
| ASSERT( postamblecnt <= POSTAMBLEMAX ); |
| dlog_end( postamblestr, |
| postamblecnt ); |
| |
| if ( responseix == sigintix ) { |
| if ( cldmgr_stop_requested( )) { |
| return BOOL_FALSE; |
| } |
| sleep( 1 ); /* to allow main thread to begin dialog */ |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "" ); /* to block until main thread dialog complete */ |
| sleep( 1 ); /* to allow main thread to request children die */ |
| if ( cldmgr_stop_requested( )) { |
| return BOOL_FALSE; |
| } |
| mlog( MLOG_DEBUG, |
| "retrying media overwrite dialog\n" ); |
| goto retry; |
| } |
| |
| |
| return responseix == doix; |
| } |
| |
| static rv_t |
| Media_erasechk( drive_t *drivep, |
| intgen_t dcaps, |
| bool_t intr_allowed, |
| bool_t prevmediapresentpr ) |
| { |
| if ( prevmediapresentpr == BOOL_TRUE ) { |
| return RV_NOTOK; |
| } |
| |
| if ( sc_preerasepr ) { |
| if ( dcaps & DRIVE_CAP_ERASE ) { |
| if ( dlog_allowed( )) { |
| bool_t ok; |
| ok = Media_prompt_erase( drivep ); |
| if ( intr_allowed && cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( ok ) { |
| return RV_OK; |
| } else { |
| return RV_NOTOK; |
| } |
| } else { |
| return RV_OK; |
| } |
| } else { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "drive does not support media erase: " |
| "ignoring -%c option\n"), |
| GETOPT_ERASE ); |
| return RV_NOTOK; |
| } |
| } else { |
| return RV_NOTOK; |
| } |
| } |
| |
| static bool_t |
| Media_prompt_erase( drive_t *drivep ) |
| { |
| fold_t fold; |
| char question[ 100 ]; |
| char *preamblestr[ PREAMBLEMAX ]; |
| size_t preamblecnt; |
| char *querystr[ QUERYMAX ]; |
| size_t querycnt; |
| char *choicestr[ CHOICEMAX ]; |
| size_t choicecnt; |
| char *ackstr[ ACKMAX ]; |
| size_t ackcnt; |
| char *postamblestr[ POSTAMBLEMAX ]; |
| size_t postamblecnt; |
| ix_t doix; |
| ix_t dontix; |
| ix_t responseix; |
| ix_t sigintix; |
| |
| retry: |
| preamblecnt = 0; |
| fold_init( fold, "media erase dialog", '=' ); |
| preamblestr[ preamblecnt++ ] = "\n"; |
| preamblestr[ preamblecnt++ ] = fold; |
| preamblestr[ preamblecnt++ ] = "\n\n"; |
| ASSERT( preamblecnt <= PREAMBLEMAX ); |
| dlog_begin( preamblestr, preamblecnt ); |
| |
| /* query: ask if overwrite ok |
| */ |
| sprintf( question, |
| "pre-erase (-%c) option specified " |
| "and non-blank media encountered:\n" |
| "please confirm media erase " |
| "drive %u\n", |
| GETOPT_ERASE, |
| (unsigned int)drivep->d_index ); |
| querycnt = 0; |
| querystr[ querycnt++ ] = question; |
| ASSERT( querycnt <= QUERYMAX ); |
| choicecnt = 0; |
| dontix = choicecnt; |
| choicestr[ choicecnt++ ] = "don't erase"; |
| doix = choicecnt; |
| choicestr[ choicecnt++ ] = "erase"; |
| ASSERT( choicecnt <= CHOICEMAX ); |
| sigintix = IXMAX - 1; |
| |
| responseix = dlog_multi_query( querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| doix, /* defaultix */ |
| DLOG_TIMEOUT_MEDIA, |
| dontix, /* timeout ix */ |
| sigintix, /* sigint ix */ |
| dontix, /* sighup ix */ |
| dontix ); /* sigquit ix */ |
| ackcnt = 0; |
| if ( responseix == doix ) { |
| ackstr[ ackcnt++ ] = "media will be erased\n"; |
| } else if ( responseix == dontix ) { |
| ackstr[ ackcnt++ ] = "media will NOT be erased\n"; |
| } else { |
| ackstr[ ackcnt++ ] = "keyboard interrupt\n"; |
| } |
| ASSERT( ackcnt <= ACKMAX ); |
| dlog_multi_ack( ackstr, |
| ackcnt ); |
| |
| postamblecnt = 0; |
| fold_init( fold, "end dialog", '-' ); |
| postamblestr[ postamblecnt++ ] = "\n"; |
| postamblestr[ postamblecnt++ ] = fold; |
| postamblestr[ postamblecnt++ ] = "\n\n"; |
| ASSERT( postamblecnt <= POSTAMBLEMAX ); |
| dlog_end( postamblestr, |
| postamblecnt ); |
| |
| if ( responseix == sigintix ) { |
| if ( cldmgr_stop_requested( )) { |
| return BOOL_FALSE; |
| } |
| sleep( 1 ); /* to allow main thread to begin dialog */ |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "" ); /* to block until main thread dialog complete */ |
| sleep( 1 ); /* to allow main thread to request children die */ |
| if ( cldmgr_stop_requested( )) { |
| return BOOL_FALSE; |
| } |
| mlog( MLOG_DEBUG, |
| "retrying media erase dialog\n" ); |
| goto retry; |
| } |
| |
| |
| return responseix == doix; |
| } |
| |
| static void |
| Media_prompt_label_cb( void *uctxp, dlog_pcbp_t pcb, void *pctxp ) |
| { |
| drive_t *drivep = ( drive_t * )uctxp; |
| |
| /* query: ask for a label |
| */ |
| ( * pcb )( pctxp, |
| "please enter label for media in " |
| "drive %u", |
| drivep->d_index ); |
| } |
| |
| static char * |
| Media_prompt_label( drive_t *drivep, char *bufp, size_t bufsz ) |
| { |
| fold_t fold; |
| char *preamblestr[ PREAMBLEMAX ]; |
| size_t preamblecnt; |
| char *ackstr[ ACKMAX ]; |
| size_t ackcnt; |
| char *postamblestr[ POSTAMBLEMAX ]; |
| size_t postamblecnt; |
| const ix_t timeoutix = 1; |
| const ix_t abortix = 2; |
| const ix_t sigintix = 3; |
| const ix_t okix = 4; |
| ix_t responseix; |
| |
| retry: |
| preamblecnt = 0; |
| fold_init( fold, "media label dialog", '=' ); |
| preamblestr[ preamblecnt++ ] = "\n"; |
| preamblestr[ preamblecnt++ ] = fold; |
| preamblestr[ preamblecnt++ ] = "\n\n"; |
| ASSERT( preamblecnt <= PREAMBLEMAX ); |
| dlog_begin( preamblestr, preamblecnt ); |
| |
| responseix = dlog_string_query( Media_prompt_label_cb, |
| ( void * )drivep, |
| bufp, |
| bufsz, |
| DLOG_TIMEOUT, |
| timeoutix,/* timeout ix */ |
| sigintix, /* sigint ix */ |
| abortix, /* sighup ix */ |
| abortix, /* sigquit ix */ |
| okix ); /* ok ix */ |
| ackcnt = 0; |
| if ( responseix == okix ) { |
| ackstr[ ackcnt++ ] = "media label entered: \""; |
| ackstr[ ackcnt++ ] = bufp; |
| ackstr[ ackcnt++ ] = "\"\n"; |
| } else if ( responseix == timeoutix ) { |
| ackstr[ ackcnt++ ] = "timeout: media label left blank\n"; |
| } else if ( responseix == sigintix ) { |
| ackstr[ ackcnt++ ] = "keyboard interrupt\n"; |
| } else { |
| ackstr[ ackcnt++ ] = "abort\n"; |
| } |
| |
| ASSERT( ackcnt <= ACKMAX ); |
| dlog_string_ack( ackstr, |
| ackcnt ); |
| |
| postamblecnt = 0; |
| fold_init( fold, "end dialog", '-' ); |
| postamblestr[ postamblecnt++ ] = "\n"; |
| postamblestr[ postamblecnt++ ] = fold; |
| postamblestr[ postamblecnt++ ] = "\n\n"; |
| ASSERT( postamblecnt <= POSTAMBLEMAX ); |
| dlog_end( postamblestr, |
| postamblecnt ); |
| |
| if ( responseix == sigintix ) { |
| if ( cldmgr_stop_requested( )) { |
| return 0; |
| } |
| sleep( 1 ); /* to allow main thread to begin dialog */ |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "" ); /* to block until main thread dialog complete */ |
| sleep( 1 ); /* to allow main thread to request children die */ |
| if ( cldmgr_stop_requested( )) { |
| return 0; |
| } |
| mlog( MLOG_DEBUG, |
| "retrying media label dialog\n" ); |
| goto retry; |
| } |
| |
| |
| if ( responseix == okix ) { |
| return bufp; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void |
| set_mcflag( ix_t thrdix ) |
| { |
| lock( ); |
| sc_mcflag[ thrdix ] = BOOL_TRUE; |
| content_media_change_needed = BOOL_TRUE; |
| unlock( ); |
| } |
| |
| static void |
| clr_mcflag( ix_t thrdix ) |
| { |
| lock( ); |
| sc_mcflag[ thrdix ] = BOOL_FALSE; |
| for ( thrdix = 0 ; thrdix < drivecnt ; thrdix++ ) { |
| if ( sc_mcflag[ thrdix ] ) { |
| unlock( ); |
| return; |
| } |
| } |
| content_media_change_needed = BOOL_FALSE; |
| unlock( ); |
| } |
| |
| static bool_t |
| check_complete_flags( void ) |
| { |
| ix_t strmix; |
| bool_t completepr = BOOL_TRUE; |
| |
| for ( strmix = 0 ; strmix < drivecnt ; strmix++ ) { |
| context_t *contextp = &sc_contextp[ strmix ]; |
| if ( ! contextp->cc_completepr ) { |
| completepr = BOOL_FALSE; |
| break; |
| } |
| } |
| |
| return completepr; |
| } |
| |
| extern bool_t |
| is_quota_file(ino_t ino) |
| { |
| int i; |
| |
| for(i = 0; i < (sizeof(quotas) / sizeof(quotas[0])); i++) { |
| if (quotas[i].savequotas && ino == quotas[i].quotaino) |
| return BOOL_TRUE; |
| } |
| return BOOL_FALSE; |
| } |
| |
| #define REPQUOTA "xfs_quota" |
| |
| static bool_t |
| save_quotas( char *mntpnt, quota_info_t *quotainfo ) |
| { |
| int sts = 0; |
| char buf[1024] = ""; |
| int fd; |
| char tmp; |
| struct stat statb; |
| |
| mlog( MLOG_VERBOSE, _( |
| "saving %s information for: %s\n"), quotainfo->desc, mntpnt ); |
| |
| if( unlink( quotainfo->quotapath ) == 0 ) { |
| mlog( MLOG_WARNING, _("overwriting: %s\n"), quotainfo->quotapath); |
| } |
| else { |
| if( errno != ENOENT ) { |
| mlog( MLOG_ERROR, _( |
| "unable to remove %s: %s\n"), |
| quotainfo->quotapath, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| } |
| |
| sprintf( buf, |
| "%s -x -c 'dump %s %s' %s 2> /dev/null", |
| REPQUOTA, |
| quotainfo->repquotaargs, |
| quotainfo->quotapath, |
| mntpnt ); |
| |
| mlog( MLOG_NITTY, "saving quotas: %s\n", buf ); |
| |
| sts = system( buf ); |
| if( sts != 0 ) { |
| mlog( MLOG_ERROR, _( |
| "%s failed with exit status: %d\n"), REPQUOTA, |
| sts == -1 ? -1 : WEXITSTATUS(sts)); |
| return BOOL_FALSE; |
| } |
| if((fd = open( quotainfo->quotapath, O_RDONLY|O_DSYNC)) < 0) { |
| mlog( MLOG_ERROR, _( |
| "open failed %s: %s\n"), |
| quotainfo->quotapath, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| if(fstat(fd, &statb) < 0) { |
| mlog( MLOG_ERROR, _( |
| "stat failed %s: %s\n"), |
| quotainfo->quotapath, |
| strerror( errno )); |
| close(fd); |
| return BOOL_FALSE; |
| } |
| quotainfo->quotaino = statb.st_ino; |
| |
| /* open and read dump file to ensure it is in the dump */ |
| read(fd, &tmp, 1); |
| close(fd); |
| return BOOL_TRUE; |
| } |
| |
| static int |
| getxfsqstat(char *fsname) |
| { |
| fs_quota_stat_t qstat; |
| |
| /* |
| * See if quotas is on. If not, nada. |
| */ |
| memset(&qstat, 0, sizeof(fs_quota_stat_t)); |
| if (quotactl(QCMD(Q_XGETQSTAT, 0), fsname, 0, |
| (caddr_t)&qstat) < 0) { |
| return (-1); |
| } |
| return ((int)qstat.qs_flags); |
| } |