2 * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
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.
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.
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.
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.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
33 #include <xfs/libxfs.h>
41 #define rounddown(x, y) (((x)/(y))*(y))
43 extern int platform_check_ismounted(char *, char *, struct stat64
*, int);
48 char LOGFILE_NAME
[] = "/var/tmp/xfs_copy.log.XXXXXX";
53 unsigned int source_blocksize
; /* source filesystem blocksize */
54 unsigned int source_sectorsize
; /* source disk sectorsize */
56 xfs_agblock_t first_agbno
;
58 __uint64_t barcount
[11];
60 unsigned int num_targets
;
61 target_control
*target
;
69 thread_control glob_masks
;
72 pthread_mutex_t mainwait
;
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
);
80 /* general purpose message reporting routine */
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 */
89 do_message(int flags
, int code
, const char *fmt
, ...)
96 if (vfprintf(logerr
, fmt
, ap
) <= 0)
99 flags
|= ERR
; /* failed, force stderr */
101 vfprintf(stderr
, fmt
, ap
);
102 else if (flags
& OUT
)
103 vfprintf(stdout
, fmt
, ap
);
107 do_message(flags
& ~PRE
, 0, ": %s\n", strerror(code
));
110 _("Check logfile \"%s\" for more details\n"),
114 /* logfile is broken, force a write to stderr */
116 fprintf(stderr
, _("%s: could not write to logfile \"%s\".\n"),
117 progname
, logfile_name
);
119 _("Aborting XFS copy -- logfile error -- reason: %s\n"),
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() \
133 do_message(ERR|LOG|PRE|LAST, errno, \
134 _("Aborting XFS copy - reason")); \
141 int i
, first_error
= 0;
143 for (i
= 0; i
< num_targets
; i
++) {
144 if (target
[i
].state
== INACTIVE
) {
145 if (first_error
== 0) {
148 _("THE FOLLOWING COPIES FAILED TO COMPLETE\n"));
150 do_log(" %s -- ", target
[i
].name
);
151 if (target
[i
].err_type
== 0)
152 do_log(_("write error"));
154 do_log(_("lseek64 error"));
155 do_log(_(" at offset %lld\n"), target
[i
].position
);
158 if (first_error
== 0) {
159 fprintf(stdout
, _("All copies completed.\n"));
162 fprintf(stderr
, _("See \"%s\" for more details.\n"),
169 * don't have to worry about alignment and mins because those
170 * are taken care of when the buffer's read in
173 do_write(thread_args
*args
)
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;
181 target
[args
->id
].position
= w_buf
.position
;
185 if ((res
= write(target
[args
->id
].fd
, w_buf
.data
,
186 w_buf
.length
)) == w_buf
.length
) {
187 target
[args
->id
].position
+= res
;
193 target
[args
->id
].error
= errno
;
194 target
[args
->id
].position
= w_buf
.position
;
200 begin_reader(void *arg
)
202 thread_args
*args
= arg
;
205 pthread_mutex_lock(&args
->wait
);
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
);
216 /* error will be logged by primary thread */
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
);
232 /* only the parent gets to kill things */
234 if (getpid() != parent_pid
)
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
);
249 pid_t pid
= getpid();
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 */
261 if (target
[i
].err_type
== 0) {
263 _("%s: write error on target %d \"%s\" at offset %lld\n"),
264 progname
, i
, target
[i
].name
,
268 _("%s: lseek64 error on target %d \"%s\" at offset %lld\n"),
269 progname
, i
, target
[i
].name
,
273 do_vfatal(target
[i
].error
,
274 _("Aborting target %d - reason"), i
);
278 _("Aborting XFS copy - no more targets.\n"));
283 signal(SIGCHLD
, handler
);
286 /* it just croaked it bigtime, log it */
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"));
300 /* unknown child -- something very wrong */
302 do_warn(_("%s: Unknown child died (should never happen!)\n"), progname
);
305 signal(SIGCHLD
, handler
);
312 _("Usage: %s [-bd] [-L logfile] source target [target ...]\n"),
318 init_bar(__uint64_t source_blocks
)
322 for (i
= 0; i
< 11; i
++)
323 barcount
[i
] = (source_blocks
/10)*i
;
327 bump_bar(int tenths
, __uint64_t numblocks
)
329 static char *bar
[11] = {
344 printf("%s", bar
[10]);
347 while (tenths
< 10 && numblocks
> barcount
[tenths
]) {
348 printf("%s", bar
[tenths
]);
356 static xfs_off_t source_position
= -1;
359 wbuf_init(wbuf
*buf
, int data_size
, int data_align
, int min_io_size
, int id
)
362 if ((buf
->data
= memalign(data_align
, data_size
)) == 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
);
371 read_wbuf(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
378 newpos
= rounddown(buf
->position
, (xfs_off_t
) buf
->min_io_size
);
380 if (newpos
!= buf
->position
) {
381 diff
= buf
->position
- newpos
;
382 buf
->position
= newpos
;
387 if (source_position
!= buf
->position
) {
388 lres
= lseek64(fd
, buf
->position
, SEEK_SET
);
390 do_warn(_("%s: lseek64 failure at offset %lld\n"),
391 progname
, source_position
);
394 source_position
= buf
->position
;
397 ASSERT(source_position
% source_sectorsize
== 0);
399 /* round up length for direct I/O if necessary */
401 if (buf
->length
% buf
->min_io_size
!= 0)
402 buf
->length
= roundup(buf
->length
, buf
->min_io_size
);
404 if (buf
->length
> buf
->size
) {
405 do_warn(_("assert error: buf->length = %d, buf->size = %d\n"),
406 buf
->length
, buf
->size
);
411 if ((res
= read(fd
, buf
->data
, buf
->length
)) < 0) {
412 do_warn(_("%s: read failure at offset %lld\n"),
413 progname
, source_position
);
417 if (res
< buf
->length
&&
418 source_position
+ res
== mp
->m_sb
.sb_dblocks
* source_blocksize
)
421 ASSERT(res
== buf
->length
);
422 source_position
+= res
;
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
)
435 /* initial settings */
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
;
442 /* handle alignment stuff */
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
;
451 /* round up length for direct I/O if necessary */
453 if (buf
->length
% buf
->min_io_size
!= 0)
454 buf
->length
= roundup(buf
->length
, buf
->min_io_size
);
457 read_wbuf(fd
, buf
, mp
);
458 ASSERT(buf
->length
>= length
);
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
);
475 /* verify target threads */
476 for (i
= 0; i
< num_targets
; i
++)
477 if (target
[i
].state
!= INACTIVE
)
478 glob_masks
.num_working
++;
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 */
486 pthread_mutex_lock(&mainwait
);
492 main(int argc
, char **argv
)
497 xfs_off_t pos
, end_pos
;
499 int c
, first_residue
, tmp_residue
;
500 __uint64_t size
, sizeb
;
501 __uint64_t numblocks
= 0;
508 int source_is_file
= 0;
509 int buffered_output
= 0;
511 uint btree_levels
, current_level
;
517 xfs_agnumber_t num_ags
, agno
;
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
;
527 struct stat64 statbuf
;
529 progname
= basename(argv
[0]);
531 setlocale(LC_ALL
, "");
532 bindtextdomain(PACKAGE
, LOCALEDIR
);
535 while ((c
= getopt(argc
, argv
, "bdL:V")) != EOF
) {
544 logfile_name
= optarg
;
547 printf(_("%s version %s\n"), progname
, VERSION
);
554 if (argc
- optind
< 2)
558 logfd
= open(logfile_name
, O_CREAT
|O_WRONLY
|O_EXCL
, 0600);
560 logfile_name
= LOGFILE_NAME
;
561 logfd
= mkstemp(logfile_name
);
565 fprintf(stderr
, _("%s: couldn't open log file \"%s\"\n"),
566 progname
, logfile_name
);
567 perror(_("Aborting XFS copy - reason"));
571 if ((logerr
= fdopen(logfd
, "w")) == NULL
) {
572 fprintf(stderr
, _("%s: couldn't set up logfile stream\n"),
574 perror(_("Aborting XFS copy - reason"));
578 source_name
= argv
[optind
];
582 num_targets
= argc
- optind
;
583 if ((target
= malloc(sizeof(target_control
) * num_targets
)) == NULL
) {
584 do_log(_("Couldn't allocate target array\n"));
587 for (i
= 0; optind
< argc
; i
++, optind
++) {
588 target
[i
].name
= argv
[optind
];
590 target
[i
].position
= -1;
591 target
[i
].state
= INACTIVE
;
593 target
[i
].err_type
= 0;
596 parent_pid
= getpid();
598 if (atexit(killall
)) {
599 do_log(_("%s: couldn't register atexit function.\n"), progname
);
603 /* open up source -- is it a file? */
605 open_flags
= O_RDONLY
;
607 if ((source_fd
= open(source_name
, open_flags
)) < 0) {
608 do_log(_("%s: couldn't open source \"%s\"\n"),
609 progname
, source_name
);
613 if (fstat64(source_fd
, &statbuf
) < 0) {
614 do_log(_("%s: couldn't stat source \"%s\"\n"),
615 progname
, source_name
);
619 if (S_ISREG(statbuf
.st_mode
))
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
);
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
);
634 wbuf_align
= d
.d_mem
;
635 wbuf_size
= d
.d_maxiosz
;
636 wbuf_miniosize
= d
.d_miniosz
;
638 /* set arbitrary I/O params, miniosize at least 1 disk block */
641 wbuf_size
= 1024 * 4000;
642 wbuf_miniosize
= -1; /* set after mounting source fs */
645 if (!source_is_file
) {
647 * check to make sure a filesystem isn't mounted
650 if (platform_check_ismounted(source_name
, NULL
, &statbuf
, 0)) {
652 _("%s: Warning -- a filesystem is mounted on the source device.\n"),
655 _("\t\tGenerated copies may be corrupt unless the source is\n"));
657 _("\t\tunmounted or mounted read-only. Copy proceeding...\n"));
661 /* prepare the libxfs_init structure */
663 memset(&xargs
, 0, sizeof(xargs
));
664 xargs
.notvolmsg
= "oh no %s";
665 xargs
.isreadonly
= LIBXFS_ISREADONLY
;
668 if (source_is_file
) {
669 xargs
.dname
= source_name
;
672 xargs
.volname
= source_name
;
674 if (!libxfs_init(&xargs
)) {
675 do_log(_("%s: couldn't initialize XFS library\n"
676 "%s: Aborting.\n"), progname
, progname
);
680 /* prepare the mount structure */
682 sbp
= libxfs_readbuf(xargs
.ddev
, XFS_SB_DADDR
, 1, 0);
683 memset(&mbuf
, 0, sizeof(xfs_mount_t
));
685 libxfs_xlate_sb(XFS_BUF_PTR(sbp
), sb
, 1, ARCH_CONVERT
, XFS_SB_ALL_BITS
);
687 mp
= libxfs_mount(&mbuf
, sb
, xargs
.ddev
, xargs
.logdev
, xargs
.rtdev
, 1);
689 do_log(_("%s: %s filesystem failed to initialize\n"
690 "%s: Aborting.\n"), progname
, source_name
, progname
);
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
);
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
);
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
);
706 source_blocksize
= mp
->m_sb
.sb_blocksize
;
707 source_sectorsize
= mp
->m_sb
.sb_sectsize
;
709 if (wbuf_miniosize
== -1)
710 wbuf_miniosize
= source_sectorsize
;
712 ASSERT(source_blocksize
% source_sectorsize
== 0);
713 ASSERT(source_sectorsize
% BBSIZE
== 0);
715 if (source_blocksize
> source_sectorsize
) {
716 /* get number of leftover sectors in last block of ag header */
718 tmp_residue
= ((XFS_AGFL_DADDR(mp
) + 1) * source_sectorsize
)
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
) {
726 do_log(_("Error: filesystem block size is smaller than the"
727 " disk sectorsize.\nAborting XFS copy now.\n"));
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);
737 /* now open targets */
741 for (i
= 0; i
< num_targets
; i
++) {
742 int write_last_block
= 0;
744 if (stat64(target
[i
].name
, &statbuf
) < 0) {
745 /* ok, assume it's a file and create it */
747 do_out(_("Creating file %s\n"), target
[i
].name
);
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;
760 * check to make sure a filesystem isn't mounted
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."
769 progname
, target
[i
].name
, progname
);
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
);
781 if (write_last_block
) {
782 /* ensure regular files are correctly sized */
784 if (ftruncate64(target
[i
].fd
, mp
->m_sb
.sb_dblocks
*
786 do_log(_("%s: cannot grow data section.\n"),
790 if (platform_test_xfs_fd(target
[i
].fd
)) {
791 if (xfsctl(target
[i
].name
, target
[i
].fd
,
792 XFS_IOC_DIOINFO
, &d
) < 0) {
794 _("%s: xfsctl on \"%s\" failed.\n"),
795 progname
, target
[i
].name
);
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
,
805 char *lb
[XFS_MAX_SECTORSIZE
] = { 0 };
808 /* ensure device files are sufficiently large */
810 off
= mp
->m_sb
.sb_dblocks
* source_blocksize
;
812 if (pwrite64(target
[i
].fd
, lb
, sizeof(lb
), off
) < 0) {
813 do_log(_("%s: failed to write last block\n"),
815 do_log(_("\tIs target \"%s\" too small?\n"),
822 /* initialize locks and bufs */
824 if (pthread_mutex_init(&glob_masks
.mutex
, NULL
) != 0) {
825 do_log(_("Couldn't initialize global thread mask\n"));
828 glob_masks
.num_working
= 0;
830 if (wbuf_init(&w_buf
, wbuf_size
, wbuf_align
,
831 wbuf_miniosize
, 0) == NULL
) {
832 do_log(_("Error initializing wbuf 0\n"));
836 wblocks
= wbuf_size
/ BBSIZE
;
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"));
844 if (pthread_mutex_init(&mainwait
,NULL
) != 0) {
845 do_log(_("Error creating first semaphore.\n"));
849 /* need to start out blocking */
850 pthread_mutex_lock(&mainwait
);
852 /* set up sigchild signal handler */
854 signal(SIGCHLD
, handler
);
859 if ((targ
= malloc(num_targets
* sizeof(thread_args
))) == NULL
) {
860 do_log(_("Couldn't malloc space for thread args\n"));
865 for (i
= 0, tcarg
= targ
; i
< num_targets
; i
++, tcarg
++) {
867 uuid_generate(tcarg
->uuid
);
869 uuid_copy(tcarg
->uuid
, mp
->m_sb
.sb_uuid
);
871 if (pthread_mutex_init(&tcarg
->wait
, NULL
) != 0) {
872 do_log(_("Error creating thread mutex %d\n"), i
);
876 /* need to start out blocking */
877 pthread_mutex_lock(&tcarg
->wait
);
880 for (i
= 0, tcarg
= targ
; i
< num_targets
; i
++, tcarg
++) {
882 tcarg
->fd
= target
[i
].fd
;
884 target
[i
].state
= ACTIVE
;
887 if (pthread_create(&target
[i
].pid
, NULL
,
888 begin_reader
, (void *)tcarg
)) {
889 do_log(_("Error creating thread for target %d\n"), i
);
894 ASSERT(num_targets
== num_threads
);
896 /* set up statistics */
898 num_ags
= mp
->m_sb
.sb_agcount
;
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
));
905 block
= (xfs_alloc_block_t
*) btree_buf
.data
;
907 for (agno
= 0; agno
< num_ags
&& kids
> 0; agno
++) {
908 /* read in first blocks of the ag */
910 read_ag_header(source_fd
, agno
, &w_buf
, &ag_hdr
, mp
,
911 source_blocksize
, source_sectorsize
);
913 /* set the in_progress bit for the first AG */
916 INT_SET(ag_hdr
.xfs_sb
->sb_inprogress
, ARCH_CONVERT
, 1);
918 /* save what we need (agf) in the btree buffer */
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
;
924 /* write the ag header out */
928 /* traverse btree until we get to the leftmost leaf node */
930 bno
= INT_GET(ag_hdr
.xfs_agf
->agf_roots
[XFS_BTNUM_BNOi
],
933 btree_levels
= INT_GET(
934 ag_hdr
.xfs_agf
->agf_levels
[XFS_BTNUM_BNOi
],
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
;
942 /* none of this touches the w_buf buffer */
944 ASSERT(current_level
< btree_levels
);
948 btree_buf
.position
= pos
= (xfs_off_t
)
949 XFS_AGB_TO_DADDR(mp
,agno
,bno
) << BBSHIFT
;
950 btree_buf
.length
= source_blocksize
;
952 read_wbuf(source_fd
, &btree_buf
, mp
);
953 block
= (xfs_alloc_block_t
*) ((char *) btree_buf
.data
954 + pos
- btree_buf
.position
);
956 ASSERT(INT_GET(block
->bb_magic
,ARCH_CONVERT
) ==
959 if (INT_GET(block
->bb_level
,ARCH_CONVERT
) == 0)
962 ptr
= XFS_BTREE_PTR_ADDR(sourceb_blocksize
, xfs_alloc
,
963 block
, 1, mp
->m_alloc_mxr
[1]),
965 bno
= INT_GET(ptr
[0], ARCH_CONVERT
);
968 /* align first data copy but don't overwrite ag header */
970 pos
= w_buf
.position
>> BBSHIFT
;
971 length
= w_buf
.length
>> BBSHIFT
;
972 next_begin
= pos
+ length
;
973 ag_begin
= next_begin
;
975 ASSERT(w_buf
.position
% source_sectorsize
== 0);
977 /* handle the rest of the ag */
980 if (INT_GET(block
->bb_level
,ARCH_CONVERT
) != 0) {
982 _("WARNING: source filesystem inconsistent.\n"));
984 _(" A leaf btree rec isn't a leaf. Aborting now.\n"));
988 rec_ptr
= XFS_BTREE_REC_ADDR(source_blocksize
,
989 xfs_alloc
, block
, 1, mp
->m_alloc_mxr
[0]);
992 i
< INT_GET(block
->bb_numrecs
,ARCH_CONVERT
);
994 /* calculate in daddr's */
999 * protect against pathological case of a
1000 * hole right after the ag header in a
1004 if (begin
< ag_begin
)
1008 * round size up to ensure we copy a
1009 * range bigger than required
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
);
1019 w_buf
.position
= (xfs_off_t
)
1024 * let lower layer do alignment
1026 if (size
> w_buf
.size
) {
1027 w_buf
.length
= w_buf
.size
;
1030 numblocks
+= wblocks
;
1032 w_buf
.length
= size
;
1037 read_wbuf(source_fd
, &w_buf
, mp
);
1040 w_buf
.position
+= w_buf
.length
;
1047 /* round next starting point down */
1049 new_begin
= XFS_AGB_TO_DADDR(mp
, agno
,
1050 INT_GET(rec_ptr
->ar_startblock
,
1052 INT_GET(rec_ptr
->ar_blockcount
,
1054 next_begin
= rounddown(new_begin
,
1055 w_buf
.min_io_size
>> BBSHIFT
);
1058 if (INT_GET(block
->bb_rightsib
,ARCH_CONVERT
) ==
1062 /* read in next btree record block */
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
;
1070 /* let read_wbuf handle alignment */
1072 read_wbuf(source_fd
, &btree_buf
, mp
);
1074 block
= (xfs_alloc_block_t
*) ((char *) btree_buf
.data
1075 + pos
- btree_buf
.position
);
1077 ASSERT(INT_GET(block
->bb_magic
,ARCH_CONVERT
) ==
1082 * write out range of used blocks after last range
1083 * of free blocks in AG
1085 if (next_begin
< ag_end
) {
1088 sizeb
= ag_end
- begin
;
1089 size
= roundup(sizeb
<< BBSHIFT
, wbuf_miniosize
);
1094 w_buf
.position
= (xfs_off_t
) begin
<< BBSHIFT
;
1098 * let lower layer do alignment
1100 if (size
> w_buf
.size
) {
1101 w_buf
.length
= w_buf
.size
;
1104 numblocks
+= wblocks
;
1106 w_buf
.length
= size
;
1111 read_wbuf(source_fd
, &w_buf
, mp
);
1114 w_buf
.position
+= w_buf
.length
;
1116 howfar
= bump_bar(howfar
, numblocks
);
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
,
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
);
1137 while (w_buf
.position
< end_pos
) {
1139 w_buf
.position
+= w_buf
.length
;
1147 /* reread and rewrite superblocks (UUID and in-progress) */
1148 /* [backwards, so inprogress bit only updated when done] */
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
);
1154 ag_hdr
.xfs_sb
->sb_inprogress
= 0;
1156 /* do each thread in turn, each has its own UUID */
1158 for (j
= 0, tcarg
= targ
; j
< num_targets
; j
++) {
1159 uuid_copy(ag_hdr
.xfs_sb
->sb_uuid
, tcarg
->uuid
);
1176 next_log_chunk(xfs_caddr_t p
, int offset
, void *private)
1178 wbuf
*buf
= (wbuf
*)private;
1180 if (buf
->length
< (int)(p
- buf
->data
) + offset
) {
1181 /* need to flush this one, then start afresh */
1183 do_write(buf
->owner
);
1184 memset(buf
->data
, 0, buf
->length
);
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.
1194 * Returns the next buffer-length-aligned disk address.
1197 write_log_header(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
1199 xfs_caddr_t p
= buf
->data
;
1203 logstart
= XFS_FSB_TO_DADDR(mp
, mp
->m_sb
.sb_logstart
) << BBSHIFT
;
1204 buf
->position
= rounddown(logstart
, (xfs_off_t
)buf
->length
);
1206 memset(p
, 0, buf
->size
);
1207 if (logstart
% buf
->length
) { /* unaligned */
1208 read_wbuf(fd
, buf
, mp
);
1209 offset
= logstart
- buf
->position
;
1211 memset(p
, 0, buf
->length
- offset
);
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
);
1220 return logstart
+ roundup(offset
, buf
->length
);
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.
1229 write_log_trailer(int fd
, wbuf
*buf
, xfs_mount_t
*mp
)
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
);
1237 buf
->position
= rounddown(logend
, (xfs_off_t
)buf
->length
);
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
);
1246 return buf
->position
;