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