xfs_agblock_t first_agbno;
+__uint64_t barcount[11];
+
unsigned int num_targets;
target_control *target;
#define ACTIVE 1
#define INACTIVE 2
+xfs_off_t write_log_trailer(int fd, wbuf *w, xfs_mount_t *mp);
+xfs_off_t write_log_header(int fd, wbuf *w, xfs_mount_t *mp);
/* general purpose message reporting routine */
#define do_fatal(e,s) do_message(ERR|LOG|PRE|LAST, e, s)
#define do_vfatal(e,s,args...) do_message(ERR|LOG|PRE|LAST, e, s, ## args)
#define die_perror(void) \
- do { \
- do_message(ERR|LOG|PRE|LAST, errno, \
- _("Aborting XFS copy - reason")); \
- exit(1); \
- } while (0);
+ do { \
+ do_message(ERR|LOG|PRE|LAST, errno, \
+ _("Aborting XFS copy - reason")); \
+ exit(1); \
+ } while (0)
void
check_errors(void)
{
- int i, first_error = 0;
-
- /* now check for errors */
+ int i, first_error = 0;
for (i = 0; i < num_targets; i++) {
if (target[i].state == INACTIVE) {
* don't have to worry about alignment and mins because those
* are taken care of when the buffer's read in
*/
+int
+do_write(thread_args *args)
+{
+ int res, error = 0;
+
+ if (target[args->id].position != w_buf.position) {
+ if (lseek64(args->fd, w_buf.position, SEEK_SET) < 0) {
+ error = target[args->id].err_type = 1;
+ } else {
+ target[args->id].position = w_buf.position;
+ }
+ }
+
+ if ((res = write(target[args->id].fd, w_buf.data,
+ w_buf.length)) == w_buf.length) {
+ target[args->id].position += res;
+ } else {
+ error = 2;
+ }
+
+ if (error) {
+ target[args->id].error = errno;
+ target[args->id].position = w_buf.position;
+ }
+ return error;
+}
void *
begin_reader(void *arg)
{
thread_args *args = arg;
- int res, error = 0;
for (;;) {
pthread_mutex_lock(&args->wait);
-
- /* write */
- if (target[args->id].position != w_buf.position) {
- if (lseek64(args->fd, w_buf.position, SEEK_SET) < 0) {
- error = 1;
- target[args->id].err_type = 1;
- } else {
- target[args->id].position = w_buf.position;
- }
- }
-
- if ((res = write(target[args->id].fd, w_buf.data,
- w_buf.length)) == w_buf.length) {
- target[args->id].position += res;
- } else {
- error = 2;
- }
-
- if (error) {
+ if (do_write(args))
goto handle_error;
- }
-
pthread_mutex_lock(&glob_masks.mutex);
if (--glob_masks.num_working == 0)
pthread_mutex_unlock(&mainwait);
handle_error:
/* error will be logged by primary thread */
- target[args->id].error = errno;
- target[args->id].position = w_buf.position;
-
pthread_mutex_lock(&glob_masks.mutex);
target[args->id].state = INACTIVE;
if (--glob_masks.num_working == 0)
do_warn(
_("%s: thread %d died unexpectedly, target \"%s\" incomplete\n"),
progname, i, target[i].name);
-
- do_warn(
- _("%s: offset was probably %lld\n"),
+ do_warn(_("%s: offset was probably %lld\n"),
progname, target[i].position);
do_fatal(target[i].error,
_("Aborting XFS copy - reason"));
exit(1);
}
-__uint64_t barcount[11];
-int howfar = 0;
-char *bar[11] = {
- " 0% ",
- " ... 10% ",
- " ... 20% ",
- " ... 30% ",
- " ... 40% ",
- " ... 50% ",
- " ... 60% ",
- " ... 70% ",
- " ... 80% ",
- " ... 90% ",
- " ... 100%\n\n",
-};
-
void
-bump_bar(int tenths)
+init_bar(__uint64_t source_blocks)
+{
+ int i;
+
+ for (i = 0; i < 11; i++)
+ barcount[i] = (source_blocks/10)*i;
+}
+
+int
+bump_bar(int tenths, __uint64_t numblocks)
{
- printf("%s", bar[tenths]);
- fflush(stdout);
+ static char *bar[11] = {
+ " 0% ",
+ " ... 10% ",
+ " ... 20% ",
+ " ... 30% ",
+ " ... 40% ",
+ " ... 50% ",
+ " ... 60% ",
+ " ... 70% ",
+ " ... 80% ",
+ " ... 90% ",
+ " ... 100%\n\n",
+ };
+
+ if (tenths > 10) {
+ printf("%s", bar[10]);
+ fflush(stdout);
+ } else {
+ while (tenths < 10 && numblocks > barcount[tenths]) {
+ printf("%s", bar[tenths]);
+ fflush(stdout);
+ tenths++;
+ }
+ }
+ return tenths;
}
static xfs_off_t source_position = -1;
buf->length = res;
}
-int
+void
read_ag_header(int fd, xfs_agnumber_t agno, wbuf *buf, ag_header_t *ag,
xfs_mount_t *mp, int blocksize, int sectorsize)
{
ag->xfs_agi = (xfs_agi_t *) (buf->data + diff + 2*sectorsize);
ASSERT(INT_GET(ag->xfs_agi->agi_magicnum, ARCH_CONVERT)==XFS_AGI_MAGIC);
ag->xfs_agfl = (xfs_agfl_t *) (buf->data + diff + 3*sectorsize);
- return 1;
}
+
void
write_wbuf(void)
{
int
main(int argc, char **argv)
{
- int i;
+ int i, j;
+ int howfar = 0;
int open_flags;
- xfs_off_t pos;
+ xfs_off_t pos, end_pos;
size_t length;
int c, size, sizeb, first_residue, tmp_residue;
__uint64_t numblocks = 0;
- __uint64_t source_blocks;
int wblocks = 0;
int num_threads = 0;
struct dioattr d;
int source_is_file = 0;
int buffered_output = 0;
int duplicate_uuids = 0;
- uuid_t fsid;
uint btree_levels, current_level;
ag_header_t ag_hdr;
xfs_mount_t *mp;
ASSERT( ((((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
+ first_residue) % source_blocksize) == 0);
- if (!duplicate_uuids)
- uuid_generate(fsid);
-
/* now open targets */
open_flags = O_RDWR;
for (i = 0; i < num_targets; i++) {
int write_last_block = 0;
-
+
if (stat64(target[i].name, &statbuf) < 0) {
/* ok, assume it's a file and create it */
progname, target[i].name);
die_perror();
}
+ wbuf_align = MAX(wbuf_align, d.d_mem);
+ wbuf_size = MIN(d.d_maxiosz, wbuf_size);
+ wbuf_miniosize = MAX(d.d_miniosz, wbuf_miniosize);
+
} else {
- char *buf[XFS_MAX_SECTORSIZE] = { 0 };
+ char *lb[XFS_MAX_SECTORSIZE] = { 0 };
+ off64_t off;
/* ensure device files are sufficiently large */
- if (pwrite64(target[i].fd, buf, sizeof(buf),
- (mp->m_sb.sb_dblocks * source_blocksize)
- - sizeof(buf))) {
+ off = mp->m_sb.sb_dblocks * source_blocksize;
+ off -= sizeof(lb);
+ if (pwrite64(target[i].fd, lb, sizeof(lb), off) < 0) {
do_log(_("%s: failed to write last block\n"),
progname);
do_log(_("\tIs target \"%s\" too small?\n"),
die_perror();
}
}
-
- wbuf_align = MAX(wbuf_align, d.d_mem);
- wbuf_size = MIN(d.d_maxiosz, wbuf_size);
- wbuf_miniosize = MAX(d.d_miniosz, wbuf_miniosize);
}
/* initialize locks and bufs */
wblocks = wbuf_size / BBSIZE;
- if (wbuf_init(&btree_buf, MAX(MAX(source_blocksize, source_sectorsize),
- wbuf_miniosize), wbuf_align,
- wbuf_miniosize, 1) == NULL) {
+ if (wbuf_init(&btree_buf, MAX(source_blocksize, wbuf_miniosize),
+ wbuf_align, wbuf_miniosize, 1) == NULL) {
do_log(_("Error initializing btree buf 1\n"));
die_perror();
}
}
for (i = 0, tcarg = targ; i < num_targets; i++, tcarg++) {
+ if (!duplicate_uuids)
+ uuid_generate(tcarg->uuid);
+ else
+ uuid_copy(tcarg->uuid, mp->m_sb.sb_uuid);
+
if (pthread_mutex_init(&tcarg->wait, NULL) != 0) {
do_log(_("Error creating thread mutex %d\n"), i);
die_perror();
num_ags = mp->m_sb.sb_agcount;
- source_blocks = mp->m_sb.sb_blocksize / BBSIZE
+ init_bar(mp->m_sb.sb_blocksize / BBSIZE
* ((__uint64_t)mp->m_sb.sb_dblocks
- - (__uint64_t)mp->m_sb.sb_fdblocks + 10 * num_ags);
-
- for (i = 0; i < 11; i++)
- barcount[i] = (source_blocks/10)*i;
+ - (__uint64_t)mp->m_sb.sb_fdblocks + 10 * num_ags));
kids = num_targets;
block = (xfs_alloc_block_t *) btree_buf.data;
read_ag_header(source_fd, agno, &w_buf, &ag_hdr, mp,
source_blocksize, source_sectorsize);
- /* reset uuid and if applicable the in_progress bit */
-
- if (!duplicate_uuids)
- uuid_copy(ag_hdr.xfs_sb->sb_uuid, fsid);
+ /* set the in_progress bit for the first AG */
if (agno == 0)
INT_SET(ag_hdr.xfs_sb->sb_inprogress, ARCH_CONVERT, 1);
w_buf.position += w_buf.length;
- while (howfar < 10 && numblocks
- > barcount[howfar]) {
- bump_bar(howfar);
- howfar++;
- }
+ howfar = bump_bar(
+ howfar, numblocks);
}
}
w_buf.position += w_buf.length;
- while (howfar < 10 &&
- numblocks > barcount[howfar]) {
- bump_bar(howfar);
- howfar++;
- }
+ howfar = bump_bar(howfar, numblocks);
}
}
}
}
if (kids > 0) {
- /* reread and rewrite the first ag */
+ /* write a clean log using the specified UUID */
- read_ag_header(source_fd, 0, &w_buf, &ag_hdr, mp,
- source_blocksize, source_sectorsize);
+ for (j = 0, tcarg = targ; j < num_targets; j++) {
+ w_buf.owner = tcarg;
+ w_buf.length = rounddown(w_buf.size, w_buf.min_io_size);
- ag_hdr.xfs_sb->sb_inprogress = 0;
+ pos = write_log_header(source_fd, &w_buf, mp);
+ end_pos = write_log_trailer(source_fd, &w_buf, mp);
- if (!duplicate_uuids)
- uuid_copy(ag_hdr.xfs_sb->sb_uuid, fsid);
+ w_buf.position = pos;
+ memset(w_buf.data, 0, w_buf.length);
- write_wbuf();
- bump_bar(10);
- }
+ while (w_buf.position < end_pos) {
+ do_write(tcarg);
+ w_buf.position += w_buf.length;
+ }
+ tcarg++;
+ }
- /* nathans TODO: come back and do this properly... */
- for (i = 0; i < num_targets; i++) {
- char command[2048];
+ /* reread and rewrite superblocks (UUID and in-progress) */
+ /* [backwards, so inprogress bit only updated when done] */
+
+ if (duplicate_uuids)
+ num_ags = 1;
+ for (i = num_ags - 1; i >= 0; i--) {
+ read_ag_header(source_fd, i, &w_buf, &ag_hdr, mp,
+ source_blocksize, source_sectorsize);
+ if (i == 0)
+ ag_hdr.xfs_sb->sb_inprogress = 0;
- snprintf(command, sizeof(command),
- "%s -x -c 'uuid rewrite' %s >/dev/null 2>&1\n",
- "/usr/sbin/xfs_db", target[i].name);
- system(command);
+ /* do each thread in turn, each has its own UUID */
+
+ for (j = 0, tcarg = targ; j < num_targets; j++) {
+ uuid_copy(ag_hdr.xfs_sb->sb_uuid, tcarg->uuid);
+ do_write(tcarg);
+ tcarg++;
+ }
+ }
+
+ bump_bar(100, 0);
}
check_errors();
/*NOTREACHED*/
return 0;
}
+
+xfs_caddr_t
+next_log_chunk(xfs_caddr_t p, int offset, void *private)
+{
+ wbuf *buf = (wbuf *)private;
+
+ if (buf->length < (int)(p - buf->data) + offset) {
+ /* need to flush this one, then start afresh */
+
+ do_write(buf->owner);
+ memset(buf->data, 0, buf->length);
+ return buf->data;
+ }
+ return p + offset;
+}
+
+/*
+ * Writes a log header at the start of the log (with the real
+ * filesystem UUID embedded into it), and writes to all targets.
+ *
+ * Returns the next buffer-length-aligned disk address.
+ */
+xfs_off_t
+write_log_header(int fd, wbuf *buf, xfs_mount_t *mp)
+{
+ xfs_caddr_t p = buf->data;
+ xfs_off_t logstart;
+ int offset;
+
+ logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
+ buf->position = rounddown(logstart, (xfs_off_t)buf->length);
+
+ memset(p, 0, buf->size);
+ if (logstart % buf->length) { /* unaligned */
+ read_wbuf(fd, buf, mp);
+ offset = logstart - buf->position;
+ p += offset;
+ memset(p, 0, buf->length - offset);
+ }
+
+ offset = libxfs_log_header(p, &buf->owner->uuid,
+ XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ? 2 : 1,
+ mp->m_sb.sb_logsunit, XLOG_FMT,
+ next_log_chunk, buf);
+ do_write(buf->owner);
+
+ return logstart + roundup(offset, buf->length);
+}
+
+/*
+ * May do an aligned read of the last buffer in the log (& zero
+ * the start of that buffer). Returns the disk address at the
+ * end of last aligned buffer in the log.
+ */
+xfs_off_t
+write_log_trailer(int fd, wbuf *buf, xfs_mount_t *mp)
+{
+ xfs_off_t logend;
+ int offset;
+
+ logend = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
+ logend += XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks);
+
+ buf->position = rounddown(logend, (xfs_off_t)buf->length);
+
+ if (logend % buf->length) { /* unaligned */
+ read_wbuf(fd, buf, mp);
+ offset = (int)(logend - buf->position);
+ memset(buf->data, 0, offset);
+ do_write(buf->owner);
+ }
+
+ return buf->position;
+}
free(z);
}
-int
-libxfs_log_clear(
- dev_t device,
- xfs_daddr_t start,
- uint length,
- uuid_t *fs_uuid,
- int version,
- int sunit,
- int fmt)
+static void unmount_record(void *p)
{
- xfs_buf_t *buf;
- xlog_rec_header_t *head;
- xlog_op_header_t *op;
- int i, len;
+ xlog_op_header_t *op = (xlog_op_header_t *)p;
/* the data section must be 32 bit size aligned */
struct {
__uint16_t magic;
__uint32_t pad2; /* may as well make it 64 bits */
} magic = { XLOG_UNMOUNT_TYPE, 0, 0 };
+ memset(p, 0, BBSIZE);
+ INT_SET(op->oh_tid, ARCH_CONVERT, 1);
+ INT_SET(op->oh_len, ARCH_CONVERT, sizeof(magic));
+ INT_SET(op->oh_clientid, ARCH_CONVERT, XFS_LOG);
+ INT_SET(op->oh_flags, ARCH_CONVERT, XLOG_UNMOUNT_TRANS);
+ INT_SET(op->oh_res2, ARCH_CONVERT, 0);
+
+ /* and the data for this op */
+ memcpy(p + sizeof(xlog_op_header_t), &magic, sizeof(magic));
+}
+
+static xfs_caddr_t next(xfs_caddr_t ptr, int offset, void *private)
+{
+ xfs_buf_t *buf = (xfs_buf_t *)private;
+
+ if (XFS_BUF_COUNT(buf) < (int)(ptr - XFS_BUF_PTR(buf)) + offset)
+ abort();
+ return ptr + offset;
+}
+
+int
+libxfs_log_clear(
+ dev_t device,
+ xfs_daddr_t start,
+ uint length,
+ uuid_t *fs_uuid,
+ int version,
+ int sunit,
+ int fmt)
+{
+ xfs_buf_t *buf;
+ int len;
+
if (!device || !fs_uuid)
return -EINVAL;
libxfs_device_zero(device, start, length);
/* then write a log record header */
- if ((version == 2) && sunit)
- len = BTOBB(sunit);
- else
- len = 1;
+ len = ((version == 2) && sunit) ? BTOBB(sunit) : 2;
+ len = MAX(len, 2);
buf = libxfs_getbuf(device, start, len);
if (!buf)
return -1;
+ libxfs_log_header(XFS_BUF_PTR(buf),
+ fs_uuid, version, sunit, fmt, next, buf);
+ if (libxfs_writebuf(buf, 0))
+ return -1;
+
+ return 0;
+}
- memset(XFS_BUF_PTR(buf), 0, BBSIZE * len);
- head = (xlog_rec_header_t *)XFS_BUF_PTR(buf);
+int
+libxfs_log_header(
+ xfs_caddr_t caddr,
+ uuid_t *fs_uuid,
+ int version,
+ int sunit,
+ int fmt,
+ libxfs_get_block_t *nextfunc,
+ void *private)
+{
+ xlog_rec_header_t *head = (xlog_rec_header_t *)caddr;
+ xfs_caddr_t p = caddr;
+ uint cycle_lsn;
+ int i, len;
+
+ len = ((version == 2) && sunit) ? BTOBB(sunit) : 1;
/* note that oh_tid actually contains the cycle number
* and the tid is stored in h_cycle_data[0] - that's the
* way things end up on disk.
*/
-
+ memset(p, 0, BBSIZE);
INT_SET(head->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM);
INT_SET(head->h_cycle, ARCH_CONVERT, 1);
INT_SET(head->h_version, ARCH_CONVERT, version);
memcpy(&head->h_fs_uuid, fs_uuid, sizeof(uuid_t));
- if (len > 1) {
- xfs_caddr_t dp;
- uint cycle_lsn;
+ len = MAX(len, 2);
+ p = nextfunc(p, BBSIZE, private);
+ unmount_record(p);
- cycle_lsn = CYCLE_LSN_NOCONV(head->h_lsn, ARCH_CONVERT);
- dp = XFS_BUF_PTR(buf) + BBSIZE;
- for (i = 1; i < len; i++) {
- *(uint *)dp = cycle_lsn;
- dp += BBSIZE;
- }
+ cycle_lsn = CYCLE_LSN_NOCONV(head->h_lsn, ARCH_CONVERT);
+ for (i = 2; i < len; i++) {
+ p = nextfunc(p, BBSIZE, private);
+ memset(p, 0, BBSIZE);
+ *(uint *)p = cycle_lsn;
}
- if (libxfs_writebuf(buf, 0))
- return -1;
-
- buf = libxfs_getbuf(device, start + 1, 1);
- if (!buf)
- return -1;
-
- /* now a log unmount op */
- memset(XFS_BUF_PTR(buf), 0, BBSIZE);
- op = (xlog_op_header_t *)XFS_BUF_PTR(buf);
- INT_SET(op->oh_tid, ARCH_CONVERT, 1);
- INT_SET(op->oh_len, ARCH_CONVERT, sizeof(magic));
- INT_SET(op->oh_clientid, ARCH_CONVERT, XFS_LOG);
- INT_SET(op->oh_flags, ARCH_CONVERT, XLOG_UNMOUNT_TRANS);
- INT_SET(op->oh_res2, ARCH_CONVERT, 0);
-
- /* and the data for this op */
-
- memcpy(XFS_BUF_PTR(buf) + sizeof(xlog_op_header_t),
- &magic,
- sizeof(magic));
-
- if (libxfs_writebuf(buf, 0))
- return -1;
-
- return 0;
+ return BBTOB(len);
}
+
/*
* Simple I/O interface
*/