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