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