blob: 54d933ca7e554eb9a6d702aa814140ae1eeb73ee [file] [log] [blame]
/*
* 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++;