2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
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.
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.
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
19 #include <xfs/libxfs.h>
27 #define rounddown(x, y) (((x)/(y))*(y))
28 #define uuid_equal(s,d) (platform_uuid_compare((s),(d)) == 0)
30 extern int platform_check_ismounted(char *, char *, struct stat64
*, int);
35 char LOGFILE_NAME
[] = "/var/tmp/xfs_copy.log.XXXXXX";
40 unsigned int source_blocksize
; /* source filesystem blocksize */
41 unsigned int source_sectorsize
; /* source disk sectorsize */
43 xfs_agblock_t first_agbno
;
45 __uint64_t barcount
[11];
47 unsigned int num_targets
;
48 target_control
*target
;
56 thread_control glob_masks
;
59 pthread_mutex_t mainwait
;
64 xfs_off_t
write_log_trailer(int fd
, wbuf
*w
, xfs_mount_t
*mp
);
65 xfs_off_t
write_log_header(int fd
, wbuf
*w
, xfs_mount_t
*mp
);
67 /* general purpose message reporting routine */
69 #define OUT 0x01 /* use stdout stream */
70 #define ERR 0x02 /* use stderr stream */
71 #define LOG 0x04 /* use logerr stream */
72 #define PRE 0x08 /* append strerror string */
73 #define LAST 0x10 /* final message we print */
76 do_message(int flags
, int code
, const char *fmt
, ...)
83 if (vfprintf(logerr
, fmt
, ap
) <= 0)
88 flags
|= ERR
; /* failed, force stderr */
91 vfprintf(stderr
, fmt
, ap
);
93 } else if (flags
& OUT
) {
95 vfprintf(stdout
, fmt
, ap
);
100 do_message(flags
& ~PRE
, 0, ": %s\n", strerror(code
));
103 _("Check logfile \"%s\" for more details\n"),
107 /* logfile is broken, force a write to stderr */
109 fprintf(stderr
, _("%s: could not write to logfile \"%s\".\n"),
110 progname
, logfile_name
);
112 _("Aborting XFS copy -- logfile error -- reason: %s\n"),
118 #define do_out(args...) do_message(OUT|LOG, 0, ## args)
119 #define do_log(args...) do_message(ERR|LOG, 0, ## args)
120 #define do_warn(args...) do_message(LOG, 0, ## args)
121 #define do_error(e,s) do_message(ERR|LOG|PRE, e, s)
122 #define do_fatal(e,s) do_message(ERR|LOG|PRE|LAST, e, s)
123 #define do_vfatal(e,s,args...) do_message(ERR|LOG|PRE|LAST, e, s, ## args)
124 #define die_perror() \
126 do_message(ERR|LOG|PRE|LAST, errno, \
127 _("Aborting XFS copy - reason")); \
134 int i
, first_error
= 0;
136 for (i
= 0; i
< num_targets
; i
++) {
137 if (target
[i
].state
== INACTIVE
) {
138 if (first_error
== 0) {
141 _("THE FOLLOWING COPIES FAILED TO COMPLETE\n"));
143 do_log(" %s -- ", target
[i
].name
);
144 if (target
[i
].err_type
== 0)
145 do_log(_("write error"));
147 do_log(_("lseek64 error"));
148 do_log(_(" at offset %lld\n"), target
[i
].position
);
151 if (first_error
== 0) {
152 fprintf(stdout
, _("All copies completed.\n"));
155 fprintf(stderr
, _("See \"%s\" for more details.\n"),
162 * don't have to worry about alignment and mins because those
163 * are taken care of when the buffer's read in
166 do_write(thread_args
*args
)
170 if (target
[args
->id
].position
!= w_buf
.position
) {
171 if (lseek64(args
->fd
, w_buf
.position
, SEEK_SET
) < 0) {
172 error
= target
[args
->id
].err_type
= 1;
174 target
[args
->id
].position
= w_buf
.position
;
178 if ((res
= write(target
[args
->id
].fd
, w_buf
.data
,
179 w_buf
.length
)) == w_buf
.length
) {
180 target
[args
->id
].position
+= res
;
186 target
[args
->id
].error
= errno
;
187 target
[args
->id
].position
= w_buf
.position
;
193 begin_reader(void *arg
)
195 thread_args
*args
= arg
;
198 pthread_mutex_lock(&args
->wait
);
201 pthread_mutex_lock(&glob_masks
.mutex
);
202 if (--glob_masks
.num_working
== 0)
203 pthread_mutex_unlock(&mainwait
);
204 pthread_mutex_unlock(&glob_masks
.mutex
);
209 /* error will be logged by primary thread */
211 pthread_mutex_lock(&glob_masks
.mutex
);
212 target
[args
->id
].state
= INACTIVE
;
213 if (--glob_masks
.num_working
== 0)
214 pthread_mutex_unlock(&mainwait
);
215 pthread_mutex_unlock(&glob_masks
.mutex
);
230 for (i
= 0; i
< num_targets
; i
++) {
231 if (target
[i
].pid
== pid
) {
232 if (target
[i
].state
== INACTIVE
) {
233 /* thread got an I/O error */
235 if (target
[i
].err_type
== 0) {
237 _("%s: write error on target %d \"%s\" at offset %lld\n"),
238 progname
, i
, target
[i
].name
,
242 _("%s: lseek64 error on target %d \"%s\" at offset %lld\n"),
243 progname
, i
, target
[i
].name
,
247 do_vfatal(target
[i
].error
,
248 _("Aborting target %d - reason"), i
);
252 _("Aborting XFS copy - no more targets.\n"));
257 signal(SIGCHLD
, handler
);
260 /* it just croaked it bigtime, log it */
263 _("%s: thread %d died unexpectedly, target \"%s\" incomplete\n"),
264 progname
, i
, target
[i
].name
);
265 do_warn(_("%s: offset was probably %lld\n"),
266 progname
, target
[i
].position
);
267 do_fatal(target
[i
].error
,
268 _("Aborting XFS copy - reason"));
274 /* unknown child -- something very wrong */
276 do_warn(_("%s: Unknown child died (should never happen!)\n"), progname
);
279 signal(SIGCHLD
, handler
);
286 _("Usage: %s [-bdV] [-L logfile] source target [target ...]\n"),
292 init_bar(__uint64_t source_blocks
)
296 for (i
= 0; i
< 11; i
++)
297 barcount
[i
] = (source_blocks
/10)*i
;
301 bump_bar(int tenths
, __uint64_t numblocks
)
303 static char *bar
[11] = {
318 printf("%s", bar
[10]);
321 while (tenths
< 10 && numblocks
> barcount
[tenths
]) {
322 printf("%s", bar
[tenths
]);
330 static xfs_off_t source_position
= -1;
333 wbuf_init(wbuf
*buf
, int data_size
, int data_align
, int min_io_size
, int id
)
335 ASSERT(data_size
% BBSIZE
== 0);
336 while ((buf
->data
= memalign(data_align
, data_size
)) == NULL
) {
338 if (data_size
< min_io_size
)
341 ASSERT(min_io_size
% BBSIZE
== 0);
342 buf
->min_io_size
= min_io_size
;
343 buf
->size
= data_size
;
349 read_wbuf(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
356 newpos
= rounddown(buf
->position
, (xfs_off_t
) buf
->min_io_size
);
358 if (newpos
!= buf
->position
) {
359 diff
= buf
->position
- newpos
;
360 buf
->position
= newpos
;
365 if (source_position
!= buf
->position
) {
366 lres
= lseek64(fd
, buf
->position
, SEEK_SET
);
368 do_warn(_("%s: lseek64 failure at offset %lld\n"),
369 progname
, source_position
);
372 source_position
= buf
->position
;
375 ASSERT(source_position
% source_sectorsize
== 0);
377 /* round up length for direct I/O if necessary */
379 if (buf
->length
% buf
->min_io_size
!= 0)
380 buf
->length
= roundup(buf
->length
, buf
->min_io_size
);
382 if (buf
->length
> buf
->size
) {
383 do_warn(_("assert error: buf->length = %d, buf->size = %d\n"),
384 buf
->length
, buf
->size
);
388 if ((res
= read(fd
, buf
->data
, buf
->length
)) < 0) {
389 do_warn(_("%s: read failure at offset %lld\n"),
390 progname
, source_position
);
394 if (res
< buf
->length
&&
395 source_position
+ res
== mp
->m_sb
.sb_dblocks
* source_blocksize
)
398 ASSERT(res
== buf
->length
);
399 source_position
+= res
;
404 read_ag_header(int fd
, xfs_agnumber_t agno
, wbuf
*buf
, ag_header_t
*ag
,
405 xfs_mount_t
*mp
, int blocksize
, int sectorsize
)
412 /* initial settings */
415 off
= XFS_AG_DADDR(mp
, agno
, XFS_SB_DADDR
);
416 buf
->position
= (xfs_off_t
) off
* (xfs_off_t
) BBSIZE
;
417 length
= buf
->length
= first_agbno
* blocksize
;
419 do_log(_("ag header buffer invalid!\n"));
423 /* handle alignment stuff */
425 newpos
= rounddown(buf
->position
, (xfs_off_t
) buf
->min_io_size
);
426 if (newpos
!= buf
->position
) {
427 diff
= buf
->position
- newpos
;
428 buf
->position
= newpos
;
432 /* round up length for direct I/O if necessary */
434 if (buf
->length
% buf
->min_io_size
!= 0)
435 buf
->length
= roundup(buf
->length
, buf
->min_io_size
);
437 read_wbuf(fd
, buf
, mp
);
438 ASSERT(buf
->length
>= length
);
440 ag
->xfs_sb
= (xfs_dsb_t
*) (buf
->data
+ diff
);
441 ASSERT(be32_to_cpu(ag
->xfs_sb
->sb_magicnum
) == XFS_SB_MAGIC
);
442 ag
->xfs_agf
= (xfs_agf_t
*) (buf
->data
+ diff
+ sectorsize
);
443 ASSERT(be32_to_cpu(ag
->xfs_agf
->agf_magicnum
) == XFS_AGF_MAGIC
);
444 ag
->xfs_agi
= (xfs_agi_t
*) (buf
->data
+ diff
+ 2 * sectorsize
);
445 ASSERT(be32_to_cpu(ag
->xfs_agi
->agi_magicnum
) == XFS_AGI_MAGIC
);
446 ag
->xfs_agfl
= (xfs_agfl_t
*) (buf
->data
+ diff
+ 3 * sectorsize
);
455 /* verify target threads */
456 for (i
= 0; i
< num_targets
; i
++)
457 if (target
[i
].state
!= INACTIVE
)
458 glob_masks
.num_working
++;
460 /* release target threads */
461 for (i
= 0; i
< num_targets
; i
++)
462 if (target
[i
].state
!= INACTIVE
)
463 pthread_mutex_unlock(&targ
[i
].wait
); /* wake up */
466 pthread_mutex_lock(&mainwait
);
477 * If this filesystem has CRCs, the original UUID is stamped into
478 * all metadata. If we are changing the UUID in the copy, we need
479 * to copy the original UUID into the meta_uuid slot and set the
480 * set the incompat flag if that hasn't already been done.
482 if (!uuid_equal(&tcarg
->uuid
, &ag_hdr
->xfs_sb
->sb_uuid
) &&
483 xfs_sb_version_hascrc(sb
) && !xfs_sb_version_hasmetauuid(sb
)) {
486 feat
= be32_to_cpu(ag_hdr
->xfs_sb
->sb_features_incompat
);
487 feat
|= XFS_SB_FEAT_INCOMPAT_META_UUID
;
488 ag_hdr
->xfs_sb
->sb_features_incompat
= cpu_to_be32(feat
);
489 platform_uuid_copy(&ag_hdr
->xfs_sb
->sb_meta_uuid
,
490 &ag_hdr
->xfs_sb
->sb_uuid
);
493 platform_uuid_copy(&ag_hdr
->xfs_sb
->sb_uuid
, &tcarg
->uuid
);
495 /* We may have changed the UUID, so update the superblock CRC */
496 if (xfs_sb_version_hascrc(sb
))
497 xfs_update_cksum((char *)ag_hdr
->xfs_sb
, sb
->sb_sectsize
,
502 main(int argc
, char **argv
)
507 xfs_off_t pos
, end_pos
;
510 __uint64_t size
, sizeb
;
511 __uint64_t numblocks
= 0;
518 int source_is_file
= 0;
519 int buffered_output
= 0;
521 uint btree_levels
, current_level
;
527 xfs_agnumber_t num_ags
, agno
;
529 xfs_daddr_t begin
, next_begin
, ag_begin
, new_begin
, ag_end
;
530 struct xfs_btree_block
*block
;
531 xfs_alloc_ptr_t
*ptr
;
532 xfs_alloc_rec_t
*rec_ptr
;
537 struct stat64 statbuf
;
539 progname
= basename(argv
[0]);
541 setlocale(LC_ALL
, "");
542 bindtextdomain(PACKAGE
, LOCALEDIR
);
545 while ((c
= getopt(argc
, argv
, "bdL:V")) != EOF
) {
554 logfile_name
= optarg
;
557 printf(_("%s version %s\n"), progname
, VERSION
);
564 if (argc
- optind
< 2)
568 logfd
= open(logfile_name
, O_CREAT
|O_WRONLY
|O_EXCL
, 0600);
570 logfile_name
= LOGFILE_NAME
;
571 logfd
= mkstemp(logfile_name
);
575 fprintf(stderr
, _("%s: couldn't open log file \"%s\"\n"),
576 progname
, logfile_name
);
577 perror(_("Aborting XFS copy - reason"));
581 if ((logerr
= fdopen(logfd
, "w")) == NULL
) {
582 fprintf(stderr
, _("%s: couldn't set up logfile stream\n"),
584 perror(_("Aborting XFS copy - reason"));
588 source_name
= argv
[optind
];
592 num_targets
= argc
- optind
;
593 if ((target
= malloc(sizeof(target_control
) * num_targets
)) == NULL
) {
594 do_log(_("Couldn't allocate target array\n"));
597 for (i
= 0; optind
< argc
; i
++, optind
++) {
598 target
[i
].name
= argv
[optind
];
600 target
[i
].position
= -1;
601 target
[i
].state
= INACTIVE
;
603 target
[i
].err_type
= 0;
606 parent_pid
= getpid();
608 /* open up source -- is it a file? */
610 open_flags
= O_RDONLY
;
612 if ((source_fd
= open(source_name
, open_flags
)) < 0) {
613 do_log(_("%s: couldn't open source \"%s\"\n"),
614 progname
, source_name
);
618 if (fstat64(source_fd
, &statbuf
) < 0) {
619 do_log(_("%s: couldn't stat source \"%s\"\n"),
620 progname
, source_name
);
624 if (S_ISREG(statbuf
.st_mode
))
627 if (source_is_file
&& platform_test_xfs_fd(source_fd
)) {
628 if (fcntl(source_fd
, F_SETFL
, open_flags
| O_DIRECT
) < 0) {
629 do_log(_("%s: Cannot set direct I/O flag on \"%s\".\n"),
630 progname
, source_name
);
633 if (xfsctl(source_name
, source_fd
, XFS_IOC_DIOINFO
, &d
) < 0) {
634 do_log(_("%s: xfsctl on file \"%s\" failed.\n"),
635 progname
, source_name
);
639 wbuf_align
= d
.d_mem
;
640 wbuf_size
= MIN(d
.d_maxiosz
, 1 * 1024 * 1024);
641 wbuf_miniosize
= d
.d_miniosz
;
643 /* set arbitrary I/O params, miniosize at least 1 disk block */
645 wbuf_align
= getpagesize();
646 wbuf_size
= 1 * 1024 * 1024;
647 wbuf_miniosize
= -1; /* set after mounting source fs */
650 if (!source_is_file
) {
652 * check to make sure a filesystem isn't mounted
655 if (platform_check_ismounted(source_name
, NULL
, &statbuf
, 0)) {
657 _("%s: Warning -- a filesystem is mounted on the source device.\n"),
660 _("\t\tGenerated copies may be corrupt unless the source is\n"));
662 _("\t\tunmounted or mounted read-only. Copy proceeding...\n"));
666 /* prepare the libxfs_init structure */
668 memset(&xargs
, 0, sizeof(xargs
));
669 xargs
.isdirect
= LIBXFS_DIRECT
;
670 xargs
.isreadonly
= LIBXFS_ISREADONLY
;
672 if (source_is_file
) {
673 xargs
.dname
= source_name
;
676 xargs
.volname
= source_name
;
678 if (!libxfs_init(&xargs
)) {
679 do_log(_("%s: couldn't initialize XFS library\n"
680 "%s: Aborting.\n"), progname
, progname
);
684 /* prepare the mount structure */
686 memset(&mbuf
, 0, sizeof(xfs_mount_t
));
687 libxfs_buftarg_init(&mbuf
, xargs
.ddev
, xargs
.logdev
, xargs
.rtdev
);
688 sbp
= libxfs_readbuf(mbuf
.m_ddev_targp
, XFS_SB_DADDR
, 1, 0,
691 libxfs_sb_from_disk(sb
, XFS_BUF_TO_SBP(sbp
));
693 mp
= libxfs_mount(&mbuf
, sb
, xargs
.ddev
, xargs
.logdev
, xargs
.rtdev
, 0);
695 do_log(_("%s: %s filesystem failed to initialize\n"
696 "%s: Aborting.\n"), progname
, source_name
, progname
);
698 } else if (mp
->m_sb
.sb_inprogress
) {
699 do_log(_("%s %s filesystem failed to initialize\n"
700 "%s: Aborting.\n"), progname
, source_name
, progname
);
702 } else if (mp
->m_sb
.sb_logstart
== 0) {
703 do_log(_("%s: %s has an external log.\n%s: Aborting.\n"),
704 progname
, source_name
, progname
);
706 } else if (mp
->m_sb
.sb_rextents
!= 0) {
707 do_log(_("%s: %s has a real-time section.\n"
708 "%s: Aborting.\n"), progname
, source_name
, progname
);
712 source_blocksize
= mp
->m_sb
.sb_blocksize
;
713 source_sectorsize
= mp
->m_sb
.sb_sectsize
;
715 if (wbuf_miniosize
== -1)
716 wbuf_miniosize
= source_sectorsize
;
718 ASSERT(source_blocksize
% source_sectorsize
== 0);
719 ASSERT(source_sectorsize
% BBSIZE
== 0);
721 if (source_blocksize
< source_sectorsize
) {
722 do_log(_("Error: filesystem block size is smaller than the"
723 " disk sectorsize.\nAborting XFS copy now.\n"));
727 first_agbno
= XFS_AGFL_BLOCK(mp
) + 1;
729 /* now open targets */
733 for (i
= 0; i
< num_targets
; i
++) {
734 int write_last_block
= 0;
736 if (stat64(target
[i
].name
, &statbuf
) < 0) {
737 /* ok, assume it's a file and create it */
739 do_out(_("Creating file %s\n"), target
[i
].name
);
741 open_flags
|= O_CREAT
;
742 if (!buffered_output
)
743 open_flags
|= O_DIRECT
;
744 write_last_block
= 1;
745 } else if (S_ISREG(statbuf
.st_mode
)) {
746 open_flags
|= O_TRUNC
;
747 if (!buffered_output
)
748 open_flags
|= O_DIRECT
;
749 write_last_block
= 1;
752 * check to make sure a filesystem isn't mounted
755 if (platform_check_ismounted(target
[i
].name
,
756 NULL
, &statbuf
, 0)) {
757 do_log(_("%s: a filesystem is mounted "
758 "on target device \"%s\".\n"
759 "%s cannot copy to mounted filesystems."
761 progname
, target
[i
].name
, progname
);
766 target
[i
].fd
= open(target
[i
].name
, open_flags
, 0644);
767 if (target
[i
].fd
< 0) {
768 do_log(_("%s: couldn't open target \"%s\"\n"),
769 progname
, target
[i
].name
);
773 if (write_last_block
) {
774 /* ensure regular files are correctly sized */
776 if (ftruncate64(target
[i
].fd
, mp
->m_sb
.sb_dblocks
*
778 do_log(_("%s: cannot grow data section.\n"),
782 if (platform_test_xfs_fd(target
[i
].fd
)) {
783 if (xfsctl(target
[i
].name
, target
[i
].fd
,
784 XFS_IOC_DIOINFO
, &d
) < 0) {
786 _("%s: xfsctl on \"%s\" failed.\n"),
787 progname
, target
[i
].name
);
790 wbuf_align
= MAX(wbuf_align
, d
.d_mem
);
791 wbuf_size
= MIN(d
.d_maxiosz
, wbuf_size
);
792 wbuf_miniosize
= MAX(d
.d_miniosz
,
797 char *lb
[XFS_MAX_SECTORSIZE
] = { NULL
};
800 /* ensure device files are sufficiently large */
802 off
= mp
->m_sb
.sb_dblocks
* source_blocksize
;
804 if (pwrite64(target
[i
].fd
, lb
, sizeof(lb
), off
) < 0) {
805 do_log(_("%s: failed to write last block\n"),
807 do_log(_("\tIs target \"%s\" too small?\n"),
814 /* initialize locks and bufs */
816 if (pthread_mutex_init(&glob_masks
.mutex
, NULL
) != 0) {
817 do_log(_("Couldn't initialize global thread mask\n"));
820 glob_masks
.num_working
= 0;
822 if (wbuf_init(&w_buf
, wbuf_size
, wbuf_align
,
823 wbuf_miniosize
, 0) == NULL
) {
824 do_log(_("Error initializing wbuf 0\n"));
828 wblocks
= wbuf_size
/ BBSIZE
;
830 if (wbuf_init(&btree_buf
, MAX(source_blocksize
, wbuf_miniosize
),
831 wbuf_align
, wbuf_miniosize
, 1) == NULL
) {
832 do_log(_("Error initializing btree buf 1\n"));
836 if (pthread_mutex_init(&mainwait
,NULL
) != 0) {
837 do_log(_("Error creating first semaphore.\n"));
841 /* need to start out blocking */
842 pthread_mutex_lock(&mainwait
);
844 /* set up sigchild signal handler */
846 signal(SIGCHLD
, handler
);
851 if ((targ
= malloc(num_targets
* sizeof(thread_args
))) == NULL
) {
852 do_log(_("Couldn't malloc space for thread args\n"));
857 for (i
= 0, tcarg
= targ
; i
< num_targets
; i
++, tcarg
++) {
859 platform_uuid_generate(&tcarg
->uuid
);
861 platform_uuid_copy(&tcarg
->uuid
, &mp
->m_sb
.sb_uuid
);
863 if (pthread_mutex_init(&tcarg
->wait
, NULL
) != 0) {
864 do_log(_("Error creating thread mutex %d\n"), i
);
868 /* need to start out blocking */
869 pthread_mutex_lock(&tcarg
->wait
);
872 for (i
= 0, tcarg
= targ
; i
< num_targets
; i
++, tcarg
++) {
874 tcarg
->fd
= target
[i
].fd
;
876 target
[i
].state
= ACTIVE
;
879 if (pthread_create(&target
[i
].pid
, NULL
,
880 begin_reader
, (void *)tcarg
)) {
881 do_log(_("Error creating thread for target %d\n"), i
);
886 ASSERT(num_targets
== num_threads
);
888 /* set up statistics */
890 num_ags
= mp
->m_sb
.sb_agcount
;
892 init_bar(mp
->m_sb
.sb_blocksize
/ BBSIZE
893 * ((__uint64_t
)mp
->m_sb
.sb_dblocks
894 - (__uint64_t
)mp
->m_sb
.sb_fdblocks
+ 10 * num_ags
));
898 for (agno
= 0; agno
< num_ags
&& kids
> 0; agno
++) {
899 /* read in first blocks of the ag */
901 read_ag_header(source_fd
, agno
, &w_buf
, &ag_hdr
, mp
,
902 source_blocksize
, source_sectorsize
);
904 /* set the in_progress bit for the first AG */
907 ag_hdr
.xfs_sb
->sb_inprogress
= 1;
909 /* save what we need (agf) in the btree buffer */
911 memmove(btree_buf
.data
, ag_hdr
.xfs_agf
, source_sectorsize
);
912 ag_hdr
.xfs_agf
= (xfs_agf_t
*) btree_buf
.data
;
913 btree_buf
.length
= source_blocksize
;
915 /* write the ag header out */
919 /* traverse btree until we get to the leftmost leaf node */
921 bno
= be32_to_cpu(ag_hdr
.xfs_agf
->agf_roots
[XFS_BTNUM_BNOi
]);
923 btree_levels
= be32_to_cpu(ag_hdr
.xfs_agf
->
924 agf_levels
[XFS_BTNUM_BNOi
]);
926 ag_end
= XFS_AGB_TO_DADDR(mp
, agno
,
927 be32_to_cpu(ag_hdr
.xfs_agf
->agf_length
) - 1)
928 + source_blocksize
/ BBSIZE
;
931 /* none of this touches the w_buf buffer */
933 if (current_level
>= btree_levels
) {
935 _("Error: current level %d >= btree levels %d\n"),
936 current_level
, btree_levels
);
942 btree_buf
.position
= pos
= (xfs_off_t
)
943 XFS_AGB_TO_DADDR(mp
,agno
,bno
) << BBSHIFT
;
944 btree_buf
.length
= source_blocksize
;
946 read_wbuf(source_fd
, &btree_buf
, mp
);
947 block
= (struct xfs_btree_block
*)
948 ((char *)btree_buf
.data
+
949 pos
- btree_buf
.position
);
951 if (be32_to_cpu(block
->bb_magic
) !=
952 (xfs_sb_version_hascrc(&mp
->m_sb
) ?
953 XFS_ABTB_CRC_MAGIC
: XFS_ABTB_MAGIC
)) {
954 do_log(_("Bad btree magic 0x%x\n"),
955 be32_to_cpu(block
->bb_magic
));
959 if (be16_to_cpu(block
->bb_level
) == 0)
962 ptr
= XFS_ALLOC_PTR_ADDR(mp
, block
, 1,
964 bno
= be32_to_cpu(ptr
[0]);
967 /* align first data copy but don't overwrite ag header */
969 pos
= w_buf
.position
>> BBSHIFT
;
970 length
= w_buf
.length
>> BBSHIFT
;
971 next_begin
= pos
+ length
;
972 ag_begin
= next_begin
;
974 ASSERT(w_buf
.position
% source_sectorsize
== 0);
976 /* handle the rest of the ag */
979 if (be16_to_cpu(block
->bb_level
) != 0) {
981 _("WARNING: source filesystem inconsistent.\n"));
983 _(" A leaf btree rec isn't a leaf. Aborting now.\n"));
987 rec_ptr
= XFS_ALLOC_REC_ADDR(mp
, block
, 1);
988 for (i
= 0; i
< be16_to_cpu(block
->bb_numrecs
);
990 /* calculate in daddr's */
995 * protect against pathological case of a
996 * hole right after the ag header in a
1000 if (begin
< ag_begin
)
1004 * round size up to ensure we copy a
1005 * range bigger than required
1008 sizeb
= XFS_AGB_TO_DADDR(mp
, agno
,
1009 be32_to_cpu(rec_ptr
->ar_startblock
)) -
1011 size
= roundup(sizeb
<<BBSHIFT
, wbuf_miniosize
);
1015 w_buf
.position
= (xfs_off_t
)
1020 * let lower layer do alignment
1022 if (size
> w_buf
.size
) {
1023 w_buf
.length
= w_buf
.size
;
1026 numblocks
+= wblocks
;
1028 w_buf
.length
= size
;
1033 read_wbuf(source_fd
, &w_buf
, mp
);
1036 w_buf
.position
+= w_buf
.length
;
1043 /* round next starting point down */
1045 new_begin
= XFS_AGB_TO_DADDR(mp
, agno
,
1046 be32_to_cpu(rec_ptr
->ar_startblock
) +
1047 be32_to_cpu(rec_ptr
->ar_blockcount
));
1048 next_begin
= rounddown(new_begin
,
1049 w_buf
.min_io_size
>> BBSHIFT
);
1052 if (be32_to_cpu(block
->bb_u
.s
.bb_rightsib
) == NULLAGBLOCK
)
1055 /* read in next btree record block */
1057 btree_buf
.position
= pos
= (xfs_off_t
)
1058 XFS_AGB_TO_DADDR(mp
, agno
, be32_to_cpu(
1059 block
->bb_u
.s
.bb_rightsib
)) << BBSHIFT
;
1060 btree_buf
.length
= source_blocksize
;
1062 /* let read_wbuf handle alignment */
1064 read_wbuf(source_fd
, &btree_buf
, mp
);
1066 block
= (struct xfs_btree_block
*)
1067 ((char *) btree_buf
.data
+
1068 pos
- btree_buf
.position
);
1070 ASSERT(be32_to_cpu(block
->bb_magic
) == XFS_ABTB_MAGIC
);
1074 * write out range of used blocks after last range
1075 * of free blocks in AG
1077 if (next_begin
< ag_end
) {
1080 sizeb
= ag_end
- begin
;
1081 size
= roundup(sizeb
<< BBSHIFT
, wbuf_miniosize
);
1086 w_buf
.position
= (xfs_off_t
) begin
<< BBSHIFT
;
1090 * let lower layer do alignment
1092 if (size
> w_buf
.size
) {
1093 w_buf
.length
= w_buf
.size
;
1096 numblocks
+= wblocks
;
1098 w_buf
.length
= size
;
1103 read_wbuf(source_fd
, &w_buf
, mp
);
1106 w_buf
.position
+= w_buf
.length
;
1108 howfar
= bump_bar(howfar
, numblocks
);
1117 /* write a clean log using the specified UUID */
1118 for (j
= 0, tcarg
= targ
; j
< num_targets
; j
++) {
1119 w_buf
.owner
= tcarg
;
1120 w_buf
.length
= rounddown(w_buf
.size
,
1122 pos
= write_log_header(
1123 source_fd
, &w_buf
, mp
);
1124 end_pos
= write_log_trailer(
1125 source_fd
, &w_buf
, mp
);
1126 w_buf
.position
= pos
;
1127 memset(w_buf
.data
, 0, w_buf
.length
);
1129 while (w_buf
.position
< end_pos
) {
1131 w_buf
.position
+= w_buf
.length
;
1139 /* reread and rewrite superblocks (UUID and in-progress) */
1140 /* [backwards, so inprogress bit only updated when done] */
1142 for (i
= num_ags
- 1; i
>= 0; i
--) {
1143 read_ag_header(source_fd
, i
, &w_buf
, &ag_hdr
, mp
,
1144 source_blocksize
, source_sectorsize
);
1146 ag_hdr
.xfs_sb
->sb_inprogress
= 0;
1148 /* do each thread in turn, each has its own UUID */
1150 for (j
= 0, tcarg
= targ
; j
< num_targets
; j
++) {
1151 sb_update_uuid(sb
, &ag_hdr
, tcarg
);
1165 next_log_chunk(xfs_caddr_t p
, int offset
, void *private)
1167 wbuf
*buf
= (wbuf
*)private;
1169 if (buf
->length
< (int)(p
- buf
->data
) + offset
) {
1170 /* need to flush this one, then start afresh */
1172 do_write(buf
->owner
);
1173 memset(buf
->data
, 0, buf
->length
);
1180 * Writes a log header at the start of the log (with the real
1181 * filesystem UUID embedded into it), and writes to all targets.
1183 * Returns the next buffer-length-aligned disk address.
1186 write_log_header(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
1188 xfs_caddr_t p
= buf
->data
;
1192 logstart
= XFS_FSB_TO_DADDR(mp
, mp
->m_sb
.sb_logstart
) << BBSHIFT
;
1193 buf
->position
= rounddown(logstart
, (xfs_off_t
)buf
->length
);
1195 memset(p
, 0, buf
->size
);
1196 if (logstart
% buf
->length
) { /* unaligned */
1197 read_wbuf(fd
, buf
, mp
);
1198 offset
= logstart
- buf
->position
;
1200 memset(p
, 0, buf
->length
- offset
);
1203 offset
= libxfs_log_header(p
, &buf
->owner
->uuid
,
1204 xfs_sb_version_haslogv2(&mp
->m_sb
) ? 2 : 1,
1205 mp
->m_sb
.sb_logsunit
, XLOG_FMT
,
1206 next_log_chunk
, buf
);
1207 do_write(buf
->owner
);
1209 return roundup(logstart
+ offset
, buf
->length
);
1213 * May do an aligned read of the last buffer in the log (& zero
1214 * the start of that buffer). Returns the disk address at the
1215 * end of last aligned buffer in the log.
1218 write_log_trailer(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
1223 logend
= XFS_FSB_TO_DADDR(mp
, mp
->m_sb
.sb_logstart
) << BBSHIFT
;
1224 logend
+= XFS_FSB_TO_B(mp
, mp
->m_sb
.sb_logblocks
);
1226 buf
->position
= rounddown(logend
, (xfs_off_t
)buf
->length
);
1228 if (logend
% buf
->length
) { /* unaligned */
1229 read_wbuf(fd
, buf
, mp
);
1230 offset
= (int)(logend
- buf
->position
);
1231 memset(buf
->data
, 0, offset
);
1232 do_write(buf
->owner
);
1235 return buf
->position
;