]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - copy/xfs_copy.c
Update copyright/license notices to match SGI legal prefered boilerplate.
[thirdparty/xfsprogs-dev.git] / copy / xfs_copy.c
CommitLineData
6404bb81 1/*
da23017d
NS
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
46eca962 4 *
da23017d
NS
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
6404bb81 7 * published by the Free Software Foundation.
46eca962 8 *
da23017d
NS
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
46eca962 13 *
da23017d
NS
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
6404bb81
NS
17 */
18
6404bb81
NS
19#include <xfs/libxfs.h>
20#include <sys/stat.h>
6404bb81
NS
21#include <sys/wait.h>
22#include <pthread.h>
23#include <signal.h>
24#include <stdarg.h>
25#include "xfs_copy.h"
26
27#define rounddown(x, y) (((x)/(y))*(y))
28
7066fb24
NS
29extern int platform_check_ismounted(char *, char *, struct stat64 *, int);
30
6404bb81
NS
31int logfd;
32char *logfile_name;
33FILE *logerr;
34char LOGFILE_NAME[] = "/var/tmp/xfs_copy.log.XXXXXX";
35
36char *source_name;
37int source_fd;
38
39unsigned int source_blocksize; /* source filesystem blocksize */
40unsigned int source_sectorsize; /* source disk sectorsize */
41
42xfs_agblock_t first_agbno;
43
989b74bc
NS
44__uint64_t barcount[11];
45
6404bb81
NS
46unsigned int num_targets;
47target_control *target;
48
49wbuf w_buf;
50wbuf btree_buf;
51
52pid_t parent_pid;
53unsigned int kids;
54
55thread_control glob_masks;
56thread_args *targ;
57
58pthread_mutex_t mainwait;
59
60#define ACTIVE 1
61#define INACTIVE 2
62
989b74bc
NS
63xfs_off_t write_log_trailer(int fd, wbuf *w, xfs_mount_t *mp);
64xfs_off_t write_log_header(int fd, wbuf *w, xfs_mount_t *mp);
6404bb81
NS
65
66/* general purpose message reporting routine */
67
68#define OUT 0x01 /* use stdout stream */
69#define ERR 0x02 /* use stderr stream */
70#define LOG 0x04 /* use logerr stream */
71#define PRE 0x08 /* append strerror string */
72#define LAST 0x10 /* final message we print */
73
74void
75do_message(int flags, int code, const char *fmt, ...)
76{
77 va_list ap;
78 int eek = 0;
79
80 va_start(ap, fmt);
81 if (flags & LOG)
82 if (vfprintf(logerr, fmt, ap) <= 0)
83 eek = 1;
84 if (eek)
85 flags |= ERR; /* failed, force stderr */
86 if (flags & ERR)
87 vfprintf(stderr, fmt, ap);
88 else if (flags & OUT)
89 vfprintf(stdout, fmt, ap);
90 va_end(ap);
91
92 if (flags & PRE) {
93 do_message(flags & ~PRE, 0, ": %s\n", strerror(code));
94 if (flags & LAST)
95 fprintf(stderr,
96 _("Check logfile \"%s\" for more details\n"),
97 logfile_name);
98 }
99
100 /* logfile is broken, force a write to stderr */
101 if (eek) {
102 fprintf(stderr, _("%s: could not write to logfile \"%s\".\n"),
103 progname, logfile_name);
104 fprintf(stderr,
105 _("Aborting XFS copy -- logfile error -- reason: %s\n"),
106 strerror(errno));
107 pthread_exit(NULL);
108 }
109}
110
111#define do_out(args...) do_message(OUT|LOG, 0, ## args)
112#define do_log(args...) do_message(ERR|LOG, 0, ## args)
113#define do_warn(args...) do_message(LOG, 0, ## args)
114#define do_error(e,s) do_message(ERR|LOG|PRE, e, s)
115#define do_fatal(e,s) do_message(ERR|LOG|PRE|LAST, e, s)
116#define do_vfatal(e,s,args...) do_message(ERR|LOG|PRE|LAST, e, s, ## args)
a92e6823 117#define die_perror() \
989b74bc
NS
118 do { \
119 do_message(ERR|LOG|PRE|LAST, errno, \
120 _("Aborting XFS copy - reason")); \
121 exit(1); \
122 } while (0)
6404bb81
NS
123
124void
125check_errors(void)
126{
989b74bc 127 int i, first_error = 0;
6404bb81
NS
128
129 for (i = 0; i < num_targets; i++) {
130 if (target[i].state == INACTIVE) {
131 if (first_error == 0) {
132 first_error++;
133 do_log(
134 _("THE FOLLOWING COPIES FAILED TO COMPLETE\n"));
135 }
136 do_log(" %s -- ", target[i].name);
137 if (target[i].err_type == 0)
138 do_log(_("write error"));
139 else
140 do_log(_("lseek64 error"));
141 do_log(_(" at offset %lld\n"), target[i].position);
142 }
143 }
144 if (first_error == 0) {
145 fprintf(stdout, _("All copies completed.\n"));
146 fflush(NULL);
147 } else {
148 fprintf(stderr, _("See \"%s\" for more details.\n"),
149 logfile_name);
150 exit(1);
151 }
152}
153
154/*
155 * don't have to worry about alignment and mins because those
156 * are taken care of when the buffer's read in
157 */
989b74bc
NS
158int
159do_write(thread_args *args)
160{
161 int res, error = 0;
162
163 if (target[args->id].position != w_buf.position) {
164 if (lseek64(args->fd, w_buf.position, SEEK_SET) < 0) {
165 error = target[args->id].err_type = 1;
166 } else {
167 target[args->id].position = w_buf.position;
168 }
169 }
170
171 if ((res = write(target[args->id].fd, w_buf.data,
172 w_buf.length)) == w_buf.length) {
173 target[args->id].position += res;
174 } else {
175 error = 2;
176 }
177
178 if (error) {
179 target[args->id].error = errno;
180 target[args->id].position = w_buf.position;
181 }
182 return error;
183}
6404bb81
NS
184
185void *
186begin_reader(void *arg)
187{
188 thread_args *args = arg;
6404bb81
NS
189
190 for (;;) {
191 pthread_mutex_lock(&args->wait);
989b74bc 192 if (do_write(args))
6404bb81 193 goto handle_error;
6404bb81
NS
194 pthread_mutex_lock(&glob_masks.mutex);
195 if (--glob_masks.num_working == 0)
196 pthread_mutex_unlock(&mainwait);
197 pthread_mutex_unlock(&glob_masks.mutex);
198 }
199 /* NOTREACHED */
200
201handle_error:
202 /* error will be logged by primary thread */
203
6404bb81
NS
204 pthread_mutex_lock(&glob_masks.mutex);
205 target[args->id].state = INACTIVE;
206 if (--glob_masks.num_working == 0)
207 pthread_mutex_unlock(&mainwait);
208 pthread_mutex_unlock(&glob_masks.mutex);
209 pthread_exit(NULL);
32a82561 210 return NULL;
6404bb81
NS
211}
212
213void
214killall(void)
215{
216 int i;
217
218 /* only the parent gets to kill things */
219
220 if (getpid() != parent_pid)
221 return;
222
223 for (i = 0; i < num_targets; i++) {
224 if (target[i].state == ACTIVE) {
225 /* kill up target threads */
226 pthread_kill(target[i].pid, SIGKILL);
227 pthread_mutex_unlock(&targ[i].wait);
228 }
229 }
230}
231
232void
233handler()
234{
235 pid_t pid = getpid();
236 int status, i;
237
238 pid = wait(&status);
239
240 kids--;
241
242 for (i = 0; i < num_targets; i++) {
243 if (target[i].pid == pid) {
244 if (target[i].state == INACTIVE) {
245 /* thread got an I/O error */
246
247 if (target[i].err_type == 0) {
248 do_warn(
249 _("%s: write error on target %d \"%s\" at offset %lld\n"),
250 progname, i, target[i].name,
251 target[i].position);
252 } else {
253 do_warn(
254 _("%s: lseek64 error on target %d \"%s\" at offset %lld\n"),
255 progname, i, target[i].name,
256 target[i].position);
257 }
258
259 do_vfatal(target[i].error,
260 _("Aborting target %d - reason"), i);
261
262 if (kids == 0) {
263 do_log(
264 _("Aborting XFS copy - no more targets.\n"));
265 check_errors();
266 pthread_exit(NULL);
267 }
268
32a82561 269 signal(SIGCHLD, handler);
6404bb81
NS
270 return;
271 } else {
272 /* it just croaked it bigtime, log it */
273
274 do_warn(
275 _("%s: thread %d died unexpectedly, target \"%s\" incomplete\n"),
276 progname, i, target[i].name);
989b74bc 277 do_warn(_("%s: offset was probably %lld\n"),
6404bb81
NS
278 progname, target[i].position);
279 do_fatal(target[i].error,
280 _("Aborting XFS copy - reason"));
281 pthread_exit(NULL);
282 }
283 }
284 }
285
286 /* unknown child -- something very wrong */
287
288 do_warn(_("%s: Unknown child died (should never happen!)\n"), progname);
289 die_perror();
290 pthread_exit(NULL);
32a82561 291 signal(SIGCHLD, handler);
6404bb81
NS
292}
293
294void
295usage(void)
296{
297 fprintf(stderr,
298 _("Usage: %s [-bd] [-L logfile] source target [target ...]\n"),
299 progname);
300 exit(1);
301}
302
6404bb81 303void
989b74bc
NS
304init_bar(__uint64_t source_blocks)
305{
306 int i;
307
308 for (i = 0; i < 11; i++)
309 barcount[i] = (source_blocks/10)*i;
310}
311
312int
313bump_bar(int tenths, __uint64_t numblocks)
6404bb81 314{
989b74bc
NS
315 static char *bar[11] = {
316 " 0% ",
317 " ... 10% ",
318 " ... 20% ",
319 " ... 30% ",
320 " ... 40% ",
321 " ... 50% ",
322 " ... 60% ",
323 " ... 70% ",
324 " ... 80% ",
325 " ... 90% ",
326 " ... 100%\n\n",
327 };
328
329 if (tenths > 10) {
330 printf("%s", bar[10]);
331 fflush(stdout);
332 } else {
333 while (tenths < 10 && numblocks > barcount[tenths]) {
334 printf("%s", bar[tenths]);
335 fflush(stdout);
336 tenths++;
337 }
338 }
339 return tenths;
6404bb81
NS
340}
341
342static xfs_off_t source_position = -1;
343
344wbuf *
345wbuf_init(wbuf *buf, int data_size, int data_align, int min_io_size, int id)
346{
347 buf->id = id;
348 if ((buf->data = memalign(data_align, data_size)) == NULL)
349 return NULL;
350 ASSERT(min_io_size % BBSIZE == 0);
351 buf->min_io_size = min_io_size;
352 buf->size = MAX(data_size, 2*min_io_size);
353 return buf;
354}
355
356void
357read_wbuf(int fd, wbuf *buf, xfs_mount_t *mp)
358{
359 int res = 0;
360 xfs_off_t lres = 0;
361 xfs_off_t newpos;
362 size_t diff;
363
364 newpos = rounddown(buf->position, (xfs_off_t) buf->min_io_size);
365
366 if (newpos != buf->position) {
367 diff = buf->position - newpos;
368 buf->position = newpos;
369
370 buf->length += diff;
371 }
372
373 if (source_position != buf->position) {
374 lres = lseek64(fd, buf->position, SEEK_SET);
375 if (lres < 0LL) {
376 do_warn(_("%s: lseek64 failure at offset %lld\n"),
377 progname, source_position);
378 die_perror();
379 }
380 source_position = buf->position;
381 }
382
383 ASSERT(source_position % source_sectorsize == 0);
384
385 /* round up length for direct I/O if necessary */
386
387 if (buf->length % buf->min_io_size != 0)
388 buf->length = roundup(buf->length, buf->min_io_size);
389
390 if (buf->length > buf->size) {
391 do_warn(_("assert error: buf->length = %d, buf->size = %d\n"),
392 buf->length, buf->size);
393 killall();
394 abort();
395 }
396
397 if ((res = read(fd, buf->data, buf->length)) < 0) {
398 do_warn(_("%s: read failure at offset %lld\n"),
399 progname, source_position);
400 die_perror();
401 }
402
403 if (res < buf->length &&
404 source_position + res == mp->m_sb.sb_dblocks * source_blocksize)
405 res = buf->length;
406 else
407 ASSERT(res == buf->length);
408 source_position += res;
409 buf->length = res;
410}
411
989b74bc 412void
6404bb81
NS
413read_ag_header(int fd, xfs_agnumber_t agno, wbuf *buf, ag_header_t *ag,
414 xfs_mount_t *mp, int blocksize, int sectorsize)
415{
416 xfs_daddr_t off;
417 int length;
418 xfs_off_t newpos;
419 size_t diff;
420
421 /* initial settings */
422
423 diff = 0;
424 off = XFS_AG_DADDR(mp, agno, XFS_SB_DADDR);
425 buf->position = (xfs_off_t) off * (xfs_off_t) BBSIZE;
426 length = buf->length = first_agbno * blocksize;
427
428 /* handle alignment stuff */
429
430 newpos = rounddown(buf->position, (xfs_off_t) buf->min_io_size);
431 if (newpos != buf->position) {
432 diff = buf->position - newpos;
433 buf->position = newpos;
434 buf->length += diff;
435 }
436
437 /* round up length for direct I/O if necessary */
438
439 if (buf->length % buf->min_io_size != 0)
440 buf->length = roundup(buf->length, buf->min_io_size);
441
442 ASSERT(length != 0);
443 read_wbuf(fd, buf, mp);
444 ASSERT(buf->length >= length);
445
446 ag->xfs_sb = (xfs_sb_t *) (buf->data + diff);
447 ASSERT(INT_GET(ag->xfs_sb->sb_magicnum, ARCH_CONVERT)==XFS_SB_MAGIC);
448 ag->xfs_agf = (xfs_agf_t *) (buf->data + diff + sectorsize);
449 ASSERT(INT_GET(ag->xfs_agf->agf_magicnum, ARCH_CONVERT)==XFS_AGF_MAGIC);
450 ag->xfs_agi = (xfs_agi_t *) (buf->data + diff + 2*sectorsize);
451 ASSERT(INT_GET(ag->xfs_agi->agi_magicnum, ARCH_CONVERT)==XFS_AGI_MAGIC);
452 ag->xfs_agfl = (xfs_agfl_t *) (buf->data + diff + 3*sectorsize);
6404bb81
NS
453}
454
989b74bc 455
6404bb81
NS
456void
457write_wbuf(void)
458{
459 int i;
460
461 /* verify target threads */
462 for (i = 0; i < num_targets; i++)
463 if (target[i].state != INACTIVE)
464 glob_masks.num_working++;
465
466 /* release target threads */
467 for (i = 0; i < num_targets; i++)
468 if (target[i].state != INACTIVE)
469 pthread_mutex_unlock(&targ[i].wait); /* wake up */
470
32a82561 471 sigrelse(SIGCHLD);
6404bb81 472 pthread_mutex_lock(&mainwait);
32a82561 473 sighold(SIGCHLD);
6404bb81
NS
474}
475
476
477int
478main(int argc, char **argv)
479{
989b74bc
NS
480 int i, j;
481 int howfar = 0;
6404bb81 482 int open_flags;
989b74bc 483 xfs_off_t pos, end_pos;
6404bb81 484 size_t length;
0b9a02a6
NS
485 int c, first_residue, tmp_residue;
486 __uint64_t size, sizeb;
6404bb81 487 __uint64_t numblocks = 0;
6404bb81
NS
488 int wblocks = 0;
489 int num_threads = 0;
490 struct dioattr d;
491 int wbuf_size;
492 int wbuf_align;
493 int wbuf_miniosize;
494 int source_is_file = 0;
495 int buffered_output = 0;
84c032c3 496 int duplicate = 0;
6404bb81
NS
497 uint btree_levels, current_level;
498 ag_header_t ag_hdr;
499 xfs_mount_t *mp;
500 xfs_mount_t mbuf;
501 xfs_buf_t *sbp;
502 xfs_sb_t *sb;
503 xfs_agnumber_t num_ags, agno;
504 xfs_agblock_t bno;
505 xfs_daddr_t begin, next_begin, ag_begin, new_begin, ag_end;
506 xfs_alloc_block_t *block;
507 xfs_alloc_ptr_t *ptr;
508 xfs_alloc_rec_t *rec_ptr;
509 extern char *optarg;
510 extern int optind;
511 libxfs_init_t xargs;
512 thread_args *tcarg;
6404bb81
NS
513 struct stat64 statbuf;
514
515 progname = basename(argv[0]);
516
517 setlocale(LC_ALL, "");
518 bindtextdomain(PACKAGE, LOCALEDIR);
519 textdomain(PACKAGE);
520
521 while ((c = getopt(argc, argv, "bdL:V")) != EOF) {
522 switch (c) {
523 case 'b':
524 buffered_output = 1;
525 break;
526 case 'd':
84c032c3 527 duplicate = 1;
6404bb81
NS
528 break;
529 case 'L':
530 logfile_name = optarg;
531 break;
532 case 'V':
533 printf(_("%s version %s\n"), progname, VERSION);
534 exit(0);
535 case '?':
536 usage();
537 }
538 }
539
540 if (argc - optind < 2)
541 usage();
542
543 if (logfile_name) {
544 logfd = open(logfile_name, O_CREAT|O_WRONLY|O_EXCL, 0600);
545 } else {
546 logfile_name = LOGFILE_NAME;
547 logfd = mkstemp(logfile_name);
548 }
549
550 if (logfd < 0) {
551 fprintf(stderr, _("%s: couldn't open log file \"%s\"\n"),
552 progname, logfile_name);
553 perror(_("Aborting XFS copy - reason"));
554 exit(1);
555 }
556
557 if ((logerr = fdopen(logfd, "w")) == NULL) {
558 fprintf(stderr, _("%s: couldn't set up logfile stream\n"),
559 progname);
560 perror(_("Aborting XFS copy - reason"));
561 exit(1);
562 }
563
564 source_name = argv[optind];
565 source_fd = -1;
566 optind++;
567
568 num_targets = argc - optind;
569 if ((target = malloc(sizeof(target_control) * num_targets)) == NULL) {
570 do_log(_("Couldn't allocate target array\n"));
571 die_perror();
572 }
573 for (i = 0; optind < argc; i++, optind++) {
574 target[i].name = argv[optind];
575 target[i].fd = -1;
576 target[i].position = -1;
577 target[i].state = INACTIVE;
578 target[i].error = 0;
579 target[i].err_type = 0;
580 }
581
582 parent_pid = getpid();
583
584 if (atexit(killall)) {
585 do_log(_("%s: couldn't register atexit function.\n"), progname);
586 die_perror();
587 }
588
589 /* open up source -- is it a file? */
590
591 open_flags = O_RDONLY;
592
593 if ((source_fd = open(source_name, open_flags)) < 0) {
594 do_log(_("%s: couldn't open source \"%s\"\n"),
595 progname, source_name);
596 die_perror();
597 }
598
599 if (fstat64(source_fd, &statbuf) < 0) {
600 do_log(_("%s: couldn't stat source \"%s\"\n"),
601 progname, source_name);
602 die_perror();
603 }
604
605 if (S_ISREG(statbuf.st_mode))
606 source_is_file = 1;
607
608 if (source_is_file && platform_test_xfs_fd(source_fd)) {
609 if (fcntl(source_fd, F_SETFL, open_flags | O_DIRECT) < 0) {
610 do_log(_("%s: Cannot set direct I/O flag on \"%s\".\n"),
611 progname, source_name);
612 die_perror();
613 }
614 if (xfsctl(source_name, source_fd, XFS_IOC_DIOINFO, &d) < 0) {
615 do_log(_("%s: xfsctl on file \"%s\" failed.\n"),
616 progname, source_name);
617 die_perror();
618 }
619
620 wbuf_align = d.d_mem;
621 wbuf_size = d.d_maxiosz;
622 wbuf_miniosize = d.d_miniosz;
623 } else {
624 /* set arbitrary I/O params, miniosize at least 1 disk block */
625
626 wbuf_align = 4096*4;
627 wbuf_size = 1024 * 4000;
628 wbuf_miniosize = -1; /* set after mounting source fs */
629 }
630
631 if (!source_is_file) {
632 /*
633 * check to make sure a filesystem isn't mounted
634 * on the device
635 */
32a82561 636 if (platform_check_ismounted(source_name, NULL, &statbuf, 0)) {
6404bb81
NS
637 do_log(
638 _("%s: Warning -- a filesystem is mounted on the source device.\n"),
639 progname);
640 do_log(
641 _("\t\tGenerated copies may be corrupt unless the source is\n"));
642 do_log(
643 _("\t\tunmounted or mounted read-only. Copy proceeding...\n"));
644 }
645 }
646
647 /* prepare the libxfs_init structure */
648
649 memset(&xargs, 0, sizeof(xargs));
650 xargs.notvolmsg = "oh no %s";
651 xargs.isreadonly = LIBXFS_ISREADONLY;
652 xargs.notvolok = 1;
653
654 if (source_is_file) {
655 xargs.dname = source_name;
656 xargs.disfile = 1;
657 } else
658 xargs.volname = source_name;
659
660 if (!libxfs_init(&xargs)) {
661 do_log(_("%s: couldn't initialize XFS library\n"
662 "%s: Aborting.\n"), progname, progname);
663 exit(1);
664 }
665
666 /* prepare the mount structure */
667
668 sbp = libxfs_readbuf(xargs.ddev, XFS_SB_DADDR, 1, 0);
669 memset(&mbuf, 0, sizeof(xfs_mount_t));
670 sb = &mbuf.m_sb;
46eca962 671 libxfs_xlate_sb(XFS_BUF_PTR(sbp), sb, 1, XFS_SB_ALL_BITS);
6404bb81
NS
672
673 mp = libxfs_mount(&mbuf, sb, xargs.ddev, xargs.logdev, xargs.rtdev, 1);
674 if (mp == NULL) {
675 do_log(_("%s: %s filesystem failed to initialize\n"
676 "%s: Aborting.\n"), progname, source_name, progname);
677 exit(1);
678 } else if (mp->m_sb.sb_inprogress) {
679 do_log(_("%s %s filesystem failed to initialize\n"
680 "%s: Aborting.\n"), progname, source_name, progname);
681 exit(1);
682 } else if (mp->m_sb.sb_logstart == 0) {
683 do_log(_("%s: %s has an external log.\n%s: Aborting.\n"),
684 progname, source_name, progname);
685 exit(1);
686 } else if (mp->m_sb.sb_rextents != 0) {
687 do_log(_("%s: %s has a real-time section.\n"
688 "%s: Aborting.\n"), progname, source_name, progname);
689 exit(1);
690 }
691
692 source_blocksize = mp->m_sb.sb_blocksize;
693 source_sectorsize = mp->m_sb.sb_sectsize;
694
695 if (wbuf_miniosize == -1)
696 wbuf_miniosize = source_sectorsize;
697
698 ASSERT(source_blocksize % source_sectorsize == 0);
699 ASSERT(source_sectorsize % BBSIZE == 0);
700
701 if (source_blocksize > source_sectorsize) {
702 /* get number of leftover sectors in last block of ag header */
703
704 tmp_residue = ((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
705 % source_blocksize;
706 first_residue = (tmp_residue == 0) ? 0 :
707 source_blocksize - tmp_residue;
708 ASSERT(first_residue % source_sectorsize == 0);
709 } else if (source_blocksize == source_sectorsize) {
710 first_residue = 0;
711 } else {
712 do_log(_("Error: filesystem block size is smaller than the"
713 " disk sectorsize.\nAborting XFS copy now.\n"));
714 exit(1);
715 }
716
717 first_agbno = (((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
718 + first_residue) / source_blocksize;
719 ASSERT(first_agbno != 0);
720 ASSERT( ((((XFS_AGFL_DADDR(mp) + 1) * source_sectorsize)
721 + first_residue) % source_blocksize) == 0);
722
6404bb81
NS
723 /* now open targets */
724
725 open_flags = O_RDWR;
726
727 for (i = 0; i < num_targets; i++) {
728 int write_last_block = 0;
989b74bc 729
6404bb81
NS
730 if (stat64(target[i].name, &statbuf) < 0) {
731 /* ok, assume it's a file and create it */
732
733 do_out(_("Creating file %s\n"), target[i].name);
734
735 open_flags |= O_CREAT;
736 if (!buffered_output)
737 open_flags |= O_DIRECT;
738 write_last_block = 1;
739 } else if (S_ISREG(statbuf.st_mode)) {
740 open_flags |= O_TRUNC;
741 if (!buffered_output)
742 open_flags |= O_DIRECT;
743 write_last_block = 1;
744 } else {
745 /*
746 * check to make sure a filesystem isn't mounted
747 * on the device
748 */
32a82561
NS
749 if (platform_check_ismounted(target[i].name,
750 NULL, &statbuf, 0)) {
6404bb81
NS
751 do_log(_("%s: a filesystem is mounted "
752 "on target device \"%s\".\n"
753 "%s cannot copy to mounted filesystems."
754 " Aborting\n"),
755 progname, target[i].name, progname);
756 exit(1);
757 }
758 }
759
760 target[i].fd = open(target[i].name, open_flags, 0644);
761 if (target[i].fd < 0) {
762 do_log(_("%s: couldn't open target \"%s\"\n"),
763 progname, target[i].name);
764 die_perror();
765 }
766
767 if (write_last_block) {
768 /* ensure regular files are correctly sized */
769
770 if (ftruncate64(target[i].fd, mp->m_sb.sb_dblocks *
771 source_blocksize)) {
772 do_log(_("%s: cannot grow data section.\n"),
773 progname);
774 die_perror();
775 }
3422261d
NS
776 if (platform_test_xfs_fd(target[i].fd)) {
777 if (xfsctl(target[i].name, target[i].fd,
6404bb81 778 XFS_IOC_DIOINFO, &d) < 0) {
3422261d
NS
779 do_log(
780 _("%s: xfsctl on \"%s\" failed.\n"),
781 progname, target[i].name);
782 die_perror();
783 } else {
784 wbuf_align = MAX(wbuf_align, d.d_mem);
785 wbuf_size = MIN(d.d_maxiosz, wbuf_size);
786 wbuf_miniosize = MAX(d.d_miniosz,
787 wbuf_miniosize);
788 }
6404bb81
NS
789 }
790 } else {
989b74bc
NS
791 char *lb[XFS_MAX_SECTORSIZE] = { 0 };
792 off64_t off;
6404bb81
NS
793
794 /* ensure device files are sufficiently large */
795
989b74bc
NS
796 off = mp->m_sb.sb_dblocks * source_blocksize;
797 off -= sizeof(lb);
798 if (pwrite64(target[i].fd, lb, sizeof(lb), off) < 0) {
6404bb81
NS
799 do_log(_("%s: failed to write last block\n"),
800 progname);
801 do_log(_("\tIs target \"%s\" too small?\n"),
802 target[i].name);
803 die_perror();
804 }
805 }
6404bb81
NS
806 }
807
808 /* initialize locks and bufs */
809
810 if (pthread_mutex_init(&glob_masks.mutex, NULL) != 0) {
811 do_log(_("Couldn't initialize global thread mask\n"));
812 die_perror();
813 }
814 glob_masks.num_working = 0;
815
816 if (wbuf_init(&w_buf, wbuf_size, wbuf_align,
817 wbuf_miniosize, 0) == NULL) {
818 do_log(_("Error initializing wbuf 0\n"));
819 die_perror();
820 }
821
822 wblocks = wbuf_size / BBSIZE;
823
989b74bc
NS
824 if (wbuf_init(&btree_buf, MAX(source_blocksize, wbuf_miniosize),
825 wbuf_align, wbuf_miniosize, 1) == NULL) {
6404bb81
NS
826 do_log(_("Error initializing btree buf 1\n"));
827 die_perror();
828 }
829
830 if (pthread_mutex_init(&mainwait,NULL) != 0) {
831 do_log(_("Error creating first semaphore.\n"));
832 die_perror();
833 exit(1);
834 }
835 /* need to start out blocking */
836 pthread_mutex_lock(&mainwait);
837
838 /* set up sigchild signal handler */
839
32a82561
NS
840 signal(SIGCHLD, handler);
841 sighold(SIGCHLD);
6404bb81
NS
842
843 /* make children */
844
845 if ((targ = malloc(num_targets * sizeof(thread_args))) == NULL) {
846 do_log(_("Couldn't malloc space for thread args\n"));
847 die_perror();
848 exit(1);
849 }
850
851 for (i = 0, tcarg = targ; i < num_targets; i++, tcarg++) {
84c032c3 852 if (!duplicate)
989b74bc
NS
853 uuid_generate(tcarg->uuid);
854 else
855 uuid_copy(tcarg->uuid, mp->m_sb.sb_uuid);
856
6404bb81
NS
857 if (pthread_mutex_init(&tcarg->wait, NULL) != 0) {
858 do_log(_("Error creating thread mutex %d\n"), i);
859 die_perror();
860 exit(1);
861 }
862 /* need to start out blocking */
863 pthread_mutex_lock(&tcarg->wait);
864 }
865
866 for (i = 0, tcarg = targ; i < num_targets; i++, tcarg++) {
867 tcarg->id = i;
868 tcarg->fd = target[i].fd;
869
870 target[i].state = ACTIVE;
871 num_threads++;
872
873 if (pthread_create(&target[i].pid, NULL,
874 begin_reader, (void *)tcarg)) {
875 do_log(_("Error creating thread for target %d\n"), i);
876 die_perror();
877 }
878 }
879
880 ASSERT(num_targets == num_threads);
881
882 /* set up statistics */
883
884 num_ags = mp->m_sb.sb_agcount;
885
989b74bc 886 init_bar(mp->m_sb.sb_blocksize / BBSIZE
6404bb81 887 * ((__uint64_t)mp->m_sb.sb_dblocks
989b74bc 888 - (__uint64_t)mp->m_sb.sb_fdblocks + 10 * num_ags));
6404bb81
NS
889
890 kids = num_targets;
891 block = (xfs_alloc_block_t *) btree_buf.data;
892
893 for (agno = 0; agno < num_ags && kids > 0; agno++) {
894 /* read in first blocks of the ag */
895
896 read_ag_header(source_fd, agno, &w_buf, &ag_hdr, mp,
897 source_blocksize, source_sectorsize);
898
989b74bc 899 /* set the in_progress bit for the first AG */
6404bb81
NS
900
901 if (agno == 0)
902 INT_SET(ag_hdr.xfs_sb->sb_inprogress, ARCH_CONVERT, 1);
903
904 /* save what we need (agf) in the btree buffer */
905
906 bcopy(ag_hdr.xfs_agf, btree_buf.data, source_sectorsize);
907 ag_hdr.xfs_agf = (xfs_agf_t *) btree_buf.data;
908 btree_buf.length = source_blocksize;
909
910 /* write the ag header out */
911
912 write_wbuf();
913
914 /* traverse btree until we get to the leftmost leaf node */
915
916 bno = INT_GET(ag_hdr.xfs_agf->agf_roots[XFS_BTNUM_BNOi],
917 ARCH_CONVERT);
918 current_level = 0;
919 btree_levels = INT_GET(
920 ag_hdr.xfs_agf->agf_levels[XFS_BTNUM_BNOi],
921 ARCH_CONVERT);
922
923 ag_end = XFS_AGB_TO_DADDR(mp, agno,
924 INT_GET(ag_hdr.xfs_agf->agf_length,ARCH_CONVERT) - 1)
925 + source_blocksize/BBSIZE;
926
927 for (;;) {
928 /* none of this touches the w_buf buffer */
929
930 ASSERT(current_level < btree_levels);
931
932 current_level++;
933
934 btree_buf.position = pos = (xfs_off_t)
935 XFS_AGB_TO_DADDR(mp,agno,bno) << BBSHIFT;
936 btree_buf.length = source_blocksize;
937
938 read_wbuf(source_fd, &btree_buf, mp);
939 block = (xfs_alloc_block_t *) ((char *) btree_buf.data
940 + pos - btree_buf.position);
941
942 ASSERT(INT_GET(block->bb_magic,ARCH_CONVERT) ==
943 XFS_ABTB_MAGIC);
944
945 if (INT_GET(block->bb_level,ARCH_CONVERT) == 0)
946 break;
947
948 ptr = XFS_BTREE_PTR_ADDR(sourceb_blocksize, xfs_alloc,
949 block, 1, mp->m_alloc_mxr[1]),
950
b0b5cf61 951 bno = INT_GET(ptr[0], ARCH_CONVERT);
6404bb81
NS
952 }
953
954 /* align first data copy but don't overwrite ag header */
955
956 pos = w_buf.position >> BBSHIFT;
957 length = w_buf.length >> BBSHIFT;
958 next_begin = pos + length;
959 ag_begin = next_begin;
960
961 ASSERT(w_buf.position % source_sectorsize == 0);
962
963 /* handle the rest of the ag */
964
965 for (;;) {
966 if (INT_GET(block->bb_level,ARCH_CONVERT) != 0) {
967 do_log(
968 _("WARNING: source filesystem inconsistent.\n"));
969 do_log(
970 _(" A leaf btree rec isn't a leaf. Aborting now.\n"));
971 exit(1);
972 }
973
974 rec_ptr = XFS_BTREE_REC_ADDR(source_blocksize,
975 xfs_alloc, block, 1, mp->m_alloc_mxr[0]);
976
977 for (i = 0;
978 i < INT_GET(block->bb_numrecs,ARCH_CONVERT);
979 i++, rec_ptr++) {
980 /* calculate in daddr's */
981
982 begin = next_begin;
983
984 /*
985 * protect against pathological case of a
986 * hole right after the ag header in a
987 * mis-aligned case
988 */
989
990 if (begin < ag_begin)
991 begin = ag_begin;
992
993 /*
994 * round size up to ensure we copy a
995 * range bigger than required
996 */
997
998 sizeb = XFS_AGB_TO_DADDR(mp, agno,
999 INT_GET(rec_ptr->ar_startblock,
1000 ARCH_CONVERT)) - begin;
1001 size = roundup(sizeb <<BBSHIFT, wbuf_miniosize);
1002 if (size > 0) {
1003 /* copy extent */
1004
1005 w_buf.position = (xfs_off_t)
1006 begin << BBSHIFT;
1007
1008 while (size > 0) {
1009 /*
1010 * let lower layer do alignment
1011 */
1012 if (size > w_buf.size) {
1013 w_buf.length = w_buf.size;
1014 size -= w_buf.size;
1015 sizeb -= wblocks;
1016 numblocks += wblocks;
1017 } else {
1018 w_buf.length = size;
1019 numblocks += sizeb;
1020 size = 0;
1021 }
1022
1023 read_wbuf(source_fd, &w_buf, mp);
1024 write_wbuf();
1025
1026 w_buf.position += w_buf.length;
1027
989b74bc
NS
1028 howfar = bump_bar(
1029 howfar, numblocks);
6404bb81
NS
1030 }
1031 }
1032
1033 /* round next starting point down */
1034
1035 new_begin = XFS_AGB_TO_DADDR(mp, agno,
1036 INT_GET(rec_ptr->ar_startblock,
1037 ARCH_CONVERT) +
1038 INT_GET(rec_ptr->ar_blockcount,
1039 ARCH_CONVERT));
1040 next_begin = rounddown(new_begin,
1041 w_buf.min_io_size >> BBSHIFT);
1042 }
1043
1044 if (INT_GET(block->bb_rightsib,ARCH_CONVERT) ==
1045 NULLAGBLOCK)
1046 break;
1047
1048 /* read in next btree record block */
1049
1050 btree_buf.position = pos = (xfs_off_t)
1051 XFS_AGB_TO_DADDR(mp, agno,
1052 INT_GET(block->bb_rightsib,
1053 ARCH_CONVERT)) << BBSHIFT;
1054 btree_buf.length = source_blocksize;
1055
1056 /* let read_wbuf handle alignment */
1057
1058 read_wbuf(source_fd, &btree_buf, mp);
1059
1060 block = (xfs_alloc_block_t *) ((char *) btree_buf.data
1061 + pos - btree_buf.position);
1062
1063 ASSERT(INT_GET(block->bb_magic,ARCH_CONVERT) ==
1064 XFS_ABTB_MAGIC);
1065 }
1066
1067 /*
1068 * write out range of used blocks after last range
1069 * of free blocks in AG
1070 */
1071 if (next_begin < ag_end) {
1072 begin = next_begin;
1073
1074 sizeb = ag_end - begin;
1075 size = roundup(sizeb << BBSHIFT, wbuf_miniosize);
1076
1077 if (size > 0) {
1078 /* copy extent */
1079
1080 w_buf.position = (xfs_off_t) begin << BBSHIFT;
1081
1082 while (size > 0) {
1083 /*
1084 * let lower layer do alignment
1085 */
1086 if (size > w_buf.size) {
1087 w_buf.length = w_buf.size;
1088 size -= w_buf.size;
1089 sizeb -= wblocks;
1090 numblocks += wblocks;
1091 } else {
1092 w_buf.length = size;
1093 numblocks += sizeb;
1094 size = 0;
1095 }
1096
1097 read_wbuf(source_fd, &w_buf, mp);
1098 write_wbuf();
1099
1100 w_buf.position += w_buf.length;
1101
989b74bc 1102 howfar = bump_bar(howfar, numblocks);
6404bb81
NS
1103 }
1104 }
1105 }
1106 }
1107
1108 if (kids > 0) {
84c032c3 1109 if (!duplicate) {
6404bb81 1110
84c032c3
NS
1111 /* write a clean log using the specified UUID */
1112 for (j = 0, tcarg = targ; j < num_targets; j++) {
1113 w_buf.owner = tcarg;
1114 w_buf.length = rounddown(w_buf.size,
1115 w_buf.min_io_size);
1116 pos = write_log_header(
1117 source_fd, &w_buf, mp);
1118 end_pos = write_log_trailer(
1119 source_fd, &w_buf, mp);
1120 w_buf.position = pos;
1121 memset(w_buf.data, 0, w_buf.length);
1122
1123 while (w_buf.position < end_pos) {
1124 do_write(tcarg);
1125 w_buf.position += w_buf.length;
1126 }
1127 tcarg++;
989b74bc 1128 }
84c032c3
NS
1129 } else {
1130 num_ags = 1;
989b74bc 1131 }
6404bb81 1132
989b74bc
NS
1133 /* reread and rewrite superblocks (UUID and in-progress) */
1134 /* [backwards, so inprogress bit only updated when done] */
1135
989b74bc
NS
1136 for (i = num_ags - 1; i >= 0; i--) {
1137 read_ag_header(source_fd, i, &w_buf, &ag_hdr, mp,
1138 source_blocksize, source_sectorsize);
1139 if (i == 0)
1140 ag_hdr.xfs_sb->sb_inprogress = 0;
6404bb81 1141
989b74bc
NS
1142 /* do each thread in turn, each has its own UUID */
1143
1144 for (j = 0, tcarg = targ; j < num_targets; j++) {
1145 uuid_copy(ag_hdr.xfs_sb->sb_uuid, tcarg->uuid);
1146 do_write(tcarg);
1147 tcarg++;
1148 }
1149 }
1150
1151 bump_bar(100, 0);
6404bb81
NS
1152 }
1153
1154 check_errors();
1155 killall();
1156 pthread_exit(NULL);
1157 /*NOTREACHED*/
1158 return 0;
1159}
989b74bc
NS
1160
1161xfs_caddr_t
1162next_log_chunk(xfs_caddr_t p, int offset, void *private)
1163{
1164 wbuf *buf = (wbuf *)private;
1165
1166 if (buf->length < (int)(p - buf->data) + offset) {
1167 /* need to flush this one, then start afresh */
1168
1169 do_write(buf->owner);
1170 memset(buf->data, 0, buf->length);
1171 return buf->data;
1172 }
1173 return p + offset;
1174}
1175
1176/*
1177 * Writes a log header at the start of the log (with the real
1178 * filesystem UUID embedded into it), and writes to all targets.
1179 *
1180 * Returns the next buffer-length-aligned disk address.
1181 */
1182xfs_off_t
1183write_log_header(int fd, wbuf *buf, xfs_mount_t *mp)
1184{
1185 xfs_caddr_t p = buf->data;
1186 xfs_off_t logstart;
1187 int offset;
1188
1189 logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
1190 buf->position = rounddown(logstart, (xfs_off_t)buf->length);
1191
1192 memset(p, 0, buf->size);
1193 if (logstart % buf->length) { /* unaligned */
1194 read_wbuf(fd, buf, mp);
1195 offset = logstart - buf->position;
1196 p += offset;
1197 memset(p, 0, buf->length - offset);
1198 }
1199
1200 offset = libxfs_log_header(p, &buf->owner->uuid,
1201 XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ? 2 : 1,
1202 mp->m_sb.sb_logsunit, XLOG_FMT,
1203 next_log_chunk, buf);
1204 do_write(buf->owner);
1205
1206 return logstart + roundup(offset, buf->length);
1207}
1208
1209/*
1210 * May do an aligned read of the last buffer in the log (& zero
1211 * the start of that buffer). Returns the disk address at the
1212 * end of last aligned buffer in the log.
1213 */
1214xfs_off_t
1215write_log_trailer(int fd, wbuf *buf, xfs_mount_t *mp)
1216{
1217 xfs_off_t logend;
1218 int offset;
1219
1220 logend = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart) << BBSHIFT;
1221 logend += XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks);
1222
1223 buf->position = rounddown(logend, (xfs_off_t)buf->length);
1224
1225 if (logend % buf->length) { /* unaligned */
1226 read_wbuf(fd, buf, mp);
1227 offset = (int)(logend - buf->position);
1228 memset(buf->data, 0, offset);
1229 do_write(buf->owner);
1230 }
1231
1232 return buf->position;
1233}