blob: 7ec3a4d5d0f643aca0fabe72ab2151fc24620bed [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 <unistd.h>
#include <stdlib.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 <assert.h>
#include <string.h>
#include <uuid/uuid.h>
#include <xfs/xfs.h>
#include <xfs/jdm.h> /* only for util.h */
#include "config.h"
#include "types.h"
#include "timeutil.h"
#include "util.h" /* only for r/w routines, ALIGN_PTR */
#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)
*/
int 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];
int sc_fd;
int sc_hsmflags;
/*
* we have to set the owner before we set extended attributes otherwise
* capabilities will not be restored correctly as setting the owner with
* fchmod will strip the capability attribute from the file. Hence we
* need to do this before restoring xattrs and record it so we don't do
* it again on completion of file restoration.
*/
bool_t sc_ownerset;
};
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 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.
*/
uint64_t stat_inocnt;
/* number of non-dir inos to restore during session
*/
uint64_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
*/
int 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,
int 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 int egrpcmp(egrp_t *egrpap, egrp_t *egrpbp);
static void display_dump_label(bool_t lockpr,
int 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 quotafilecheck(char *type, char *dstdir, char *quotafile);
/* definition of locally defined global variables ****************************/
bool_t content_media_change_needed;
bool_t restore_rootdir_permissions;
bool_t need_fixrootdir;
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(int 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 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;
int c;
bool_t ok;
int 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;
restoreextattrpr = BOOL_TRUE;
sesscpltpr = BOOL_FALSE;
stcnt = 0;
firststsensepr = firststsenseprvalpr = BOOL_FALSE;
stsz = 0;
interpr = BOOL_FALSE;
restore_rootdir_permissions = BOOL_FALSE;
need_fixrootdir = 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:
mlog(MLOG_NORMAL | MLOG_ERROR, _(
"-%c option no longer supported\n"),
GETOPT_SETDM);
usage();
return BOOL_FALSE;
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;
case GETOPT_FIXROOTDIR:
need_fixrootdir = 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 (!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;
int 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, (uint64_t)0);
if (!ok) {
return BOOL_FALSE;
}
ok = namreg_init(tranp->t_hkdir, BOOL_TRUE, (uint64_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;
}
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, (uint64_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, (uint64_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
*/
int
content_stream_restore(ix_t thrdix)
{
dh_t fileh;
Media_t *Mediap; /* local media abstraction */
char *path1;
char *path2;
drive_t *drivep;
int 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;
int 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(NULL,
&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};
int 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.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};
int 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;
}
if (need_fixrootdir)
tree_fixroot();
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;
}