| /* |
| * Copyright (c) 2000-2001 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <xfs/xfs.h> |
| #include <xfs/jdm.h> |
| |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <sys/mman.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <attr/attributes.h> |
| #include <xfs/handle.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <dirent.h> |
| #include <utime.h> |
| #include <malloc.h> |
| #include <pthread.h> |
| |
| #include "types.h" |
| #include "timeutil.h" |
| #include "util.h" |
| #include "cldmgr.h" |
| #include "qlock.h" |
| #include "lock.h" |
| #include "path.h" |
| #include "openutil.h" |
| #include "exit.h" |
| #include "getopt.h" |
| #include "mlog.h" |
| #include "dlog.h" |
| #include "bag.h" |
| #include "node.h" |
| #include "namreg.h" |
| #include "stream.h" |
| #include "global.h" |
| #include "drive.h" |
| #include "media.h" |
| #include "content.h" |
| #include "content_inode.h" |
| #include "inomap.h" |
| #include "dirattr.h" |
| #include "tree.h" |
| #include "inventory.h" |
| #include "mmap.h" |
| #include "arch_xlate.h" |
| #include "win.h" |
| #include "hsmapi.h" |
| |
| /* content.c - manages restore content |
| */ |
| |
| /* structure definitions used locally ****************************************/ |
| |
| #define HOUSEKEEPING_MAGIC 0x686b6d61 |
| /* "hkma" - see the housekeeping_magic field of pers_t below. |
| */ |
| #define HOUSEKEEPING_VERSION 2 |
| /* see the housekeeping_version field of pers_t below. |
| * version 2 changed the size of a gen_t, which caused node_t |
| * to change in size. also p_truncategenpr was added to treepers_t. |
| */ |
| |
| #define WRITE_TRIES_MAX 3 |
| /* retry loop tuning for write(2) workaround |
| */ |
| typedef enum { SYNC_INIT, SYNC_BUSY, SYNC_DONE } sync_t; |
| /* for lock-step synchronization |
| */ |
| typedef struct { xfs_ino_t eg_ino; off64_t eg_off; } egrp_t; |
| /* extent group descriptor |
| */ |
| typedef char label_t[ GLOBAL_HDR_STRING_SZ ]; |
| /* dump or mobj label |
| */ |
| typedef enum { PURP_SEARCH, PURP_DIR, PURP_NONDIR } purp_t; |
| /* to describe purpose for a media file request. may be for |
| * searching for a dump to restore, for dir restore, or non-dir |
| */ |
| typedef off_t dh_t; |
| /* handles for descriptors in persistent state inventory |
| * encoded as byte offset plus one of descriptor into descriptor |
| * portion of persistent state. plus one so DH_NULL can be zero. |
| */ |
| #define DH_NULL ( ( dh_t )0 ) |
| /* NULL inv. descriptor handles, to terminate linked descriptor lists. |
| * must be zero-valued, so memset of pers.s sets freeh to DH_NULL. |
| */ |
| #define DH2F( h ) ( ( pers_file_t * )( ( char * )descp + ( h - 1 ))) |
| #define DH2O( h ) ( ( pers_obj_t * )( ( char * )descp + ( h - 1 ))) |
| #define DH2S( h ) ( ( pers_strm_t * )( ( char * )descp + ( h - 1 ))) |
| #define DH2D( h ) ( ( pers_desc_t * )( ( char * )descp + ( h - 1 ))) |
| /* convert file, object, and stream inv. descriptor handle into |
| * descriptor pointers |
| */ |
| #define DAU 1 |
| /* number of descriptor pages to allocate when free list exhausted |
| */ |
| #define IBPGINCR 32 |
| /* session inv. restore retry buffer increment |
| */ |
| |
| /* Media state abstraction |
| */ |
| struct Media { |
| drive_t *M_drivep; |
| global_hdr_t *M_grhdrp; |
| drive_hdr_t *M_drhdrp; |
| media_hdr_t *M_mrhdrp; |
| content_hdr_t *M_crhdrp; |
| content_inode_hdr_t *M_scrhdrp; |
| |
| enum { POS_UNKN, /* whenever media file not open */ |
| POS_ATHDR, /* at beginning of media file */ |
| POS_INDIR, /* at beginning of inomap/dirdump */ |
| POS_ATNONDIR, /* at first non-dir file */ |
| POS_END, /* EOM/EOD */ |
| POS_USELESS, /* current object contains nothing useful */ |
| POS_BLANK /* like useless */ |
| } M_pos; |
| /* media positioning info. initially UNKN, set back to |
| * unkn whenever end_read op called. |
| */ |
| ix_t M_fmfix; |
| ix_t M_lmfix; |
| ix_t M_pmfix; |
| bool_t M_pmfixvalpr; |
| purp_t M_mfixpurp; |
| bool_t M_flmfixvalpr; |
| /* the indices within the current media object of the first |
| * and last media files seen, as well as previous last. |
| * invalidated whenever purpose changes or media is changed. |
| * previous (pmfix) not valid until second media file seen. |
| */ |
| ix_t M_fsfix; |
| ix_t M_fsoix; |
| ix_t M_fssix; |
| bool_t M_fsfixvalpr; |
| /* index within the current media object of the first |
| * media file that is part of dump being restored, |
| * and indices of the obj and stream containing that mfile. |
| * invalidated on media change. |
| */ |
| }; |
| |
| typedef struct Media Media_t; |
| |
| |
| /* persistent state - mmapped, has linked lists of dump streams, media |
| * objects, and media files. descriptors for each fit into PERS_DESCSZ |
| * bytes, and are allocated from a common free pool. |
| */ |
| |
| /* persistent media file descriptor |
| */ |
| struct pers_file { |
| dh_t f_nexth; |
| /* singly-linked list of files withing object |
| */ |
| dh_t f_parh; |
| /* parent object |
| */ |
| bool_t f_szvalpr; |
| off64_t f_sz; |
| /* if this info came from an inventory (on-line or on-media), |
| * we know the media file size |
| */ |
| bool_t f_dirtriedpr; |
| /* set if attempted to restore dirs from this media file. |
| * says nothing about success or failure. prevents us from |
| * trying to restore dirs from this media file again. |
| */ |
| bool_t f_valpr; |
| /* following three fields are valid |
| */ |
| egrp_t f_firstegrp; |
| /* first extent group in this media file |
| */ |
| egrp_t f_curegrp; |
| /* next extent group to be restored from this media file. |
| * initially equals f_firstegrp. |
| */ |
| drive_mark_t f_curmark; |
| /* drive manager mark for seeking to current extent group |
| */ |
| bool_t f_nondirdonepr; |
| /* TRUE when non-dirs from this media file completely restored, |
| * or as restored as they can be (some or all lost due to |
| * media corruption). |
| */ |
| bool_t f_nondirskippr; |
| /* no non-dirs are needed from this nmedia file (due to |
| * subtree or interactive selections) |
| */ |
| intgen_t f_flags; |
| /* mark terminators and inventories |
| */ |
| bool_t f_underheadpr; |
| /* the drive is currently positioned at or in this media file |
| */ |
| }; |
| |
| /* f_flags |
| */ |
| #define PF_INV ( 1 << 0 ) |
| #define PF_TERM ( 1 << 1 ) |
| |
| typedef struct pers_file pers_file_t; |
| |
| /* persistent media object descriptor |
| */ |
| struct pers_obj { |
| dh_t o_nexth; |
| /* singly-linked list of objects in stream |
| */ |
| dh_t o_parh; |
| /* parent dump stream descriptor |
| */ |
| dh_t o_cldh; |
| /* head of list of pertinent media files contained in |
| * this media object |
| */ |
| bool_t o_idlabvalpr; |
| /* id and label fields are valid |
| */ |
| uuid_t o_id; |
| /* uuid of media object |
| */ |
| label_t o_lab; |
| /* label of media object |
| */ |
| ix_t o_fmfmix; |
| bool_t o_fmfmixvalpr; |
| /* 0-based index into this mobj's mfiles of first |
| * mfile in the mobj that is part of the dump stream. |
| */ |
| ix_t o_fmfsix; |
| bool_t o_fmfsixvalpr; |
| /* 0-based index into this dump stream's mfiles of first |
| * mfile in the mobj that is part of the dump stream. |
| */ |
| bool_t o_lmfknwnpr; |
| /* TRUE if last media file on object is represented in |
| * children list. |
| */ |
| bool_t o_indrivepr; |
| ix_t o_indriveix; |
| /* TRUE if this object is in a drive, and which drive it is |
| * in. |
| */ |
| }; |
| |
| typedef struct pers_obj pers_obj_t; |
| |
| /* media dump stream descriptor |
| */ |
| struct pers_strm { |
| dh_t s_nexth; |
| /* singly-linked list of streams generated by dump |
| */ |
| dh_t s_cldh; |
| /* head of list of mobjs containing this dstrm's mfiles |
| */ |
| bool_t s_lastobjknwnpr; |
| /* TRUE if if last object in the stream is represented in |
| * children list. |
| */ |
| }; |
| |
| typedef struct pers_strm pers_strm_t; |
| |
| /* media descriptor allocation object (for free list) |
| */ |
| union pers_desc { |
| dh_t d_nexth; |
| /* singly-linked free list of descriptors |
| */ |
| pers_file_t d_file; |
| /* media file descriptor overlay; |
| */ |
| pers_obj_t d_obj; |
| /* media object descriptor overlay; |
| */ |
| pers_strm_t d_strm; |
| /* media stream descriptor overlay; |
| */ |
| }; |
| |
| typedef union pers_desc pers_desc_t; |
| |
| #define PERS_DESCSZ 512 |
| /* size of media object, media file, and media stream descriptors. |
| * need to fit integral number into a page, single allocator |
| * used allocate and free all types . |
| */ |
| |
| /* subtree descriptor - the subtree command line arguments are transcribed |
| * into variable-length descriptors and placed in an integral number of |
| * pages after the persistent header, and before the media descriptor free list. |
| */ |
| #define STDESCALIGN 8 |
| |
| struct stdesc { |
| bool_t std_sensepr; |
| /* TRUE if this is a subtree to INCLUDE, FALSE if EXCLUDE |
| */ |
| off_t std_nextoff; |
| /* offset to next descriptor, in bytes relative to this |
| */ |
| char std_path[ 1 ]; |
| /* first character of a NULL-terminated string containing the |
| * the relative subtree pathname |
| */ |
| }; |
| |
| typedef struct stdesc stdesc_t; |
| |
| /* byte span descriptor - registers a span of a file restored. |
| */ |
| struct bytespan { |
| off64_t offset; |
| off64_t endoffset; |
| } ; |
| |
| typedef struct bytespan bytespan_t; |
| |
| /* partial restore descriptor - Keeps track of different byte spans restored |
| * for a specific inode. Used to sync operations between restore streams. |
| */ |
| struct partial_rest { |
| xfs_ino_t is_ino; |
| /* inode number */ |
| bytespan_t is_bs[STREAM_SIMMAX]; |
| /* each stream could conceivably be writing to a single |
| * file simultaneously if one file spans all device streams. |
| * Need a record for each possible place in the file. |
| */ |
| }; |
| |
| typedef struct partial_rest partial_rest_t; |
| |
| struct stream_context { |
| bstat_t sc_bstat; |
| char sc_path[2 * MAXPATHLEN]; |
| intgen_t sc_fd; |
| intgen_t sc_hsmflags; |
| }; |
| |
| typedef struct stream_context stream_context_t; |
| |
| /* persistent state file header - on-disk format information plus |
| * accumulation state (which spans several sessions) and session state. |
| * the latter two have a valid bit, and their fields are not valid until |
| * the valid bit is set. all elements defined such that a bzero results |
| * in a valid initial state. |
| */ |
| struct pers { |
| /* on-disk format information used to verify that xfsrestore |
| * can make sense of the data in xfsrestorehousekeepingdir |
| * when running in cumulative mode or when resuming a restore. |
| * |
| * for backwards/forwards compatibility, this struct must be |
| * the first field! also any changes to the struct must address |
| * compatibility with other xfsrestore versions. |
| */ |
| struct { |
| size32_t housekeeping_magic; |
| /* used to determine if this struct has been |
| * initialized, and whether the machine's |
| * endianness is the same as the previous |
| * invocation. (data written to xfsrestore's |
| * state directory is not converted to an |
| * endian-neutral format since it only persists |
| * for the life of one or more restore sessions.) |
| */ |
| size32_t housekeeping_version; |
| /* version of the data structures used in the |
| * state files in housekeepingdir. this must be |
| * bumped whenever the on-disk format changes. |
| */ |
| size64_t pagesize; |
| /* headers in the persistent state files |
| * are aligned on page size boundaries, so |
| * this cannot change betweeen invocations. |
| */ |
| } v; |
| |
| /* command line arguments from first session, and session |
| * history. |
| */ |
| struct { |
| bool_t valpr; |
| /* not set until a BASE dump has been identified |
| * and validated for restoral, and an attempt has |
| * been made to load the dump inventory into persistent |
| * state, and the namreg and tree abstractions |
| * have been initialized, and the session history |
| * has been initialized and validated. |
| */ |
| char dstdir[ MAXPATHLEN ]; |
| /* absolute pathname of the destination directory |
| */ |
| bool_t dstdirisxfspr; |
| /* destination directory is an xfs filesystem; xfs-specific |
| * calls can be made when needed. |
| */ |
| ix_t dumpcnt; |
| /* how many dumps have been applied completedly (A1) |
| */ |
| uuid_t lastdumpid; |
| /* uuid of the last dump completely restored (A1) |
| */ |
| label_t lastdumplab; |
| /* label of the last dump completely restored (A1) |
| */ |
| bool_t cumpr; |
| /* is a cumulative restore (-r) |
| */ |
| bool_t interpr; |
| /* interactive mode specified on command line (-i) |
| */ |
| bool_t existpr; |
| /* existing files may not be overwritten (-e) |
| */ |
| bool_t changepr; |
| /* only missing or old files may be overwritten (-E) |
| */ |
| bool_t newerpr; |
| time32_t newertime; |
| /* only files older than example may be overwritten (-n) |
| */ |
| bool_t ownerpr; |
| /* attempt to restore owner/group (-o) |
| */ |
| ix_t stcnt; |
| /* how many subtree args (both inclusive and exclusive) |
| * are recorded in the subtree pages (-s) |
| */ |
| bool_t firststsensepr; |
| bool_t firststsenseprvalpr; |
| /* sense of first subtree arg |
| */ |
| ix_t stpgcnt; |
| /* how many pages following the header page are reserved |
| * for the subtree descriptors |
| */ |
| bool_t restoredmpr; |
| /* restore DMAPI event settings |
| */ |
| bool_t restoreextattrpr; |
| /* restore extended attributes |
| */ |
| |
| ix_t parrestcnt; |
| /* Count of partialy restored files. Used to speed |
| * up searches in parrest. |
| */ |
| |
| partial_rest_t parrest[ STREAM_SIMMAX * 2 - 2 ]; |
| /* record of bytes restored to partially restored files. |
| * Max possible is two per stream except the first |
| * drive will never finish another drives file and the |
| * last drive will never leave a file for another to |
| * complete. |
| */ |
| } a; |
| |
| /* session state. |
| */ |
| struct { |
| bool_t valpr; |
| /* until this is true, a resume will ignore (and bzero) |
| * this structure. validate just prior to applying |
| * the directory dump, and after all fields marked (A2) |
| * are correct. invalidate as soon as the session is |
| * complete, and atomically update all fields marked |
| * (A1) at the same time. dirattr abstraction must be |
| * initialized prior to setting this. |
| */ |
| time32_t accumtime; |
| /* for measuring elapsed time of restore |
| */ |
| uuid_t dumpid; |
| /* id of dump currently being applied |
| */ |
| label_t dumplab; |
| /* label of the dump being applied (A2) |
| */ |
| time32_t begintime; |
| /* set when session begun and each time resumed |
| */ |
| bool_t stat_valpr; |
| /* the following stats are not valid until the |
| * first media file header has been read. |
| */ |
| u_int64_t stat_inocnt; |
| /* number of non-dir inos to restore during session |
| */ |
| u_int64_t stat_inodone; |
| /* number of non-dir inos restored so far |
| */ |
| off64_t stat_datacnt; |
| /* bytes of ordinary files to restore during session |
| */ |
| off64_t stat_datadone; |
| /* bytes of ordinary files restored so far |
| */ |
| ix_t descpgcnt; |
| /* number of pages mapped for pers. media descriptors |
| */ |
| dh_t descfreeh; |
| /* linked list of free media descriptor alloc objs (A2) |
| */ |
| dh_t strmheadh; |
| /* head of singly-linked list of stream descriptors (A2) |
| */ |
| bool_t fullinvpr; |
| /* have discovered and incorporated a full inventory |
| * description into pers. may come from online or a |
| * inventory media file. |
| */ |
| bool_t marknorefdonepr; |
| /* have marked tree nodes as unreferenced by directory |
| * entries, and nulled dirattr handles. |
| */ |
| bool_t dirdonepr; |
| /* have applied all directories from a dirdump. |
| */ |
| bool_t adjrefdonepr; |
| /* have adjusted marking of nodes no longer referenced |
| * by directory entries. |
| */ |
| bool_t inomapsanitizedonepr; |
| /* the inomap needs to b sanitized prior to subtree |
| * or interactive selections |
| */ |
| bool_t stdonepr; |
| /* have applied subtree selections |
| */ |
| bool_t interdonepr; |
| /* have completed interactive subtree dialog |
| */ |
| bool_t treepostdonepr; |
| /* all of the above treep ost-processing steps have |
| * been completed. |
| */ |
| /* |
| * nondir restore done here |
| */ |
| bool_t dirattrdonepr; |
| /* directory attributes have been restored and |
| * directory attributes registry has been deleted |
| */ |
| bool_t orphdeltriedpr; |
| /* removed (or tried to remove) orphanage |
| */ |
| bool_t inomapdelpr; |
| /* deleted session ino map |
| */ |
| } s; |
| }; |
| |
| typedef struct pers pers_t; |
| |
| /* transient state. re-generated during each restore session |
| */ |
| |
| struct tran { |
| time32_t t_starttime; |
| /* for measuring elapsed time of restore session |
| */ |
| size64_t t_dircnt; |
| size64_t t_dirdonecnt; |
| size64_t t_direntcnt; |
| /* for displaying stats on directory reconstruction |
| */ |
| size64_t t_vmsz; |
| /* how much vm may be used. recorded here from main, |
| * passed to tree_init() once we have a valid media |
| * file header |
| */ |
| bool_t t_toconlypr; |
| /* just display table of contents; don't restore files |
| */ |
| bool_t t_noinvupdatepr; |
| /* true if inventory is NOT to be updated when on-media |
| * inventory encountered. |
| */ |
| bool_t t_dumpidknwnpr; |
| /* determined during initialization; if false, set during |
| * per-stream init |
| */ |
| bool_t t_dirattrinitdonepr; |
| bool_t t_namreginitdonepr; |
| bool_t t_treeinitdonepr; |
| /* determinied during initialization, used during |
| * per-stream restore |
| */ |
| uuid_t t_reqdumpid; |
| bool_t t_reqdumpidvalpr; |
| /* uuid of the dump as requested on cmd line |
| */ |
| char * t_reqdumplab; |
| bool_t t_reqdumplabvalpr; |
| /* label of the dump as requested on cmd line |
| */ |
| char *t_hkdir; |
| /* absolute pathname of housekeeping directory |
| */ |
| intgen_t t_persfd; |
| /* file descriptor of the persistent state file |
| */ |
| size64_t t_dirdumps; |
| /* bitset of streams which contain a directory dump |
| */ |
| bool_t t_truncategenpr; |
| /* force use of truncated generation numbers |
| */ |
| sync_t t_sync1; |
| /* to single-thread attempt to validate command line |
| * selection of dump with online inventory |
| */ |
| sync_t t_sync2; |
| /* to single-thread dump selection by media scan |
| */ |
| sync_t t_sync3; |
| /* to single-thread attempt to apply dirdump to tree |
| */ |
| sync_t t_sync4; |
| /* to single-thread attempt to do tree post-processing |
| * prior to non-directory restore |
| */ |
| sync_t t_sync5; |
| /* to single-thread cleanup after applying non-dir restore |
| */ |
| qlockh_t t_pilockh; |
| /* to establish critical regions while updating pers |
| * inventory |
| */ |
| }; |
| |
| typedef struct tran tran_t; |
| |
| |
| /* declarations of externally defined global symbols *************************/ |
| |
| extern void usage( void ); |
| extern bool_t preemptchk( void ); |
| extern char *homedir; |
| extern bool_t pipeline; |
| extern bool_t stdoutpiped; |
| extern char *sistr; |
| extern size_t pgsz; |
| extern size_t pgmask; |
| |
| |
| /* forward declarations of locally defined static functions ******************/ |
| |
| static void toconly_cleanup( void ); |
| |
| static Media_t *Media_create( ix_t thrdix ); |
| static void Media_indir( Media_t *Mediap ); |
| static void Media_indir( Media_t *Mediap ); |
| static void Media_atnondir( Media_t *Mediap ); |
| static rv_t Media_mfile_next( Media_t *Mediap, |
| purp_t purp, |
| sync_t *donesyncp, |
| dh_t *filehp, |
| global_hdr_t **grhdrpp, |
| drive_hdr_t **drhdrpp, |
| media_hdr_t **mrhdrpp, |
| content_hdr_t **crhdrpp, |
| content_inode_hdr_t **scrhdrpp, |
| drive_t **drivepp, |
| filehdr_t *fhdr ); |
| static void Media_end( Media_t *Mediap ); |
| static bool_t Media_prompt_change( drive_t *drivep, |
| purp_t purp, |
| bag_t *bagp, |
| bool_t knownholespr, |
| bool_t maybeholespr ); |
| |
| static bool_t Inv_validate_cmdline( void ); |
| static bool_t dumpcompat( bool_t resumepr, |
| ix_t level, |
| uuid_t baseid, |
| bool_t logpr ); |
| static bool_t promptdumpmatch( ix_t thrdix, |
| global_hdr_t *grhdrp, |
| media_hdr_t *mrhdrp, |
| content_hdr_t *crhdrp, |
| content_inode_hdr_t *scrhdrp ); |
| |
| static void pi_checkpoint( dh_t fileh, |
| drive_mark_t *drivemarkp, |
| xfs_ino_t ino, |
| off64_t off ); |
| static bool_t pi_transcribe( inv_session_t *sessp ); |
| static dh_t pi_addfile( Media_t *Mediap, |
| global_hdr_t *grhdrp, |
| drive_hdr_t *drhdrp, |
| media_hdr_t *mrhdrp, |
| content_inode_hdr_t *scrhdrp, |
| drive_t * drivep ); |
| static void pi_seestrmend( ix_t strmix ); |
| static void pi_seeobjstrmend( ix_t strmix, ix_t mediaix ); |
| static xfs_ino_t pi_scanfileendino( dh_t fileh ); |
| static bool_t pi_alldone( void ); |
| static bag_t * pi_neededobjs_dir_alloc( bool_t *knownholesprp, |
| bool_t *maybeholesprp ); |
| static bag_t * pi_neededobjs_nondir_alloc( bool_t *knownholesprp, |
| bool_t *maybeholesprp, |
| bool_t showobjindrivepr, |
| bool_t markskippr ); |
| static void pi_neededobjs_free( bag_t *bagp ); |
| static void pi_bracketneededegrps( dh_t thisfileh, |
| egrp_t *first_egrp, |
| egrp_t *next_egrp ); |
| static void pi_update_stats( off64_t sz ); |
| static void pi_hiteod( ix_t strmix, ix_t objix ); |
| static void pi_hiteom( ix_t strmix, ix_t objix ); |
| static void pi_hitnextdump( ix_t strmix, ix_t objix, ix_t lastfileix ); |
| static bool_t pi_know_no_more_on_object( purp_t purp, ix_t strmix, ix_t objix ); |
| static bool_t pi_know_no_more_beyond_on_object( purp_t purp, |
| ix_t strmix, |
| ix_t objix, |
| ix_t fileix ); |
| static void pi_preclean( void ); |
| static void pi_driveempty( ix_t driveix ); |
| static void pi_note_indrive( ix_t driveix, uuid_t mediaid ); |
| static void pi_note_underhead( dh_t thisobjh, dh_t thisfileh ); |
| static void pi_lock( void ); |
| static void pi_unlock( void ); |
| |
| static rv_t applydirdump( drive_t *drivep, |
| dh_t fileh, |
| content_inode_hdr_t *scrhdrp, |
| filehdr_t *fhdrp ); |
| static rv_t treepost( char *path1, char *path2 ); |
| static rv_t applynondirdump( drive_t *drivep, |
| dh_t fileh, |
| content_inode_hdr_t *scrhdrp, |
| char *path1, |
| char *path2, |
| filehdr_t *fhdrp ); |
| static rv_t finalize( char *path1, char *path2 ); |
| static void wipepersstate( void ); |
| |
| static rv_t read_filehdr( drive_t *drivep, filehdr_t *fhdrp, bool_t fhcs ); |
| static rv_t restore_file( drive_t *drivep, |
| filehdr_t *fhdrp, |
| bool_t ehcs, |
| bool_t ahcs, |
| char *path1, |
| char *path2 ); |
| static bool_t restore_reg( drive_t *drivep, |
| filehdr_t *fhdrp, |
| rv_t *rvp, |
| char *path ); |
| static bool_t restore_extent_group( drive_t *drivep, |
| filehdr_t *fhdrp, |
| char *path, |
| intgen_t fd, |
| bool_t ehcs, |
| rv_t *rvp); |
| static bool_t restore_complete_reg( stream_context_t* ); |
| static bool_t restore_spec( filehdr_t *fhdrp, rv_t *rvp, char *path ); |
| static bool_t restore_symlink( drive_t *drivep, |
| filehdr_t *fhdrp, |
| rv_t *rvp, |
| char *path, |
| char *scratchpath, |
| bool_t ehcs ); |
| static rv_t read_extenthdr( drive_t *drivep, extenthdr_t *ehdrp, bool_t ehcs ); |
| static rv_t read_dirent( drive_t *drivep, |
| direnthdr_t *dhdrp, |
| size_t direntbufsz, |
| bool_t dhcs ); |
| static rv_t discard_padding( size_t sz, drive_t *drivep ); |
| static rv_t restore_extent( filehdr_t *fhdrp, |
| extenthdr_t *ehdrp, |
| int fd, |
| char *path, |
| drive_t *drivep, |
| off64_t *bytesreadp ); |
| static bool_t askinvforbaseof( uuid_t baseid, inv_session_t *sessp ); |
| static void addobj( bag_t *bagp, |
| uuid_t *objidp, |
| label_t objlabel, |
| bool_t indrivepr, |
| ix_t indriveix ); |
| static size_t cntobj( bag_t *bagp ); |
| static bool_t gapneeded( egrp_t *firstegrpp, egrp_t *lastegrpp ); |
| static char * ehdr_typestr( int32_t type ); |
| static intgen_t egrpcmp( egrp_t *egrpap, egrp_t *egrpbp ); |
| static void display_dump_label( bool_t lockpr, |
| intgen_t mllevel, |
| char *introstr, |
| global_hdr_t *grhdrp, |
| media_hdr_t *mrhdrp, |
| content_hdr_t *crhdrp, |
| content_inode_hdr_t *scrhdrp ); |
| static void display_needed_objects( purp_t purp, |
| bag_t *bagp, |
| bool_t knownholespr, |
| bool_t maybeholespr ); |
| static void set_mcflag( ix_t thrdix ); |
| static void clr_mcflag( ix_t thrdix ); |
| static void pi_show( char *introstring ); |
| static void pi_show_nomloglock( void ); |
| |
| static bool_t extattr_init( size_t drivecnt ); |
| static char * get_extattrbuf( ix_t which ); |
| static rv_t restore_extattr( drive_t *drivep, |
| filehdr_t *fhdrp, |
| char *path, |
| bool_t ahcs, |
| bool_t isdirpr, |
| bool_t onlydoreadpr, |
| dah_t dah ); |
| static bool_t restore_dir_extattr_cb( char *path, dah_t dah ); |
| static bool_t restore_dir_extattr_cb_cb( extattrhdr_t *ahdrp, void *ctxp ); |
| static void setextattr( char *path, extattrhdr_t *ahdrp ); |
| static void partial_reg(ix_t d_index, xfs_ino_t ino, off64_t fsize, |
| off64_t offset, off64_t sz); |
| static bool_t partial_check (xfs_ino_t ino, off64_t fsize); |
| static bool_t partial_check2 (partial_rest_t *isptr, off64_t fsize); |
| static int do_fssetdm_by_handle( char *path, fsdmidata_t *fdmp); |
| static int quotafilecheck(char *type, char *dstdir, char *quotafile); |
| |
| /* definition of locally defined global variables ****************************/ |
| |
| bool_t content_media_change_needed; |
| bool_t restore_rootdir_permissions; |
| char *media_change_alert_program = NULL; |
| size_t perssz; |
| |
| |
| /* definition of locally defined static variables *****************************/ |
| |
| static pers_t *persp; /* mapped at init, ok to use */ |
| static tran_t *tranp; /* allocated at init, ok to use */ |
| static pers_desc_t *descp = 0; /* mapped on the fly; don't use! (see macros) */ |
| static char *hkdirname = "xfsrestorehousekeepingdir"; |
| static char *persname = "state"; |
| static char *perspath = 0; |
| static bool_t mcflag[ STREAM_SIMMAX ]; /* media change flag */ |
| |
| |
| /* definition of locally defined global functions ****************************/ |
| |
| bool_t |
| content_init( intgen_t argc, char *argv[ ], size64_t vmsz ) |
| { |
| char *dstdir; /* abs. path to destination dir */ |
| bool_t cumpr; /* cmd line cumulative restore specification */ |
| bool_t resumepr;/* cmd line resumed restore specification */ |
| bool_t existpr; /* cmd line overwrite inhibit specification */ |
| bool_t newerpr; /* cmd line overwrite inhibit specification */ |
| time32_t newertime = 0; |
| bool_t changepr;/* cmd line overwrite inhibit specification */ |
| bool_t interpr; /* cmd line interactive mode requested */ |
| bool_t ownerpr; /* cmd line chown/chmod requested */ |
| bool_t restoredmpr; /* cmd line restore dm api attrs specification */ |
| bool_t restoreextattrpr; /* cmd line restore extended attr spec */ |
| bool_t sesscpltpr; /* force completion of prev interrupted session */ |
| ix_t stcnt; /* cmd line number of subtrees requested */ |
| bool_t firststsensepr; |
| bool_t firststsenseprvalpr; |
| ix_t stsz; /* bytes required to record subtree selections */ |
| ix_t stpgcnt; /* pages required to hold subtree selections */ |
| ix_t newstpgcnt;/* pages required to hold subtree selections */ |
| ix_t descpgcnt; /* pages allocated for persistent descriptors */ |
| struct stat statbuf; |
| pid_t pid; |
| intgen_t c; |
| bool_t ok; |
| intgen_t rval; |
| bool_t fullpr; |
| |
| /* Calculate the size needed for the persistent inventory |
| */ |
| for ( perssz = pgsz; perssz < sizeof(pers_t); perssz += pgsz ) |
| ; |
| |
| /* sanity checks |
| */ |
| ASSERT( sizeof( pers_desc_t ) <= PERS_DESCSZ ); |
| ASSERT( PERS_DESCSZ <= pgsz ); |
| ASSERT( ! ( pgsz % PERS_DESCSZ )); |
| ASSERT( sizeof( extattrhdr_t ) == EXTATTRHDR_SZ ); |
| |
| ASSERT( ! ( perssz % pgsz )); |
| |
| ASSERT( SYNC_INIT == 0 ); |
| |
| mlog( MLOG_NITTY, |
| "sizeof( pers_desc_t ) == %d, pgsz == %d, perssz == %d \n", |
| sizeof( pers_desc_t ), pgsz, perssz ); |
| |
| /* allocate transient state |
| */ |
| tranp = ( tran_t * )calloc( 1, sizeof( tran_t )); |
| ASSERT( tranp ); |
| |
| /* allocate a qlock for establishing pi critical regions |
| */ |
| tranp->t_pilockh = qlock_alloc( QLOCK_ORD_PI ); |
| |
| /* record vmsz; will be used later to init tree abstraction |
| */ |
| tranp->t_vmsz = vmsz; |
| |
| /* record the start time for stats display |
| */ |
| tranp->t_starttime = time( 0 ); |
| |
| /* get command line options |
| */ |
| cumpr = BOOL_FALSE; |
| resumepr = BOOL_FALSE; |
| existpr = BOOL_FALSE; |
| newerpr = BOOL_FALSE; |
| changepr = BOOL_FALSE; |
| ownerpr = BOOL_FALSE; |
| restoredmpr = BOOL_FALSE; |
| restoreextattrpr = BOOL_TRUE; |
| sesscpltpr = BOOL_FALSE; |
| stcnt = 0; |
| firststsensepr = firststsenseprvalpr = BOOL_FALSE; |
| stsz = 0; |
| interpr = BOOL_FALSE; |
| restore_rootdir_permissions = BOOL_FALSE; |
| optind = 1; |
| opterr = 0; |
| while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) { |
| switch ( c ) { |
| case GETOPT_TOC: |
| tranp->t_toconlypr = BOOL_TRUE; |
| break; |
| case GETOPT_CUMULATIVE: |
| cumpr = BOOL_TRUE; |
| break; |
| case GETOPT_RESUME: |
| resumepr = BOOL_TRUE; |
| break; |
| case GETOPT_EXISTING: |
| existpr = BOOL_TRUE; |
| break; |
| case GETOPT_NEWER: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( stat( optarg, &statbuf )) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "unable to get status of -%c argument %s:" |
| " %s\n"), |
| c, |
| optarg, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| newerpr = BOOL_TRUE; |
| newertime = statbuf.st_mtime; |
| break; |
| case GETOPT_CHANGED: |
| changepr = BOOL_TRUE; |
| break; |
| case GETOPT_OWNER: |
| ownerpr = BOOL_TRUE; |
| break; |
| case GETOPT_WORKSPACE: |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( optarg[ 0 ] != '/' ) { |
| tranp->t_hkdir = path_reltoabs( optarg, |
| homedir ); |
| if ( ! tranp->t_hkdir ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument %s is an " |
| "invalid pathname\n"), |
| c, |
| optarg ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| mlog( MLOG_DEBUG, |
| "alternate workspace path converted " |
| "from %s to %s\n", |
| optarg, |
| tranp->t_hkdir ); |
| } else { |
| tranp->t_hkdir = optarg; |
| } |
| rval = stat( tranp->t_hkdir, &statbuf ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cannot stat -%c argument %s (%s): %s\n"), |
| c, |
| optarg, |
| tranp->t_hkdir, |
| strerror( errno )); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( ( statbuf.st_mode & S_IFMT ) != S_IFDIR ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument %s (%s) " |
| "is not a directory\n"), |
| c, |
| optarg, |
| tranp->t_hkdir ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| |
| break; |
| case GETOPT_DUMPLABEL: |
| if ( tranp->t_reqdumplabvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "too many -%c arguments: " |
| "\"-%c %s\" already given\n"), |
| c, |
| c, |
| tranp->t_reqdumplab ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( strlen( optarg ) |
| > |
| sizeofmember( pers_t, s.dumplab )) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument %s too long: max is %d\n"), |
| c, |
| optarg, |
| sizeofmember( pers_t, s.dumplab )); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| tranp->t_reqdumplab = optarg; |
| tranp->t_reqdumplabvalpr = BOOL_TRUE; |
| break; |
| case GETOPT_SESSIONID: |
| if ( tranp->t_reqdumpidvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "too many -%c arguments\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( ! optarg || optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if (uuid_parse( optarg, tranp->t_reqdumpid ) < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c argument not a valid uuid\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| tranp->t_reqdumpidvalpr = BOOL_TRUE; |
| break; |
| case GETOPT_SUBTREE: |
| case GETOPT_NOSUBTREE: |
| if ( ! optarg |
| || |
| optarg[ 0 ] == 0 |
| || |
| optarg[ 0 ] == '-' ) { |
| mlog( MLOG_NORMAL, _( |
| "-%c argument missing\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( optarg[ 0 ] == '/' ) { |
| mlog( MLOG_NORMAL, _( |
| "-%c argument must be relative\n"), |
| c ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| stcnt++; |
| if ( ! firststsenseprvalpr ) { |
| if ( c == GETOPT_SUBTREE ) { |
| firststsensepr = BOOL_TRUE; |
| } else { |
| firststsensepr = BOOL_FALSE; |
| } |
| firststsenseprvalpr = BOOL_TRUE; |
| } |
| stsz += sizeof( stdesc_t ) |
| + |
| strlen( optarg ) |
| + |
| ( STDESCALIGN - 1 ); |
| stsz &= ~( STDESCALIGN - 1 ); |
| break; |
| case GETOPT_INTERACTIVE: |
| if ( ! dlog_allowed( )) { |
| mlog( MLOG_NORMAL, _( |
| "-%c unavailable: no /dev/tty\n"), |
| GETOPT_INTERACTIVE ); |
| return BOOL_FALSE; |
| } |
| interpr = BOOL_TRUE; |
| break; |
| case GETOPT_NOINVUPDATE: |
| tranp->t_noinvupdatepr = BOOL_TRUE; |
| break; |
| case GETOPT_SETDM: |
| restoredmpr = 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: |
| restoreextattrpr = BOOL_FALSE; |
| break; |
| case GETOPT_SESSCPLT: |
| sesscpltpr = BOOL_TRUE; |
| break; |
| case GETOPT_SMALLWINDOW: |
| /* obsolete */ |
| break; |
| case GETOPT_ROOTPERM: |
| restore_rootdir_permissions = BOOL_TRUE; |
| break; |
| case GETOPT_FMT2COMPAT: |
| tranp->t_truncategenpr = BOOL_TRUE; |
| break; |
| } |
| } |
| |
| /* command line option error checking |
| */ |
| if ( cumpr && tranp->t_toconlypr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c and -%c option cannot be used together\n"), |
| GETOPT_TOC, |
| GETOPT_CUMULATIVE ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( resumepr && tranp->t_toconlypr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c and -%c option cannot be used together\n"), |
| GETOPT_TOC, |
| GETOPT_RESUME ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| |
| /* assume all streams contain a directory dump. streams will remove |
| * themselves from this bitset if they do not contain a directory dump. |
| */ |
| ASSERT( drivecnt <= sizeof(tranp->t_dirdumps) * NBBY ); |
| tranp->t_dirdumps = ( 1ULL << drivecnt ) - 1; |
| |
| /* the user may specify stdin as the restore source stream, |
| * by a single dash ('-') with no option letter. This must |
| * appear between the last lettered argument and the destination |
| * directory pathname. |
| */ |
| if ( optind < argc && ! strcmp( argv[ optind ], "-" )) { |
| optind++; |
| } |
| |
| /* the last argument must be the destination directory. not |
| * required if table-of-contents display, or if a resumed restore |
| * or a delta restore. |
| */ |
| if ( ! tranp->t_toconlypr ) { |
| if ( optind >= argc ) { |
| dstdir = 0; |
| } else { |
| if ( argv[ optind ][ 0 ] != '/' ) { |
| dstdir = path_reltoabs( argv[ optind ], |
| homedir ); |
| if ( ! dstdir ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "destination directory %s " |
| "invalid pathname\n"), |
| argv[ optind ] ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| mlog( MLOG_DEBUG, |
| "restore destination path converted " |
| "from %s to %s\n", |
| argv[ optind ], |
| dstdir ); |
| } else { |
| dstdir = argv[ optind ]; |
| } |
| rval = stat( dstdir, &statbuf ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cannot stat destination directory %s: " |
| "%s\n"), |
| dstdir, |
| strerror( errno )); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( ( statbuf.st_mode & S_IFMT ) != S_IFDIR ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "specified destination %s " |
| "is not a directory\n"), |
| dstdir ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| } |
| } else { |
| if ( optind < argc ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "do not specify destination directory if " |
| "contents only restore invoked (-%c option)\n"), |
| GETOPT_TOC ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| dstdir = "."; |
| } |
| |
| /* generate a full pathname for the housekeeping dir. |
| * the housekeeping dir will by default be placed in the |
| * destination directory, unless this is a toc, in which case |
| * it will be placed in the current directory. in either case, an |
| * alternate directory may be specified on the command line. |
| * if this is toconly, modify the housekeeping dir's name with |
| * the pid. |
| */ |
| if ( ! tranp->t_hkdir ) { |
| if ( tranp->t_toconlypr ) { |
| tranp->t_hkdir = homedir; |
| } else { |
| if ( ! dstdir ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "destination directory " |
| "not specified\n") ); |
| usage( ); |
| return BOOL_FALSE; |
| } else { |
| tranp->t_hkdir = dstdir; |
| } |
| } |
| } |
| if ( tranp->t_toconlypr ) { |
| pid = getpid( ); |
| } else { |
| pid = 0; |
| } |
| tranp->t_hkdir = open_pathalloc( tranp->t_hkdir, hkdirname, pid ); |
| |
| /* if this is a table-of-contents only restore, register an |
| * exit handler to get rid of the housekeeping directory and |
| * its contents. NOTE: needs several tran fields initialized! |
| */ |
| if ( tranp->t_toconlypr ) { |
| atexit( toconly_cleanup ); |
| } |
| |
| /* create housekeeping dir if not present |
| */ |
| rval = mkdir( tranp->t_hkdir, S_IRWXU ); |
| if ( rval && errno != EEXIST ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "unable to create %s: %s\n"), |
| tranp->t_hkdir, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| /* build a full pathname to pers. state file |
| */ |
| ASSERT( ! perspath ); |
| perspath = open_pathalloc( tranp->t_hkdir, persname, 0 ); |
| |
| /* open, creating if non-existent |
| */ |
| tranp->t_persfd = open( perspath, |
| O_CREAT | O_RDWR, |
| S_IRUSR | S_IWUSR ); |
| if ( tranp->t_persfd < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "could not open/create persistent state file %s: %s\n"), |
| perspath, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| /* temporarily mmap just the header, and validate the command line |
| * arguments. three cases: no dumps applied so far, or one or more |
| * dumps applied completely, or restore session was interrupted |
| */ |
| persp = ( pers_t * ) mmap_autogrow(perssz, tranp->t_persfd, 0); |
| |
| if ( persp == ( pers_t * )-1 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "could not map persistent state file hdr %s: %s\n"), |
| perspath, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| /* but first setup or verify the on-disk format information |
| */ |
| if ( ! persp->a.valpr ) { |
| /* this is the first restore session |
| */ |
| persp->v.housekeeping_magic = HOUSEKEEPING_MAGIC; |
| persp->v.housekeeping_version = HOUSEKEEPING_VERSION; |
| persp->v.pagesize = pgsz; |
| |
| } else { |
| /* cumulative or resuming a restore, verify the header |
| */ |
| if ( persp->v.housekeeping_magic != HOUSEKEEPING_MAGIC ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "%s format corrupt or wrong endianness " |
| "(0x%x, expected 0x%x)\n"), |
| hkdirname, |
| persp->v.housekeeping_magic, |
| HOUSEKEEPING_MAGIC ); |
| return BOOL_FALSE; |
| } |
| if ( persp->v.housekeeping_version != HOUSEKEEPING_VERSION ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "%s format version differs from previous " |
| "restore (%u, expected %u)\n"), |
| hkdirname, |
| persp->v.housekeeping_version, |
| HOUSEKEEPING_VERSION ); |
| return BOOL_FALSE; |
| } |
| if ( persp->v.pagesize != pgsz ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "%s format differs from previous " |
| "restore due to page size change " |
| "(was %lu, now %lu)\n"), |
| hkdirname, |
| persp->v.pagesize, |
| pgsz ); |
| return BOOL_FALSE; |
| } |
| } |
| |
| if ( ! persp->a.valpr ) { |
| if ( ! dstdir ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "destination directory not specified\n") ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( strlen( dstdir ) >= sizeofmember( pers_t, a.dstdir )) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "destination directory pathname too long: " |
| "max is %d characters\n"), |
| sizeofmember( pers_t, a.dstdir ) - 1 ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( resumepr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c option invalid: there is no " |
| "interrupted restore to resume\n"), |
| GETOPT_RESUME ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( sesscpltpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c option invalid: there is no " |
| "interrupted restore to force completion of\n"), |
| GETOPT_SESSCPLT ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| } else if ( ! persp->s.valpr ) { |
| if ( ! cumpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "must rm -rf %s prior to noncumulative restore\n"), |
| tranp->t_hkdir ); |
| return BOOL_FALSE; |
| } |
| if ( resumepr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c option invalid: there is no " |
| "interrupted restore to resume\n"), |
| GETOPT_RESUME ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( sesscpltpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c option invalid: there is no " |
| "interrupted restore to force completion of\n"), |
| GETOPT_SESSCPLT ); |
| usage( ); |
| return BOOL_FALSE; |
| } |
| if ( existpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_EXISTING ); |
| return BOOL_FALSE; |
| } |
| if ( newerpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_NEWER ); |
| return BOOL_FALSE; |
| } |
| if ( changepr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_CHANGED ); |
| return BOOL_FALSE; |
| } |
| if ( ownerpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_OWNER ); |
| return BOOL_FALSE; |
| } |
| if ( stcnt ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c and -%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_SUBTREE, |
| GETOPT_NOSUBTREE ); |
| return BOOL_FALSE; |
| } |
| if ( tranp->t_truncategenpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating " |
| "cumulative restore\n"), |
| GETOPT_FMT2COMPAT ); |
| return BOOL_FALSE; |
| } |
| } else { |
| if ( ! resumepr && ! sesscpltpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c option required to resume " |
| "or " |
| "-%c option required to force completion of " |
| "previously " |
| "interrupted restore session\n"), |
| GETOPT_RESUME, |
| GETOPT_SESSCPLT ); |
| return BOOL_FALSE; |
| } |
| if ( tranp->t_reqdumplabvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_DUMPLABEL ); |
| return BOOL_FALSE; |
| } |
| if ( tranp->t_reqdumpidvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_SESSIONID ); |
| return BOOL_FALSE; |
| } |
| if ( existpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_EXISTING ); |
| return BOOL_FALSE; |
| } |
| if ( newerpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_NEWER ); |
| return BOOL_FALSE; |
| } |
| if ( changepr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_CHANGED ); |
| return BOOL_FALSE; |
| } |
| if ( ownerpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_OWNER ); |
| return BOOL_FALSE; |
| } |
| if ( interpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_INTERACTIVE ); |
| return BOOL_FALSE; |
| } |
| if ( stcnt ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c and -%c valid only when initiating restore\n"), |
| GETOPT_SUBTREE, |
| GETOPT_NOSUBTREE ); |
| return BOOL_FALSE; |
| } |
| if ( tranp->t_truncategenpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c valid only when initiating restore\n"), |
| GETOPT_FMT2COMPAT ); |
| return BOOL_FALSE; |
| } |
| } |
| |
| if ( persp->a.valpr ) { |
| if ( restoredmpr && persp->a.restoredmpr != restoredmpr) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c cannot reset flag from previous restore\n"), |
| GETOPT_SETDM ); |
| return BOOL_FALSE; |
| } |
| if ( ! restoreextattrpr && |
| persp->a.restoreextattrpr != restoreextattrpr) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "-%c cannot reset flag from previous restore\n"), |
| GETOPT_NOEXTATTR ); |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* force owner option if root |
| */ |
| ownerpr = ( geteuid( ) == 0 ) ? BOOL_TRUE : ownerpr; |
| |
| /* force completion of interrupted restore if asked to do so |
| */ |
| if ( sesscpltpr ) { |
| char *path1; |
| char *path2; |
| rv_t rv; |
| intgen_t rval; |
| |
| path1 = ( char * )calloc( 1, 2 * MAXPATHLEN ); |
| ASSERT( path1 ); |
| path2 = ( char * )calloc( 1, 2 * MAXPATHLEN ); |
| ASSERT( path2 ); |
| ASSERT( persp->a.valpr ); |
| ASSERT( persp->s.valpr ); |
| rval = chdir( persp->a.dstdir ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL, _( |
| "chdir %s failed: %s\n"), |
| persp->a.dstdir, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| ok = dirattr_init( tranp->t_hkdir, BOOL_TRUE, ( u_int64_t )0 ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| ok = namreg_init( tranp->t_hkdir, BOOL_TRUE, ( u_int64_t )0 ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| ok = inomap_sync_pers( tranp->t_hkdir ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| |
| /* This is only a full restore if we're doing a level |
| * 0 restore. |
| */ |
| if (persp->a.dumpcnt == 0) { |
| fullpr = BOOL_TRUE; |
| } else { |
| fullpr = BOOL_FALSE; |
| } |
| |
| ok = tree_sync( tranp->t_hkdir, |
| persp->a.dstdir, |
| tranp->t_toconlypr, |
| fullpr, |
| persp->a.dstdirisxfspr ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| rv = finalize( path1, path2 ); |
| free( ( void * )path1 ); |
| free( ( void * )path2 ); |
| switch ( rv ) { |
| case RV_OK: |
| break; |
| case RV_ERROR: |
| return EXIT_ERROR; |
| case RV_INTR: |
| return EXIT_NORMAL; |
| case RV_CORE: |
| default: |
| return EXIT_FAULT; |
| } |
| } |
| |
| /* for the three cases, calculate old and new mapping params |
| * and wipe partial state |
| */ |
| if ( ! persp->a.valpr ) { |
| stpgcnt = 0; |
| newstpgcnt = ( stsz + pgmask ) / pgsz; |
| descpgcnt = 0; |
| memset( ( void * )&persp->a, 0, |
| sizeof( pers_t ) - offsetofmember( pers_t, a )); |
| } else if ( ! persp->s.valpr ) { |
| stpgcnt = persp->a.stpgcnt; |
| newstpgcnt = stpgcnt; |
| descpgcnt = 0; |
| memset( ( void * )&persp->s, 0, sizeofmember( pers_t, s )); |
| } else { |
| stpgcnt = persp->a.stpgcnt; |
| newstpgcnt = stpgcnt; |
| descpgcnt = persp->s.descpgcnt; |
| ASSERT( resumepr ); |
| mlog( MLOG_VERBOSE, _( |
| "resuming restore previously begun %s\n"), |
| ctimennl( &persp->s.begintime )); |
| persp->s.begintime = time( 0 ); |
| } |
| |
| /* unmap temp mapping of hdr, truncate, and remap hdr/subtrees |
| */ |
| rval = munmap( ( void * )persp, perssz ); |
| ASSERT( ! rval ); |
| rval = ftruncate( tranp->t_persfd, ( off_t )perssz |
| + |
| ( off_t )( stpgcnt + descpgcnt ) |
| * |
| ( off_t )pgsz ); |
| ASSERT( ! rval ); |
| stpgcnt = newstpgcnt; |
| persp = ( pers_t * ) mmap_autogrow( perssz + stpgcnt * pgsz, |
| tranp->t_persfd, 0); |
| if ( persp == ( pers_t * )-1 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "could not map persistent state file %s: %s\n"), |
| perspath, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| |
| /* if first restore session, record cmd line args and subtrees |
| * and start time. |
| */ |
| if ( ! persp->a.valpr ) { |
| stdesc_t *stdescp; |
| |
| strcpy( persp->a.dstdir, dstdir ); |
| persp->a.dstdirisxfspr = platform_test_xfs_path( dstdir ); |
| if ( cumpr ) { |
| persp->a.cumpr = cumpr; |
| } |
| if ( interpr ) { |
| persp->a.interpr = interpr; |
| } |
| if ( existpr ) { |
| persp->a.existpr = existpr; |
| } |
| if ( changepr ) { |
| persp->a.changepr = changepr; |
| } |
| if ( ownerpr ) { |
| persp->a.ownerpr = ownerpr; |
| } |
| if ( newerpr ) { |
| persp->a.newerpr = newerpr; |
| persp->a.newertime = newertime; |
| } |
| persp->a.restoredmpr = restoredmpr; |
| if ( ! persp->a.dstdirisxfspr ) { |
| restoreextattrpr = BOOL_FALSE; |
| } |
| persp->a.restoreextattrpr = restoreextattrpr; |
| persp->a.stcnt = stcnt; |
| persp->a.firststsensepr = firststsensepr; |
| persp->a.firststsenseprvalpr = firststsenseprvalpr; |
| persp->a.stpgcnt = stpgcnt; |
| optind = 1; |
| opterr = 0; |
| stdescp = ( stdesc_t * )( ( char * )persp + perssz ); |
| while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) { |
| size_t stdsz; |
| switch ( c ) { |
| case GETOPT_SUBTREE: |
| case GETOPT_NOSUBTREE: |
| stdescp->std_sensepr = ( c == GETOPT_SUBTREE ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| stdsz = sizeof( stdesc_t ) |
| + |
| strlen( optarg ) |
| + |
| ( STDESCALIGN - 1 ); |
| stdsz &= ~( STDESCALIGN - 1 ); |
| ASSERT( stdsz <= ( size_t )OFFMAX ); |
| stdescp->std_nextoff = ( off_t )stdsz; |
| strcpy( stdescp->std_path, optarg ); |
| stdescp = ( stdesc_t * ) |
| ( ( char * )stdescp + stdsz ); |
| stcnt--; |
| break; |
| } |
| } |
| ASSERT( stcnt == 0 ); |
| } |
| |
| /* initialize the local extattr abstraction. must be done even if |
| * we don't intend to restore extended attributes |
| */ |
| ok = extattr_init( drivecnt ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| |
| /* effectively initialize libhandle on this filesystem by |
| * allocating a file system handle. this needs to be done |
| * before any open_by_handle() calls (and possibly other |
| * libhandle calls). |
| */ |
| if ( persp->a.dstdirisxfspr ) { |
| void *fshanp; |
| size_t fshlen=0; |
| |
| if(path_to_fshandle(persp->a.dstdir, &fshanp, &fshlen)) { |
| mlog( MLOG_NORMAL, |
| _("unable to construct a file " |
| "system handle for %s: %s\n"), |
| persp->a.dstdir, |
| strerror( errno )); |
| return BOOL_FALSE; |
| } |
| /* libhandle has it cached, release this copy */ |
| free_handle(fshanp, fshlen); |
| } |
| |
| /* map in pers. inv. descriptors, if any. NOTE: this ptr is to be |
| * referenced ONLY via the macros provided; the descriptors will be |
| * occasionally remapped, causing the ptr to change. |
| */ |
| ASSERT( ! descp ); |
| if ( descpgcnt ) { |
| descp = ( pers_desc_t * ) mmap_autogrow( descpgcnt * pgsz, |
| tranp->t_persfd, |
| ( off_t )perssz |
| + |
| ( off_t )( stpgcnt * pgsz )); |
| if ( descp == ( pers_desc_t * )-1 ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "could not map persistent state file inv %s: " |
| "%s (%d)\n"), |
| perspath, |
| strerror( errno ), |
| errno ); |
| descp = 0; |
| return BOOL_FALSE; |
| } |
| pi_preclean( ); |
| } |
| |
| /* if resuming an interrupted restore, indicate we know the id |
| * of the dump session being restored. otherwise, it will be determined |
| * during coordination of per-drive threads. |
| */ |
| if ( persp->a.valpr && persp->s.valpr ) { |
| persp->s.begintime = time( 0 ); |
| tranp->t_dumpidknwnpr = BOOL_TRUE; |
| } |
| |
| /* sync up with the directory attributes registry. |
| * starts fresh with each dump session restored. |
| * determine if full init needed instead. |
| */ |
| if ( persp->a.valpr && persp->s.valpr ) { |
| ok = dirattr_init( tranp->t_hkdir, BOOL_TRUE, ( u_int64_t )0 ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| tranp->t_dirattrinitdonepr = BOOL_TRUE; |
| } |
| |
| /* sync up with the name registry. created by the |
| * first session, retained by subsequent sessions. |
| * determine if full init needed instead. |
| */ |
| if ( persp->a.valpr ) { |
| ok = namreg_init( tranp->t_hkdir, BOOL_TRUE, ( u_int64_t )0 ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| tranp->t_namreginitdonepr = BOOL_TRUE; |
| } |
| |
| /* sync up with the inomap abstraction. created anew with each fresh |
| * restore session, but persistent after tree updated with dirdump. |
| * determine if full init needed instead. |
| */ |
| ok = inomap_sync_pers( tranp->t_hkdir ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| |
| /* sync up with the tree abstraction. created by the |
| * first session, retained by subsequent sessions. |
| * don't call tree_init( ) from here; can only be called |
| * when a valid media file header is at hand. |
| */ |
| if ( persp->a.valpr ) { |
| /* This is only a full restore if we're doing a level |
| * 0 restore. |
| */ |
| if (persp->a.dumpcnt == 0) { |
| fullpr = BOOL_TRUE; |
| } else { |
| fullpr = BOOL_FALSE; |
| } |
| |
| ok = tree_sync( tranp->t_hkdir, |
| persp->a.dstdir, |
| tranp->t_toconlypr, |
| fullpr, |
| persp->a.dstdirisxfspr ); |
| if ( ! ok ) { |
| return BOOL_FALSE; |
| } |
| tranp->t_treeinitdonepr = BOOL_TRUE; |
| } |
| |
| /* set media change flags to FALSE; |
| */ |
| { |
| ix_t ix; |
| ix_t endix = sizeof( mcflag ) |
| / |
| sizeof( mcflag[ 0 ] ); |
| for ( ix = 0 ; ix < endix ; ix++ ) { |
| mcflag[ ix ] = BOOL_FALSE; |
| } |
| } |
| content_media_change_needed = BOOL_FALSE; |
| |
| pi_show( " at initialization" ); |
| return BOOL_TRUE; |
| } |
| |
| /* stream thread entry point - returns exit code |
| */ |
| intgen_t |
| content_stream_restore( ix_t thrdix ) |
| { |
| dh_t fileh; |
| Media_t *Mediap; /* local media abstraction */ |
| char *path1; |
| char *path2; |
| drive_t *drivep; |
| intgen_t dcaps; |
| global_hdr_t *grhdrp; |
| drive_hdr_t *drhdrp; |
| media_hdr_t *mrhdrp; |
| content_hdr_t *crhdrp; |
| content_inode_hdr_t *scrhdrp; |
| stream_context_t *strctxp; |
| filehdr_t fhdr; /* save hdr terminating dir restore */ |
| uuid_t lastdumprejectedid; |
| rv_t rv; |
| bool_t ok; |
| intgen_t rval; |
| |
| /* allocate two path buffers |
| */ |
| path1 = ( char * )calloc( 1, 2 * MAXPATHLEN ); |
| ASSERT( path1 ); |
| path2 = ( char * )calloc( 1, 2 * MAXPATHLEN ); |
| ASSERT( path2 ); |
| |
| /* set the current directory to dstdir. the tree abstraction |
| * depends on the current directory being the root of the |
| * destination file system. |
| */ |
| rval = chdir( persp->a.dstdir ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL, _( |
| "chdir %s failed: %s\n"), |
| persp->a.dstdir, |
| strerror( errno )); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| |
| /* set my file creation mask to zero, to avoid modifying the |
| * dumped mode bits |
| */ |
| ( void )umask( 0 ); |
| |
| /* initialize the Media abstraction |
| */ |
| Mediap = Media_create( thrdix ); |
| |
| /* |
| * initialize the stream context |
| */ |
| strctxp = (stream_context_t *)calloc(1, sizeof(stream_context_t)); |
| if (!strctxp) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, |
| _("malloc of stream context failed (%d bytes): %s\n"), |
| sizeof(stream_context_t), |
| strerror( errno )); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| strctxp->sc_fd = -1; |
| Mediap->M_drivep->d_strmcontextp = (void *)strctxp; |
| |
| /* if we don't know the dump session id to restore, |
| * first see if command line options can be validated |
| * against the online inventory to identify it. only |
| * one stream needs to do this; the others just wait. |
| * side-effect of validation is to incorporate the online |
| * inventory into the persistent state. |
| */ |
| if ( tranp->t_dumpidknwnpr ) { |
| tranp->t_sync1 = SYNC_DONE; |
| } |
| while ( tranp->t_sync1 != SYNC_DONE ) { |
| lock( ); |
| if ( tranp->t_sync1 == SYNC_BUSY ) { |
| unlock( ); |
| sleep( 1 ); |
| if ( cldmgr_stop_requested( )) { |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| continue; |
| } |
| if ( tranp->t_sync1 == SYNC_DONE ) { |
| unlock( ); |
| continue; |
| } |
| tranp->t_sync1 = SYNC_BUSY; |
| unlock( ); |
| mlog( MLOG_DEBUG, |
| "checking and validating command line dump id/label\n" ); |
| ok = Inv_validate_cmdline( ); |
| /* side-effect - searches for and incorporates online inv |
| * into pi, and makes persp->s.dumpid valid. |
| */ |
| if ( ok == BOOL_ERROR ) { |
| return mlog_exit(EXIT_ERROR, RV_OPT); |
| } |
| tranp->t_dumpidknwnpr = ok; |
| tranp->t_sync1 = SYNC_DONE; |
| } |
| |
| /* if we still don't know the session to restore, search the |
| * media for a match either to the command line arguments or |
| * until the operator selects a media file from the desired |
| * dump. |
| */ |
| if ( tranp->t_dumpidknwnpr ) { |
| tranp->t_sync2 = SYNC_DONE; |
| } |
| uuid_clear(lastdumprejectedid); |
| if ( tranp->t_sync2 != SYNC_DONE ) { |
| mlog( MLOG_VERBOSE, _( |
| "searching media for dump\n") ); |
| } |
| while ( tranp->t_sync2 != SYNC_DONE ) { |
| bool_t matchpr; |
| inv_session_t *sessp; |
| bool_t resumepr; |
| ix_t level; |
| uuid_t *baseidp; |
| |
| rv = Media_mfile_next( Mediap, |
| PURP_SEARCH, |
| &tranp->t_sync2, |
| 0, |
| &grhdrp, |
| &drhdrp, |
| &mrhdrp, |
| &crhdrp, |
| &scrhdrp, |
| &drivep, |
| &fhdr ); |
| switch ( rv ) { |
| case RV_OK: |
| break; |
| case RV_DONE: |
| case RV_NOMORE: |
| continue; |
| case RV_INTR: |
| case RV_QUIT: |
| case RV_DRIVE: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| dcaps = drivep->d_capabilities; |
| |
| lock( ); |
| while ( tranp->t_sync2 == SYNC_BUSY ) { |
| unlock( ); |
| sleep( 1 ); |
| if ( cldmgr_stop_requested( )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| lock( ); |
| } |
| if ( tranp->t_sync2 == SYNC_DONE ) { |
| unlock( ); |
| continue; |
| } |
| tranp->t_sync2 = SYNC_BUSY; |
| |
| unlock( ); |
| mlog( MLOG_DEBUG, |
| "dump found: checking\n" ); |
| matchpr = BOOL_FALSE; |
| resumepr = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_RESUME ); |
| ASSERT( scrhdrp->cih_level >= 0 ); |
| level = ( ix_t )scrhdrp->cih_level; |
| baseidp = resumepr |
| ? |
| &scrhdrp->cih_resume_id |
| : |
| &scrhdrp->cih_last_id; |
| if ( tranp->t_reqdumpidvalpr ) { |
| if ( uuid_compare( tranp->t_reqdumpid, |
| grhdrp->gh_dumpid) == 0) { |
| matchpr = BOOL_TRUE; |
| display_dump_label( BOOL_TRUE, /* lock */ |
| MLOG_VERBOSE, _( |
| "found dump matching " |
| "specified id:\n"), |
| grhdrp, |
| mrhdrp, |
| crhdrp, |
| scrhdrp ); |
| } |
| } else if ( tranp->t_reqdumplabvalpr ) { |
| if ( ! strncmp( tranp->t_reqdumplab, |
| grhdrp->gh_dumplabel, |
| sizeof( grhdrp->gh_dumplabel ))) { |
| matchpr = BOOL_TRUE; |
| display_dump_label( BOOL_TRUE, /* lock */ |
| MLOG_VERBOSE, _( |
| "found dump matching " |
| "specified label:\n"), |
| grhdrp, |
| mrhdrp, |
| crhdrp, |
| scrhdrp ); |
| } |
| } else if ( dumpcompat( resumepr, |
| level, |
| *baseidp, |
| BOOL_FALSE )) { |
| if ( uuid_compare( lastdumprejectedid, |
| grhdrp->gh_dumpid) == 0) { |
| matchpr = BOOL_FALSE; |
| } else { |
| if ( dlog_allowed( ) |
| && |
| ( ( dcaps & DRIVE_CAP_FILES ) |
| || |
| ( dcaps & DRIVE_CAP_REMOVABLE ) |
| || |
| drivecnt > 1 )) { |
| matchpr = promptdumpmatch( thrdix, |
| grhdrp, |
| mrhdrp, |
| crhdrp, |
| scrhdrp ); |
| } else { |
| matchpr = BOOL_TRUE; |
| display_dump_label( BOOL_TRUE,/* lock */ |
| MLOG_VERBOSE, _( |
| "dump " |
| "description: \n"), |
| grhdrp, |
| mrhdrp, |
| crhdrp, |
| scrhdrp ); |
| } |
| } |
| } |
| if ( cldmgr_stop_requested( )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| if ( ! matchpr ) { |
| Media_end( Mediap ); |
| uuid_copy(lastdumprejectedid, grhdrp->gh_dumpid); |
| tranp->t_sync2 = SYNC_INIT; |
| if ( ! dlog_allowed( ) |
| || |
| ( ! ( dcaps & DRIVE_CAP_FILES ) |
| && |
| ! ( dcaps & DRIVE_CAP_REMOVABLE ))) { |
| return mlog_exit(EXIT_NORMAL, RV_QUIT); |
| } |
| continue; |
| } |
| if ( ! dumpcompat( resumepr, level, *baseidp, BOOL_TRUE )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_COMPAT); |
| } |
| strncpyterm( persp->s.dumplab, |
| grhdrp->gh_dumplabel, |
| sizeof( persp->s.dumplab )); |
| sessp = 0; |
| |
| /* don't look at the online inventory if the input is piped |
| */ |
| if ( ! drivep->d_isnamedpipepr |
| && |
| ! drivep->d_isunnamedpipepr ) { |
| ok = inv_get_session_byuuid( &grhdrp->gh_dumpid, |
| &sessp ); |
| if ( ok && sessp ) { |
| mlog( MLOG_VERBOSE, _( |
| "using online session inventory\n") ); |
| persp->s.fullinvpr = pi_transcribe( sessp ); |
| inv_free_session( &sessp ); |
| } |
| } |
| fileh = pi_addfile( Mediap, |
| grhdrp, |
| drhdrp, |
| mrhdrp, |
| scrhdrp, |
| drivep ); |
| /* done here because Media_mfile_next doesn't know |
| * if this is a match |
| */ |
| if ( fileh == DH_NULL ) { |
| return mlog_exit(EXIT_FAULT, RV_ERROR); |
| } |
| uuid_copy(persp->s.dumpid,grhdrp->gh_dumpid); |
| persp->s.begintime = time( 0 ); |
| tranp->t_dumpidknwnpr = BOOL_TRUE; |
| tranp->t_sync2 = SYNC_DONE; |
| } |
| |
| /* all drives coordinate in attempt to apply session dir dump. |
| * only one actually completes. |
| */ |
| if ( persp->s.dirdonepr ) { |
| tranp->t_sync3 = SYNC_DONE; |
| } |
| if ( tranp->t_sync3 != SYNC_DONE ) { |
| mlog( MLOG_VERBOSE, _( |
| "searching media for directory dump\n") ); |
| } |
| while ( tranp->t_sync3 != SYNC_DONE ) { |
| rv = Media_mfile_next( Mediap, |
| PURP_DIR, |
| &tranp->t_sync3, |
| &fileh, |
| &grhdrp, |
| &drhdrp, |
| &mrhdrp, |
| &crhdrp, |
| &scrhdrp, |
| &drivep, |
| &fhdr ); |
| switch ( rv ) { |
| case RV_OK: |
| break; |
| case RV_DONE: |
| case RV_NOMORE: |
| continue; |
| case RV_INTR: |
| case RV_QUIT: |
| case RV_DRIVE: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| dcaps = drivep->d_capabilities; |
| ASSERT( fileh != DH_NULL ); |
| lock( ); |
| if ( tranp->t_sync3 == SYNC_BUSY ) { |
| unlock( ); |
| mlog( MLOG_TRACE, |
| "waiting for directories to be restored\n" ); |
| lock( ); |
| } |
| while ( tranp->t_sync3 == SYNC_BUSY ) { |
| unlock( ); |
| #if DEBUG_DUMPSTREAMS |
| { |
| static int count[STREAM_MAX] = {0}; |
| intgen_t streamix = stream_getix( pthread_self() ); |
| if (++(count[streamix]) == 30) { |
| mlog( MLOG_TRACE, |
| "still waiting for dirs to be restored\n"); |
| count[streamix] = 0; |
| } |
| } |
| #endif |
| sleep( 1 ); |
| if ( cldmgr_stop_requested( )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| lock( ); |
| } |
| if ( tranp->t_sync3 == SYNC_DONE ) { |
| unlock( ); |
| continue; |
| } |
| if ( !(scrhdrp->cih_dumpattr & CIH_DUMPATTR_DIRDUMP) ) { |
| /* if no streams have a directory dump, issue a |
| * message and exit. first set SYNC_BUSY to prevent |
| * other threads from coming through here and issuing |
| * the same message. |
| */ |
| tranp->t_dirdumps &= ~(1ULL << thrdix); |
| if ( !tranp->t_dirdumps ) { |
| tranp->t_sync3 = SYNC_BUSY; |
| } |
| unlock( ); |
| if ( !tranp->t_dirdumps ) { |
| mlog( MLOG_VERBOSE | MLOG_ERROR, _( |
| "no directory dump found\n") ); |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_ERROR); |
| } |
| sleep( 1 ); |
| if ( cldmgr_stop_requested( )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| continue; |
| } |
| tranp->t_sync3 = SYNC_BUSY; |
| unlock( ); |
| if ( ! tranp->t_dirattrinitdonepr ) { |
| mlog( MLOG_TRACE, |
| "initializing directory attributes registry\n" ); |
| mlog( MLOG_NITTY, |
| "content_stream_restore: dircnt %llu\n", |
| scrhdrp->cih_inomap_dircnt ); |
| ok = dirattr_init( tranp->t_hkdir, |
| BOOL_FALSE, |
| scrhdrp->cih_inomap_dircnt ); |
| if ( ! ok ) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| tranp->t_dirattrinitdonepr = BOOL_TRUE; |
| } |
| |
| if ( ! tranp->t_namreginitdonepr ) { |
| mlog( MLOG_TRACE, |
| "initializing directory entry name registry\n" ); |
| ok = namreg_init( tranp->t_hkdir, |
| BOOL_FALSE, |
| scrhdrp->cih_inomap_dircnt |
| + |
| scrhdrp->cih_inomap_nondircnt ); |
| if ( ! ok ) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| tranp->t_namreginitdonepr = BOOL_TRUE; |
| } |
| |
| if ( ! tranp->t_treeinitdonepr ) { |
| bool_t fullpr; |
| |
| fullpr = ( scrhdrp->cih_level |
| == |
| 0 ) |
| && |
| ! ( scrhdrp->cih_dumpattr |
| & |
| CIH_DUMPATTR_RESUME ); |
| |
| mlog( MLOG_TRACE, |
| "initializing directory hierarchy image\n" ); |
| ok = tree_init( tranp->t_hkdir, |
| persp->a.dstdir, |
| tranp->t_toconlypr, |
| persp->a.ownerpr, |
| scrhdrp->cih_rootino, |
| scrhdrp->cih_inomap_firstino, |
| scrhdrp->cih_inomap_lastino, |
| scrhdrp->cih_inomap_dircnt, |
| scrhdrp->cih_inomap_nondircnt, |
| tranp->t_vmsz, |
| fullpr, |
| persp->a.restoredmpr, |
| persp->a.dstdirisxfspr, |
| grhdrp->gh_version, |
| tranp->t_truncategenpr ); |
| if ( ! ok ) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| tranp->t_treeinitdonepr = BOOL_TRUE; |
| |
| } else { |
| ok = tree_check_dump_format( grhdrp->gh_version ); |
| if ( ! ok ) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| } |
| } |
| |
| /* commit the session and accumulative state |
| */ |
| persp->s.valpr = BOOL_TRUE; |
| persp->a.valpr = BOOL_TRUE; |
| |
| mlog( MLOG_VERBOSE, _( |
| "reading directories\n") ); |
| win_locks_off(); /* we are single threaded here */ |
| rv = applydirdump( drivep, fileh, scrhdrp, &fhdr ); |
| win_locks_on(); |
| mlog( MLOG_TRACE, |
| "number of mmap calls for windows = %lu\n", win_getnum_mmaps()); |
| switch ( rv ) { |
| case RV_OK: |
| DH2F( fileh )->f_dirtriedpr = BOOL_TRUE; |
| Media_atnondir( Mediap ); |
| tranp->t_sync3 = SYNC_DONE; |
| break; |
| case RV_CORRUPT: |
| Media_indir( Mediap ); |
| DH2F( fileh )->f_dirtriedpr = BOOL_TRUE; |
| tranp->t_sync3 = SYNC_INIT; |
| break; |
| case RV_INTR: |
| case RV_DRIVE: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| } |
| |
| /* now let one thread do all tree post-processing prior to |
| * non-dir restore |
| */ |
| if ( persp->s.treepostdonepr ) { |
| tranp->t_sync4 = SYNC_DONE; |
| } |
| while ( tranp->t_sync4 != SYNC_DONE ) { |
| lock( ); |
| if ( tranp->t_sync4 == SYNC_BUSY ) { |
| unlock( ); |
| mlog( MLOG_TRACE, |
| "waiting for directory post-processing " |
| "to complete\n" ); |
| lock( ); |
| } |
| while ( tranp->t_sync4 == SYNC_BUSY ) { |
| unlock( ); |
| #if DEBUG_DUMPSTREAMS |
| { |
| static int count[STREAM_MAX] = {0}; |
| intgen_t streamix = stream_getix( pthread_self() ); |
| if (++(count[streamix]) == 30) { |
| mlog( MLOG_NORMAL, |
| "still waiting for dirs post-processing\n"); |
| count[streamix] = 0; |
| } |
| } |
| #endif |
| sleep( 1 ); |
| if ( cldmgr_stop_requested( )) { |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, RV_INTR); |
| } |
| lock( ); |
| } |
| if ( tranp->t_sync4 == SYNC_DONE ) { |
| unlock( ); |
| continue; |
| } |
| tranp->t_sync4 = SYNC_BUSY; |
| unlock( ); |
| mlog( MLOG_VERBOSE, _( |
| "directory post-processing\n") ); |
| win_locks_off(); /* we are single threaded here */ |
| rv = treepost( path1, path2 ); |
| win_locks_on(); |
| switch ( rv ) { |
| case RV_OK: |
| break; |
| case RV_ERROR: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_ERROR, RV_ERROR); |
| case RV_INTR: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_INTERRUPT, RV_INTR); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| |
| /* now that we have a tree and inomap, scan the |
| * pi to see what media files can be skipped. |
| * this func has cancer: too many flags and |
| * side-effects! |
| */ |
| { |
| bool_t dummyknownholespr; |
| bool_t dummymaybeholespr; |
| bag_t *bagp = pi_neededobjs_nondir_alloc( &dummyknownholespr, |
| &dummymaybeholespr, |
| BOOL_FALSE, |
| BOOL_TRUE ); |
| if ( bagp ) { |
| pi_neededobjs_free( bagp ); |
| bagp = 0; |
| } |
| } |
| |
| /* release exclusion |
| */ |
| tranp->t_sync4 = SYNC_DONE; |
| } |
| |
| /* now all are free to do concurrent non-dir restore! |
| * apply media files until there are no more, or we are interrupted |
| */ |
| for (;;) { |
| mlog( MLOG_DEBUG, |
| "getting next media file for non-dir restore\n" ); |
| rv = Media_mfile_next( Mediap, |
| PURP_NONDIR, |
| 0, |
| &fileh, |
| &grhdrp, |
| &drhdrp, |
| &mrhdrp, |
| &crhdrp, |
| &scrhdrp, |
| &drivep, |
| &fhdr ); |
| if ( rv == RV_NOMORE ) { |
| break; |
| } |
| switch ( rv ) { |
| case RV_OK: |
| break; |
| case RV_INTR: |
| case RV_QUIT: |
| case RV_DRIVE: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| dcaps = drivep->d_capabilities; |
| ASSERT( fileh > DH_NULL ); |
| if ( tranp->t_toconlypr ) { |
| mlog( MLOG_VERBOSE, _( |
| "reading non-directory files\n") ); |
| } else { |
| mlog( MLOG_VERBOSE, _( |
| "restoring non-directory files\n") ); |
| } |
| mlog( MLOG_TRACE, |
| "media file %u in " |
| "object %u " |
| "of stream %u\n", |
| mrhdrp->mh_mediafileix, |
| mrhdrp->mh_mediaix, |
| drhdrp->dh_driveix ); |
| mlog( MLOG_DEBUG, |
| "file %u in stream, " |
| "file %u in dump %u on object\n", |
| mrhdrp->mh_dumpfileix, |
| mrhdrp->mh_dumpmediafileix, |
| mrhdrp->mh_dumpmediaix ); |
| rv = applynondirdump( drivep, |
| fileh, |
| scrhdrp, |
| path1, |
| path2, |
| &fhdr ); |
| switch ( rv ) { |
| case RV_OK: |
| DH2F( fileh )->f_nondirdonepr = BOOL_TRUE; |
| Media_end( Mediap ); |
| break; |
| case RV_INTR: |
| case RV_DRIVE: |
| case RV_INCOMPLETE: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_NORMAL, rv); |
| case RV_CORE: |
| default: |
| Media_end( Mediap ); |
| return mlog_exit(EXIT_FAULT, rv); |
| } |
| } |
| |
| /* finally, choose one thread to do final processing |
| * and cleanup. the winner waits, the losers all exit. |
| * once the losers exit, the winner can perform cleanup. |
| */ |
| lock( ); |
| if ( tranp->t_sync5 == SYNC_BUSY ) { |
| unlock( ); |
| return mlog_exit(EXIT_NORMAL, RV_DONE); |
| } |
| tranp->t_sync5 = SYNC_BUSY; |
| unlock( ); |
| if ( drivecnt > 1 ) { |
| mlog( MLOG_TRACE, |
| "waiting for other streams to exit\n" ); |
| } |
| while ( cldmgr_otherstreamsremain( thrdix )) { |
| sleep( 1 ); |
| } |
| |
| mlog( MLOG_DEBUG, |
| "tree finalize\n" ); |
| rv = finalize( path1, path2 ); |
| if (rv == RV_OK || rv == RV_INTR) { |
| rval = EXIT_NORMAL; |
| } else if (rv == RV_ERROR) { |
| rval = EXIT_ERROR; |
| } else { |
| rval = EXIT_FAULT; |
| } |
| |
| return mlog_exit(rval, rv); |
| } |
| |
| /* called after all threads have exited. scans state to decide |
| * if interrupted or not. |
| */ |
| bool_t |
| content_complete( void ) |
| { |
| bool_t completepr; |
| time_t elapsed; |
| |
| if ( ! persp ) { |
| completepr = BOOL_TRUE; |
| } else if ( ! persp->a.valpr ) { |
| completepr = BOOL_TRUE; |
| } else if ( ! persp->s.valpr ) { |
| completepr = BOOL_TRUE; |
| } else { |
| completepr = BOOL_FALSE; |
| } |
| |
| elapsed = time( 0 ) - tranp->t_starttime; |
| if ( persp ) { |
| elapsed += persp->s.accumtime; |
| } |
| |
| if ( completepr ) { |
| if ( tranp->t_toconlypr ) { |
| mlog( MLOG_VERBOSE, _( |
| "table of contents display complete" |
| ": %ld seconds elapsed" |
| "\n"), |
| elapsed ); |
| } else { |
| int found; |
| |
| found = quotafilecheck("user", |
| persp->a.dstdir, |
| CONTENT_QUOTAFILE); |
| found += quotafilecheck("project", |
| persp->a.dstdir, |
| CONTENT_PQUOTAFILE); |
| found += quotafilecheck("group", |
| persp->a.dstdir, |
| CONTENT_GQUOTAFILE); |
| |
| if (found) |
| mlog( MLOG_NORMAL, |
| _("use \'xfs_quota\' to restore quotas\n") ); |
| |
| mlog( MLOG_VERBOSE, _( |
| "restore complete" |
| ": %ld seconds elapsed" |
| "\n"), |
| elapsed ); |
| } |
| } else if ( tranp->t_toconlypr ) { |
| mlog( MLOG_VERBOSE | MLOG_NOTE, _( |
| "table of contents display interrupted" |
| ": %ld seconds elapsed" |
| "\n"), |
| elapsed ); |
| } else { |
| mlog( MLOG_VERBOSE | MLOG_NOTE, _( |
| "restore interrupted" |
| ": %ld seconds elapsed" |
| ": may resume later using -%c option" |
| "\n"), |
| elapsed, |
| GETOPT_RESUME ); |
| } |
| |
| /* accumulate total elapsed time |
| */ |
| if ( persp ) { |
| persp->s.accumtime = elapsed; |
| } |
| |
| if ( ! persp->a.valpr ) { |
| wipepersstate( ); |
| persp = 0; |
| } |
| |
| return completepr; |
| } |
| |
| #define STATLINESZ 160 |
| |
| size_t |
| content_statline( char **linespp[ ] ) |
| { |
| static char statlinebuf[ 1 ][ STATLINESZ ]; |
| static char *statline[ 1 ]; |
| size64_t inodone; |
| off64_t datadone; |
| size64_t inocnt; |
| off64_t datacnt; |
| double percent; |
| time_t elapsed; |
| time_t now; |
| struct tm *tmp; |
| ix_t i; |
| |
| /* build and supply the line array |
| */ |
| for ( i = 0 ; i < 1 ; i++ ) { |
| statline[ i ] = &statlinebuf[ i ][ 0 ]; |
| } |
| *linespp = statline; |
| |
| if ( ! persp->s.stat_valpr ) { |
| return 0; |
| } |
| |
| /* calculate the elapsed time |
| */ |
| elapsed = persp->s.accumtime |
| + |
| ( time( 0 ) - tranp->t_starttime ); |
| |
| /* get local time |
| */ |
| now = time( 0 ); |
| tmp = localtime( &now ); |
| |
| if ( ! persp->s.dirdonepr ) { |
| if ( ! tranp->t_dircnt ) { |
| return 0; |
| } |
| |
| percent = ( double )tranp->t_dirdonecnt |
| / |
| ( double )tranp->t_dircnt; |
| percent *= 100.0; |
| sprintf( statline[ 0 ], _( |
| "status at %02d:%02d:%02d: " |
| "%llu/%llu directories reconstructed, " |
| "%.1f%%%% complete, " |
| "%llu directory entries processed, " |
| "%ld seconds elapsed\n"), |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| (unsigned long long)tranp->t_dirdonecnt, |
| (unsigned long long)tranp->t_dircnt, |
| percent, |
| (unsigned long long)tranp->t_direntcnt, |
| elapsed ); |
| ASSERT( strlen( statline[ 0 ] ) < STATLINESZ ); |
| |
| return 1; |
| } |
| |
| /* get the accumulated totals for non-dir inos and data bytes dumped. |
| * not under lock! |
| */ |
| inodone = persp->s.stat_inodone; |
| datadone = persp->s.stat_datadone; |
| inocnt = persp->s.stat_inocnt; |
| datacnt = persp->s.stat_datacnt; |
| |
| /* calculate percentage of data dumped |
| */ |
| if ( datacnt ) { |
| percent = ( double )datadone |
| / |
| ( double )datacnt; |
| 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 restored, " |
| "%.1f%%%% complete, " |
| "%ld seconds elapsed\n"), |
| tmp->tm_hour, |
| tmp->tm_min, |
| tmp->tm_sec, |
| (unsigned long long)inodone, |
| (unsigned long long)inocnt, |
| percent, |
| elapsed ); |
| ASSERT( strlen( statline[ 0 ] ) < STATLINESZ ); |
| |
| /* return buffer to caller |
| */ |
| return 1; |
| } |
| |
| void |
| content_showinv( void ) |
| { |
| pi_show_nomloglock( ); |
| } |
| |
| void |
| content_showremainingobjects( void ) |
| { |
| bool_t knownholespr = BOOL_FALSE; |
| bool_t maybeholespr = BOOL_FALSE; |
| bag_t *bagp; |
| |
| bagp = pi_neededobjs_nondir_alloc( &knownholespr, |
| &maybeholespr, |
| BOOL_TRUE, |
| BOOL_FALSE ); |
| display_needed_objects( PURP_NONDIR, |
| bagp, |
| knownholespr, |
| maybeholespr ); |
| if ( bagp ) { |
| pi_neededobjs_free( bagp ); |
| bagp = 0; |
| } |
| } |
| |
| /* dlog_begin already called; this is a second-level dialog. |
| * prompt for each thread currently waiting for confirmation, |
| * as well as an info prompt. |
| */ |
| #define PREAMBLEMAX 3 |
| #define QUERYMAX 36 |
| #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; |
| char *ackstr[ ACKMAX ]; |
| size_t ackcnt; |
| size_t maxdrvchoiceix; |
| size_t infoix; |
| size_t nochangeix; |
| size_t responseix; |
| ix_t thrdix; |
| |
| infoix = querycnt = 0; |
| querystr[ querycnt++ ] = |
| _("select a drive to acknowledge media change\n"); |
| choicecnt = 0; |
| maxdrvchoiceix = 0; |
| for ( thrdix = 0 ; thrdix < STREAM_SIMMAX ; thrdix++ ) { |
| if ( mcflag[ thrdix ] ) { |
| choicetothrdmap[ choicecnt ].thrdix = thrdix; |
| sprintf( choicetothrdmap[ choicecnt ].choicestr, |
| _("drive %u"), |
| (unsigned int)thrdix ); |
| choicestr[ choicecnt ] = |
| choicetothrdmap[ choicecnt ].choicestr; |
| maxdrvchoiceix = choicecnt; |
| choicecnt++; |
| } |
| } |
| if ( persp->s.valpr ) { |
| infoix = choicecnt; |
| choicestr[ choicecnt++ ] = _("display needed media objects"); |
| } |
| 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, |
| 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"); |
| } |
| if ( responseix == infoix ) { |
| bool_t knownholespr = BOOL_FALSE; |
| bool_t maybeholespr = BOOL_FALSE; |
| bag_t *bagp = pi_neededobjs_nondir_alloc( &knownholespr, |
| &maybeholespr, |
| BOOL_FALSE, |
| BOOL_FALSE ); |
| display_needed_objects( PURP_NONDIR, |
| bagp, |
| knownholespr, |
| maybeholespr ); |
| if ( bagp ) { |
| pi_neededobjs_free( bagp ); |
| bagp = 0; |
| } |
| ackcnt = 0; |
| dlog_multi_ack( ackstr, |
| ackcnt ); |
| querycnt = 0; |
| choicecnt = 0; |
| 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, |
| nochangeix, /* timeout ix */ |
| nochangeix, /* sigint ix */ |
| nochangeix, /* sighup ix */ |
| nochangeix);/* sigquit ix */ |
| return _("continuing\n"); |
| } |
| ASSERT( responseix == nochangeix ); |
| return _("continuing\n"); |
| } |
| |
| /* definition of locally defined static functions ****************************/ |
| |
| /* does all pre-processing leading up to applying the dirdump, |
| * then applies the dirdump. updates pers progress flags along the way. |
| * does NOT do any post-processing! |
| */ |
| /* ARGSUSED */ |
| static rv_t |
| applydirdump( drive_t *drivep, |
| dh_t fileh, |
| content_inode_hdr_t *scrhdrp, |
| filehdr_t *fhdrp ) |
| { |
| bool_t fhcs; |
| bool_t dhcs; |
| bool_t ahcs; |
| |
| fhcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_FILEHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| dhcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_DIRENTHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| ahcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_EXTATTRHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| |
| if ( ! persp->s.marknorefdonepr ) { |
| tree_marknoref( ); |
| persp->s.marknorefdonepr = BOOL_TRUE; |
| } |
| |
| if ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_NOTSELFCONTAINED ) { |
| mlog( MLOG_NORMAL | MLOG_NOTE, _( |
| "dump is not self-contained, " |
| "orphaned files expected if base dump(s) " |
| "was not applied\n") ); |
| } |
| |
| if ( ! persp->s.dirdonepr ) { |
| rv_t rv; |
| dah_t dah; |
| |
| char _direntbuf[ sizeof( direnthdr_t ) |
| + |
| NAME_MAX + 1 |
| + |
| DIRENTHDR_ALIGN ]; |
| char *direntbuf = ALIGN_PTR(_direntbuf, DIRENTHDR_ALIGN); |
| size_t direntbufsz = |
| sizeof(_direntbuf) - (direntbuf - _direntbuf); |
| |
| mlog( MLOG_TRACE, |
| "reading the ino map\n" ); |
| rv = inomap_restore_pers( drivep, scrhdrp, tranp->t_hkdir ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| tranp->t_dircnt = scrhdrp->cih_inomap_dircnt; |
| tranp->t_dirdonecnt = 0; |
| tranp->t_direntcnt = 0; |
| |
| mlog( MLOG_TRACE, |
| "reading the directories \n" ); |
| |
| dah = DAH_NULL; |
| for (;;) { |
| nh_t dirh; |
| |
| /* read the file header |
| */ |
| rv = read_filehdr( drivep, fhdrp, fhcs ); |
| if ( rv ) { |
| return rv; |
| } |
| |
| /* if this is a null file hdr, we're done |
| * reading dirs, and there are no nondirs. |
| * done. |
| */ |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_NULL ) { |
| break; |
| } |
| |
| /* if its not a directory, must be the |
| * first non-dir file. done. |
| */ |
| if ( ( fhdrp->fh_stat.bs_mode & S_IFMT ) != S_IFDIR ) { |
| break; |
| } |
| |
| /* if stop requested bail out gracefully |
| */ |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* if in a pipeline , call preemptchk( ) to |
| * print status reports |
| */ |
| if ( pipeline ) |
| { |
| mlog( MLOG_DEBUG , |
| "preemptchk( )\n"); |
| preemptchk( ); |
| } |
| |
| /* may be an extended attributes file hdr |
| */ |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_EXTATTR ) { |
| rv = restore_extattr( drivep, |
| fhdrp, |
| 0, |
| ahcs, |
| BOOL_TRUE, /* isdirpr */ |
| BOOL_FALSE, /* onlydoreadpr */ |
| dah ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| continue; |
| } |
| |
| /* add the directory to the tree. save the |
| * returned tree node id, to associate with |
| * the directory entries. get the dirattr handle, |
| * so that any extattr following will be associated |
| * with that dir. |
| */ |
| dah = DAH_NULL; |
| dirh = tree_begindir( fhdrp, &dah ); |
| if (dirh == NH_NULL) |
| return RV_ERROR; |
| |
| /* read the directory entries, and populate the |
| * tree with them. we can tell when we are done |
| * by looking for a null dirent. |
| */ |
| for ( ; ; ) { |
| register direnthdr_t *dhdrp = |
| ( direnthdr_t * )direntbuf; |
| register size_t namelen; |
| |
| rv = read_dirent( drivep, |
| dhdrp, |
| direntbufsz, |
| dhcs ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| /* if null, we're done with this dir. |
| * break out of this inner loop and |
| * move on th the next dir. |
| */ |
| if ( dhdrp->dh_ino == 0 ) { |
| break; |
| } |
| namelen = strlen( dhdrp->dh_name ); |
| ASSERT( namelen <= NAME_MAX ); |
| |
| /* add this dirent to the tree. |
| */ |
| rv = tree_addent( dirh, |
| dhdrp->dh_ino, |
| dhdrp->dh_gen, |
| dhdrp->dh_name, |
| namelen ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| tranp->t_direntcnt++; |
| } |
| |
| tree_enddir( dirh ); |
| |
| tranp->t_dirdonecnt++; |
| } |
| |
| if ((rv = dirattr_flush()) != RV_OK) { |
| return rv; |
| } |
| |
| if ((rv = namreg_map()) != RV_OK) { |
| return rv; |
| } |
| |
| persp->s.dirdonepr = BOOL_TRUE; |
| } |
| |
| mlog( MLOG_VERBOSE, _("%llu directories and %llu entries processed\n"), |
| tranp->t_dirdonecnt, tranp->t_direntcnt); |
| |
| return RV_OK; |
| } |
| |
| /* like applydirdump, but just eats up the inomap/dirdump portion of the |
| * dump, doesn't use it. the first non-dir filehdr_t is copied into supplied |
| * buffer. returns integer error code from drive ops used. |
| */ |
| /* ARGSUSED */ |
| rv_t |
| eatdirdump( drive_t *drivep, |
| dh_t fileh, |
| content_inode_hdr_t *scrhdrp, |
| filehdr_t *fhdrp ) |
| { |
| bool_t fhcs; |
| bool_t dhcs; |
| bool_t ahcs; |
| |
| char _direntbuf[ sizeof( direnthdr_t ) |
| + |
| NAME_MAX + 1 |
| + |
| DIRENTHDR_ALIGN ]; |
| char *direntbuf = ALIGN_PTR(_direntbuf, DIRENTHDR_ALIGN); |
| size_t direntbufsz = sizeof(_direntbuf) - (direntbuf - _direntbuf); |
| rv_t rv; |
| |
| fhcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_FILEHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| dhcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_DIRENTHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| ahcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_EXTATTRHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| |
| mlog( MLOG_DEBUG, |
| "discarding ino map\n" ); |
| rv = inomap_discard( drivep, scrhdrp ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| mlog( MLOG_DEBUG, |
| "discarding directories \n" ); |
| for (;;) { |
| /* read the file header |
| */ |
| rv = read_filehdr( drivep, fhdrp, fhcs ); |
| if ( rv ) { |
| return rv; |
| } |
| |
| /* if this is a null file hdr, we're done |
| * reading dirs, and there are no nondirs. |
| * done. |
| */ |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_NULL ) { |
| break; |
| } |
| |
| /* if its not a directory, must be the |
| * first non-dir file. done. |
| */ |
| if ( ( fhdrp->fh_stat.bs_mode & S_IFMT ) != S_IFDIR ) { |
| break; |
| } |
| |
| /* if stop requested bail out gracefully |
| */ |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* may be an extended attributes file hdr |
| */ |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_EXTATTR ) { |
| rv = restore_extattr( drivep, |
| fhdrp, |
| 0, |
| ahcs, |
| BOOL_TRUE, /* isdirpr */ |
| BOOL_TRUE, /* onlydoreadpr */ |
| DAH_NULL ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| continue; |
| } |
| |
| /* read the directory entries. |
| * we can tell when we are done |
| * by looking for a null dirent. |
| */ |
| for ( ; ; ) { |
| register direnthdr_t *dhdrp = |
| ( direnthdr_t * )direntbuf; |
| /* REFERENCED */ |
| register size_t namelen; |
| |
| rv = read_dirent( drivep, |
| dhdrp, |
| direntbufsz, |
| dhcs ); |
| if ( rv ) { |
| return rv; |
| } |
| |
| /* if null, we're done with this dir. |
| * break out of this inner loop and |
| * move on th the next dir. |
| */ |
| if ( dhdrp->dh_ino == 0 ) { |
| break; |
| } |
| namelen = strlen( dhdrp->dh_name ); |
| ASSERT( namelen <= NAME_MAX ); |
| } |
| } |
| |
| return RV_OK; |
| } |
| |
| /* does all post-processing of the tree required prior to restoral of |
| * the non-directory portion of the dump. only one thread at a time, |
| * so no locking needed. since the steps are interdependent, loops |
| * until no point in doing so. |
| */ |
| static rv_t |
| treepost( char *path1, char *path2 ) |
| { |
| bool_t ok; |
| |
| #ifdef TREE_CHK |
| /* first scan the tree for corruption |
| */ |
| mlog( MLOG_DEBUG | MLOG_TREE, |
| "checking tree for consistency\n" ); |
| if ( ! tree_chk( )) { |
| return RV_CORE; |
| } |
| #endif /* TREE_CHK */ |
| |
| /* adjust ref flags based on what dirs were dumped |
| */ |
| if ( ! persp->s.adjrefdonepr ) { |
| mlog( MLOG_DEBUG | MLOG_TREE, |
| "adjusting dirent ref flags\n" ); |
| ok = tree_adjref( ); |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| persp->s.adjrefdonepr = BOOL_TRUE; |
| } |
| |
| /* if a subtree or interactive restore, sanitize the inomap |
| * so only inos selected by subtree or interactive cmds will |
| * be present in inomap. |
| */ |
| if ( ! persp->s.inomapsanitizedonepr ) { |
| if ( persp->a.interpr |
| || |
| ( persp->a.firststsenseprvalpr |
| && |
| persp->a.firststsensepr )) { |
| inomap_sanitize( ); |
| } |
| persp->s.inomapsanitizedonepr = BOOL_TRUE; |
| } |
| |
| /* apply subtree selections |
| */ |
| if ( ! persp->s.stdonepr ) { |
| ix_t stix; |
| stdesc_t *stdescp; |
| |
| mlog( MLOG_DEBUG | MLOG_TREE, |
| "applying subtree selections\n" ); |
| |
| /* if first subtree selection is inclusive in sense, |
| * first mark the entire tree as unselected. otherwise, |
| * select all (no subtree selections or first was excluding). |
| */ |
| if ( ( persp->a.interpr |
| && |
| ( ! persp->a.firststsenseprvalpr |
| || |
| persp->a.firststsensepr )) |
| || |
| ( persp->a.firststsenseprvalpr |
| && |
| persp->a.firststsensepr )) { |
| tree_markallsubtree( BOOL_FALSE ); |
| } else { |
| tree_markallsubtree( BOOL_TRUE ); |
| } |
| |
| /* now apply all subtree commands from command line |
| */ |
| for ( stix = 0, |
| stdescp = ( stdesc_t * )( ( char * )persp + perssz ) |
| ; |
| stix < persp->a.stcnt |
| ; |
| stix++, |
| stdescp = ( stdesc_t * )( ( char * )stdescp |
| + |
| stdescp->std_nextoff )) { |
| ok = tree_subtree_parse( stdescp->std_sensepr, |
| stdescp->std_path ); |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "subtree argument %s invalid\n"), |
| stdescp->std_path ); |
| return RV_ERROR; |
| } |
| } |
| persp->s.stdonepr = BOOL_TRUE; |
| } |
| |
| /* next engage interactive subtree selection |
| */ |
| if ( ! persp->s.interdonepr ) { |
| if ( persp->a.interpr ) { |
| ok = tree_subtree_inter( ); |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| } |
| persp->s.interdonepr = BOOL_TRUE; |
| } |
| |
| ok = tree_post( path1, path2 ); |
| |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| |
| ok = tree_extattr( restore_dir_extattr_cb, path1 ); |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| |
| persp->s.treepostdonepr = BOOL_TRUE; |
| |
| return RV_OK; |
| } |
| |
| static rv_t |
| applynondirdump( drive_t *drivep, |
| dh_t fileh, |
| content_inode_hdr_t *scrhdrp, |
| char *path1, |
| char *path2, |
| filehdr_t *fhdrp ) |
| { |
| rv_t rv = RV_UNKNOWN; |
| bool_t fhcs; |
| bool_t ehcs; |
| bool_t ahcs; |
| egrp_t first_egrp; |
| egrp_t next_egrp; |
| stream_context_t *strctxp = (stream_context_t *)drivep->d_strmcontextp; |
| |
| /* determine if file header and/or extent heade checksums present |
| */ |
| fhcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_FILEHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| ehcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_EXTENTHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| ahcs = ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_EXTATTRHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| |
| /* determine the first and next egrps needed from this media file. |
| * used to decide if stats should be updated |
| */ |
| pi_bracketneededegrps( fileh, &first_egrp, &next_egrp ); |
| |
| /* initialize the stream context |
| */ |
| memset(&strctxp->sc_bstat, 0, sizeof(bstat_t)); |
| strctxp->sc_path[0] = '\0'; |
| strctxp->sc_fd = -1; |
| |
| for ( ; ; ) { |
| drive_ops_t *dop = drivep->d_opsp; |
| drive_mark_t drivemark; |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| bool_t resyncpr = BOOL_FALSE; |
| intgen_t rval; |
| |
| /* if a null file header, break |
| */ |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_NULL ) { |
| rv = RV_OK; |
| goto applynondirdump_out; |
| } |
| |
| /* if working on a different file than we were previously, |
| * complete the old one and begin the new one. |
| */ |
| if ( bstatp->bs_ino != strctxp->sc_bstat.bs_ino ) { |
| |
| restore_complete_reg(strctxp); |
| |
| /* start new ino */ |
| memcpy(&strctxp->sc_bstat, bstatp, sizeof(bstat_t)); |
| strctxp->sc_path[0] = '\0'; |
| strctxp->sc_fd = -1; |
| |
| rv = restore_file( drivep, fhdrp, ehcs, ahcs, path1, path2 ); |
| |
| } else if ( fhdrp->fh_flags & FILEHDR_FLAGS_EXTATTR ) { |
| rv = restore_extattr( drivep, |
| fhdrp, |
| strctxp->sc_path, |
| ahcs, |
| BOOL_FALSE, /* isdirpr */ |
| BOOL_FALSE, /* onlydoreadpr */ |
| DAH_NULL ); |
| } else { |
| /* Must be another extent group for the current file */ |
| restore_extent_group( drivep, |
| fhdrp, |
| strctxp->sc_path, |
| strctxp->sc_fd, |
| ehcs, |
| &rv ); |
| } |
| |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_EOD: |
| rv = RV_OK; |
| goto applynondirdump_out; |
| case RV_CORRUPT: |
| rval = ( * dop->do_next_mark )( drivep ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to resync media file: " |
| "some portion of dump will NOT " |
| "be restored\n") ); |
| rv = RV_OK; /* treat as EOD */ |
| goto applynondirdump_out; |
| } |
| resyncpr = BOOL_TRUE; |
| break; |
| default: |
| goto applynondirdump_out; |
| } |
| |
| /* update stats if appropriate |
| */ |
| if ( ( ( bstatp->bs_mode & S_IFMT ) == S_IFREG ) |
| && |
| ! ( fhdrp->fh_flags & FILEHDR_FLAGS_EXTATTR ) |
| && |
| fhdrp->fh_offset == 0 ) { |
| egrp_t cur_egrp; |
| cur_egrp.eg_ino = fhdrp->fh_stat.bs_ino; |
| cur_egrp.eg_off = fhdrp->fh_offset; |
| if (cur_egrp.eg_ino > first_egrp.eg_ino ) { |
| if ( cur_egrp.eg_ino < next_egrp.eg_ino |
| || |
| next_egrp.eg_off > 0 ) { |
| ASSERT( cur_egrp.eg_ino |
| <= |
| next_egrp.eg_ino ); |
| pi_update_stats( bstatp->bs_blocks |
| * |
| ( off64_t ) |
| bstatp->bs_blksize ); |
| } |
| } |
| } |
| |
| do { |
| /* get a mark for the next read, in case we restart here |
| */ |
| ( * dop->do_get_mark )( drivep, &drivemark ); |
| |
| /* read the file header. |
| */ |
| rv = read_filehdr( drivep, fhdrp, fhcs ); |
| switch( rv ) { |
| case RV_OK: |
| break; |
| case RV_EOD: |
| rv = RV_OK; |
| goto applynondirdump_out; |
| case RV_CORRUPT: |
| rval = ( * dop->do_next_mark )( drivep ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "unable to resync media file: " |
| "some portion of dump will NOT " |
| "be restored\n") ); |
| rv = RV_OK; /* treat as EOD */ |
| goto applynondirdump_out; |
| } |
| resyncpr = BOOL_TRUE; |
| default: |
| goto applynondirdump_out; |
| } |
| |
| if ( resyncpr && rv == RV_OK ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "resynchronization achieved at " |
| "ino %llu offset %lld\n"), |
| fhdrp->fh_stat.bs_ino, |
| fhdrp->fh_offset ); |
| resyncpr = BOOL_FALSE; |
| } |
| } while ( resyncpr ); |
| |
| /* checkpoint into persistent state if not a null file hdr |
| */ |
| if ( ! ( fhdrp->fh_flags & FILEHDR_FLAGS_NULL )) { |
| pi_checkpoint( fileh, |
| &drivemark, |
| fhdrp->fh_stat.bs_ino, |
| fhdrp->fh_offset ); |
| } |
| |
| /* if in a pipeline , call preemptchk( ) to |
| * print status reports |
| */ |
| if ( pipeline ) |
| { |
| mlog( MLOG_DEBUG , |
| "preemptchk( )\n"); |
| preemptchk( ); |
| } |
| } |
| |
| applynondirdump_out: |
| |
| /* We've hit the end of this media file or encountered corruption. |
| * In either case, we may not be back to complete the metadata for |
| * this file, so attempt to complete it now. |
| */ |
| restore_complete_reg(strctxp); |
| |
| return rv; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| finalize( char *path1, char *path2 ) |
| { |
| bool_t ok; |
| |
| if ( ! tranp->t_toconlypr ) { |
| |
| /* restore directory attributes |
| */ |
| if ( ! persp->s.dirattrdonepr ) {; |
| ok = tree_setattr( path1 ); |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| persp->s.dirattrdonepr = BOOL_TRUE; |
| } |
| |
| /* remove orphanage if empty |
| */ |
| if ( ! persp->s.orphdeltriedpr ) {; |
| ok = tree_delorph( ); |
| if ( ! ok ) { |
| return RV_INTR; |
| } |
| persp->s.orphdeltriedpr = BOOL_TRUE; |
| } |
| |
| /* delete the persistent ino map |
| */ |
| if ( ! persp->s.inomapdelpr ) { |
| inomap_del_pers( tranp->t_hkdir ); |
| persp->s.inomapdelpr = BOOL_TRUE; |
| } |
| } |
| |
| /* at this point, all session-only persistent state has been deleted. |
| * if this is a cumulative restore, just update the pers cum state and |
| * invalidate the pers session state. otherwise, invalidate the |
| * persistent state. content_complete will remove housekeeping dir. |
| */ |
| if ( persp->a.cumpr ) { |
| /* following must be atomic! |
| */ |
| persp->a.dumpcnt++; |
| uuid_copy(persp->a.lastdumpid, persp->s.dumpid); |
| strcpy( persp->a.lastdumplab, persp->s.dumplab ); |
| persp->s.valpr = BOOL_FALSE; |
| } else { |
| persp->a.valpr = BOOL_FALSE; |
| } |
| return RV_OK; |
| } |
| |
| static void |
| toconly_cleanup( void ) |
| { |
| if ( ! tranp ) { |
| return; |
| } |
| |
| if ( ! tranp->t_toconlypr ) { |
| return; |
| } |
| |
| if ( ! tranp->t_hkdir ) { |
| return; |
| } |
| |
| if ( ! strlen( tranp->t_hkdir )) { |
| return; |
| } |
| |
| wipepersstate( ); |
| } |
| |
| static void |
| wipepersstate( void ) |
| { |
| DIR *dirp; |
| struct dirent64 *direntp; |
| char pathname[ MAXPATHLEN ]; |
| dirp = opendir( tranp->t_hkdir ); |
| if ( ! dirp ) { |
| return; |
| } |
| |
| while ( ( direntp = readdir64( dirp )) != 0 ) { |
| /* REFERENCED */ |
| intgen_t len; |
| if ( ! strcmp( direntp->d_name, "." )) { |
| continue; |
| } |
| if ( ! strcmp( direntp->d_name, ".." )) { |
| continue; |
| } |
| len = sprintf( pathname, |
| "%s/%s", |
| tranp->t_hkdir, |
| direntp->d_name ); |
| ASSERT( len > 0 ); |
| ASSERT( len < MAXPATHLEN ); |
| ( void )unlink( pathname ); |
| closedir( dirp ); |
| dirp = opendir( tranp->t_hkdir ); |
| if ( ! dirp ) { |
| return; |
| } |
| } |
| closedir( dirp ); |
| |
| rmdir( tranp->t_hkdir ); |
| } |
| |
| /* Inv abstraction ***********************************************************/ |
| |
| /* attempt to validate id or label against online inventory. |
| * sets pers id/label and pers idvalpr etc as side-effect (does NOT set valpr!) |
| */ |
| static bool_t |
| Inv_validate_cmdline( void ) |
| { |
| inv_session_t *sessp; |
| bool_t ok; |
| bool_t rok; |
| |
| ASSERT( ! persp->s.valpr ); |
| |
| ok = BOOL_FALSE; |
| sessp = 0; |
| if ( tranp->t_reqdumpidvalpr ) { |
| ok = inv_get_session_byuuid( &tranp->t_reqdumpid, &sessp ); |
| } else if ( tranp->t_reqdumplabvalpr ) { |
| ok = inv_get_session_bylabel( tranp->t_reqdumplab, &sessp ); |
| } |
| rok = BOOL_FALSE; |
| if ( ok && sessp ) { |
| uuid_t baseid; |
| |
| uuid_clear(baseid); |
| askinvforbaseof( baseid, sessp ); |
| if ( ! dumpcompat( sessp->s_isresumed, |
| ( ix_t )( sessp->s_level ), |
| baseid, |
| BOOL_TRUE )) { |
| inv_free_session( &sessp ); |
| return BOOL_ERROR; |
| } |
| |
| mlog( MLOG_VERBOSE, _( |
| "using online session inventory\n") ); |
| persp->s.fullinvpr = pi_transcribe( sessp ); |
| if ( persp->s.fullinvpr ) { |
| strncpyterm( persp->s.dumplab, |
| sessp->s_label, |
| sizeof( persp->s.dumplab )); |
| uuid_copy(persp->s.dumpid, sessp->s_sesid); |
| rok = BOOL_TRUE; |
| } |
| inv_free_session( &sessp ); |
| } |
| return rok; |
| } |
| |
| |
| /* Media abstraction *********************************************************/ |
| |
| |
| static Media_t * |
| Media_create( ix_t thrdix ) |
| { |
| drive_t *drivep = drivepp[ thrdix ]; |
| global_hdr_t *grhdrp = drivep->d_greadhdrp; |
| drive_hdr_t *drhdrp = drivep->d_readhdrp; |
| media_hdr_t *mrhdrp = ( media_hdr_t * )drhdrp->dh_upper; |
| content_hdr_t *crhdrp = ( content_hdr_t * )mrhdrp->mh_upper; |
| content_inode_hdr_t *scrhdrp = |
| ( content_inode_hdr_t * )crhdrp->ch_specific; |
| Media_t *Mediap; |
| |
| |
| mlog( MLOG_DEBUG, |
| "Media_create\n" ); |
| |
| Mediap = ( Media_t * )calloc( 1, sizeof( Media_t )); |
| Mediap->M_drivep = drivep; |
| Mediap->M_grhdrp = grhdrp; |
| Mediap->M_drhdrp = drhdrp; |
| Mediap->M_mrhdrp = mrhdrp; |
| Mediap->M_crhdrp = crhdrp; |
| Mediap->M_scrhdrp = scrhdrp; |
| ASSERT( POS_UNKN == 0 ); |
| |
| return Mediap; |
| } |
| |
| /* these calls allow the Media users to clue Media in to fine position changes |
| * within the current media file |
| */ |
| static void |
| Media_indir( Media_t *Mediap ) |
| { |
| mlog( MLOG_DEBUG, |
| "Media_indir\n" ); |
| |
| Mediap->M_pos = POS_INDIR; |
| } |
| |
| static void |
| Media_atnondir( Media_t *Mediap ) |
| { |
| mlog( MLOG_DEBUG, |
| "Media_atnondir\n" ); |
| |
| Mediap->M_pos = POS_ATNONDIR; |
| } |
| |
| /* supplies pertinent media files to the caller. if purpose is search, |
| * returns all media files. otherwise, returns only media files with the |
| * dump ID. smart enough to know that if purpose was search but is now dir, |
| * current media file can be returned again. same for other transitions. |
| * always traverses the media object in a forward direction, beginning with |
| * current media file, wrapping around to beginning of media if necessary. |
| * also supplies fresh hdr pointers and drive manager. in current |
| * implementation these do not change, but will when we use new TLM. does |
| * fine positioning within media file according to purpose of request. |
| * |
| * Note: |
| * The difference between rval and rv. |
| * rval is used for the drive_* functions (e.g. do_begin_read) |
| * and will take on values such as DRIVE_ERROR_*. |
| * However, it also set to 0 for no error and 1 for error. |
| * It is defaulted to 0 for no error. |
| * rv, however, is of type rv_t and is used for functions returning |
| * rv_t and for the result of this function. |
| * It takes on values like RV_OK, RV_EOF. |
| */ |
| static rv_t |
| Media_mfile_next( Media_t *Mediap, |
| purp_t purp, |
| sync_t *donesyncp, |
| dh_t *filehp, |
| global_hdr_t **grhdrpp, |
| drive_hdr_t **drhdrpp, |
| media_hdr_t **mrhdrpp, |
| content_hdr_t **crhdrpp, |
| content_inode_hdr_t **scrhdrpp, |
| drive_t **drivepp, |
| filehdr_t *fhdrp ) /*caller-supplied buf if NONDIR purp*/ |
| { |
| drive_t *drivep = Mediap->M_drivep; |
| drive_ops_t *dop = drivep->d_opsp; |
| global_hdr_t *grhdrp = Mediap->M_grhdrp; |
| drive_hdr_t *drhdrp = Mediap->M_drhdrp; |
| media_hdr_t *mrhdrp = Mediap->M_mrhdrp; |
| content_hdr_t *crhdrp = Mediap->M_crhdrp; |
| content_inode_hdr_t *scrhdrp = Mediap->M_scrhdrp; |
| dh_t fileh; |
| intgen_t rval = 0; /* no error by default */ |
| rv_t rv; |
| bool_t ok; |
| uuid_t prevmfiledumpid; |
| |
| mlog( MLOG_DEBUG | MLOG_MEDIA, |
| "Media_mfile_next: purp==%d pos==%d\n", |
| purp, |
| Mediap->M_pos ); |
| |
| /* pass back hdr and drive ptrs |
| */ |
| *grhdrpp = grhdrp; |
| *drhdrpp = drhdrp; |
| *mrhdrpp = mrhdrp; |
| *crhdrpp = crhdrp; |
| *scrhdrpp = scrhdrp; |
| *drivepp = drivep; |
| |
| /* if ref return for pers mfile desc supplied, pre-zero |
| */ |
| if ( filehp ) { |
| *filehp = DH_NULL; |
| } |
| |
| /* keep a close eye on the validity of fileh |
| */ |
| fileh = DH_NULL; |
| |
| /* if purpose has changed, invalidate first, last, and previous indices |
| */ |
| if ( Mediap->M_flmfixvalpr ) { |
| if ( purp != Mediap->M_mfixpurp ) { |
| Mediap->M_flmfixvalpr = BOOL_FALSE; |
| Mediap->M_pmfixvalpr = BOOL_FALSE; |
| } |
| } |
| |
| /* use a local variable to keep track of dump sessions seen on |
| * media. if not in search mode, each time we see a different |
| * dump session, log a message to keep the user informed. |
| * invalidated each time we change media or rewind. |
| */ |
| uuid_clear(prevmfiledumpid); |
| |
| /* if restore is complete, return indication. be sure to end read |
| * if active. |
| */ |
| if ( purp == PURP_NONDIR |
| && |
| pi_alldone( )) { |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| } |
| return RV_NOMORE; |
| } |
| |
| /* loop searching for an acceptable media file. |
| * change media as necessary. |
| */ |
| for ( ; ; ) { |
| bool_t emptypr; /* begin_read says drive empty */ |
| bool_t partofdumppr; |
| bool_t hassomepr; |
| bool_t resumepr; |
| bool_t canseeknextpr; |
| drive_mark_t chkpnt; |
| bool_t fhcs; |
| bag_t *bagp = NULL; |
| bool_t knownholespr; |
| bool_t maybeholespr; |
| xfs_ino_t begino; |
| xfs_ino_t endino; |
| intgen_t dcaps = drivep->d_capabilities; |
| dh_t objh = DH_NULL; |
| |
| emptypr = BOOL_FALSE; |
| |
| /* check if no point in going on |
| */ |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( donesyncp && *donesyncp == SYNC_DONE ) { |
| return RV_DONE; |
| } |
| if ( purp == PURP_NONDIR |
| && |
| pi_alldone( )) { |
| return RV_NOMORE; |
| } |
| |
| /* if we have a useless media object, get another one |
| */ |
| if ( Mediap->M_pos == POS_USELESS |
| || |
| Mediap->M_pos == POS_BLANK ) { |
| goto newmedia; |
| } |
| |
| /* if the purpose if to search, and we already have |
| * a media file, that media file has already been |
| * searched, so set pos to cause another begin read |
| */ |
| if ( purp == PURP_SEARCH ) { |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| Mediap->M_pos = POS_UNKN; |
| } |
| } |
| |
| /* if already have a media file, skip the begin_read |
| */ |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| goto validate; |
| } |
| |
| /* see if the indices say we've seen all there is to see |
| */ |
| if ( Mediap->M_flmfixvalpr ) { |
| if ( Mediap->M_pos == POS_UNKN ) { |
| if ( Mediap->M_lmfix + 1 == Mediap->M_fmfix ) { |
| goto newmedia; |
| } |
| } |
| if ( Mediap->M_pos == POS_END ) { |
| if ( Mediap->M_fmfix == 0 ) { |
| goto newmedia; |
| } |
| if ( Mediap->M_fsfixvalpr |
| && |
| Mediap->M_fmfix <= Mediap->M_fsfix ) { |
| goto newmedia; |
| } |
| } |
| } |
| |
| /* if we are at the end, do a rewind, or get new media |
| * if rewinds not possible. this may take a while, so |
| * afterwards check for interrupt or if someone else |
| * has finished the job. |
| */ |
| if ( Mediap->M_pos == POS_END ) { |
| if ( ! ( dcaps & DRIVE_CAP_REWIND )) { |
| goto newmedia; |
| } |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( donesyncp && *donesyncp == SYNC_DONE ) { |
| return RV_DONE; |
| } |
| if ( purp == PURP_NONDIR |
| && |
| pi_alldone( )) { |
| return RV_NOMORE; |
| } |
| } |
| |
| /* begin a new media file, and determine new position. |
| * bail if catastrophic. also, tell pi about EOD/EOM |
| * if appropriate. |
| */ |
| rval = ( * drivep->d_opsp->do_begin_read )( drivep ); |
| switch ( rval ) { |
| case 0: |
| mlog_lock( ); |
| mlog( MLOG_VERBOSE | MLOG_NOLOCK | MLOG_MEDIA, _( |
| "examining media file %u\n"), |
| mrhdrp->mh_mediafileix ); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "file %u in " |
| "object %u " |
| "of stream %u\n", |
| mrhdrp->mh_mediafileix, |
| mrhdrp->mh_mediaix, |
| drhdrp->dh_driveix ); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "file %u in stream, " |
| "file %u of dump %u on object\n", |
| mrhdrp->mh_dumpfileix, |
| mrhdrp->mh_dumpmediafileix, |
| mrhdrp->mh_dumpmediaix ); |
| mlog_unlock( ); |
| |
| Mediap->M_pos = POS_ATHDR; |
| if ( Mediap->M_flmfixvalpr ) { |
| Mediap->M_pmfix = Mediap->M_lmfix; |
| Mediap->M_pmfixvalpr = BOOL_TRUE; |
| } |
| |
| pi_note_indrive( drhdrp->dh_driveix, |
| mrhdrp->mh_mediaid ); |
| |
| break; |
| case DRIVE_ERROR_EOD: |
| Mediap->M_pos = POS_END; |
| if ( Mediap->M_fsfixvalpr ) { |
| ASSERT( purp != PURP_SEARCH ); |
| pi_hiteod( Mediap->M_fssix, |
| Mediap->M_fsoix ); |
| } |
| break; |
| case DRIVE_ERROR_EOM: |
| Mediap->M_pos = POS_END; |
| if ( Mediap->M_fsfixvalpr ) { |
| ASSERT( purp != PURP_SEARCH ); |
| pi_hiteom( Mediap->M_fssix, |
| Mediap->M_fsoix ); |
| } |
| break; |
| case DRIVE_ERROR_MEDIA: |
| /* |
| * pv: 766024; tes@engr |
| * The setting of emptypr, in my opinion, should only happen |
| * in the case that the drive does not have a tape online. |
| * This corresponds to a couple of cases in prepare_drive(). |
| * Otherwise, when we go to a newmedia we won't be able to eject |
| * the tape when we want/need to. |
| * This may need to be reviewed in the future. |
| */ |
| emptypr = BOOL_TRUE; /* so don't try to eject */ |
| /* fall through */ |
| case DRIVE_ERROR_FOREIGN: |
| case DRIVE_ERROR_FORMAT: |
| case DRIVE_ERROR_VERSION: |
| case DRIVE_ERROR_EOF: |
| Mediap->M_pos = POS_USELESS; |
| break; |
| case DRIVE_ERROR_BLANK: |
| Mediap->M_pos = POS_BLANK; |
| break; |
| case DRIVE_ERROR_CORRUPTION: |
| Mediap->M_pos = POS_UNKN; |
| continue; |
| case DRIVE_ERROR_STOP: |
| return RV_INTR; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| default: |
| return RV_CORE; |
| } |
| |
| validate: |
| /* update the positional indices |
| */ |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| if ( ! Mediap->M_flmfixvalpr ) { |
| Mediap->M_fmfix = mrhdrp->mh_mediafileix; |
| Mediap->M_mfixpurp = purp; |
| Mediap->M_flmfixvalpr = BOOL_TRUE; |
| } |
| Mediap->M_lmfix = mrhdrp->mh_mediafileix; |
| } |
| |
| /* check for interrupt. be sure to end_read if necessary |
| */ |
| if ( cldmgr_stop_requested( )) { |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| } |
| return RV_INTR; |
| } |
| |
| /* check if another thread has finished job (for this purpose). |
| * don't end_read, we will be back. |
| */ |
| if ( donesyncp && *donesyncp == SYNC_DONE ) { |
| return RV_DONE; |
| } |
| |
| /* we may be done due to the actions of other threads. |
| * if so, return indicating so |
| */ |
| if ( purp == PURP_NONDIR |
| && |
| pi_alldone( )) { |
| return RV_NOMORE; |
| } |
| |
| /* if the media object is useless, go get more |
| */ |
| if ( Mediap->M_pos == POS_USELESS |
| || |
| Mediap->M_pos == POS_BLANK ) { |
| goto newmedia; |
| } |
| |
| /* if we hit the end, this is not a search, and we've |
| * seen at least one media file pertaining to the dump, |
| * ask the inventory if there is any point in examining |
| * the beginning of the object. |
| */ |
| if ( Mediap->M_pos == POS_END |
| && |
| purp != PURP_SEARCH |
| && |
| Mediap->M_fsfixvalpr |
| && |
| pi_know_no_more_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix )) { |
| goto newmedia; |
| } |
| |
| /* if we hit the end, go back to the top, where |
| * we will decide if we should rewind or get new media. |
| */ |
| if ( Mediap->M_pos == POS_END ) { |
| continue; |
| } |
| |
| /* if the purpose is to search, return this media file |
| */ |
| if ( purp == PURP_SEARCH ) { |
| ASSERT( Mediap->M_pos == POS_ATHDR ); |
| return RV_OK; |
| } |
| |
| /* see if this media file is part of the desired dump session |
| */ |
| partofdumppr = ( bool_t )(uuid_compare( persp->s.dumpid, |
| grhdrp->gh_dumpid) == 0); |
| if (!partofdumppr) { |
| char gh_string_uuid[UUID_STR_LEN + 1]; |
| char inv_string_uuid[UUID_STR_LEN + 1]; |
| |
| uuid_unparse( grhdrp->gh_dumpid, gh_string_uuid); |
| uuid_unparse( persp->s.dumpid, inv_string_uuid); |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "inventory session uuid (%s) does not match " |
| "the media header's session uuid (%s)\n"), |
| inv_string_uuid, gh_string_uuid ); |
| } |
| |
| /* if media file dump id is different from the preceeding |
| * media file, print something useful at TRACE verbosity. |
| */ |
| if ( uuid_compare( prevmfiledumpid, |
| grhdrp->gh_dumpid) != 0) { |
| |
| char string_uuid[UUID_STR_LEN + 1]; |
| |
| mlog_lock( ); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "dump session label: \"%s\"\n", |
| grhdrp->gh_dumplabel ); |
| |
| uuid_unparse( grhdrp->gh_dumpid, string_uuid); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "dump session id: %s\n", |
| string_uuid ); |
| mlog( MLOG_TRACE | MLOG_NOLOCK | MLOG_MEDIA, |
| "stream %u, object %u, file %u\n", |
| drhdrp->dh_driveix, |
| mrhdrp->mh_mediaix, |
| mrhdrp->mh_dumpmediafileix ); |
| mlog_unlock( ); |
| uuid_copy(prevmfiledumpid, grhdrp->gh_dumpid); |
| } |
| |
| /* if this media file is not part of the desired dump session, |
| * and a preceeding media file on this object was part of the |
| * dump, we know we have hit the end of the stream. tell the |
| * persistent inventory. |
| */ |
| if ( ! partofdumppr |
| && |
| Mediap->M_fsfixvalpr |
| && |
| Mediap->M_lmfix > Mediap->M_fsfix ) { |
| pi_hitnextdump( Mediap->M_fssix, |
| Mediap->M_fsoix, |
| Mediap->M_lmfix ); |
| } |
| |
| /* if this media file is not part of the desired dump session, |
| * we are doing non-dir, and the preceeding media file on this |
| * object was part of the dump, we know we have hit the end of |
| * the stream. check if we are done. |
| */ |
| if ( ! partofdumppr |
| && |
| purp == PURP_NONDIR |
| && |
| Mediap->M_fsfixvalpr |
| && |
| Mediap->M_lmfix > Mediap->M_fsfix ) { |
| if ( pi_alldone( )) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| return RV_NOMORE; |
| } |
| } |
| |
| /* if this media file is not part of the desired dump session, |
| * and preceeding media files on this object were, decide if |
| * we need to rewind and look at the beginning of the object. |
| */ |
| if ( ! partofdumppr |
| && |
| Mediap->M_fsfixvalpr |
| && |
| Mediap->M_fmfix <= Mediap->M_fsfix ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| if ( dcaps & DRIVE_CAP_REWIND ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )( drivep ); |
| continue; |
| } else { |
| goto newmedia; |
| } |
| } |
| |
| /* if this media file is not part of the desired dump session, |
| * and the above conditions were not met, then keep looking |
| */ |
| if ( ! partofdumppr ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| continue; |
| } |
| |
| /* record the index within this media object of the first |
| * media file in the dump stream |
| */ |
| if ( ! Mediap->M_fsfixvalpr ) { |
| Mediap->M_fsfix = |
| mrhdrp->mh_mediafileix |
| - |
| mrhdrp->mh_dumpmediafileix; |
| Mediap->M_fsoix = mrhdrp->mh_mediaix; |
| Mediap->M_fssix = drhdrp->dh_driveix; |
| Mediap->M_fsfixvalpr = BOOL_TRUE; |
| } |
| |
| /* this media file is part of the dump. add it to the |
| * persistent inventory and get a file handle. |
| */ |
| fileh = pi_addfile( Mediap, |
| grhdrp, |
| drhdrp, |
| mrhdrp, |
| scrhdrp, |
| drivep ); |
| |
| if ( fileh == DH_NULL ) { |
| return RV_CORE; |
| } |
| |
| pi_lock( ); |
| objh = DH2F( fileh )->f_parh; |
| DH2O( objh )->o_indriveix = drivep->d_index; |
| DH2O( objh )->o_indrivepr = BOOL_TRUE; |
| pi_unlock( ); |
| |
| pi_note_underhead( objh, fileh ); |
| |
| /* if purp is nondir, we may be done. |
| */ |
| if ( purp == PURP_NONDIR && pi_alldone( )) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| return RV_NOMORE; |
| } |
| |
| /* check for a wraparound |
| */ |
| if ( Mediap->M_flmfixvalpr ) { |
| if ( Mediap->M_fsfixvalpr |
| && |
| Mediap->M_fmfix <= Mediap->M_fsfix |
| && |
| Mediap->M_lmfix < Mediap->M_fmfix ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| goto newmedia; |
| } |
| if ( Mediap->M_pmfixvalpr |
| && |
| Mediap->M_pmfix < Mediap->M_fmfix |
| && |
| Mediap->M_lmfix > Mediap->M_fmfix ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| goto newmedia; |
| } |
| } |
| |
| /* if this media file is an inventory or a terminator, |
| * we have hit the end of the stream. don't tell the persistent |
| * inventory; it already knows because of a pi_addfile. |
| * decide if any preceeding media files are useful and if so |
| * go get them. otherwise get another media object. |
| */ |
| if ( MEDIA_TERMINATOR_CHK( mrhdrp ) |
| || |
| scrhdrp->cih_mediafiletype |
| == |
| CIH_MEDIAFILETYPE_INVENTORY ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| if ( pi_know_no_more_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix )) { |
| goto newmedia; |
| } |
| if ( Mediap->M_fmfix > Mediap->M_fsfix |
| && |
| ( dcaps & DRIVE_CAP_REWIND )) { |
| pi_note_underhead( objh, DH_NULL ); |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )( drivep ); |
| continue; |
| } |
| goto newmedia; |
| } |
| |
| /* if the purpose is dir, but this media file is not positioned |
| * at the hdr or has already been tried, get another one. |
| * use the persistent inventory to do this intelligently. |
| */ |
| if ( purp == PURP_DIR |
| && |
| ( Mediap->M_pos != POS_ATHDR |
| || |
| DH2F( fileh )->f_dirtriedpr )) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| if ( pi_know_no_more_beyond_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix, |
| mrhdrp->mh_dumpmediafileix )) { |
| if ( pi_know_no_more_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix )) { |
| goto newmedia; |
| } |
| if ( Mediap->M_fmfix > Mediap->M_fsfix |
| && |
| ( dcaps & DRIVE_CAP_REWIND )) { |
| pi_note_underhead( objh, DH_NULL ); |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )(drivep); |
| continue; |
| } |
| goto newmedia; |
| } else { |
| continue; |
| } |
| } |
| |
| /* if the purpose is dir, give it to the caller |
| */ |
| if ( purp == PURP_DIR ) { |
| ASSERT( Mediap->M_pos == POS_ATHDR ); |
| if ( filehp ) { |
| ASSERT( fileh != DH_NULL ); |
| *filehp = fileh; |
| } |
| return RV_OK; |
| } |
| |
| /* if we made it this far, the purpose is NONDIR and this |
| * is a valid media file from the desired dump session. |
| */ |
| |
| /* see if this media file contains any inodes not yet restored |
| */ |
| ASSERT( fileh != DH_NULL ); |
| pi_lock( ); |
| ASSERT( DH2F( fileh )->f_valpr ); |
| begino = DH2F( fileh )->f_curegrp.eg_ino; |
| endino = pi_scanfileendino( fileh ); |
| hassomepr = inomap_rst_needed( begino, endino ); |
| |
| /* if we have already given up on this media file or |
| * it doesn't contains anything not yet restored, |
| * or it can be skipped, move on. force the done flag on, |
| * so we don't check it again. |
| */ |
| if ( DH2F( fileh )->f_nondirdonepr |
| || |
| DH2F( fileh )->f_nondirskippr |
| || |
| ! hassomepr ) { |
| if ( ! DH2F( fileh )->f_nondirskippr ) { |
| DH2F( fileh )->f_nondirdonepr = BOOL_TRUE; |
| } |
| pi_unlock( ); |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| if ( pi_know_no_more_beyond_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix, |
| mrhdrp->mh_dumpmediafileix )) { |
| if ( pi_know_no_more_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix )) { |
| goto newmedia; |
| } |
| if ( Mediap->M_fmfix > Mediap->M_fsfix |
| && |
| ( dcaps & DRIVE_CAP_REWIND )) { |
| pi_note_underhead( objh, DH_NULL ); |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )(drivep); |
| continue; |
| } |
| goto newmedia; |
| } else { |
| continue; |
| } |
| } |
| |
| /* so the purpose is NONDIR and we like this media file. |
| * be sure we are positioned at the beginning of the |
| * non-dir filehdr not yet restored, and supply to caller. |
| */ |
| |
| /* need to position just after the first |
| * non-dir filehdr_t not yet restored. |
| * may be a problem if we are currently positioned |
| * in the middle of the dir dump and have no |
| * checkpoint to seek to. if at beginning |
| * and no check point, can still get there |
| * by doing dummy read of dirdump. |
| */ |
| ASSERT( fileh != DH_NULL ); |
| ASSERT( DH2F( fileh )->f_valpr ); |
| resumepr = ( ( DH2F( fileh )->f_firstegrp.eg_ino |
| != |
| DH2F( fileh )->f_curegrp.eg_ino ) |
| || |
| ( DH2F( fileh )->f_firstegrp.eg_off |
| != |
| DH2F( fileh )->f_curegrp.eg_off )); |
| chkpnt = DH2F( fileh )->f_curmark; |
| pi_unlock( ); |
| fhcs = ( scrhdrp->cih_dumpattr |
| & |
| CIH_DUMPATTR_FILEHDR_CHECKSUM ) |
| ? |
| BOOL_TRUE |
| : |
| BOOL_FALSE; |
| canseeknextpr = dcaps & DRIVE_CAP_NEXTMARK; |
| switch( Mediap->M_pos ) { |
| case POS_ATHDR: |
| case POS_INDIR: |
| if ( resumepr ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "seeking past portion of media file " |
| "already restored\n") ); |
| rval = ( * dop->do_seek_mark )( drivep, |
| &chkpnt ); |
| if ( ! rval ) { |
| rv_t rv; |
| rv = read_filehdr( drivep, |
| fhdrp, |
| fhcs ); |
| if ( rv != RV_OK ) { |
| rval = 1; |
| } else { |
| mlog( MLOG_TRACE | MLOG_MEDIA, |
| "positioned at unrestored " |
| "portion of media file\n" ); |
| } |
| } |
| } else if ( canseeknextpr ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "seeking past media file " |
| "directory dump\n") ); |
| rval = ( * dop->do_next_mark )( drivep); |
| if ( ! rval ) { |
| rv_t rv; |
| rv = read_filehdr( drivep, |
| fhdrp, |
| fhcs ); |
| if ( rv != RV_OK ) { |
| rval = 1; |
| } else { |
| mlog( MLOG_TRACE | MLOG_MEDIA, |
| "positioned at " |
| "media file " |
| "non-directory dump\n" ); |
| } |
| } |
| } else if ( Mediap->M_pos == POS_ATHDR ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "seeking past media file " |
| "directory dump\n") ); |
| rv = eatdirdump( drivep, |
| fileh, |
| scrhdrp, |
| fhdrp ); |
| if ( rv != RV_OK ) { |
| rval = 1; |
| } else { |
| mlog( MLOG_TRACE | MLOG_MEDIA, |
| "positioned at " |
| "media file " |
| "non-directory dump\n" ); |
| } |
| } else { |
| rval = 1; |
| } |
| break; |
| case POS_ATNONDIR: |
| rval = 0; |
| break; |
| default: |
| ASSERT( 0 ); |
| rval = 1; |
| break; |
| } |
| |
| /* if error encountered during fine positioning, |
| * mark file so we won't try it again |
| */ |
| if ( rval ) { |
| DH2F( fileh )->f_nondirdonepr = BOOL_TRUE; |
| } else { |
| Mediap->M_pos = POS_ATNONDIR; |
| } |
| |
| /* if no error during fine positioning, return. |
| */ |
| if ( ! rval ) { |
| if ( filehp ) { |
| ASSERT( fileh != DH_NULL ); |
| *filehp = fileh; |
| } |
| return RV_OK; |
| } |
| |
| /* an error occurred during fine positioning. any other useful |
| * media files on this object? if so, continue; if not, get |
| * more media. |
| */ |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| fileh = DH_NULL; |
| ASSERT( purp == PURP_NONDIR ); |
| if ( pi_know_no_more_beyond_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix, |
| mrhdrp->mh_dumpmediafileix )) { |
| if ( pi_know_no_more_on_object( purp, |
| Mediap->M_fssix, |
| Mediap->M_fsoix )) { |
| goto newmedia; |
| } |
| if ( Mediap->M_fmfix > Mediap->M_fsfix |
| && |
| ( dcaps & DRIVE_CAP_REWIND )) { |
| pi_note_underhead( objh, DH_NULL ); |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "rewinding\n") ); |
| ( * drivep->d_opsp->do_rewind )(drivep); |
| continue; |
| } |
| goto newmedia; |
| } else { |
| continue; |
| } |
| /* fall through */ |
| |
| newmedia: |
| /* invalidate prev id, so we log a TRACE msg for first |
| * media file seen on new media |
| */ |
| uuid_clear(prevmfiledumpid); |
| |
| /* if we are searching and some other thread completed |
| * the search, don't pop the media unless it is useless |
| */ |
| if ( purp == PURP_SEARCH |
| && |
| Mediap->M_pos != POS_USELESS |
| && |
| Mediap->M_pos != POS_BLANK |
| && |
| donesyncp |
| && |
| *donesyncp == SYNC_DONE ) { |
| return RV_DONE; |
| } |
| |
| /* if media not removable, just return |
| */ |
| if ( ( * dop->do_get_device_class )( drivep ) |
| == |
| DEVICE_NONREMOVABLE ) |
| { |
| /* if no error has already been detected then don't log |
| a failure */ |
| if (mlog_get_hint() == RV_NONE) |
| mlog_exit_hint(RV_OK); |
| return RV_QUIT; |
| } |
| |
| /* check for an interrupt |
| */ |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| |
| /* check if we are done. |
| */ |
| switch( purp ) { |
| case PURP_SEARCH: |
| knownholespr = BOOL_TRUE; |
| maybeholespr = BOOL_FALSE; |
| bagp = 0; |
| break; |
| case PURP_DIR: |
| knownholespr = BOOL_FALSE; |
| maybeholespr = BOOL_FALSE; |
| bagp = pi_neededobjs_dir_alloc( &knownholespr, |
| &maybeholespr ); |
| break; |
| case PURP_NONDIR: |
| knownholespr = BOOL_FALSE; |
| maybeholespr = BOOL_FALSE; |
| bagp = pi_neededobjs_nondir_alloc( &knownholespr, |
| &maybeholespr, |
| BOOL_FALSE, |
| BOOL_FALSE ); |
| break; |
| default: |
| ASSERT( 0 ); |
| } |
| |
| if ( ! bagp && ! knownholespr && ! maybeholespr ) { |
| /* if PURP_DIR, this may be a problem |
| */ |
| if ( purp == PURP_NONDIR ) { |
| return RV_NOMORE; |
| } |
| } |
| |
| /* eject media if drive not already empty |
| */ |
| if ( ! emptypr ) { |
| intgen_t dcaps = drivep->d_capabilities; |
| if ( purp == PURP_SEARCH ) { |
| if ( Mediap->M_pos == POS_USELESS ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "media object not useful\n") ); |
| } else if ( Mediap->M_pos == POS_BLANK ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "media object empty\n") ); |
| } else { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "all media files examined, " |
| "none selected for restoral\n") ); |
| } |
| } |
| if ( dcaps & DRIVE_CAP_EJECT ) { |
| ( * dop->do_eject_media )( drivep ); |
| } |
| } |
| |
| /* tell the persistent inventory this drive is now empty |
| */ |
| pi_driveempty( drivep->d_index ); |
| |
| /* invalidate all positional descriptors |
| */ |
| Mediap->M_pos = POS_UNKN; |
| Mediap->M_flmfixvalpr = BOOL_FALSE; |
| Mediap->M_pmfixvalpr = BOOL_FALSE; |
| Mediap->M_fsfixvalpr = BOOL_FALSE; |
| fileh = DH_NULL; |
| |
| |
| /* ask for a media change: supply a list of media objects |
| * which may contain useful media files |
| */ |
| if ( dlog_allowed( )) { |
| /* If an alert program has been specified , run it. |
| */ |
| if (media_change_alert_program != NULL) |
| system(media_change_alert_program); |
| |
| if ( drivecnt > 1 && ! stdoutpiped ) { |
| ix_t thrdix = drivep->d_index; |
| if ( bagp ) { |
| pi_neededobjs_free( bagp ); |
| bagp = 0; |
| } |
| ASSERT( sistr ); |
| mlog( MLOG_NORMAL | MLOG_NOTE | MLOG_MEDIA, _( |
| "please change media: " |
| "type %s to confirm media change\n"), |
| sistr ); |
| set_mcflag( thrdix ); |
| while ( mcflag[ thrdix ] ) { |
| sleep( 2 ); |
| if ( cldmgr_stop_requested( )) { |
| clr_mcflag( thrdix ); |
| return RV_INTR; |
| } |
| if ( purp == PURP_NONDIR |
| && |
| pi_alldone( )) { |
| clr_mcflag( thrdix ); |
| return RV_NOMORE; |
| } |
| } |
| ok = BOOL_TRUE; |
| } else { |
| ok = Media_prompt_change( drivep, |
| purp, |
| bagp, |
| knownholespr, |
| maybeholespr ); |
| } |
| } else { |
| ok = BOOL_FALSE; |
| } |
| if ( bagp ) { |
| pi_neededobjs_free( bagp ); |
| bagp = 0; |
| } |
| if ( cldmgr_stop_requested( )) { |
| return RV_INTR; |
| } |
| if ( ! ok ) { |
| return RV_QUIT; |
| } |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* figures out and calls if needed do_end_read( ). |
| */ |
| static void |
| Media_end( Media_t *Mediap ) |
| { |
| drive_t *drivep = Mediap->M_drivep; |
| drive_ops_t *dop = drivep->d_opsp; |
| |
| mlog( MLOG_DEBUG | MLOG_MEDIA, |
| "Media_end: pos==%d\n", |
| Mediap->M_pos ); |
| |
| if ( Mediap->M_pos == POS_ATHDR |
| || |
| Mediap->M_pos == POS_INDIR |
| || |
| Mediap->M_pos == POS_ATNONDIR ) { |
| ( * dop->do_end_read )( drivep ); |
| Mediap->M_pos = POS_UNKN; |
| } |
| } |
| |
| /* Persistent inventory operators *******************************************/ |
| |
| /* the persistent inventory is an mmap()ed file containing a hierarchical |
| * representation of all the media files generated by a dump session. it |
| * is useful for asking questions about how much of the dump remains to |
| * be restored. |
| * |
| * the top of the hierarchy is a linked list of streams. each of these contains |
| * a linked list of objects, which in turn each contain a linked list of files. |
| * each stream descriptor also has a flag indicating the last object in the |
| * stream is represented. each file descriptor contains a flag indicating the |
| * last file in that object is represented. the object descriptor also contains |
| * two indices; one indicating where in the dump stream the first media file |
| * in that object lies, and where in the object the first media file pertaining |
| * to the dump stream lies. each file descriptor describes the first extent |
| * group in that media file, and the next extent group in the media file to be |
| * restored. the file descriptor also contains flags indicating if an attempt |
| * (successful or unsuccessful) to use the file for a directory dump, and |
| * a similar flag for non-dir. |
| * |
| * all media files generated during the dump are represented, including |
| * terminators and inventories. |
| */ |
| static void |
| pi_lock( void ) |
| { |
| qlock_lock( tranp->t_pilockh ); |
| } |
| |
| static void |
| pi_unlock( void ) |
| { |
| qlock_unlock( tranp->t_pilockh ); |
| } |
| |
| /* sets check point in media file descriptor |
| */ |
| static void |
| pi_checkpoint( dh_t fileh, drive_mark_t *drivemarkp, xfs_ino_t ino, off64_t off ) |
| { |
| pi_lock( ); |
| DH2F( fileh )->f_curmark = *drivemarkp; |
| DH2F( fileh )->f_curegrp.eg_ino = ino; |
| DH2F( fileh )->f_curegrp.eg_off = off; |
| pi_unlock( ); |
| } |
| |
| /* lock must be held by caller |
| */ |
| static bool_t |
| pi_allocdesc( dh_t *deschp ) |
| { |
| dh_t desch; |
| |
| if ( persp->s.descfreeh == DH_NULL ) { |
| ix_t stpgcnt = persp->a.stpgcnt; |
| ix_t olddescpgcnt = persp->s.descpgcnt; |
| ix_t newdescpgcnt = olddescpgcnt + DAU; |
| ix_t descppg = pgsz / PERS_DESCSZ; |
| ix_t descix; |
| /* REFERENCED */ |
| intgen_t rval; |
| |
| /* first unmap if any existing descriptors |
| */ |
| if ( descp ) { |
| ASSERT( olddescpgcnt > 0 ); |
| rval = munmap( ( void * )descp, |
| olddescpgcnt * pgsz ); |
| ASSERT( ! rval ); |
| descp = 0; |
| } else { |
| ASSERT( olddescpgcnt == 0 ); |
| } |
| |
| /* remap with DAU more pages of descriptors |
| */ |
| ASSERT( stpgcnt <= ( ix_t )INTGENMAX ); |
| ASSERT( newdescpgcnt > 0 ); |
| descp = ( pers_desc_t * ) mmap_autogrow( newdescpgcnt * pgsz, |
| tranp->t_persfd, |
| ( off_t )perssz |
| + |
| ( off_t )( stpgcnt * pgsz )); |
| if ( descp == ( pers_desc_t * )( -1 )) { |
| pi_unlock( ); |
| mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_MEDIA, _( |
| "could not remap persistent state file " |
| "inv %s: %d (%s)\n"), |
| perspath, |
| errno, |
| strerror( errno )); |
| pi_lock( ); |
| descp = 0; |
| return BOOL_FALSE; |
| } |
| persp->s.descfreeh = ( dh_t )( olddescpgcnt * pgsz + 1 ); |
| for ( descix = 0, desch = persp->s.descfreeh |
| ; |
| descix < ( DAU * descppg ) - 1 |
| ; |
| descix++, desch += PERS_DESCSZ ) { |
| DH2D( desch )->d_nexth = desch + PERS_DESCSZ; |
| } |
| DH2D( desch )->d_nexth = DH_NULL; |
| persp->s.descpgcnt = newdescpgcnt; |
| } |
| |
| desch = persp->s.descfreeh; |
| persp->s.descfreeh = DH2D( desch )->d_nexth; |
| memset( ( void * )DH2D( desch ), 0, sizeof( pers_desc_t )); |
| ASSERT( desch != DH_NULL ); |
| *deschp = desch; |
| return BOOL_TRUE; |
| } |
| |
| /* inserts the indexed file into the given stream. ensures that all |
| * previous files are represented as well. if dmfix is not valid, only |
| * adds objects. |
| */ |
| static dh_t |
| pi_insertfile( ix_t drivecnt, |
| ix_t driveix, |
| ix_t mediaix, |
| bool_t idlabvalpr, |
| uuid_t *mediaidp, |
| label_t medialabel, |
| bool_t previdlabvalpr, |
| uuid_t *prevmediaidp, |
| label_t prevmedialabel, |
| bool_t mfixvalpr, |
| ix_t mediafileix, |
| bool_t dmfixvalpr, |
| ix_t dumpmediafileix, |
| bool_t dfixvalpr, |
| ix_t dumpfileix, |
| bool_t egrpvalpr, |
| xfs_ino_t startino, |
| off64_t startoffset, |
| intgen_t flags, |
| bool_t fileszvalpr, |
| off64_t filesz ) |
| { |
| ix_t strmix; |
| dh_t strmh; |
| ix_t objix; |
| dh_t objh; |
| dh_t prevobjh; |
| ix_t fileix; |
| dh_t fileh; |
| dh_t prevfileh; |
| bool_t ok; |
| |
| pi_lock( ); |
| |
| /* first alloc stream descriptors if needed |
| */ |
| if ( persp->s.strmheadh == DH_NULL ) { |
| for ( strmix = 0 ; strmix < drivecnt ; strmix++ ) { |
| ok = pi_allocdesc( &strmh ); |
| if ( ! ok ) { |
| pi_unlock( ); |
| return DH_NULL; |
| } |
| DH2S( strmh )->s_nexth = persp->s.strmheadh; |
| persp->s.strmheadh = strmh; |
| } |
| } |
| |
| /* get handle to this stream |
| */ |
| for ( strmix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmix < driveix |
| ; |
| strmix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| ASSERT( strmh != DH_NULL ); |
| |
| /* get handle to this object by walking/constructing this stream's |
| * object list, up to the desired object |
| */ |
| objh = prevobjh = DH_NULL; |
| for ( objix = 0 ; objix <= mediaix ; objix++ ) { |
| prevobjh = objh; |
| if ( objix == 0 ) { |
| objh = DH2S( strmh )->s_cldh; |
| } else { |
| objh = DH2O( prevobjh )->o_nexth; |
| } |
| if ( objh == DH_NULL ) { |
| ok = pi_allocdesc( &objh ); |
| if ( ! ok ) { |
| pi_unlock( ); |
| return DH_NULL; |
| } |
| DH2O( objh )->o_parh = strmh; |
| if ( objix == 0 ) { |
| DH2S( strmh )->s_cldh = objh; |
| } else { |
| DH2O( prevobjh )->o_nexth = objh; |
| } |
| } |
| } |
| |
| /* update the object fields if not yet valid |
| */ |
| if ( idlabvalpr |
| && |
| ! DH2O( objh )->o_idlabvalpr ) { |
| uuid_copy(DH2O( objh )->o_id, *mediaidp); |
| strncpy( DH2O( objh )->o_lab, |
| medialabel, |
| sizeof( DH2O( objh )->o_lab )); |
| DH2O( objh )->o_idlabvalpr = BOOL_TRUE; |
| } |
| if ( mfixvalpr |
| && |
| dmfixvalpr |
| && |
| ! DH2O( objh )->o_fmfmixvalpr ) { |
| DH2O( objh )->o_fmfmix = mediafileix - dumpmediafileix; |
| DH2O( objh )->o_fmfmixvalpr = BOOL_TRUE; |
| } |
| if ( dfixvalpr |
| && |
| dmfixvalpr |
| && |
| ! DH2O( objh )->o_fmfsixvalpr ) { |
| DH2O( objh )->o_fmfsix = dumpfileix - dumpmediafileix; |
| DH2O( objh )->o_fmfsixvalpr = BOOL_TRUE; |
| } |
| |
| /* record the previous object's id and label if not yet valid |
| */ |
| if ( prevobjh != DH_NULL |
| && |
| previdlabvalpr |
| && |
| ! DH2O( prevobjh )->o_idlabvalpr ) { |
| uuid_copy(DH2O( prevobjh )->o_id, *prevmediaidp); |
| strncpy( DH2O( prevobjh )->o_lab, |
| prevmedialabel, |
| sizeof( DH2O( prevobjh )->o_lab )); |
| DH2O( prevobjh )->o_idlabvalpr = BOOL_TRUE; |
| } |
| |
| /* if the dump file and dump media file indices are valid, |
| * and the previous object has at least one media file with its |
| * dump file index valid, can infer the index of the last media |
| * file on the previous dump object. |
| */ |
| if ( DH2O( objh )->o_fmfsixvalpr |
| && |
| prevobjh != DH_NULL |
| && |
| DH2O( prevobjh )->o_fmfsixvalpr |
| && |
| ! DH2O( prevobjh )->o_lmfknwnpr ) { |
| size_t prevmfcnt; |
| ASSERT( DH2O( objh )->o_fmfsix > DH2O( prevobjh )->o_fmfsix ); |
| prevmfcnt = DH2O( objh )->o_fmfsix - DH2O( prevobjh )->o_fmfsix; |
| pi_unlock( ); |
| ASSERT( mediaix > 0 ); |
| ( void )pi_insertfile( drivecnt, |
| driveix, |
| mediaix - 1, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| BOOL_TRUE, |
| prevmfcnt - 1, |
| BOOL_TRUE, |
| DH2O( objh )->o_fmfsix - 1, |
| 0, |
| 0, |
| 0, |
| 0, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| pi_seeobjstrmend( strmix, mediaix - 1 ); |
| pi_lock( ); |
| } |
| |
| /* if don't know dump stream media file index, can't add any media files |
| */ |
| if ( ! dmfixvalpr ) { |
| pi_unlock( ); |
| pi_show( " after pi_insertfile no media file ix" ); |
| return DH_NULL; |
| } |
| |
| /* get handle to this file by walking/constructing this object's |
| * file list, up to the desired file |
| */ |
| fileh = DH_NULL; |
| for ( fileix = 0 ; fileix <= dumpmediafileix ; fileix++ ) { |
| prevfileh = fileh; |
| if ( fileix == 0 ) { |
| fileh = DH2O( objh )->o_cldh; |
| } else { |
| fileh = DH2F( prevfileh )->f_nexth; |
| } |
| if ( fileh == DH_NULL ) { |
| ok = pi_allocdesc( &fileh ); |
| if ( ! ok ) { |
| pi_unlock( ); |
| return DH_NULL; |
| } |
| DH2F( fileh )->f_parh = objh; |
| if ( fileix == 0 ) { |
| DH2O( objh )->o_cldh = fileh; |
| } else { |
| DH2F( prevfileh )->f_nexth = fileh; |
| } |
| } |
| } |
| |
| /* update the media file fields not yet valid |
| */ |
| if ( egrpvalpr && ! DH2F( fileh )->f_valpr ) { |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_INV )); |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_TERM )); |
| DH2F( fileh )->f_firstegrp.eg_ino = startino; |
| DH2F( fileh )->f_firstegrp.eg_off = startoffset; |
| DH2F( fileh )->f_curegrp = DH2F( fileh )->f_firstegrp; |
| DH2F( fileh )->f_valpr = BOOL_TRUE; |
| } |
| |
| /* set flags |
| */ |
| DH2F( fileh )->f_flags = flags; |
| |
| /* if we know the file size, |
| * update it |
| */ |
| if ( fileszvalpr ) { |
| DH2F( fileh )->f_sz = filesz; |
| DH2F( fileh )->f_szvalpr = BOOL_TRUE; |
| } |
| |
| pi_unlock( ); |
| pi_show( " after pi_insertfile" ); |
| return fileh; |
| } |
| |
| /* add pers file desc if not already present. will automatically |
| * update/alloc pers obj and strm descriptors. If given a session inventory, |
| * attempt to incorporate into pi. also, initializes completion stats. |
| */ |
| /* ARGSUSED */ |
| static dh_t |
| pi_addfile( Media_t *Mediap, |
| global_hdr_t *grhdrp, |
| drive_hdr_t *drhdrp, |
| media_hdr_t *mrhdrp, |
| content_inode_hdr_t *scrhdrp, |
| drive_t * drivep ) |
| { |
| dh_t fileh; |
| |
| if ( ! persp->s.stat_valpr ) { |
| persp->s.stat_inocnt = scrhdrp->cih_inomap_nondircnt; |
| persp->s.stat_inodone = 0; |
| ASSERT( scrhdrp->cih_inomap_datasz <= OFF64MAX ); |
| persp->s.stat_datacnt = ( off64_t )scrhdrp->cih_inomap_datasz; |
| persp->s.stat_datadone = 0; |
| persp->s.stat_valpr = BOOL_TRUE; |
| } |
| |
| /* if we see a terminator, we know we have seen the end of |
| * a stream. |
| */ |
| if ( MEDIA_TERMINATOR_CHK( mrhdrp )) { |
| fileh = pi_insertfile( drhdrp->dh_drivecnt, |
| drhdrp->dh_driveix, |
| mrhdrp->mh_mediaix, |
| BOOL_TRUE, |
| &mrhdrp->mh_mediaid, |
| mrhdrp->mh_medialabel, |
| BOOL_TRUE, |
| &mrhdrp->mh_prevmediaid, |
| mrhdrp->mh_prevmedialabel, |
| BOOL_TRUE, |
| mrhdrp->mh_mediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpmediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpfileix, |
| BOOL_FALSE, |
| ( xfs_ino_t )0, |
| ( off64_t )0, |
| PF_TERM, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| if ( fileh == DH_NULL ) { |
| return DH_NULL; |
| } |
| pi_seestrmend( drhdrp->dh_driveix ); |
| pi_seeobjstrmend( drhdrp->dh_driveix, mrhdrp->mh_mediaix ); |
| return fileh; |
| } |
| |
| /* data file |
| */ |
| if ( scrhdrp->cih_mediafiletype == CIH_MEDIAFILETYPE_DATA ) { |
| /* tell the inventory about this media file |
| */ |
| fileh = pi_insertfile( drhdrp->dh_drivecnt, |
| drhdrp->dh_driveix, |
| mrhdrp->mh_mediaix, |
| BOOL_TRUE, |
| &mrhdrp->mh_mediaid, |
| mrhdrp->mh_medialabel, |
| BOOL_TRUE, |
| &mrhdrp->mh_prevmediaid, |
| mrhdrp->mh_prevmedialabel, |
| BOOL_TRUE, |
| mrhdrp->mh_mediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpmediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpfileix, |
| BOOL_TRUE, |
| scrhdrp->cih_startpt.sp_ino, |
| scrhdrp->cih_startpt.sp_offset, |
| 0, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| if ( fileh == DH_NULL ) { |
| return DH_NULL; |
| } |
| ASSERT( drhdrp->dh_drivecnt > 0 ); |
| if ( drhdrp->dh_driveix < drhdrp->dh_drivecnt - 1 ) { |
| /* if this is not in the last stream, we know |
| * there is at least one other media file in |
| * the following stream, and we know its start pt |
| */ |
| ( void )pi_insertfile( drhdrp->dh_drivecnt, |
| drhdrp->dh_driveix + 1, |
| 0, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| BOOL_TRUE, |
| 0, |
| BOOL_FALSE, |
| 0, |
| BOOL_TRUE, |
| scrhdrp->cih_endpt.sp_ino, |
| scrhdrp->cih_endpt.sp_offset, |
| 0, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| } |
| if ( ! ( drivep->d_capabilities & DRIVE_CAP_FILES )) { |
| /* if drive does not support multiple files, |
| * we know this is end of object and stream |
| */ |
| pi_seestrmend( drhdrp->dh_driveix ); |
| pi_seeobjstrmend( drhdrp->dh_driveix, mrhdrp->mh_mediaix ); |
| } |
| |
| return fileh; |
| } |
| |
| /* inventory file |
| */ |
| if ( scrhdrp->cih_mediafiletype == CIH_MEDIAFILETYPE_INVENTORY ) { |
| fileh = pi_insertfile( drhdrp->dh_drivecnt, |
| drhdrp->dh_driveix, |
| mrhdrp->mh_mediaix, |
| BOOL_TRUE, |
| &mrhdrp->mh_mediaid, |
| mrhdrp->mh_medialabel, |
| BOOL_TRUE, |
| &mrhdrp->mh_prevmediaid, |
| mrhdrp->mh_prevmedialabel, |
| BOOL_TRUE, |
| mrhdrp->mh_mediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpmediafileix, |
| BOOL_TRUE, |
| mrhdrp->mh_dumpfileix, |
| BOOL_FALSE, |
| ( xfs_ino_t )0, |
| ( off64_t )0, |
| PF_INV, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| if ( fileh == DH_NULL ) { |
| return DH_NULL; |
| } |
| pi_seestrmend( drhdrp->dh_driveix ); |
| pi_seeobjstrmend( drhdrp->dh_driveix, |
| mrhdrp->mh_mediaix ); |
| if ( drhdrp->dh_driveix < drhdrp->dh_drivecnt - 1 ) { |
| ( void )pi_insertfile( drhdrp->dh_drivecnt, |
| drhdrp->dh_driveix + 1, |
| 0, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| 0, |
| BOOL_FALSE, |
| 0, |
| BOOL_TRUE, |
| 0, |
| BOOL_FALSE, |
| 0, |
| BOOL_TRUE, |
| scrhdrp->cih_endpt.sp_ino, |
| scrhdrp->cih_endpt.sp_offset, |
| 0, |
| BOOL_FALSE, |
| ( off64_t )0 ); |
| } |
| if ( ! persp->s.fullinvpr |
| && |
| Mediap->M_pos == POS_ATHDR ) { |
| size_t bufszincr; |
| size_t bufsz; |
| size_t buflen; |
| char *bufp; |
| inv_session_t *sessp; |
| invt_sessinfo_t sessinfo; |
| bool_t ok; |
| bool_t donepr; |
| |
| /* read inventory into buffer |
| */ |
| bufszincr = IBPGINCR * PGSZ; |
| /* use 4096, no need to be correlated |
| * with system page size |
| */ |
| bufsz = bufszincr; |
| buflen = 0; |
| bufp = ( char * )malloc( bufsz ); |
| |
| /* need to read until we hit EOF/EOD. that's the only |
| * way to know how big the inventory is. mark the Media |
| * current media file as no longer at hdr. |
| */ |
| Mediap->M_pos = POS_ATNONDIR; |
| donepr = BOOL_FALSE; |
| while ( ! donepr ) { |
| intgen_t nread; |
| drive_ops_t *dop = drivep->d_opsp; |
| intgen_t rval = 0; |
| nread = read_buf( bufp + buflen, |
| bufszincr, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| switch( rval ) { |
| case 0: |
| ASSERT( nread == ( intgen_t )bufszincr ); |
| buflen += ( size_t )nread; |
| bufsz += bufszincr; |
| bufp = ( char * )realloc(( void * )bufp, |
| bufsz ); |
| ASSERT( bufp ); |
| continue; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| buflen += ( size_t )nread; |
| donepr = BOOL_TRUE; |
| break; |
| default: |
| free( ( void * )bufp ); |
| return fileh; |
| } |
| } |
| |
| /* ask inventory to convert buffer into session |
| * desc. |
| */ |
| sessp = 0; |
| if ( ! buflen ) { |
| ok = BOOL_FALSE; |
| } else { |
| /* extract the session information from the buffer */ |
| if ( stobj_unpack_sessinfo( bufp, buflen, &sessinfo )<0 ) { |
| ok = BOOL_FALSE; |
| } else { |
| stobj_convert_sessinfo(&sessp, &sessinfo); |
| ok = BOOL_TRUE; |
| } |
| } |
| if ( ! ok || ! sessp ) { |
| mlog( MLOG_DEBUG | MLOG_WARNING | MLOG_MEDIA, _( |
| "on-media session " |
| "inventory corrupt\n") ); |
| } else { |
| /* if root, update online inventory. |
| */ |
| if ( ! geteuid( ) |
| && |
| ! tranp->t_noinvupdatepr ) { |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, _( |
| "incorporating on-media session " |
| "inventory into online " |
| "inventory\n") ); |
| inv_put_sessioninfo( &sessinfo ); |
| } |
| |
| /* convert into pi format |
| */ |
| mlog( MLOG_VERBOSE | MLOG_MEDIA, |
| "using on-media session inventory\n" ); |
| persp->s.fullinvpr = pi_transcribe( sessp ); |
| inv_free_session( &sessp ); |
| } |
| free( ( void * )bufp ); |
| } |
| return fileh; |
| } |
| |
| ASSERT( 0 ); |
| return DH_NULL; |
| } |
| |
| /* translate a session inventory into a pi |
| */ |
| static bool_t |
| pi_transcribe( inv_session_t *sessp ) |
| { |
| ix_t strmcnt; |
| ix_t strmix; |
| |
| /* traverse inventory, transcribing into pers inv. |
| */ |
| strmcnt = ( size_t )sessp->s_nstreams; |
| for ( strmix = 0 ; strmix < strmcnt ; strmix++ ) { |
| inv_stream_t *strmp; |
| size_t fileix; |
| size_t filecnt; |
| uuid_t lastobjid; |
| label_t lastobjlabel; |
| ix_t mediaix; |
| size_t dumpmediafileix; |
| |
| strmp = &sessp->s_streams[ strmix ]; |
| filecnt = strmp->st_nmediafiles; |
| uuid_clear(lastobjid); |
| lastobjlabel[ 0 ] = 0; |
| mediaix = 0; |
| dumpmediafileix = 0; |
| |
| /* insert all media files from this stream. note that |
| * the media object representation is inverted |
| */ |
| for ( fileix = 0 ; fileix < filecnt ; fileix++ ) { |
| inv_mediafile_t *filep; |
| bool_t fileszvalpr; |
| |
| filep = &strmp->st_mediafiles[ fileix ]; |
| if ( uuid_compare( filep->m_moid, |
| lastobjid ) != 0) { |
| dumpmediafileix = 0; |
| if ( fileix != 0 ) { |
| pi_seeobjstrmend( strmix, mediaix ); |
| mediaix++; |
| } |
| } |
| |
| fileszvalpr = BOOL_TRUE; |
| |
| ( void )pi_insertfile( strmcnt, |
| strmix, |
| mediaix, |
| BOOL_TRUE, |
| &filep->m_moid, |
| filep->m_label, |
| BOOL_TRUE, |
| &lastobjid, |
| lastobjlabel, |
| BOOL_TRUE, |
| filep->m_mfile_index, |
| BOOL_TRUE, |
| dumpmediafileix, |
| BOOL_TRUE, |
| fileix, |
| filep->m_isinvdump |
| ? |
| BOOL_FALSE |
| : |
| BOOL_TRUE, |
| filep->m_startino, |
| filep->m_startino_off, |
| filep->m_isinvdump |
| ? |
| PF_INV |
| : |
| 0, |
| fileszvalpr, |
| filep->m_size ); |
| uuid_copy(lastobjid, filep->m_moid); |
| strncpy( lastobjlabel, |
| filep->m_label, |
| sizeof( lastobjlabel )); |
| dumpmediafileix++; |
| } |
| pi_seestrmend( strmix ); |
| pi_seeobjstrmend( strmix, mediaix ); |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| /* clean up pers. inv: initially no media objects in drives. flags may |
| * be set from previously interrupted invocation. |
| */ |
| static void |
| pi_preclean( void ) |
| { |
| dh_t strmh; |
| dh_t objh; |
| dh_t fileh; |
| |
| for ( strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth ) { |
| for ( objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth ) { |
| DH2O( objh )->o_indrivepr = BOOL_FALSE; |
| for ( fileh = DH2O( objh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth ) { |
| DH2F( fileh )->f_underheadpr = BOOL_FALSE; |
| } |
| } |
| } |
| } |
| |
| /* tell pi no media objects are in this drive |
| */ |
| static void |
| pi_driveempty( ix_t driveix ) |
| { |
| dh_t strmh; |
| dh_t objh; |
| dh_t fileh; |
| |
| pi_lock( ); |
| |
| for ( strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth ) { |
| for ( objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth ) { |
| if ( DH2O( objh )->o_indrivepr |
| && |
| DH2O( objh )->o_indriveix == driveix ) { |
| DH2O( objh )->o_indrivepr = BOOL_FALSE; |
| for ( fileh = DH2O( objh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth ) { |
| DH2F( fileh )->f_underheadpr = |
| BOOL_FALSE; |
| } |
| } |
| } |
| } |
| |
| pi_unlock( ); |
| } |
| |
| /* tell pi this media object is in the drive |
| */ |
| static void |
| pi_note_indrive( ix_t driveix, uuid_t media_id ) |
| { |
| dh_t strmh; |
| dh_t objh; |
| |
| pi_lock( ); |
| |
| for ( strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth ) { |
| for ( objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth ) { |
| if ( DH2O( objh )->o_idlabvalpr |
| && |
| uuid_compare( DH2O( objh )->o_id, media_id) == 0) { |
| DH2O( objh )->o_indrivepr = BOOL_TRUE; |
| DH2O( objh )->o_indriveix = driveix; |
| goto done; |
| } |
| } |
| } |
| |
| done: |
| pi_unlock( ); |
| } |
| |
| /* tell pi this media file is under the head of the drive containing the object |
| */ |
| static void |
| pi_note_underhead( dh_t thisobjh, dh_t thisfileh ) |
| { |
| dh_t fileh; |
| |
| if ( thisobjh == DH_NULL ) { |
| return; |
| } |
| |
| pi_lock( ); |
| |
| if ( thisfileh != DH_NULL ) { |
| DH2F( thisfileh )->f_underheadpr = BOOL_TRUE; |
| } |
| |
| for ( fileh = DH2O( thisobjh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth ) { |
| if ( fileh != thisfileh ) { |
| DH2F( fileh )->f_underheadpr = BOOL_FALSE; |
| } |
| } |
| |
| pi_unlock( ); |
| } |
| |
| /* mark the pi stream indicating all objects in that stream are known. |
| */ |
| static void |
| pi_seestrmend( ix_t strmix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| |
| /* if an empty stream (can happen when dump interrupted), |
| * nothing need be done, so return |
| */ |
| if ( strmh == DH_NULL ) { |
| pi_unlock( ); |
| return; |
| } |
| |
| /* set stream flag and object and file counts |
| */ |
| DH2S( strmh )->s_lastobjknwnpr = BOOL_TRUE; |
| |
| pi_unlock( ); |
| pi_show( " after pi_seestrmend" ); |
| } |
| |
| /* mark pi indicating all media files in object are known |
| */ |
| static void |
| pi_seeobjstrmend( ix_t strmix, ix_t mediaix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| dh_t objh; |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| |
| /* if an empty stream (can happen when dump interrupted), |
| * nothing need be done, so return |
| */ |
| if ( strmh == DH_NULL ) { |
| pi_unlock( ); |
| return; |
| } |
| |
| |
| /* get handle to indexed object in stream |
| */ |
| for ( ix = 0, |
| objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL && ix < mediaix |
| ; |
| ix++, |
| objh = DH2O( objh )->o_nexth ) |
| ; |
| |
| /* if an empty object (can happen when dump interrupted), |
| * nothing need be done, so return |
| */ |
| if ( objh == DH_NULL ) { |
| pi_unlock( ); |
| return; |
| } |
| |
| |
| /* set object flag |
| */ |
| DH2O( objh )->o_lmfknwnpr = BOOL_TRUE; |
| |
| pi_unlock( ); |
| pi_show( " after pi_seeobjstrmend" ); |
| } |
| |
| /* scans pi to determine ino of last file wholly or partially contained on |
| * this mfile. must err on the high side if partial info. |
| * NOTE: assumes caller locks pi! |
| */ |
| static xfs_ino_t |
| pi_scanfileendino( dh_t fileh ) |
| { |
| dh_t strmh; |
| ix_t mode = 0; |
| |
| ASSERT( fileh != DH_NULL ); |
| |
| /* traverse the pi tree, looking for the next media file after |
| */ |
| for ( strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth ) { |
| dh_t objh; |
| |
| for ( objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth ) { |
| dh_t nexth; |
| |
| for ( nexth = DH2O( objh )->o_cldh |
| ; |
| nexth != DH_NULL |
| ; |
| nexth = DH2F( nexth )->f_nexth ) { |
| |
| switch( mode ) { |
| case 0: |
| if ( nexth == fileh ) { |
| mode = 1; |
| } |
| break; |
| default: |
| if ( DH2F( nexth )->f_valpr ) { |
| xfs_ino_t ino; |
| |
| ASSERT( ! ( DH2F( nexth )->f_flags & PF_INV )); |
| ASSERT( ! ( DH2F( nexth )->f_flags & PF_TERM )); |
| if ( DH2F( nexth )->f_firstegrp.eg_off ) { |
| ino = DH2F( nexth )->f_firstegrp.eg_ino; |
| return ino; |
| } else { |
| ASSERT( DH2F( nexth )->f_firstegrp.eg_ino > 0 ); |
| ino = DH2F( nexth )->f_firstegrp.eg_ino - 1; |
| return ino; |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| return INO64MAX; |
| } |
| |
| /* used to detemine range of extent groups still to be restored |
| * from media file. *--o |
| */ |
| static void |
| pi_bracketneededegrps( dh_t thisfileh, egrp_t *first_egrp, egrp_t *next_egrp ) |
| { |
| dh_t strmh; |
| bool_t thisfoundpr = BOOL_FALSE; |
| dh_t prech = DH_NULL; |
| dh_t follh = DH_NULL; |
| |
| |
| ASSERT( thisfileh != DH_NULL ); |
| |
| /* traverse the pi tree, looking for fileh |
| */ |
| pi_lock( ); |
| ASSERT( DH2F( thisfileh )->f_valpr ); |
| |
| for ( strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth ) { |
| dh_t objh; |
| |
| for ( objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth ) { |
| dh_t fileh; |
| |
| for ( fileh = DH2O( objh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth ) { |
| if ( ! thisfoundpr ) { |
| if ( fileh == thisfileh ) { |
| thisfoundpr = BOOL_TRUE; |
| } else if ( DH2F( fileh )->f_valpr ) { |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_INV )); |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_TERM )); |
| prech = fileh; |
| } |
| } else if ( DH2F( fileh )->f_valpr ) { |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_INV )); |
| ASSERT( ! ( DH2F( fileh )->f_flags & PF_TERM )); |
| ASSERT( follh == DH_NULL ); |
| follh = fileh; |
| goto done; |
| } |
| } |
| } |
| } |
| done: |
| |
| ASSERT( thisfoundpr ); |
| |
| /* initially the lower bracket is this file descriptor's |
| * current egrp. this catches the case where a previous restore |
| * session was interrupted while restoring this media file. |
| */ |
| *first_egrp = DH2F( thisfileh )->f_curegrp; |
| |
| /* if the closest valid preceeding media file's current egrp is |
| * greater, use it as the lower bracket |
| */ |
| if ( prech != DH_NULL |
| && |
| egrpcmp( &DH2F( prech )->f_curegrp, first_egrp ) > 0 ) { |
| *first_egrp = DH2F( prech )->f_curegrp; |
| } |
| |
| /* the upper bracket is initially the end of the world. |
| * if we found a valid following file descriptor describing a |
| * media file which has already been at least restored, use |
| * its first egrp as an upper bracket. |
| */ |
| next_egrp->eg_ino = INO64MAX; |
| next_egrp->eg_off = OFF64MAX; |
| if ( follh != DH_NULL |
| && |
| egrpcmp( &DH2F( follh )->f_curegrp, &DH2F( follh )->f_firstegrp ) |
| > |
| 0 ) { |
| *next_egrp = DH2F( follh )->f_firstegrp; |
| } |
| |
| pi_unlock( ); |
| } |
| |
| static void |
| pi_update_stats( off64_t sz ) |
| { |
| pi_lock( ); |
| ASSERT( persp->s.stat_valpr ); |
| persp->s.stat_inodone++; |
| persp->s.stat_datadone += sz; |
| pi_unlock( ); |
| } |
| |
| /* pi_iterator - each invocation of the iterator advances to the next media file |
| * in the dump session, walking the media file hierarchy depth-wise. if |
| * an object's file list is exhausted and the first media file in the next |
| * object is returned and the exhausted object's last media file has not yet |
| * been identified, the return-by-ref flag filemissingpr is set. similarly for |
| * streams, objmissingpr. |
| * NOTE: does not preset missingpr's to FALSE. |
| * NOTE: caller must lock pi. |
| */ |
| |
| struct pi_iter { |
| bool_t initializedpr; |
| dh_t strmh; |
| dh_t objh; |
| dh_t fileh; |
| bool_t donepr; |
| }; |
| |
| typedef struct pi_iter pi_iter_t; |
| |
| static pi_iter_t * |
| pi_iter_alloc( void ) |
| { |
| pi_iter_t *iterp; |
| |
| iterp = ( pi_iter_t * )calloc( 1, sizeof( pi_iter_t )); |
| ASSERT( iterp ); |
| return iterp; |
| } |
| |
| static void |
| pi_iter_free( pi_iter_t *iterp ) |
| { |
| free( ( void * )iterp ); |
| } |
| |
| static dh_t |
| pi_iter_nextfileh( pi_iter_t *iterp, |
| bool_t *objmissingprp, |
| bool_t *filemissingprp ) |
| { |
| ASSERT( ! iterp->donepr ); |
| |
| if ( persp->s.strmheadh == DH_NULL ) { |
| iterp->donepr = BOOL_TRUE; |
| return DH_NULL; |
| } |
| |
| if ( ! iterp->initializedpr ) { |
| ASSERT( persp->s.strmheadh != DH_NULL ); |
| iterp->strmh = persp->s.strmheadh; |
| iterp->objh = DH2S( iterp->strmh )->s_cldh; |
| if ( iterp->objh == DH_NULL ) { |
| if ( ! DH2S( iterp->strmh )->s_lastobjknwnpr ) { |
| *objmissingprp = BOOL_TRUE; |
| } |
| } else { |
| iterp->fileh = DH2O( iterp->objh )->o_cldh; |
| if ( iterp->fileh == DH_NULL ) { |
| if ( ! DH2O( iterp->objh )->o_lmfknwnpr ) { |
| *filemissingprp = BOOL_TRUE; |
| } |
| } |
| } |
| |
| while ( iterp->fileh == DH_NULL ) { |
| while ( iterp->objh == DH_NULL ) { |
| if ( ! DH2S( iterp->strmh )->s_lastobjknwnpr ) { |
| *objmissingprp = BOOL_TRUE; |
| } |
| iterp->strmh = DH2S( iterp->strmh )->s_nexth; |
| if ( iterp->strmh == DH_NULL ) { |
| iterp->donepr = BOOL_TRUE; |
| return DH_NULL; |
| } |
| iterp->objh = DH2S( iterp->strmh )->s_cldh; |
| } |
| iterp->fileh = DH2O( iterp->objh )->o_cldh; |
| if ( iterp->fileh == DH_NULL ) { |
| if ( ! DH2O( iterp->objh )->o_lmfknwnpr ) { |
| *filemissingprp = BOOL_TRUE; |
| } |
| iterp->objh = DH2O( iterp->objh )->o_nexth; |
| } |
| } |
| iterp->initializedpr = BOOL_TRUE; |
| return iterp->fileh; |
| } |
| |
| iterp->fileh = DH2F( iterp->fileh )->f_nexth; |
| while ( iterp->fileh == DH_NULL ) { |
| if ( ! DH2O( iterp->objh )->o_lmfknwnpr ) { |
| *filemissingprp = BOOL_TRUE; |
| } |
| iterp->objh = DH2O( iterp->objh )->o_nexth; |
| while ( iterp->objh == DH_NULL ) { |
| if ( ! DH2S( iterp->strmh )->s_lastobjknwnpr ) { |
| *objmissingprp = BOOL_TRUE; |
| } |
| iterp->strmh = DH2S( iterp->strmh )->s_nexth; |
| if ( iterp->strmh == DH_NULL ) { |
| iterp->donepr = BOOL_TRUE; |
| return DH_NULL; |
| } |
| iterp->objh = DH2S( iterp->strmh )->s_cldh; |
| } |
| iterp->fileh = DH2O( iterp->objh )->o_cldh; |
| } |
| |
| return iterp->fileh; |
| } |
| |
| /* produces a list of media objects needed. also indicates if we know |
| * some unidentified media objects are needed, and if it is possible |
| * that we need some unidentifed objects, but don't know for sure. |
| * if markskippr is set, set the f_nondirskipr flag if the media file |
| * does not contain any nondirs of interest. |
| */ |
| |
| struct bagobj { |
| bagelem_t bagelem; |
| uuid_t id; |
| label_t label; |
| bool_t indrivepr; |
| ix_t indriveix; |
| }; |
| |
| typedef struct bagobj bagobj_t; |
| |
| static bag_t * |
| pi_neededobjs_nondir_alloc( bool_t *knownholesprp, |
| bool_t *maybeholesprp, |
| bool_t showobjindrivepr, |
| bool_t markskippr ) |
| { |
| bag_t *bagp; |
| pi_iter_t *headiterp; |
| pi_iter_t *tailiterp; |
| dh_t headh; |
| dh_t tailh; |
| egrp_t tailegrp; |
| bool_t knownobjmissingpr; |
| bool_t maybeobjmissingpr; |
| bool_t maybefilemissingpr; |
| dh_t lastobjaddedh; |
| intgen_t objlistlen; |
| |
| /* no point in proceeding if pi not begun |
| */ |
| if ( persp->s.strmheadh == DH_NULL ) { |
| *knownholesprp = BOOL_TRUE; |
| *maybeholesprp = BOOL_FALSE; |
| return 0; |
| } |
| |
| /* to hold a list of media object handles: caller must free |
| * using pi_neededobjs_free( ). |
| */ |
| bagp = bag_alloc( ); |
| |
| /* allocate two iterators to scan pi |
| */ |
| tailiterp = pi_iter_alloc( ); |
| headiterp = pi_iter_alloc( ); |
| |
| /* set the handle to the last file added to the list to NULL. |
| * this will be updated each time we add an object to the list, |
| * preventing the same object from being added more than once. |
| * this works because the media files for a given object will |
| * always appear contiguous and just once in a pi iteration. |
| */ |
| lastobjaddedh = DH_NULL; |
| objlistlen = 0; |
| |
| /* these will be set TRUE if the tail iterator ever indicates |
| * we crossed an object or stream boundary and did not see a |
| * valid last file or last object respectively. can accumulate |
| * the booleans, since iterator never sets FALSE, just TRUE. |
| */ |
| maybeobjmissingpr = BOOL_FALSE; |
| maybefilemissingpr = BOOL_FALSE; |
| |
| /* this will be set TRUE if we see a needed media file but the |
| * object containing the media file has not been IDed. |
| */ |
| knownobjmissingpr = BOOL_FALSE; |
| |
| tailegrp.eg_ino = 0; |
| tailegrp.eg_off = 0; |
| |
| tailh = DH_NULL; |
| |
| /* lock up the inventory during the scan |
| */ |
| pi_lock( ); |
| |
| do { |
| egrp_t headegrp; |
| bool_t foundgappr; |
| |
| /* advance the head until we see the next media file which has |
| * a valid egrp, or until we run out of media files. |
| */ |
| do { |
| bool_t dummyobjmissingpr; |
| bool_t dummyfilemissingpr; |
| headh = pi_iter_nextfileh( headiterp, |
| &dummyobjmissingpr, |
| &dummyfilemissingpr ); |
| } while ( headh != DH_NULL && ! DH2F( headh )->f_valpr ); |
| if ( headh == DH_NULL ) { |
| headegrp.eg_ino = INO64MAX; |
| headegrp.eg_off = OFF64MAX; |
| } else { |
| ASSERT( ! ( DH2F( headh )->f_flags & PF_INV )); |
| ASSERT( ! ( DH2F( headh )->f_flags & PF_TERM )); |
| headegrp = DH2F( headh )->f_firstegrp; |
| } |
| |
| /* see if the range of egrps from head up to but not including |
| * tail needed according to ino map |
| */ |
| if ( gapneeded( &tailegrp, &headegrp )) { |
| foundgappr = BOOL_TRUE; |
| } else { |
| foundgappr = BOOL_FALSE; |
| } |
| |
| /* now bring tail up to head, adding objects and setting flags |
| * along the way. note special handling of NULL tailh. possible |
| * only first time through: ignore. also, ignore inv and term. |
| */ |
| do { |
| /* if requested, mark media files not needed |
| */ |
| if ( markskippr |
| && |
| ! foundgappr |
| && |
| tailh != DH_NULL |
| && |
| ! ( DH2F( tailh )->f_flags & PF_INV ) |
| && |
| ! ( DH2F( tailh )->f_flags & PF_TERM ) |
| && |
| ! DH2F( tailh )->f_nondirskippr ) { |
| DH2F( tailh )->f_nondirskippr = BOOL_TRUE; |
| } |
| |
| /* build up list of needed objects |
| */ |
| if ( foundgappr |
| && |
| tailh != DH_NULL |
| && |
| ! ( DH2F( tailh )->f_flags & PF_INV ) |
| && |
| ! ( DH2F( tailh )->f_flags & PF_TERM ) |
| && |
| ! DH2F( tailh )->f_nondirdonepr |
| && |
| ! DH2F( tailh )->f_nondirskippr ) { |
| |
| dh_t objh = DH2F( tailh )->f_parh; |
| |
| if ( ! DH2O( objh )->o_indrivepr |
| || |
| showobjindrivepr ) { |
| if ( DH2O( objh )->o_idlabvalpr ) { |
| if ( objh != lastobjaddedh ) { |
| addobj( bagp, |
| &DH2O( objh )->o_id, |
| DH2O( objh )->o_lab, |
| DH2O( objh )->o_indrivepr, |
| DH2O( objh )->o_indriveix ); |
| lastobjaddedh = objh; |
| objlistlen++; |
| } |
| } else { |
| knownobjmissingpr = BOOL_TRUE; |
| } |
| } |
| } |
| |
| /* pull the tail up to the next media file |
| */ |
| tailh = pi_iter_nextfileh( tailiterp, |
| &maybeobjmissingpr, |
| &maybefilemissingpr ); |
| } while ( tailh != headh ); |
| |
| tailegrp = headegrp; |
| |
| } while ( headh != DH_NULL ); |
| |
| pi_unlock( ); |
| |
| /* free the iterators |
| */ |
| pi_iter_free( tailiterp ); |
| pi_iter_free( headiterp ); |
| |
| /* free the bag and return NULL if object list empty |
| */ |
| if ( objlistlen == 0 ) { |
| bag_free( bagp ); |
| bagp = 0; |
| } |
| |
| *maybeholesprp = ( maybeobjmissingpr || maybefilemissingpr ); |
| *knownholesprp = knownobjmissingpr; |
| return bagp; |
| } |
| |
| static bag_t * |
| pi_neededobjs_dir_alloc( bool_t *knownholesprp, bool_t *maybeholesprp ) |
| { |
| bag_t *bagp; |
| dh_t fileh; |
| pi_iter_t *iterp; |
| bool_t knownobjmissingpr; |
| bool_t maybeobjmissingpr; |
| bool_t maybefilemissingpr; |
| dh_t lastobjaddedh; |
| intgen_t objlistlen; |
| |
| bagp = bag_alloc( ); |
| iterp = pi_iter_alloc( ); |
| |
| knownobjmissingpr = BOOL_FALSE; |
| maybeobjmissingpr = BOOL_FALSE; |
| maybefilemissingpr = BOOL_FALSE; |
| lastobjaddedh = DH_NULL; |
| objlistlen = 0; |
| |
| pi_lock( ); |
| |
| while ( ( fileh = pi_iter_nextfileh( iterp, |
| &maybeobjmissingpr, |
| &maybefilemissingpr )) |
| != DH_NULL ) { |
| if ( ! DH2F( fileh )->f_dirtriedpr ) { |
| dh_t objh = DH2F( fileh )->f_parh; |
| if ( ! DH2O( objh )->o_indrivepr ) { |
| if ( DH2O( objh )->o_idlabvalpr ) { |
| if ( objh != lastobjaddedh ) { |
| addobj( bagp, |
| &DH2O( objh )->o_id, |
| DH2O( objh )->o_lab, |
| DH2O( objh )->o_indrivepr, |
| DH2O( objh )->o_indriveix ); |
| lastobjaddedh = objh; |
| objlistlen++; |
| } |
| } else { |
| knownobjmissingpr = BOOL_TRUE; |
| } |
| } |
| } |
| } |
| |
| pi_unlock( ); |
| |
| pi_iter_free( iterp ); |
| |
| if ( objlistlen == 0 ) { |
| bag_free( bagp ); |
| bagp = 0; |
| } |
| |
| *maybeholesprp = ( maybeobjmissingpr || maybefilemissingpr ); |
| *knownholesprp = knownobjmissingpr; |
| return bagp; |
| } |
| |
| static void |
| pi_neededobjs_free( bag_t *bagp ) |
| { |
| bagiter_t bagiter; |
| bagobj_t *bagobjp; |
| bagelem_t *bagelemp; |
| size64_t dummykey; |
| void *dummypayloadp; |
| |
| ASSERT( bagp ); |
| |
| bagiter_init( bagp, &bagiter ); |
| |
| bagobjp = 0; |
| while (( bagelemp = bagiter_next( &bagiter, ( void ** )&bagobjp ) )) { |
| bag_remove( bagp, bagelemp, &dummykey, &dummypayloadp ); |
| ASSERT( bagobjp ); |
| ASSERT( bagobjp == ( bagobj_t * )dummypayloadp ); |
| free( ( void * )bagobjp ); |
| bagobjp = 0; |
| } |
| |
| bag_free( bagp ); |
| } |
| |
| /* a macro predicate to indicate if we know we are done. if we are not |
| * done or don't know, returns FALSE. |
| */ |
| static bool_t |
| pi_alldone( void ) |
| { |
| bag_t *bagp; |
| bool_t knownholespr; |
| bool_t maybeholespr; |
| size_t cnt; |
| |
| knownholespr = BOOL_FALSE; |
| maybeholespr = BOOL_FALSE; |
| bagp = pi_neededobjs_nondir_alloc( &knownholespr, |
| &maybeholespr, |
| BOOL_TRUE, |
| BOOL_FALSE ); |
| if ( bagp ) { |
| cnt = cntobj( bagp ); |
| pi_neededobjs_free( bagp ); |
| } else { |
| cnt = 0; |
| } |
| |
| if ( cnt || knownholespr || maybeholespr ) { |
| return BOOL_FALSE; |
| } else { |
| return BOOL_TRUE; |
| } |
| } |
| |
| /* tells the persistent inventory we hit end-of-data while examining the |
| * object specified by the index param. this tells us we've seen the end |
| * of the stream as well as the end of the object. |
| */ |
| static void |
| pi_hiteod( ix_t strmix, ix_t objix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| dh_t objh; |
| size_t objcnt; |
| ix_t lastobjix; |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| ASSERT( strmh != DH_NULL ); |
| |
| /* get index to last object in stream |
| */ |
| for ( objcnt = 0, objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth, objcnt++ ) |
| ; |
| ASSERT( objcnt != 0 ); |
| lastobjix = objcnt - 1; |
| |
| pi_unlock( ); |
| |
| /* can't possibly happen, but check for case where pi indicates |
| * other media objects beyond this one. |
| */ |
| if ( objix != lastobjix ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "hit EOD at stream %u object %u, " |
| "yet inventory indicates last object index is %u\n"), |
| strmix, |
| objix, |
| lastobjix ); |
| } else { |
| pi_seestrmend( strmix ); |
| } |
| |
| pi_seeobjstrmend( strmix, lastobjix ); |
| } |
| |
| /* tells the persistent inventory we hit end-of-media while examining the |
| * object specified by the index param. this tells us we've seen the end |
| * of the object. |
| */ |
| static void |
| pi_hiteom( ix_t strmix, ix_t objix ) |
| { |
| pi_seeobjstrmend( strmix, objix ); |
| } |
| |
| static void |
| pi_hitnextdump( ix_t strmix, ix_t objix, ix_t lastfileix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| dh_t objh; |
| size_t objcnt; |
| ix_t lastobjix; |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| ASSERT( strmh != DH_NULL ); |
| |
| /* get index to last object in stream |
| */ |
| for ( objcnt = 0, objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth, objcnt++ ) |
| ; |
| ASSERT( objcnt != 0 ); |
| lastobjix = objcnt - 1; |
| |
| pi_unlock( ); |
| |
| /* can't possibly happen, but check for case where pi indicates |
| * other media objects beyond this one. |
| */ |
| if ( objix != lastobjix ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "hit next dump at stream %u object %u file %u, " |
| "yet inventory indicates last object index is %u\n"), |
| strmix, |
| objix, |
| lastfileix, |
| lastobjix ); |
| } else { |
| pi_seestrmend( strmix ); |
| } |
| |
| pi_seeobjstrmend( strmix, lastobjix ); |
| } |
| |
| /* returns TRUE if pi is certain no more useful media files remaining |
| * on object. if any doubt, such as not knowing the last media file on |
| * the object, returns FALSE. |
| */ |
| static bool_t |
| pi_know_no_more_on_object( purp_t purp, ix_t strmix, ix_t objix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| dh_t objh; |
| dh_t fileh; |
| |
| ASSERT( purp == PURP_DIR || purp == PURP_NONDIR ); |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| ASSERT( strmh != DH_NULL ); |
| |
| /* get handle to indexed object |
| */ |
| for ( ix = 0, objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL && ix < objix |
| ; |
| ix++, |
| objh = DH2O( objh )->o_nexth ) |
| ; |
| ASSERT( objh != DH_NULL ); |
| |
| /* if don't know last media file on object, return FALSE |
| */ |
| if ( ! DH2O( objh )->o_lmfknwnpr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| |
| /* check all media files on object. if any are not marked done, |
| * return FALSE. |
| */ |
| for ( fileh = DH2O( objh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth ) { |
| if ( DH2F( fileh )->f_flags & PF_INV ) { |
| continue; |
| } |
| if ( DH2F( fileh )->f_flags & PF_TERM ) { |
| continue; |
| } |
| if ( purp == PURP_DIR ) { |
| if ( ! DH2F( fileh )->f_dirtriedpr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| } else { |
| if ( ! DH2F( fileh )->f_nondirskippr |
| && |
| ! DH2F( fileh )->f_nondirdonepr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| } |
| } |
| |
| pi_unlock( ); |
| return BOOL_TRUE; |
| } |
| |
| static bool_t |
| pi_know_no_more_beyond_on_object( purp_t purp, |
| ix_t strmix, |
| ix_t objix, |
| ix_t fileix ) |
| { |
| ix_t ix; |
| dh_t strmh; |
| dh_t objh; |
| dh_t fileh; |
| |
| ASSERT( purp == PURP_DIR || purp == PURP_NONDIR ); |
| |
| pi_lock( ); |
| |
| /* get handle to the indexed stream |
| */ |
| for ( ix = 0, |
| strmh = persp->s.strmheadh |
| ; |
| strmh != DH_NULL && ix < strmix |
| ; |
| ix++, |
| strmh = DH2S( strmh )->s_nexth ) |
| ; |
| ASSERT( strmh != DH_NULL ); |
| |
| /* get handle to indexed object |
| */ |
| for ( ix = 0, |
| objh = DH2S( strmh )->s_cldh |
| ; |
| objh != DH_NULL && ix < objix |
| ; |
| ix++, |
| objh = DH2O( objh )->o_nexth ) |
| ; |
| ASSERT( objh != DH_NULL ); |
| |
| /* if don't know last media file on object, return FALSE |
| */ |
| if ( ! DH2O( objh )->o_lmfknwnpr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| |
| /* check all files on object after indexed file. if any are not marked |
| * done, return FALSE. skip inventory and terminator files. |
| */ |
| for ( ix = 0, |
| fileh = DH2O( objh )->o_cldh |
| ; |
| fileh != DH_NULL |
| ; |
| ix++, |
| fileh = DH2F( fileh )->f_nexth ) { |
| if ( ix <= fileix ) { |
| continue; |
| } |
| if ( DH2F( fileh )->f_flags & PF_INV ) { |
| continue; |
| } |
| if ( DH2F( fileh )->f_flags & PF_TERM ) { |
| continue; |
| } |
| if ( purp == PURP_DIR ) { |
| if ( ! DH2F( fileh )->f_dirtriedpr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| } else { |
| if ( ! DH2F( fileh )->f_nondirdonepr |
| && |
| ! DH2F( fileh )->f_nondirskippr ) { |
| pi_unlock( ); |
| return BOOL_FALSE; |
| } |
| } |
| } |
| |
| pi_unlock( ); |
| return BOOL_TRUE; |
| } |
| |
| /* indicates if the given extent group range is called for by the |
| * ino map. *---o (endpoint not inclusive) |
| */ |
| static bool_t |
| gapneeded( egrp_t *firstegrpp, egrp_t *lastegrpp ) |
| { |
| xfs_ino_t endino; |
| |
| if ( firstegrpp->eg_ino > lastegrpp->eg_ino ) { |
| return BOOL_FALSE; |
| } |
| |
| if ( firstegrpp->eg_ino == lastegrpp->eg_ino |
| && |
| firstegrpp->eg_off > lastegrpp->eg_off ) { |
| return BOOL_FALSE; |
| } |
| |
| if ( lastegrpp->eg_off > 0 || lastegrpp->eg_ino == 0 ) { |
| endino = lastegrpp->eg_ino; |
| } else { |
| endino = lastegrpp->eg_ino - 1; |
| } |
| |
| if ( ! inomap_rst_needed( firstegrpp->eg_ino, endino )) { |
| return BOOL_FALSE; |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| static void |
| addobj( bag_t *bagp, |
| uuid_t *idp, |
| label_t label, |
| bool_t indrivepr, |
| ix_t indriveix ) |
| { |
| bagobj_t *bagobjp; |
| |
| bagobjp = ( bagobj_t * )calloc( 1, sizeof( bagobj_t )); |
| ASSERT( bagobjp ); |
| uuid_copy(bagobjp->id, *idp); |
| strncpy( bagobjp->label, |
| label, |
| sizeof( bagobjp->label )); |
| bagobjp->indrivepr = indrivepr; |
| bagobjp->indriveix = indriveix; |
| bag_insert( bagp, |
| &bagobjp->bagelem, |
| ( size64_t )0, |
| ( void * )bagobjp ); |
| } |
| |
| static size_t |
| cntobj( bag_t *bagp ) |
| { |
| bagiter_t bagiter; |
| bagobj_t *bagobjp; |
| size_t cnt; |
| |
| ASSERT( bagp ); |
| |
| bagiter_init( bagp, &bagiter ); |
| cnt = 0; |
| bagobjp = 0; /* keep lint happy */ |
| while ( bagiter_next( &bagiter, ( void ** )&bagobjp )) { |
| cnt++; |
| bagobjp = 0; /* keep lint happy */ |
| } |
| |
| return cnt; |
| } |
| |
| |
| /* misc. static functions ***************************************************/ |
| |
| /* queries inventory for the base of the given session. if the given session |
| * was a resumed dump, then must be last dump of same level. otherwise, |
| * must be last dump of a lesser level |
| */ |
| static bool_t |
| askinvforbaseof( uuid_t baseid, inv_session_t *sessp ) |
| { |
| ix_t level; |
| bool_t resumedpr; |
| inv_idbtoken_t invtok; |
| inv_session_t *basesessp; |
| bool_t ok; |
| |
| level = ( ix_t )sessp->s_level; |
| resumedpr = sessp->s_isresumed; |
| |
| /* don't look for base if level 0 and not resumed |
| */ |
| if ( level == 0 && ! resumedpr ) { |
| return BOOL_TRUE; |
| } |
| |
| /* open the inventory for this file system |
| */ |
| invtok = inv_open( INV_BY_UUID, |
| INV_SEARCH_ONLY, |
| ( void * )&sessp->s_fsid ); |
| if ( invtok == INV_TOKEN_NULL ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "unable to open inventory to validate dump\n") ); |
| return BOOL_FALSE; |
| } |
| |
| /* get the base session |
| */ |
| if ( resumedpr ) { |
| ok = inv_lastsession_level_equalto( invtok, |
| ( u_char_t )level, |
| &basesessp ); |
| } else { |
| ok = inv_lastsession_level_lessthan( invtok, |
| ( u_char_t )level, |
| &basesessp ); |
| } |
| if ( ! ok ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_MEDIA, _( |
| "unable to find base dump in inventory " |
| "to validate dump\n") ); |
| return BOOL_FALSE; |
| } |
| |
| /* close the inventory |
| */ |
| ok = inv_close( invtok ); |
| ASSERT( ok ); |
| |
| /* return id of base session |
| */ |
| uuid_copy(baseid, basesessp->s_sesid); |
| |
| /* free the base session descriptor |
| */ |
| inv_free_session( &basesessp ); |
| |
| return BOOL_TRUE; |
| } |
| |
| static bool_t |
| dumpcompat( bool_t resumepr, ix_t level, uuid_t baseid, bool_t logpr ) |
| { |
| if ( persp->a.cumpr ) { |
| if ( persp->a.dumpcnt == 0 ) { |
| if ( resumepr ) { |
| if ( logpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cumulative restores must begin with " |
| "an initial (not resumed) " |
| "level 0 dump\n") ); |
| } |
| return BOOL_FALSE; |
| } |
| if ( level > 0 ) { |
| if ( logpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "cumulative restores must begin with " |
| "a level 0 dump\n") ); |
| } |
| return BOOL_FALSE; |
| } |
| } else { |
| if ( resumepr ) { |
| if ( uuid_compare( persp->a.lastdumpid, |
| baseid) != 0) { |
| if ( logpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "selected resumed dump " |
| "not a resumption of " |
| "previously applied dump\n")); |
| } |
| return BOOL_FALSE; |
| } |
| } else { |
| if ( uuid_compare( persp->a.lastdumpid, |
| baseid) != 0) { |
| if ( logpr ) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "selected dump not based on " |
| "previously applied dump\n")); |
| } |
| return BOOL_FALSE; |
| } |
| } |
| } |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| /* prompts for a new media object. supplies list of media objects still |
| * needed, and indicates if there are or may be unidentified media objects |
| * still needed/available |
| */ |
| static bool_t |
| Media_prompt_change( drive_t *drivep, |
| purp_t purp, |
| bag_t *bagp, |
| bool_t knownholespr, |
| bool_t maybeholespr ) |
| { |
| 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 invstatix; |
| ix_t neededix; |
| ix_t responseix; |
| ix_t sigintix; |
| |
| retry: |
| fold_init( fold, _("change media dialog"), '=' ); |
| preamblecnt = 0; |
| preamblestr[ preamblecnt++ ] = "\n"; |
| preamblestr[ preamblecnt++ ] = fold; |
| preamblestr[ preamblecnt++ ] = "\n\n"; |
| ASSERT( preamblecnt <= PREAMBLEMAX ); |
| dlog_begin( preamblestr, preamblecnt ); |
| |
| /* query: ask if media changed or declined |
| */ |
| if ( drivecnt > 1 ) { |
| sprintf( question, _( |
| "please change media in " |
| "drive %u\n"), |
| (unsigned int)drivep->d_index ); |
| } else { |
| sprintf( question, _( |
| "please change media in " |
| "drive\n") ); |
| } |
| querycnt = 0; |
| querystr[ querycnt++ ] = question; |
| ASSERT( querycnt <= QUERYMAX ); |
| choicecnt = 0; |
| dontix = choicecnt; |
| choicestr[ choicecnt++ ] = _("media change declined"); |
| if ( purp != PURP_SEARCH ) { |
| invstatix = choicecnt; |
| choicestr[ choicecnt++ ] = _("display media inventory status"); |
| neededix = choicecnt; |
| choicestr[ choicecnt++ ] = _("list needed media objects"); |
| } else { |
| invstatix = IXMAX; |
| neededix = IXMAX; |
| } |
| doix = choicecnt; |
| choicestr[ choicecnt++ ] = _("media changed"); |
| 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++ ] = _("examining new media\n"); |
| } else if ( responseix == dontix ) { |
| ackstr[ ackcnt++ ] = _("media change aborted\n"); |
| } else if ( responseix == invstatix ) { |
| ackstr[ ackcnt++ ] = "\n"; |
| ASSERT( ackcnt <= ACKMAX ); |
| dlog_multi_ack( ackstr, |
| ackcnt ); |
| pi_show_nomloglock( ); |
| postamblecnt = 0; |
| fold_init( fold, _("end dialog"), '-' ); |
| postamblestr[ postamblecnt++ ] = "\n"; |
| postamblestr[ postamblecnt++ ] = fold; |
| postamblestr[ postamblecnt++ ] = "\n\n"; |
| ASSERT( postamblecnt <= POSTAMBLEMAX ); |
| dlog_end( postamblestr, |
| postamblecnt ); |
| goto retry; |
| } else if ( responseix == neededix ) { |
| ackstr[ ackcnt++ ] = "\n"; |
| ASSERT( ackcnt <= ACKMAX ); |
| dlog_multi_ack( ackstr, |
| ackcnt ); |
| display_needed_objects( purp, |
| bagp, |
| knownholespr, |
| maybeholespr ); |
| postamblecnt = 0; |
| fold_init( fold, "end dialog", '-' ); |
| postamblestr[ postamblecnt++ ] = "\n"; |
| postamblestr[ postamblecnt++ ] = fold; |
| postamblestr[ postamblecnt++ ] = "\n\n"; |
| ASSERT( postamblecnt <= POSTAMBLEMAX ); |
| dlog_end( postamblestr, |
| postamblecnt ); |
| goto retry; |
| } else { |
| ASSERT( responseix == sigintix ); |
| 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 change dialog\n" ); |
| goto retry; |
| } |
| |
| return responseix == doix; |
| } |
| |
| /* prompts the operator, asking if the current media file header describes |
| * the dump to be restored |
| */ |
| static bool_t |
| promptdumpmatch( ix_t thrdix, |
| global_hdr_t *grhdrp, |
| media_hdr_t *mrhdrp, |
| content_hdr_t *crhdrp, |
| content_inode_hdr_t *scrhdrp ) |
| { |
| fold_t fold; |
| char introstring[ 80 ]; |
| 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, _("dump selection dialog"), '=' ); |
| preamblestr[ preamblecnt++ ] = "\n"; |
| preamblestr[ preamblecnt++ ] = fold; |
| preamblestr[ preamblecnt++ ] = "\n\n"; |
| ASSERT( preamblecnt <= PREAMBLEMAX ); |
| dlog_begin( preamblestr, preamblecnt ); |
| |
| /* display vital stats and ask if this one should be restored |
| */ |
| if ( drivecnt > 0 ) { |
| sprintf( introstring, _( |
| "the following dump has been found" |
| " on drive %u" |
| "\n\n"), |
| (unsigned int)thrdix ); |
| } else { |
| sprintf( introstring, _( |
| "the following dump has been found" |
| "\n\n") ); |
| } |
| ASSERT( strlen( introstring ) < sizeof( introstring )); |
| display_dump_label( BOOL_FALSE, |
| MLOG_NORMAL | MLOG_BARE, |
| introstring, |
| grhdrp, |
| mrhdrp, |
| crhdrp, |
| scrhdrp ); |
| |
| querycnt = 0; |
| if ( tranp->t_toconlypr ) { |
| querystr[ querycnt++ ] = _("\nexamine this dump?\n"); |
| } else { |
| querystr[ querycnt++ ] = (persp->a.interpr) ? |
| _("\ninteractively restore from this dump?\n") |
| : _("\nrestore this dump?\n"); |
| } |
| ASSERT( querycnt <= QUERYMAX ); |
| choicecnt = 0; |
| dontix = choicecnt; |
| choicestr[ choicecnt++ ] = _("skip"); |
| doix = choicecnt; |
| choicestr[ choicecnt++ ] = (persp->a.interpr) ? |
| _("interactively restore\n") : _("restore\n"); |
| ASSERT( choicecnt <= CHOICEMAX ); |
| sigintix = IXMAX - 1; |
| |
| responseix = dlog_multi_query( querystr, |
| querycnt, |
| choicestr, |
| choicecnt, |
| 0, /* hilitestr */ |
| IXMAX, /* hiliteix */ |
| 0, /* defaultstr */ |
| doix, /* defaultix */ |
| 0, |
| IXMAX, /* timeout ix */ |
| sigintix, /* sigint ix */ |
| dontix, /* sighup ix */ |
| dontix ); /* sigquit ix */ |
| ackcnt = 0; |
| if ( responseix == doix ) { |
| ackstr[ ackcnt++ ] = (persp->a.interpr) ? |
| _("this dump selected for interactive restoral\n") |
| : _("this dump selected for restoral\n"); |
| } else if ( responseix == dontix ) { |
| ackstr[ ackcnt++ ] = _("dump skipped\n"); |
| } else { |
| ASSERT( responseix == sigintix ); |
| 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 dump selection dialog\n" ); |
| goto retry; |
| } |
| |
| return responseix == doix; |
| } |
| |
| /* restore_file - knows how to restore non-directory files |
| * |
| * uses the tree's callback iterator, which will call me for each |
| * link to the specified inode. |
| */ |
| struct cb_context { |
| drive_t *cb_drivep; |
| filehdr_t *cb_fhdrp; |
| rv_t cb_rv; |
| bool_t cb_ehcs; |
| bool_t cb_ahcs; |
| char *cb_path1; |
| char *cb_path2; |
| }; |
| |
| typedef struct cb_context cb_context_t; |
| |
| static bool_t restore_file_cb( void *, bool_t, char *, char * ); |
| |
| static rv_t |
| restore_file( drive_t *drivep, |
| filehdr_t *fhdrp, |
| bool_t ehcs, |
| bool_t ahcs, |
| char *path1, |
| char *path2 ) |
| { |
| rv_t rv; |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| cb_context_t context; |
| |
| /* ask the tree to call me back for each link to this inode. |
| * my callback will restore the file the first time it is |
| * invoked, and create a hard link in subsequent calls. |
| */ |
| context.cb_drivep = drivep; |
| context.cb_fhdrp = fhdrp; |
| context.cb_rv = RV_OK; |
| context.cb_ehcs = ehcs; |
| context.cb_ahcs = ahcs; |
| context.cb_path1 = path1; |
| context.cb_path2 = path2; |
| rv = tree_cb_links( bstatp->bs_ino, |
| bstatp->bs_gen, |
| bstatp->bs_ctime.tv_sec, |
| bstatp->bs_mtime.tv_sec, |
| restore_file_cb, |
| &context, |
| path1, |
| path2 ); |
| if (context.cb_rv) /* context error result has precedence */ |
| return context.cb_rv; /* this would be set by callback */ |
| else |
| return rv; |
| } |
| |
| /* called for each link to the file described by fhdr. the first |
| * call is detected by noting linkpr is FALSE, and is used to create/ |
| * update the first link to the file, using path1. subsequent calls have |
| * linkpr set false, and should link path1 to path2. if path1 is ever null, |
| * just pull from media: don't create. |
| * if this func returns FALSE, will cause tree_cb_links to abort |
| */ |
| static bool_t |
| restore_file_cb( void *cp, bool_t linkpr, char *path1, char *path2 ) |
| { |
| cb_context_t *contextp = ( cb_context_t * )cp; |
| drive_t *drivep = contextp->cb_drivep; |
| filehdr_t *fhdrp = contextp->cb_fhdrp; |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| rv_t *rvp = &contextp->cb_rv; |
| bool_t ehcs = contextp->cb_ehcs; |
| bool_t ahcs = contextp->cb_ahcs; |
| stream_context_t *strctxp = (stream_context_t *)drivep->d_strmcontextp; |
| |
| int rval; |
| bool_t ok; |
| |
| if ( cldmgr_stop_requested( )) { |
| *rvp = RV_INTR; |
| return BOOL_FALSE; |
| } |
| |
| if ( ! linkpr ) { |
| if (path1) { |
| /* cache the path for use in restoring attributes |
| * and extended attributes |
| */ |
| strcpy(strctxp->sc_path, path1); |
| } |
| |
| /* call type-specific function to create the file |
| */ |
| switch( bstatp->bs_mode & S_IFMT ) { |
| case S_IFREG: |
| ok = restore_reg( drivep, fhdrp, rvp, path1 ); |
| if (!ok) |
| return ok; |
| if ( fhdrp->fh_flags & FILEHDR_FLAGS_EXTATTR ) { |
| *rvp = restore_extattr( drivep, |
| fhdrp, |
| path1, |
| ahcs, |
| BOOL_FALSE, /* isdirpr */ |
| BOOL_FALSE, /* onlydoreadpr */ |
| DAH_NULL ); |
| } else { |
| ok = restore_extent_group( drivep, |
| fhdrp, |
| path1, |
| strctxp->sc_fd, |
| ehcs, |
| rvp ); |
| } |
| return ok; |
| case S_IFBLK: |
| case S_IFCHR: |
| case S_IFIFO: |
| #ifdef S_IFNAM |
| case S_IFNAM: |
| #endif |
| case S_IFSOCK: |
| ok = restore_spec( fhdrp, rvp, path1 ); |
| return ok; |
| case S_IFLNK: |
| ok = restore_symlink( drivep, |
| fhdrp, |
| rvp, |
| path1, |
| path2, |
| ehcs ); |
| return ok; |
| default: |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "ino %llu: unknown file type: %08x\n"), |
| bstatp->bs_ino, |
| bstatp->bs_mode ); |
| return BOOL_FALSE; |
| } |
| } else if ( ! tranp->t_toconlypr ) { |
| ASSERT( path1 ); |
| ASSERT( path2 ); |
| mlog( MLOG_TRACE, |
| "linking %s to %s\n", |
| path1, |
| path2 ); |
| rval = unlink( path2 ); |
| if ( rval && errno != ENOENT ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to unlink " |
| "current file prior to linking " |
| "%s to %s:" |
| " %s\n"), |
| path1, |
| path2, |
| strerror( errno )); |
| } else { |
| rval = link( path1, path2 ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "attempt to " |
| "link %s to %s failed:" |
| " %s\n"), |
| path1, |
| path2, |
| strerror( errno )); |
| } |
| } |
| return BOOL_TRUE; |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "%s\n", |
| path2 ); |
| return BOOL_TRUE; |
| } |
| } |
| |
| /* called to begin a regular file. if no path given, or if just toc, |
| * don't actually write, just read. also get into that situation if |
| * cannot prepare destination. fd == -1 signifies no write. *statp |
| * is set to indicate drive errors. returns FALSE if should abort |
| * this iteration. |
| */ |
| static bool_t |
| restore_reg( drive_t *drivep, |
| filehdr_t *fhdrp, |
| rv_t *rvp, |
| char *path ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| stream_context_t *strctxp = (stream_context_t *)drivep->d_strmcontextp; |
| intgen_t *fdp = &strctxp->sc_fd; |
| intgen_t rval; |
| struct fsxattr fsxattr; |
| struct stat64 stat; |
| intgen_t oflags; |
| |
| if ( !path ) |
| return BOOL_TRUE; |
| |
| if ( fhdrp->fh_offset ) { |
| if ( ! tranp->t_toconlypr ) { |
| mlog( MLOG_TRACE, |
| "restoring regular file ino %llu %s" |
| " (offset %lld)\n", |
| bstatp->bs_ino, |
| path, |
| fhdrp->fh_offset ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| _("%s (offset %lld)\n"), |
| path, |
| fhdrp->fh_offset ); |
| } |
| } else { |
| if ( ! tranp->t_toconlypr ) { |
| mlog( MLOG_TRACE, |
| "restoring regular file ino %llu %s\n", |
| bstatp->bs_ino, |
| path ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "%s\n", |
| path ); |
| } |
| } |
| |
| if ( tranp->t_toconlypr ) |
| return BOOL_TRUE; |
| |
| oflags = O_CREAT | O_RDWR; |
| if (persp->a.dstdirisxfspr && bstatp->bs_xflags & XFS_XFLAG_REALTIME) |
| oflags |= O_DIRECT; |
| |
| *fdp = open( path, oflags, S_IRUSR | S_IWUSR ); |
| if ( *fdp < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, |
| _("open of %s failed: %s: discarding ino %llu\n"), |
| path, |
| strerror( errno ), |
| bstatp->bs_ino ); |
| return BOOL_TRUE; |
| } |
| |
| rval = fstat64( *fdp, &stat ); |
| if ( rval != 0 ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("attempt to stat %s failed: %s\n"), |
| path, |
| strerror( errno )); |
| } else { |
| if ( stat.st_size != bstatp->bs_size ) { |
| mlog( MLOG_TRACE, |
| "truncating %s from %lld to %lld\n", |
| path, |
| stat.st_size, |
| bstatp->bs_size ); |
| |
| rval = ftruncate64( *fdp, bstatp->bs_size ); |
| if ( rval != 0 ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("attempt to truncate %s failed: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| } |
| } |
| |
| if ( persp->a.dstdirisxfspr ) { |
| |
| /* set the extended inode flags, except those which must |
| * be set only after all data has been restored. |
| */ |
| ASSERT( bstatp->bs_extsize >= 0 ); |
| memset((void *)&fsxattr, 0, sizeof( fsxattr )); |
| fsxattr.fsx_xflags = bstatp->bs_xflags & ~POST_DATA_XFLAGS; |
| fsxattr.fsx_extsize = (u_int32_t) bstatp->bs_extsize; |
| fsxattr.fsx_projid = bstat_projid(bstatp); |
| |
| rval = ioctl( *fdp, XFS_IOC_FSSETXATTR, (void *)&fsxattr); |
| if ( rval < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, |
| _("attempt to set extended attributes " |
| "(xflags 0x%x, extsize = 0x%x, projid = 0x%x) " |
| "of %s failed: %s\n"), |
| fsxattr.fsx_xflags, |
| fsxattr.fsx_extsize, |
| fsxattr.fsx_projid, |
| path, |
| strerror(errno)); |
| } |
| } |
| |
| if ( persp->a.dstdirisxfspr && persp->a.restoredmpr ) { |
| HsmBeginRestoreFile( bstatp, |
| *fdp, |
| &strctxp->sc_hsmflags ); |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| /* called to peel a regular file's extent groups from the media. |
| * if no path given, or if just toc, don't actually write, just |
| * read. fd == -1 signifies no write. *rvp is set to indicate |
| * drive errors. returns FALSE if should abort this iteration. |
| */ |
| static bool_t |
| restore_extent_group( drive_t *drivep, |
| filehdr_t *fhdrp, |
| char *path, |
| intgen_t fd, |
| bool_t ehcs, |
| rv_t *rvp ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| off64_t restoredsz = 0; |
| extenthdr_t ehdr; |
| off64_t bytesread; |
| rv_t rv; |
| |
| /* copy data extents from media to the file |
| */ |
| for ( ; ; ) { |
| /* read the extent header |
| */ |
| rv = read_extenthdr( drivep, &ehdr, ehcs ); |
| if ( rv != RV_OK ) { |
| *rvp = rv; |
| return BOOL_FALSE; |
| } |
| mlog( MLOG_NITTY, |
| "read extent hdr type %s offset %lld sz %lld flags %x\n", |
| ehdr_typestr( ehdr.eh_type ), |
| ehdr.eh_offset, |
| ehdr.eh_sz, |
| ehdr.eh_flags ); |
| |
| /* if we see the specially marked last extent hdr, |
| * we are done. |
| */ |
| if ( ehdr.eh_type == EXTENTHDR_TYPE_LAST ) { |
| break; |
| } |
| |
| /* if its an ALIGNment extent, discard the extent. |
| */ |
| if ( ehdr.eh_type == EXTENTHDR_TYPE_ALIGN ) { |
| size_t sz; |
| ASSERT( ehdr.eh_sz <= INTGENMAX ); |
| sz = ( size_t )ehdr.eh_sz; |
| rv = discard_padding( sz, drivep ); |
| if ( rv != RV_OK ) { |
| *rvp = rv; |
| return BOOL_FALSE; |
| } |
| continue; |
| } |
| |
| /* Add up extents restored to later check if the file |
| * is done. |
| */ |
| restoredsz += ehdr.eh_sz; /* Increments of block size (usually 512) */ |
| |
| /* Holes do not need to be restored since we now |
| * unlink the file at the start of the restore. |
| */ |
| if ( ehdr.eh_type == EXTENTHDR_TYPE_HOLE ) { |
| continue; |
| } |
| |
| /* real data |
| */ |
| ASSERT( ehdr.eh_type == EXTENTHDR_TYPE_DATA ); |
| bytesread = 0; |
| rv = restore_extent( fhdrp, |
| &ehdr, |
| fd, |
| path, |
| drivep, |
| &bytesread ); |
| if ( rv != RV_OK ) { |
| *rvp = rv; |
| return BOOL_FALSE; |
| } |
| |
| if ( cldmgr_stop_requested( )) { |
| *rvp = RV_INTR; |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* The extent group has been restored. If the file is not |
| * complete, we may need to co-ordinate with other restore |
| * streams to time the restoration of extended attributes |
| * and certain extended inode flags. Register the portion |
| * of the file completed here in the persistent state. |
| */ |
| if (bstatp->bs_size > restoredsz) { |
| partial_reg(drivep->d_index, |
| bstatp->bs_ino, |
| bstatp->bs_size, |
| fhdrp->fh_offset, |
| restoredsz); |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| /* apply the attributes that can only go on now that all data |
| * and extended attributes have been applied. fd == -1 signifies |
| * no write, due to unknown path or toc only. |
| */ |
| static bool_t |
| restore_complete_reg(stream_context_t *strcxtp) |
| { |
| bstat_t *bstatp = &strcxtp->sc_bstat; |
| char *path = strcxtp->sc_path; |
| intgen_t fd = strcxtp->sc_fd; |
| struct utimbuf utimbuf; |
| intgen_t rval; |
| |
| // only applies to regular files |
| if (!S_ISREG((strcxtp->sc_bstat.bs_mode))) |
| return BOOL_TRUE; |
| |
| if (fd < 0) |
| return BOOL_TRUE; |
| |
| if (!partial_check(bstatp->bs_ino, bstatp->bs_size)) { |
| close(fd); |
| return BOOL_TRUE; |
| } |
| |
| /* set the access and modification times |
| */ |
| utimbuf.actime = ( time32_t )bstatp->bs_atime.tv_sec; |
| utimbuf.modtime = ( time32_t )bstatp->bs_mtime.tv_sec; |
| rval = utime( path, &utimbuf ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to set access and modification " |
| "times of %s: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| |
| /* set the owner and group (if enabled) |
| */ |
| if ( persp->a.ownerpr ) { |
| rval = fchown( fd, |
| ( uid_t )bstatp->bs_uid, |
| ( gid_t )bstatp->bs_gid ); |
| if ( rval ) { |
| mode_t mode = (mode_t)bstatp->bs_mode; |
| |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("chown (uid=%u, gid=%u) %s failed: %s\n"), |
| bstatp->bs_uid, |
| bstatp->bs_gid, |
| path, |
| strerror( errno )); |
| |
| if ( mode & S_ISUID ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("stripping setuid bit on %s " |
| "since chown failed\n"), |
| path ); |
| mode &= ~S_ISUID; |
| } |
| if ( (mode & (S_ISGID|S_IXGRP)) == (S_ISGID|S_IXGRP) ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("stripping setgid bit on %s " |
| "since chown failed\n"), |
| path ); |
| mode &= ~S_ISGID; |
| } |
| if ( mode != (mode_t)bstatp->bs_mode ) { |
| rval = fchmod( fd, mode ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_ERROR, |
| _("unable to strip setuid/setgid " |
| "on %s, unlinking file.\n"), |
| path ); |
| unlink( path ); |
| } |
| } |
| } |
| } |
| |
| /* set the permissions/mode |
| */ |
| rval = fchmod( fd, ( mode_t )bstatp->bs_mode ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to set mode of %s: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| |
| if ( persp->a.dstdirisxfspr && persp->a.restoredmpr ) { |
| fsdmidata_t fssetdm; |
| |
| /* Set the DMAPI Fields. */ |
| fssetdm.fsd_dmevmask = bstatp->bs_dmevmask; |
| fssetdm.fsd_padding = 0; |
| fssetdm.fsd_dmstate = bstatp->bs_dmstate; |
| |
| rval = ioctl( fd, XFS_IOC_FSSETDM, ( void * )&fssetdm ); |
| if ( rval ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, |
| _("attempt to set DMI attributes of %s " |
| "failed: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| |
| HsmEndRestoreFile( path, fd, &strcxtp->sc_hsmflags ); |
| } |
| |
| /* set any extended inode flags that couldn't be set |
| * prior to restoring the data. |
| */ |
| if ( persp->a.dstdirisxfspr && bstatp->bs_xflags & POST_DATA_XFLAGS ) { |
| struct fsxattr fsxattr; |
| memset((void *)&fsxattr, 0, sizeof( fsxattr )); |
| fsxattr.fsx_xflags = bstatp->bs_xflags; |
| fsxattr.fsx_extsize = (u_int32_t)bstatp->bs_extsize; |
| fsxattr.fsx_projid = bstat_projid(bstatp); |
| |
| rval = ioctl( fd, XFS_IOC_FSSETXATTR, (void *)&fsxattr ); |
| if ( rval < 0 ) { |
| mlog(MLOG_NORMAL | MLOG_WARNING, |
| _("attempt to set extended attributes " |
| "(xflags 0x%x, extsize = 0x%x, projid = 0x%x) " |
| "of %s failed: %s\n"), |
| fsxattr.fsx_xflags, |
| fsxattr.fsx_extsize, |
| fsxattr.fsx_projid, |
| path, |
| strerror(errno)); |
| } |
| } |
| |
| close(fd); |
| return BOOL_TRUE; |
| } |
| |
| /* ARGSUSED */ |
| static bool_t |
| restore_spec( filehdr_t *fhdrp, rv_t *rvp, char *path ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| struct utimbuf utimbuf; |
| char *printstr; |
| intgen_t rval; |
| |
| if ( ! path ) { |
| return BOOL_TRUE; |
| } |
| |
| switch ( bstatp->bs_mode & S_IFMT ) { |
| case S_IFBLK: |
| printstr = _("block special file"); |
| break; |
| case S_IFCHR: |
| printstr = _("char special file"); |
| break; |
| case S_IFIFO: |
| printstr = _("named pipe"); |
| break; |
| #ifdef S_IFNAM |
| case S_IFNAM: |
| printstr = _("XENIX named pipe"); |
| break; |
| #endif |
| case S_IFSOCK: |
| printstr = _("UNIX domain socket"); |
| break; |
| default: |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "%s: unknown file type: mode 0x%x ino %llu\n"), |
| path, |
| bstatp->bs_mode, |
| fhdrp->fh_stat.bs_ino ); |
| return BOOL_TRUE; |
| } |
| |
| if ( ! tranp->t_toconlypr ) { |
| mlog( MLOG_TRACE, |
| "restoring %s ino %llu %s\n", |
| printstr, |
| fhdrp->fh_stat.bs_ino, |
| path ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "%s\n", |
| path ); |
| } |
| |
| if ( ! tranp->t_toconlypr ) { |
| if ( ( bstatp->bs_mode & S_IFMT ) == S_IFSOCK ) { |
| int sockfd; |
| struct sockaddr_un addr; |
| size_t addrlen; |
| |
| sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ); |
| if ( sockfd < 0 ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to create " |
| "%s ino %llu %s: %s: discarding\n"), |
| printstr, |
| fhdrp->fh_stat.bs_ino, |
| path, |
| strerror( errno )); |
| return BOOL_TRUE; |
| } |
| memset( ( void * )&addr, 0, sizeof( addr )); |
| addr.sun_family = AF_UNIX; |
| if ( strlen( path ) >= sizeof( addr.sun_path )) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "pathname too long for bind of " |
| "%s ino %llu %s: %s: discarding\n"), |
| printstr, |
| fhdrp->fh_stat.bs_ino, |
| path ); |
| ( void )close( sockfd ); |
| return BOOL_TRUE; |
| } |
| strcpy( addr.sun_path, path ); |
| addrlen = strlen( addr.sun_path ) |
| + |
| sizeof( addr.sun_family ); |
| rval = bind( sockfd, |
| ( struct sockaddr * )&addr, |
| ( int )addrlen ); |
| if ( rval < 0 ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to bind " |
| "%s ino %llu %s: %s: discarding\n"), |
| printstr, |
| fhdrp->fh_stat.bs_ino, |
| path, |
| strerror( errno )); |
| ( void )close( sockfd ); |
| return BOOL_TRUE; |
| } |
| ( void )close( sockfd ); |
| |
| } else { |
| /* create the node |
| */ |
| rval = mknod( path, |
| ( mode_t )bstatp->bs_mode, |
| ( dev_t )IRIX_DEV_TO_KDEVT(bstatp->bs_rdev)); |
| if ( rval && rval != EEXIST ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to create %s " |
| "ino %llu %s: %s: discarding\n"), |
| printstr, |
| fhdrp->fh_stat.bs_ino, |
| path, |
| strerror( errno )); |
| return BOOL_TRUE; |
| } |
| } |
| |
| /* set the owner and group (if enabled) |
| */ |
| if ( persp->a.ownerpr ) { |
| rval = chown( path, |
| ( uid_t )bstatp->bs_uid, |
| ( gid_t )bstatp->bs_gid ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("chown (uid=%u, gid=%u) %s " |
| "failed: %s\n"), |
| bstatp->bs_uid, |
| bstatp->bs_gid, |
| path, |
| strerror( errno )); |
| } |
| } |
| |
| /* set the permissions/mode |
| */ |
| rval = chmod( path, ( mode_t )fhdrp->fh_stat.bs_mode ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to set mode of %s: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| |
| /* set the access and modification times |
| */ |
| utimbuf.actime = ( time32_t )bstatp->bs_atime.tv_sec; |
| utimbuf.modtime = ( time32_t )bstatp->bs_mtime.tv_sec; |
| rval = utime( path, &utimbuf ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to set access and modification " |
| "times of %s: %s\n"), |
| path, |
| strerror( errno )); |
| } |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| static bool_t |
| restore_symlink( drive_t *drivep, |
| filehdr_t *fhdrp, |
| rv_t *rvp, |
| char *path, |
| char *scratchpath, |
| bool_t ehcs ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| drive_ops_t *dop = drivep->d_opsp; |
| extenthdr_t ehdr; |
| char *scratch; |
| intgen_t nread; |
| intgen_t rval; |
| rv_t rv; |
| mode_t oldumask; |
| |
| if ( path ) { |
| if ( ! tranp->t_toconlypr ) { |
| mlog( MLOG_TRACE, |
| "restoring symbolic link ino %llu %s\n", |
| bstatp->bs_ino, |
| path ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE, |
| "%s\n", |
| path ); |
| } |
| } |
| |
| /* read the extent header |
| */ |
| rv = read_extenthdr( drivep, &ehdr, ehcs ); |
| if ( rv != RV_OK ) { |
| *rvp = rv; |
| return BOOL_FALSE; |
| } |
| |
| /* symlinks always have one extent |
| */ |
| ASSERT( ehdr.eh_type == EXTENTHDR_TYPE_DATA ); |
| |
| /* read the link path extent |
| */ |
| if ( ehdr.eh_sz < ( off64_t )( 2 * MAXPATHLEN )) { |
| scratch = scratchpath; |
| } else { |
| scratch = 0; |
| } |
| nread = read_buf( scratch, |
| ( size_t )ehdr.eh_sz, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| if ( rval ) { |
| switch( rval ) { |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| *rvp = RV_EOD; |
| break; |
| case DRIVE_ERROR_CORRUPTION: |
| *rvp = RV_CORRUPT; |
| break; |
| case DRIVE_ERROR_DEVICE: |
| *rvp = RV_DRIVE; |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| *rvp = RV_CORE; |
| } |
| return BOOL_FALSE; |
| } |
| ASSERT( ( off64_t )nread == ehdr.eh_sz ); |
| if ( ! scratch ) { |
| if ( path ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to create symlink ino %llu " |
| "%s: src too long: discarding\n"), |
| bstatp->bs_ino, |
| path ); |
| } |
| return BOOL_TRUE; |
| } |
| scratchpath[ nread ] = 0; |
| if ( ! tranp->t_toconlypr && path ) { |
| /* create the symbolic link |
| */ |
| /* NOTE: There is no direct way to set mode for |
| * sym links. Do it using umask. |
| * No way of setting times for sym links. |
| */ |
| |
| oldumask = umask( (( mode_t )(~bstatp->bs_mode)) & 0777 ); |
| |
| rval = symlink( scratchpath, path ); |
| |
| umask( oldumask ); |
| |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to create " |
| "symlink ino %llu %s: %s: discarding\n"), |
| bstatp->bs_ino, |
| path, |
| strerror( errno )); |
| return BOOL_TRUE; |
| } |
| |
| /* set the owner and group (if enabled) |
| */ |
| if ( persp->a.ownerpr ) { |
| rval = lchown( path, |
| ( uid_t )bstatp->bs_uid, |
| ( gid_t )bstatp->bs_gid ); |
| if ( rval ) { |
| mlog( MLOG_VERBOSE | MLOG_WARNING, |
| _("chown (uid=%u, gid=%u) %s " |
| "failed: %s\n"), |
| bstatp->bs_uid, |
| bstatp->bs_gid, |
| path, |
| strerror( errno )); |
| } |
| } |
| |
| if ( persp->a.restoredmpr) { |
| fsdmidata_t fssetdm; |
| |
| /* Restore DMAPI fields. */ |
| |
| fssetdm.fsd_dmevmask = bstatp->bs_dmevmask; |
| fssetdm.fsd_padding = 0; |
| fssetdm.fsd_dmstate = bstatp->bs_dmstate; |
| rval = do_fssetdm_by_handle(path, &fssetdm); |
| } |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| read_filehdr( drive_t *drivep, filehdr_t *fhdrp, bool_t fhcs ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| drive_ops_t *dop = drivep->d_opsp; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| filehdr_t tmpfh; |
| |
| nread = read_buf( ( char * )&tmpfh, |
| sizeof( *fhdrp ), |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| xlate_filehdr(&tmpfh, fhdrp, 1); |
| |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( ( size_t )nread == sizeof( *fhdrp )); |
| |
| mlog( MLOG_NITTY, |
| "read file hdr off %lld flags 0x%x ino %llu mode 0x%08x\n", |
| fhdrp->fh_offset, |
| fhdrp->fh_flags, |
| bstatp->bs_ino, |
| bstatp->bs_mode ); |
| |
| if ( fhcs ) { |
| if ( ! ( fhdrp->fh_flags & FILEHDR_FLAGS_CHECKSUM )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "corrupt file header\n") ); |
| return RV_CORRUPT; |
| } |
| if ( !is_checksum_valid( fhdrp, FILEHDR_SZ )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "bad file header checksum\n") ); |
| return RV_CORRUPT; |
| } |
| } |
| |
| return RV_OK; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| read_extenthdr( drive_t *drivep, extenthdr_t *ehdrp, bool_t ehcs ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| extenthdr_t tmpeh; |
| |
| nread = read_buf( ( char * )&tmpeh, |
| sizeof( *ehdrp ), |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| xlate_extenthdr(&tmpeh, ehdrp, 1); |
| |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( ( size_t )nread == sizeof( *ehdrp )); |
| |
| mlog( MLOG_NITTY, |
| "read extent hdr size %lld offset %lld type %d flags %08x\n", |
| ehdrp->eh_sz, |
| ehdrp->eh_offset, |
| ehdrp->eh_type, |
| ehdrp->eh_flags ); |
| |
| if ( ehcs ) { |
| if ( ! ( ehdrp->eh_flags & EXTENTHDR_FLAGS_CHECKSUM )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "corrupt extent header\n") ); |
| return RV_CORRUPT; |
| } |
| if ( !is_checksum_valid( ehdrp, EXTENTHDR_SZ )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "bad extent header checksum\n") ); |
| return RV_CORRUPT; |
| } |
| } |
| |
| return RV_OK; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| read_dirent( drive_t *drivep, |
| direnthdr_t *dhdrp, |
| size_t direntbufsz, |
| bool_t dhcs ) |
| { |
| global_hdr_t *grhdrp = drivep->d_greadhdrp; |
| drive_ops_t *dop = drivep->d_opsp; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| direnthdr_t tmpdh; |
| char *namep; // beginning of name following the direnthdr_t |
| |
| ASSERT( sizeof( direnthdr_t ) == DIRENTHDR_SZ ); |
| ASSERT( sizeof( direnthdr_v1_t ) == DIRENTHDR_SZ ); |
| |
| /* read the head of the dirent |
| */ |
| nread = read_buf( ( char * )&tmpdh, |
| DIRENTHDR_SZ, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t ) |
| dop->do_return_read_buf, |
| &rval ); |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( ( size_t )nread == DIRENTHDR_SZ ); |
| |
| if ( grhdrp->gh_version >= GLOBAL_HDR_VERSION_3 ) { |
| xlate_direnthdr(&tmpdh, dhdrp, 1); |
| namep = dhdrp->dh_name + sizeof(dhdrp->dh_name); |
| |
| if ( dhcs && !is_checksum_valid( dhdrp, DIRENTHDR_SZ )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "bad directory entry header checksum\n") ); |
| return RV_CORRUPT; |
| } |
| } else { |
| direnthdr_v1_t dhdr_v1; |
| xlate_direnthdr_v1((direnthdr_v1_t *)&tmpdh, &dhdr_v1, 1); |
| dhdrp->dh_ino = dhdr_v1.dh_ino; |
| dhdrp->dh_gen = BIGGEN2GEN(dhdr_v1.dh_gen); |
| dhdrp->dh_checksum = dhdr_v1.dh_checksum; |
| dhdrp->dh_sz = dhdr_v1.dh_sz; |
| memcpy(dhdrp->dh_name, dhdr_v1.dh_name, sizeof(dhdr_v1.dh_name)); |
| namep = dhdrp->dh_name + sizeof(dhdr_v1.dh_name); |
| |
| if ( dhcs && !is_checksum_valid( &dhdr_v1, DIRENTHDR_SZ )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "bad directory entry header checksum\n") ); |
| return RV_CORRUPT; |
| } |
| } |
| |
| mlog( MLOG_NITTY, |
| "read dirent hdr ino %llu gen %u size %u\n", |
| dhdrp->dh_ino, |
| ( size_t )dhdrp->dh_gen, |
| ( size_t )dhdrp->dh_sz ); |
| |
| if ( dhdrp->dh_sz == 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "corrupt directory entry header\n") ); |
| return RV_CORRUPT; |
| } |
| |
| /* if null, return |
| */ |
| if ( dhdrp->dh_ino == 0 ) { |
| ASSERT( ( size_t )dhdrp->dh_sz == sizeof( direnthdr_t )); |
| return RV_OK; |
| } |
| |
| /* read the remainder of the dirent. |
| */ |
| ASSERT( ( size_t )dhdrp->dh_sz <= direntbufsz ); |
| ASSERT( ( size_t )dhdrp->dh_sz >= sizeof( direnthdr_t )); |
| ASSERT( ! ( ( size_t )dhdrp->dh_sz & ( DIRENTHDR_ALIGN - 1 ))); |
| if ( ( size_t )dhdrp->dh_sz > sizeof( direnthdr_t )) { |
| size_t remsz = ( size_t )dhdrp->dh_sz - sizeof( direnthdr_t ); |
| nread = read_buf( namep, |
| remsz, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t ) |
| dop->do_return_read_buf, |
| &rval ); |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( ( size_t ) nread == remsz ); |
| } |
| |
| return RV_OK; |
| } |
| |
| /* ARGSUSED */ |
| static rv_t |
| read_extattrhdr( drive_t *drivep, extattrhdr_t *ahdrp, bool_t ahcs ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| extattrhdr_t tmpah; |
| |
| nread = read_buf( ( char * )&tmpah, |
| sizeof( *ahdrp ), |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| xlate_extattrhdr(&tmpah, ahdrp, 1); |
| |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( ( size_t )nread == sizeof( *ahdrp )); |
| |
| mlog( MLOG_NITTY, |
| "read extattr hdr sz %u valoff %u flags 0x%x valsz %u cs 0x%x\n", |
| ahdrp->ah_sz, |
| ( u_intgen_t )ahdrp->ah_valoff, |
| ( u_intgen_t )ahdrp->ah_flags, |
| ahdrp->ah_valsz, |
| ahdrp->ah_checksum ); |
| |
| if ( ahcs ) { |
| if ( ahdrp->ah_flags & EXTATTRHDR_FLAGS_CHECKSUM ) { |
| if ( !is_checksum_valid( ahdrp, EXTATTRHDR_SZ )) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "bad extattr header checksum\n") ); |
| return RV_CORRUPT; |
| } |
| } else if ( ahdrp->ah_flags & EXTATTRHDR_FLAGS_OLD_CHECKSUM ) { |
| /* possibly a corrupt header, but most likely an old |
| * header, which cannot be verified due to a bug in how |
| * its checksum was calculated. |
| */ |
| static bool_t warned = BOOL_FALSE; |
| if ( !warned ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "ignoring old-style extattr " |
| "header checksums\n") ); |
| warned = BOOL_TRUE; |
| } |
| } else { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "corrupt extattr header\n") ); |
| return RV_CORRUPT; |
| } |
| } |
| |
| return RV_OK; |
| } |
| |
| static rv_t |
| discard_padding( size_t sz, drive_t *drivep ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| |
| nread = read_buf( 0, |
| sz, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| switch( rval ) { |
| case 0: |
| ASSERT( ( size_t )nread == sz ); |
| return RV_OK; |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| } |
| |
| static rv_t |
| restore_extent( filehdr_t *fhdrp, |
| extenthdr_t *ehdrp, |
| int fd, |
| char *path, |
| drive_t *drivep, |
| off64_t *bytesreadp ) |
| { |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| drive_ops_t *dop = drivep->d_opsp; |
| off64_t off = ehdrp->eh_offset; |
| off64_t sz = ehdrp->eh_sz; |
| off64_t new_off; |
| struct dioattr da; |
| bool_t isrealtime = BOOL_FALSE; |
| |
| *bytesreadp = 0; |
| |
| if ( fd != -1 ) { |
| ASSERT( path ); |
| /* seek to the beginning of the extent. |
| * must be on a basic fs blksz boundary. |
| */ |
| ASSERT( ( off & ( off64_t )( BBSIZE - 1 )) == 0 ); |
| new_off = lseek64( fd, off, SEEK_SET ); |
| if ( new_off < 0 ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "attempt to seek %s to %lld failed: %s: " |
| "not restoring extent off %lld sz %lld\n"), |
| path, |
| off, |
| strerror( errno ), |
| off, |
| sz ); |
| fd = -1; |
| new_off = off; |
| } |
| ASSERT( new_off == off ); |
| } |
| if ( (fd != -1) && (bstatp->bs_xflags & XFS_XFLAG_REALTIME) ) { |
| if ( (ioctl(fd, XFS_IOC_DIOINFO, &da) < 0) ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "dioinfo %s failed: " |
| "%s: discarding ino %llu\n"), |
| path, |
| strerror( errno ), |
| fhdrp->fh_stat.bs_ino ); |
| fd = -1; |
| } else |
| isrealtime = BOOL_TRUE; |
| } |
| |
| /* move from media to fs. |
| */ |
| while ( sz ) { |
| char *bufp; |
| size_t req_bufsz; /* requested bufsz */ |
| size_t sup_bufsz; /* supplied bufsz */ |
| intgen_t nwritten; |
| intgen_t rval; |
| size_t ntowrite; |
| |
| req_bufsz = ( size_t )min( ( off64_t )INTGENMAX, sz ); |
| bufp = ( * dop->do_read )(drivep, req_bufsz, &sup_bufsz, &rval); |
| if ( rval ) { |
| rv_t rv; |
| char *reasonstr; |
| switch( rval ) { |
| case DRIVE_ERROR_EOF: |
| rv = RV_EOD; |
| reasonstr = _("end of media file"); |
| break; |
| case DRIVE_ERROR_EOD: |
| rv = RV_EOD; |
| reasonstr = _("end of recorded data"); |
| break; |
| case DRIVE_ERROR_EOM: |
| rv = RV_EOD; |
| reasonstr = _("end of media"); |
| break; |
| case DRIVE_ERROR_MEDIA: |
| rv = RV_EOD; |
| reasonstr = _("media error or no media"); |
| break; |
| case DRIVE_ERROR_CORRUPTION: |
| rv = RV_CORRUPT; |
| reasonstr = _("end of media file"); |
| break; |
| case DRIVE_ERROR_DEVICE: |
| rv = RV_DRIVE; |
| reasonstr = _("end of media file"); |
| break; |
| case DRIVE_ERROR_CORE: |
| default: |
| rv = RV_CORE; |
| reasonstr = _("dumping core"); |
| break; |
| } |
| mlog( MLOG_NORMAL, _( |
| "attempt to read %u bytes failed: %s\n"), |
| req_bufsz, |
| reasonstr ); |
| return rv; |
| } |
| if ( off >= bstatp->bs_size ) { |
| ASSERT( off == bstatp->bs_size ); |
| ntowrite = 0; |
| } else if ((off64_t)sup_bufsz > bstatp->bs_size - off ) { |
| ntowrite = ( size_t )( bstatp->bs_size - off ); |
| } else { |
| ntowrite = sup_bufsz; |
| } |
| ASSERT( ntowrite <= ( size_t )INTGENMAX ); |
| if ( ntowrite > 0 ) { |
| *bytesreadp += ( off64_t )ntowrite; |
| if ( fd != -1 ) { |
| size_t tries; |
| size_t remaining; |
| intgen_t rval; |
| off64_t tmp_off; |
| |
| rval = 0; /* for lint */ |
| for ( nwritten = 0, |
| tries = 0, |
| remaining = ntowrite, |
| tmp_off = off |
| ; |
| nwritten < ( intgen_t )ntowrite |
| && |
| tries < WRITE_TRIES_MAX |
| ; |
| nwritten += rval, |
| tries++, |
| remaining -= ( size_t )rval, |
| tmp_off += ( off64_t )rval ) { |
| int rttrunc = 0; |
| int trycnt = 0; |
| ASSERT( remaining |
| <= |
| ( size_t )INTGENMAX ); |
| /* |
| * Realtime files must be written |
| * to the end of the block even if |
| * it has been truncated back. |
| */ |
| if ( isrealtime && |
| (remaining % da.d_miniosz != 0 || |
| remaining < da.d_miniosz) ) { |
| /* |
| * Since the ring and static |
| * buffers from the different |
| * drives are always large, we |
| * just need to write to the |
| * end of the next block |
| * boundry and truncate. |
| */ |
| rttrunc = remaining; |
| remaining += da.d_miniosz - |
| (remaining % da.d_miniosz); |
| } |
| /* |
| * Do the write. Due to delayed allocation |
| * it's possible to receive false ENOSPC |
| * errors when the filesystem is nearly |
| * full. XFS kernel code tries to avoid |
| * this, but cannot always do so. Catch |
| * ENOSPC and mimic the kernel behavior |
| * by trying to flush the current file |
| * first, then trying a system wide sync |
| * if ENOSPC still occurs. |
| */ |
| for (trycnt = 0; trycnt < 3; trycnt++) { |
| rval = write( fd, bufp, remaining ); |
| if (rval >= 0 || errno != ENOSPC) |
| break; |
| |
| ( trycnt == 0 ) ? |
| fdatasync(fd) : sync(); |
| } |
| if ( rval < 0 ) { |
| nwritten = rval; |
| break; |
| } |
| ASSERT( ( size_t )rval <= remaining ); |
| if ( rval < remaining ) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, |
| _("attempt to " |
| "write %u bytes to %s at " |
| "offset %lld failed: " |
| "only %d bytes written\n"), |
| remaining, |
| path, |
| tmp_off, |
| rval ); |
| } |
| if (rttrunc) { |
| /* truncate and re-set rval */ |
| if (rval == remaining) |
| rval = rttrunc; |
| ftruncate(fd, bstatp->bs_size); |
| } |
| } |
| } else { |
| nwritten = ( intgen_t )ntowrite; |
| } |
| } else { |
| nwritten = 0; |
| } |
| ( * dop->do_return_read_buf )( drivep, bufp, sup_bufsz ); |
| if ( ( size_t )nwritten != ntowrite ) { |
| if ( nwritten < 0 ) { |
| mlog( MLOG_NORMAL, _( |
| "attempt to write %u bytes to %s " |
| "at offset %lld failed: %s\n"), |
| ntowrite, |
| path, |
| off, |
| strerror( errno )); |
| } else { |
| ASSERT( ( size_t )nwritten < ntowrite ); |
| mlog( MLOG_NORMAL, _( |
| "attempt to write %u bytes to %s at " |
| "offset %lld failed: only %d bytes " |
| "written\n"), |
| ntowrite, |
| path, |
| off, |
| nwritten ); |
| } |
| /* stop attempting to write, but complete reads |
| */ |
| fd = -1; |
| ASSERT( ntowrite <= ( size_t )INTGENMAX ); |
| nwritten = ( intgen_t )ntowrite; |
| } |
| sz -= ( off64_t )sup_bufsz; |
| off += ( off64_t )nwritten; |
| } |
| |
| return RV_OK; |
| } |
| |
| static char *extattrbufp = 0; /* ptr to start of all the extattr buffers */ |
| static size_t extattrbufsz = 0; /* size of each extattr buffer */ |
| |
| static bool_t |
| extattr_init( size_t drivecnt ) |
| { |
| ASSERT( ! extattrbufp ); |
| extattrbufsz = EXTATTRHDR_SZ /* dump hdr */ |
| + |
| NAME_MAX /* attribute name */ |
| + |
| 1 /* NULL term. of name */ |
| + |
| ATTR_MAX_VALUELEN; /* attribute value */ |
| extattrbufsz = roundup(extattrbufsz, EXTATTRHDR_ALIGN); |
| |
| extattrbufp = memalign( EXTATTRHDR_ALIGN, extattrbufsz * drivecnt ); |
| if (extattrbufp == NULL) { |
| mlog( MLOG_NORMAL | MLOG_ERROR, _( |
| "Failed to allocate extended attribute buffer\n") ); |
| return BOOL_FALSE; |
| } |
| |
| return BOOL_TRUE; |
| } |
| |
| static char * |
| get_extattrbuf( ix_t which ) |
| { |
| return extattrbufp + (extattrbufsz * which); |
| } |
| |
| |
| struct extattr_cb_ctx { |
| extattrhdr_t *ecb_ahdrp; |
| }; |
| |
| typedef struct extattr_cb_ctx extattr_cb_ctx_t; |
| |
| static rv_t |
| restore_extattr( drive_t *drivep, |
| filehdr_t *fhdrp, |
| char *path, |
| bool_t ahcs, |
| bool_t isdirpr, |
| bool_t onlydoreadpr, |
| dah_t dah ) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| extattrhdr_t *ahdrp = ( extattrhdr_t * )get_extattrbuf( drivep->d_index ); |
| stream_context_t *strctxp = (stream_context_t *)drivep->d_strmcontextp; |
| bstat_t *bstatp = &fhdrp->fh_stat; |
| bool_t isfilerestored = BOOL_FALSE; |
| |
| ASSERT( extattrbufp ); |
| |
| if ( ! isdirpr ) |
| isfilerestored = partial_check(bstatp->bs_ino, bstatp->bs_size); |
| |
| /* peel off extattrs until null hdr hit |
| */ |
| for ( ; ; ) { |
| size_t recsz; |
| /* REFERENCED */ |
| intgen_t nread; |
| intgen_t rval; |
| rv_t rv; |
| |
| rv = read_extattrhdr( drivep, ahdrp, ahcs ); |
| if ( rv != RV_OK ) { |
| return rv; |
| } |
| |
| if ( ahdrp->ah_flags & EXTATTRHDR_FLAGS_NULL ) { |
| return RV_OK; |
| } |
| |
| recsz = ( size_t )ahdrp->ah_sz; |
| ASSERT( recsz <= extattrbufsz ); |
| ASSERT( recsz >= EXTATTRHDR_SZ ); |
| nread = read_buf( ( char * )&ahdrp[ 1 ], |
| recsz - EXTATTRHDR_SZ, |
| ( void * )drivep, |
| ( rfp_t )dop->do_read, |
| ( rrbfp_t )dop->do_return_read_buf, |
| &rval ); |
| switch( rval ) { |
| case 0: |
| break; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| return RV_EOD; |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| ASSERT( nread == ( intgen_t )( recsz - EXTATTRHDR_SZ )); |
| |
| if ( ! persp->a.restoreextattrpr && ! persp->a.restoredmpr ) { |
| continue; |
| } |
| |
| if ( onlydoreadpr || tranp->t_toconlypr ) |
| continue; |
| |
| /* NOTE: In the cases below, if we get errors then we issue warnings |
| * but we do not stop the restoration. |
| * We can still restore the file possibly without the |
| * extended attributes. |
| */ |
| if ( isdirpr ) { |
| ASSERT( ! path ); |
| if ( dah != DAH_NULL ) { |
| dirattr_addextattr( dah, ahdrp ); |
| } |
| } else if ( isfilerestored && path[0] != '\0' ) { |
| setextattr( path, ahdrp ); |
| |
| if ( persp->a.dstdirisxfspr && persp->a.restoredmpr ) { |
| int flag = 0; |
| char *attrname = (char *)&ahdrp[1]; |
| if (ahdrp->ah_flags & EXTATTRHDR_FLAGS_ROOT) |
| flag = ATTR_ROOT; |
| else if (ahdrp->ah_flags & EXTATTRHDR_FLAGS_SECURE) |
| flag = ATTR_SECURE; |
| |
| HsmRestoreAttribute( flag, |
| attrname, |
| &strctxp->sc_hsmflags ); |
| } |
| } |
| } |
| /* NOTREACHED */ |
| } |
| |
| static bool_t |
| restore_dir_extattr_cb( char *path, dah_t dah ) |
| { |
| /* |
| * directory extattr's are built during the directory phase |
| * by 1 thread so we only need one extattr buffer |
| * -> we pick the 0th one |
| */ |
| extattrhdr_t *ahdrp = ( extattrhdr_t * )get_extattrbuf( 0 ); |
| bool_t ok; |
| |
| /* ask the dirattr abstraction to call me back for each |
| * extended dirattr associated with this dah. |
| */ |
| ok = dirattr_cb_extattr( dah, |
| restore_dir_extattr_cb_cb, |
| ahdrp, |
| ( void * )path ); |
| return ok; |
| } |
| |
| static bool_t |
| restore_dir_extattr_cb_cb( extattrhdr_t *ahdrp, void *ctxp ) |
| { |
| char *path = ( char * )ctxp; |
| |
| setextattr( path, ahdrp ); |
| |
| return BOOL_TRUE; |
| } |
| |
| static void |
| setextattr( char *path, extattrhdr_t *ahdrp ) |
| { |
| static char dmiattr[] = "SGI_DMI_"; |
| bool_t isrootpr = ahdrp->ah_flags & EXTATTRHDR_FLAGS_ROOT; |
| bool_t issecurepr = ahdrp->ah_flags & EXTATTRHDR_FLAGS_SECURE; |
| bool_t isdmpr; |
| int attr_namespace; |
| intgen_t rval; |
| |
| isdmpr = ( isrootpr && |
| !strncmp((char *)(&ahdrp[1]), dmiattr, sizeof(dmiattr)-1) ); |
| |
| /* If restoreextattrpr not set, then we are here because -D was |
| * specified. So return unless it looks like a root DMAPI attribute. |
| */ |
| if ( !persp->a.restoreextattrpr && !isdmpr ) |
| return; |
| |
| if ( isrootpr ) { |
| attr_namespace = ATTR_ROOT; |
| } else if ( issecurepr ) { |
| attr_namespace = ATTR_SECURE; |
| } else { |
| attr_namespace = 0; |
| } |
| |
| rval = attr_set( path, |
| ( char * )( &ahdrp[ 1 ] ), |
| ( ( char * )ahdrp ) + ( u_long_t )ahdrp->ah_valoff, |
| ( intgen_t )ahdrp->ah_valsz, |
| attr_namespace | ATTR_DONTFOLLOW ); |
| if ( rval ) { |
| char *namespace; |
| if ( isrootpr ) { |
| namespace = _("root"); |
| } else if ( issecurepr ) { |
| namespace = _("secure"); |
| } else { |
| namespace = _("non-root"); |
| } |
| |
| mlog( MLOG_VERBOSE | MLOG_WARNING, _( |
| "unable to set %s extended attribute for %s: " |
| "%s (%d)\n"), |
| namespace, |
| path, |
| strerror( errno ), |
| errno ); |
| } |
| } |
| |
| #ifdef DEBUGPARTIALS |
| /* |
| * Debug code to view the partials in the partial register |
| */ |
| void |
| dump_partials(void) |
| { |
| partial_rest_t *isptr = NULL; |
| bytespan_t *bsptr = NULL; |
| int i; |
| |
| pi_lock(); |
| printf("\npartial_reg: count=%d\n", persp->a.parrestcnt); |
| if (persp->a.parrestcnt > 0) { |
| for (i=0; i < partialmax; i++ ) { |
| if (persp->a.parrest[i].is_ino > 0) { |
| int j; |
| |
| isptr = &persp->a.parrest[i]; |
| printf( "\tino=%lld ", isptr->is_ino); |
| for (j=0, bsptr=isptr->is_bs; |
| j < drivecnt; |
| j++, bsptr++) |
| { |
| if (bsptr->endoffset > 0) { |
| printf("%d:%lld-%lld ", |
| j, bsptr->offset, |
| bsptr->endoffset); |
| } |
| } |
| printf( "\n"); |
| } |
| } |
| } |
| printf("\n"); |
| pi_unlock(); |
| } |
| |
| |
| /* There can only be at most 2 partials for a given stream. |
| * An unfinished one from a split and the current one from |
| * a multiple group extent or another split. |
| * If there are more than 2, then there is an internal error. |
| */ |
| void |
| check_valid_partials(void) |
| { |
| int num_partials[STREAM_MAX]; /* sum of partials for a given drive */ |
| partial_rest_t *isptr = NULL; |
| bytespan_t *bsptr = NULL; |
| int i; |
| |
| /* zero the sums for each stream */ |
| memset(num_partials, 0, sizeof(num_partials)); |
| |
| pi_lock(); |
| if (persp->a.parrestcnt > 0) { |
| for (i=0; i < partialmax; i++ ) { |
| if (persp->a.parrest[i].is_ino > 0) { |
| int j; |
| |
| isptr = &persp->a.parrest[i]; |
| for (j=0, bsptr=isptr->is_bs; |
| j < drivecnt; |
| j++, bsptr++) |
| { |
| if (bsptr->endoffset > 0) { |
| num_partials[j]++; |
| if (num_partials[j] > 2) { |
| pi_unlock(); |
| mlog( MLOG_NORMAL | MLOG_WARNING, |
| "partial_reg: Too many partials (>2) for drive: %d\n", j); |
| dump_partials(); |
| exit(EXIT_ERROR); |
| } |
| } |
| } |
| } |
| } |
| } |
| pi_unlock(); |
| } |
| #endif |
| |
| /* partial_reg - Registers files that are only partially restored by |
| * a dump stream into the persistent state. |
| * |
| * This is done because DMAPI extended attributes must not be set until |
| * the entire file has been restored in order to co-ordinate with the |
| * Data Migration Facility (DMF) daemons. Since extended attributes are |
| * recorded with each extent group in the dump, this registry is used to |
| * make sure only the final dump stream applies the extended attributes. |
| * |
| * Likewise, certain extended inode flags (e.g. XFS_XFLAG_IMMUTABLE) |
| * should only be set after all data for a file has been restored. |
| */ |
| static void |
| partial_reg( ix_t d_index, |
| xfs_ino_t ino, |
| off64_t fsize, |
| off64_t offset, |
| off64_t sz) |
| { |
| off64_t endoffset; |
| partial_rest_t *isptr = NULL; |
| bytespan_t *bsptr = NULL; |
| int i; |
| |
| mlog(MLOG_NITTY, "partial_reg: d_index = %d, ino = %llu, " |
| "fsize = %lld, offset = %lld, sz = %lld\n", |
| d_index, ino, fsize, offset, sz); |
| |
| endoffset = offset + sz; |
| |
| if ( partialmax == 0 ) |
| return; |
| |
| pi_lock(); |
| |
| /* Search for a matching inode. Gaps can exist so we must search |
| * all entries. |
| */ |
| for (i=0; i < partialmax; i++ ) { |
| if (persp->a.parrest[i].is_ino == ino) { |
| isptr = &persp->a.parrest[i]; |
| break; |
| } |
| } |
| |
| /* If not found, find a free one, fill it in and return */ |
| if ( ! isptr ) { |
| mlog(MLOG_NITTY | MLOG_NOLOCK, |
| "partial_reg: no entry found for %llu\n", ino); |
| /* find a free one */ |
| for (i=0; i < partialmax; i++ ) { |
| if (persp->a.parrest[i].is_ino == 0) { |
| int j; |
| |
| isptr = &persp->a.parrest[i]; |
| isptr->is_ino = ino; |
| persp->a.parrestcnt++; |
| |
| /* Clear all endoffsets (this value is |
| * used to decide if an entry is used or |
| * not |
| */ |
| for (j=0, bsptr=isptr->is_bs; |
| j < drivecnt; j++, bsptr++) { |
| bsptr->endoffset = 0; |
| } |
| |
| goto found; |
| } |
| } |
| |
| /* Should never get here. */ |
| pi_unlock(); |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "partial_reg: Out of records. Extend attrs applied early.\n")); |
| #ifdef DEBUGPARTIALS |
| dump_partials(); |
| #endif |
| } |
| |
| found: |
| /* Update this drive's entry */ |
| bsptr = &isptr->is_bs[d_index]; |
| if (bsptr->endoffset == 0) { |
| /* no existing entry for this drive, fill in the values */ |
| bsptr->offset = offset; |
| bsptr->endoffset = endoffset; |
| mlog(MLOG_NITTY | MLOG_NOLOCK, |
| "partial_reg: update entry [%d]: " |
| "<off = %lld, end = %lld>\n", |
| d_index, offset, endoffset); |
| } else { |
| bool_t ret; |
| |
| /* entry exists for this drive, just extend the endoffset, the |
| * records will be sequential for any given drive. |
| */ |
| bsptr->endoffset = endoffset; |
| ret = partial_check2(isptr, fsize); |
| mlog(MLOG_NITTY | MLOG_NOLOCK, |
| "partial_reg: extend entry [%d]: <end = %lld>\n", |
| d_index, endoffset); |
| mlog(MLOG_NITTY | MLOG_NOLOCK, |
| "partial_reg: partial_check returns: %d\n", ret); |
| } |
| |
| pi_unlock(); |
| |
| #ifdef DEBUGPARTIALS |
| check_valid_partials(); |
| dump_partials(); |
| #endif |
| } |
| |
| |
| /* Checks the registry of files that are only partially restored by |
| * any given dump stream to see if the remainder of the file has |
| * been restored by another dump stream. |
| */ |
| static bool_t |
| partial_check (xfs_ino_t ino, off64_t fsize) |
| { |
| partial_rest_t *isptr = NULL; |
| bool_t ret; |
| int i; |
| |
| if ( partialmax == 0 ) |
| return BOOL_TRUE; |
| |
| pi_lock(); |
| |
| /* Check if no files are listed in the sync area */ |
| if (persp->a.parrestcnt == 0) { |
| pi_unlock(); |
| return BOOL_TRUE; |
| } |
| |
| /* Search for the inode. Gaps can exist so we must search |
| * all entries. |
| */ |
| for (i=0; i < partialmax; i++ ) { |
| if (persp->a.parrest[i].is_ino == ino) { |
| isptr = &persp->a.parrest[i]; |
| break; |
| } |
| } |
| |
| /* If not found, return okay */ |
| if ( ! isptr ) { |
| pi_unlock(); |
| return BOOL_TRUE; |
| } |
| |
| ret = partial_check2(isptr, fsize); |
| |
| pi_unlock(); |
| |
| #ifdef DEBUGPARTIALS |
| check_valid_partials(); |
| dump_partials(); |
| #endif |
| |
| return ret; |
| } |
| |
| /* |
| * Checks the given parrest entry to see if the file has |
| * been completely restored. |
| * Always invoked with the persistent inventory locked (pi_lock) |
| */ |
| static bool_t |
| partial_check2(partial_rest_t *isptr, off64_t fsize) |
| { |
| bytespan_t *bsptr = NULL; |
| off64_t curoffset = 0; |
| int i; |
| |
| gapsearch: |
| /* Search the entire set of bytespan records to see if the next |
| * span has been restored. Bytespans are not necessarily in order |
| * so the search is repeated from the start each time. |
| */ |
| for (i=0, bsptr=isptr->is_bs; i < drivecnt; i++, bsptr++) { |
| if (bsptr->endoffset > 0 && |
| bsptr->offset <= curoffset && |
| bsptr->endoffset > curoffset) |
| { |
| curoffset = bsptr->endoffset; |
| goto gapsearch; |
| } |
| } |
| |
| /* Check if all bytes are accounted for. */ |
| if (curoffset >= fsize) { |
| isptr->is_ino = 0; /* clear the entry */ |
| persp->a.parrestcnt--; |
| return BOOL_TRUE; |
| } else { |
| return BOOL_FALSE; |
| } |
| } |
| |
| static char * |
| ehdr_typestr( int32_t type ) |
| { |
| switch ( type ) { |
| case EXTENTHDR_TYPE_LAST: |
| return "LAST"; |
| case EXTENTHDR_TYPE_ALIGN: |
| return "ALIGN"; |
| case EXTENTHDR_TYPE_DATA: |
| return "DATA"; |
| case EXTENTHDR_TYPE_HOLE: |
| return "HOLE"; |
| default: |
| return "?"; |
| } |
| } |
| |
| /* ARGSUSED */ |
| bool_t |
| content_overwrite_ok( char *path, |
| int32_t ctime, |
| int32_t mtime, |
| char **reasonstrp, |
| bool_t *exists ) |
| { |
| struct stat statbuf; |
| |
| *exists = BOOL_TRUE; |
| |
| /* if file doesn't exist, allow |
| */ |
| |
| if ( lstat( path, &statbuf )) { |
| *reasonstrp = 0; |
| if ( errno == ENOENT ) { |
| *exists = BOOL_FALSE; |
| } |
| return BOOL_TRUE; |
| } |
| |
| /* if overwrites absolutely inhibited, disallow |
| */ |
| if ( persp->a.existpr ) { |
| *reasonstrp = _("overwrites inhibited"); |
| return BOOL_FALSE; |
| } |
| |
| /* if newer time specified, compare |
| */ |
| if ( persp->a.newerpr ) { |
| if ( ( time32_t )ctime < persp->a.newertime ) { |
| *reasonstrp = _("too old"); |
| return BOOL_FALSE; |
| } |
| } |
| |
| /* don't overwrite changed files |
| */ |
| if ( persp->a.changepr ) { |
| if ( statbuf.st_ctime >= ( time32_t )ctime ) { |
| *reasonstrp = _("existing version is newer"); |
| return BOOL_FALSE; |
| } |
| } |
| |
| *reasonstrp = 0; |
| return BOOL_TRUE; |
| } |
| |
| |
| static void |
| set_mcflag( ix_t thrdix ) |
| { |
| lock( ); |
| mcflag[ thrdix ] = BOOL_TRUE; |
| content_media_change_needed = BOOL_TRUE; |
| unlock( ); |
| } |
| |
| static void |
| clr_mcflag( ix_t thrdix ) |
| { |
| lock( ); |
| mcflag[ thrdix ] = BOOL_FALSE; |
| for ( thrdix = 0 ; thrdix < drivecnt ; thrdix++ ) { |
| if ( mcflag[ thrdix ] ) { |
| unlock( ); |
| return; |
| } |
| } |
| content_media_change_needed = BOOL_FALSE; |
| unlock( ); |
| } |
| |
| /* debug functions ***********************************************************/ |
| |
| static void |
| pi_show( char *introstring ) |
| { |
| char strbuf[ 100 ]; |
| /* REFERENCED */ |
| intgen_t strbuflen; |
| fold_t fold; |
| |
| if ( mlog_level_ss[ MLOG_SS_MEDIA ] < MLOG_NITTY + 1 ) { |
| return; |
| } |
| |
| mlog_lock( ); |
| |
| strbuflen = sprintf( strbuf, |
| "persistent inventory media file tree%s", |
| introstring ); |
| ASSERT( ( size_t )strbuflen < sizeof( strbuf )); |
| fold_init( fold, strbuf, ':' ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| "\n%s\n\n", |
| fold ); |
| |
| pi_show_nomloglock( ); |
| |
| fold_init( fold, "end persistent inventory display", '.' ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| "\n%s\n\n", |
| fold ); |
| |
| mlog_unlock( ); |
| } |
| |
| static void |
| pi_show_nomloglock( void ) |
| { |
| dh_t strmh; |
| intgen_t strmix; |
| |
| |
| /* no point in proceeding if pi not begun |
| */ |
| if ( persp->s.strmheadh == DH_NULL ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, _( |
| "session inventory unknown\n") ); |
| return; |
| } |
| |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, _( |
| "session inventory display\n") ); |
| |
| /* iterate over all streams |
| */ |
| for ( strmh = persp->s.strmheadh, strmix = 0 |
| ; |
| strmh != DH_NULL |
| ; |
| strmh = DH2S( strmh )->s_nexth, strmix++ ) { |
| dh_t objh; |
| intgen_t objix; |
| |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\nmedia stream %u:\n"), |
| strmix ); |
| if ( DH2S( strmh )->s_cldh == DH_NULL ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\n media objects not yet identified\n") ); |
| continue; |
| } |
| /* iterate over all objects |
| */ |
| for ( objh = DH2S( strmh )->s_cldh, objix = 0 |
| ; |
| objh != DH_NULL |
| ; |
| objh = DH2O( objh )->o_nexth, objix++ ) { |
| dh_t fileh; |
| ix_t fileix; |
| |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\n media object %u:\n\n"), |
| objix ); |
| if ( DH2O( objh )->o_idlabvalpr ) { |
| if ( strlen( DH2O( objh )->o_lab )) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" label: ") ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| "\"%s\"\n", |
| DH2O( objh )->o_lab ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" label is blank\n") ); |
| } |
| if ( ! uuid_is_null( DH2O( objh )->o_id)) { |
| char media_string_uuid[UUID_STR_LEN + 1]; |
| uuid_unparse( DH2O( objh )->o_id, |
| media_string_uuid); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" id: %s\n"), |
| media_string_uuid ); |
| } |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" label not identified\n") ); |
| } |
| if ( DH2O( objh )->o_fmfmixvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" index within object " |
| "of first media file: %u\n"), |
| DH2O( objh )->o_fmfmix ); |
| } |
| if ( DH2O( objh )->o_fmfsixvalpr ) { |
| mlog( MLOG_DEBUG | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" index within stream " |
| "of first media file: %u\n"), |
| DH2O( objh )->o_fmfsix ); |
| } |
| |
| if ( DH2O( objh )->o_indrivepr ) { |
| if ( drivecnt > 1 ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" now in drive %u\n"), |
| DH2O( objh )->o_indriveix ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" now in drive\n") ); |
| } |
| } |
| |
| if ( DH2O( objh )->o_cldh == DH_NULL ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" media files not yet " |
| "identified\n") ); |
| continue; |
| } |
| |
| /* iterate over all files |
| */ |
| for ( fileh = DH2O( objh )->o_cldh, fileix = 0 |
| ; |
| fileh != DH_NULL |
| ; |
| fileh = DH2F( fileh )->f_nexth, fileix++ ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\n media file %u"), |
| fileix ); |
| if ( DH2O( objh )->o_fmfmixvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| " (%u):\n", |
| DH2O( objh )->o_fmfmix + fileix ); |
| } else { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| ":\n" ); |
| } |
| if ( DH2F( fileh )->f_szvalpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" size: " |
| "%lld bytes\n"), |
| DH2F( fileh )->f_sz ); |
| } |
| if ( DH2F( fileh )->f_dirtriedpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" used for directory " |
| "restoral\n") ); |
| } |
| if ( DH2F( fileh )->f_valpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" first extent contained: " |
| "ino %llu off %lld\n"), |
| DH2F( fileh )->f_firstegrp.eg_ino, |
| DH2F( fileh )->f_firstegrp.eg_off ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" next extent to restore: " |
| "ino %llu off %lld\n"), |
| DH2F( fileh )->f_curegrp.eg_ino, |
| DH2F( fileh )->f_curegrp.eg_off ); |
| mlog( MLOG_DEBUG | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" rollback mark %lld\n"), |
| DH2F( fileh )->f_curmark ); |
| } |
| if ( DH2F( fileh )->f_nondirskippr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" contains no selected " |
| "non-directories\n") ); |
| } |
| if ( DH2F( fileh )->f_nondirdonepr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" non-directories done\n") ); |
| } |
| if ( DH2F( fileh )->f_flags & PF_INV ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" contains session " |
| "inventory\n") ); |
| } |
| if ( DH2F( fileh )->f_flags & PF_TERM ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" is stream terminator\n") ); |
| } |
| if ( DH2F( fileh )->f_underheadpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _(" now reading\n") ); |
| } |
| } |
| if ( ! DH2O( objh )->o_lmfknwnpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\n may be additional " |
| "unidentified media files\n") ); |
| } |
| } |
| if ( ! DH2S( strmh )->s_lastobjknwnpr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK | MLOG_MEDIA, |
| _("\n may be " |
| "additional unidentified media objects\n\n") ); |
| } |
| } |
| } |
| |
| static intgen_t |
| egrpcmp( egrp_t *egrpap, egrp_t *egrpbp ) |
| { |
| if ( egrpap->eg_ino < egrpbp->eg_ino ) { |
| return -1; |
| } else if ( egrpap->eg_ino > egrpbp->eg_ino ) { |
| return 1; |
| } else if ( egrpap->eg_off < egrpbp->eg_off ) { |
| return -1; |
| } else if ( egrpap->eg_off > egrpbp->eg_off ) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void |
| display_dump_label( bool_t lockpr, |
| intgen_t mllevel, |
| char *introstr, |
| global_hdr_t *grhdrp, |
| media_hdr_t *mrhdrp, |
| content_hdr_t *crhdrp, |
| content_inode_hdr_t *scrhdrp ) |
| { |
| char dateline[ 28 ]; |
| char level_string[ 2 ]; |
| char dump_string_uuid[UUID_STR_LEN + 1]; |
| char media_string_uuid[UUID_STR_LEN + 1]; |
| char fs_string_uuid[UUID_STR_LEN + 1]; |
| |
| ASSERT( scrhdrp->cih_level >= 0 ); |
| ASSERT( scrhdrp->cih_level < 10 ); |
| level_string[ 0 ] = ( char )( '0' + ( u_char_t )scrhdrp->cih_level ); |
| level_string[ 1 ] = 0; |
| |
| uuid_unparse(grhdrp->gh_dumpid, dump_string_uuid); |
| uuid_unparse(mrhdrp->mh_mediaid, media_string_uuid); |
| uuid_unparse(crhdrp->ch_fsid, fs_string_uuid); |
| |
| if ( lockpr ) { |
| mlog_lock( ); |
| } |
| |
| mlog( mllevel | MLOG_NOLOCK, |
| "%s", |
| introstr ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("hostname: %s\n"), |
| grhdrp->gh_hostname ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("mount point: %s\n"), |
| crhdrp->ch_mntpnt ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("volume: %s\n"), |
| crhdrp->ch_fsdevice ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("session time: %s"), |
| ctime32_r( &grhdrp->gh_timestamp, dateline )); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("level: %s%s\n"), |
| level_string, |
| ( scrhdrp->cih_dumpattr & CIH_DUMPATTR_RESUME ) |
| ? |
| _(" resumed") |
| : |
| "" ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("session label: ") ); |
| mlog( mllevel | MLOG_NOLOCK | MLOG_BARE, |
| "\"%s\"\n", |
| grhdrp->gh_dumplabel ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("media label: ") ); |
| mlog( mllevel | MLOG_NOLOCK | MLOG_BARE, |
| "\"%s\"\n", |
| mrhdrp->mh_medialabel ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("file system id: %s\n"), |
| fs_string_uuid ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("session id: %s\n"), |
| dump_string_uuid ); |
| mlog( mllevel | MLOG_NOLOCK, |
| _("media id: %s\n"), |
| media_string_uuid ); |
| |
| if ( lockpr ) { |
| mlog_unlock( ); |
| } |
| } |
| |
| static void |
| display_needed_objects( purp_t purp, |
| bag_t *bagp, |
| bool_t knownholespr, |
| bool_t maybeholespr ) |
| { |
| if ( bagp ) { |
| ix_t ix; |
| bagiter_t iter; |
| bagobj_t *bagobjp; |
| if ( purp == PURP_DIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, _( |
| "the following media objects " |
| "contain media files not yet tried " |
| "for directory hierarchy restoral:\n") ); |
| } |
| if ( purp == PURP_NONDIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, _( |
| "the following media objects " |
| "are needed:\n") ); |
| } |
| bagiter_init( bagp, &iter ); |
| bagobjp = 0; /* keep lint happy */ |
| ix = 0; |
| while ( bagiter_next( &iter, |
| ( void ** )&bagobjp )) { |
| char uuidstr[UUID_STR_LEN + 1]; |
| uuid_unparse( bagobjp->id, uuidstr); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| "\n" ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| _("%2u. label: "), |
| ix ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| "\"%s\"\n", |
| bagobjp->label ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| _(" id: ") ); |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| "\"%s\"\n", |
| uuidstr ); |
| if ( bagobjp->indrivepr ) { |
| if ( drivecnt > 1 ) { |
| mlog( MLOG_NORMAL |
| | |
| MLOG_BARE |
| | |
| MLOG_NOLOCK, |
| _(" now in drive %u\n"), |
| bagobjp->indriveix ); |
| } else { |
| mlog( MLOG_NORMAL |
| | |
| MLOG_BARE |
| | |
| MLOG_NOLOCK, |
| _(" now in drive\n") ); |
| } |
| } |
| ix++; |
| bagobjp = 0; /* keep lint happy */ |
| } |
| } |
| if ( knownholespr ) { |
| if ( purp == PURP_DIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| bagp ? |
| _("\nthere are additional unidentified media " |
| "objects containing media files not yet tried " |
| "for directory hierarchy restoral:\n") |
| : |
| _("\nthere are unidentified media " |
| "objects containing media files not yet tried " |
| "for directory hierarchy restoral:\n") ); |
| } |
| if ( purp == PURP_NONDIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| bagp ? |
| _("\nthere are additional unidentified media " |
| "objects not yet fully restored\n") |
| : |
| _("\nthere are unidentified media objects " |
| "not yet fully restored\n") ); |
| } |
| } else if ( maybeholespr ) { |
| if ( purp == PURP_DIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| bagp ? |
| _("\nthere may be additional unidentified media " |
| "objects containing media files not yet tried " |
| "for directory hierarchy restoral:\n") |
| : |
| _("\nthere may be unidentified media " |
| "objects containing media files not yet tried " |
| "for directory hierarchy restoral:\n") ); |
| |
| } |
| if ( purp == PURP_NONDIR ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| bagp ? |
| _("\nthere may be additional unidentified media " |
| "objects not yet fully restored\n") |
| : |
| _("\there may be unidentified media " |
| "objects not yet fully restored\n") ); |
| } |
| } |
| |
| if ( ! bagp && ! knownholespr && ! maybeholespr ) { |
| mlog( MLOG_NORMAL | MLOG_BARE | MLOG_NOLOCK, |
| _("no additional media objects needed\n") ); |
| } |
| } |
| |
| static int |
| do_fssetdm_by_handle( |
| char *path, |
| fsdmidata_t *fdmp) |
| { |
| void *hanp; |
| size_t hlen=0; |
| int rc; |
| |
| if (path_to_handle(path, &hanp, &hlen)) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "path_to_handle of %s failed:%s\n"), |
| path, strerror( errno )); |
| return -1; |
| } |
| |
| rc = fssetdm_by_handle(hanp, hlen, fdmp); |
| free_handle(hanp, hlen); |
| if (rc) { |
| mlog( MLOG_NORMAL | MLOG_WARNING, _( |
| "fssetdm_by_handle of %s failed %s\n"), |
| path, strerror( errno )); |
| } |
| return rc; |
| } |
| |
| static int |
| quotafilecheck(char *type, char *dstdir, char *quotafile) |
| { |
| struct stat s; |
| char buf[MAXPATHLEN]; |
| |
| sprintf( buf, |
| "%s/%s", |
| dstdir, |
| quotafile ); |
| |
| if ( stat (buf, &s ) >= 0 && S_ISREG(s.st_mode)) { |
| mlog( MLOG_NORMAL, _( |
| "%s quota information written to '%s'\n"), |
| type, |
| buf ); |
| return 1; |
| } |
| |
| return 0; |
| } |