2 * Copyright (c) 2005 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/sbin/jscan/dump_mirror.c,v 1.8 2005/11/06 12:32:56 swildner Exp $
38 #include <sys/vfscache.h>
40 static void dump_mirror_stream(struct jsession
*ss
, struct jstream
*js
);
41 static int dump_mirror_toprecord(struct jsession
*ss
, struct jstream
*js
,
42 off_t
*off
, off_t recsize
, int level
);
43 static int dump_mirror_subrecord(enum jdirection direction
, struct jstream
*js
,
44 off_t
*off
, off_t recsize
, int level
,
46 static int dump_mirror_payload(int16_t rectype
, struct jstream
*js
, off_t off
,
47 int recsize
, int level
, struct jattr
*jattr
);
48 static int dump_mirror_rebuild_redo(u_int16_t rectype
,
49 struct jstream
*js
, struct jattr
*jattr
);
50 static int dump_mirror_rebuild_undo(u_int16_t rectype
,
51 struct jstream
*js
, struct jattr
*jattr
);
52 static void undo_recreate(const char *filename
,
53 struct jstream
*js
, struct jattr
*jattr
);
54 static void dosetattr(const char *filename
, int fd
, struct jattr
*jattr
);
57 dump_mirror(struct jsession
*ss
, struct jdata
*jd
)
61 if ((js
= jaddrecord(ss
, jd
)) != NULL
) {
62 dump_mirror_stream(ss
, js
);
65 jsession_update_transid(ss
, jd
->jd_transid
);
69 dump_mirror_stream(struct jsession
*ss
, struct jstream
*js
)
71 struct journal_rawrecbeg head
;
75 save_umask
= umask(0);
76 jsread(js
, 0, &head
, sizeof(head
));
78 sid
= head
.streamid
& JREC_STREAMID_MASK
;
79 if (sid
>= JREC_STREAMID_JMIN
&& sid
< JREC_STREAMID_JMAX
) {
80 off_t off
= sizeof(head
);
81 dump_mirror_toprecord(ss
, js
, &off
,
82 js
->js_normalized_total
-
83 sizeof(struct journal_rawrecbeg
),
86 switch(head
.streamid
& JREC_STREAMID_MASK
) {
87 case JREC_STREAMID_SYNCPT
& JREC_STREAMID_MASK
:
89 case JREC_STREAMID_PAD
& JREC_STREAMID_MASK
:
91 case JREC_STREAMID_DISCONT
& JREC_STREAMID_MASK
:
93 case JREC_STREAMID_ANNOTATE
& JREC_STREAMID_MASK
:
103 * Execute a meta-transaction, e.g. something like 'WRITE'. Meta-transactions
104 * are almost universally nested.
107 dump_mirror_toprecord(struct jsession
*ss
, struct jstream
*js
,
108 off_t
*off
, off_t recsize
, int level
)
110 struct journal_subrecord sub
;
118 bzero(&jattr
, sizeof(jattr
));
121 while (recsize
> 0) {
122 if ((error
= jsread(js
, base
, &sub
, sizeof(sub
))) != 0)
124 if (sub
.recsize
== -1) {
125 if ((sub
.rectype
& JMASK_NESTED
) == 0) {
126 printf("Record size of -1 only works for nested records\n");
130 payload
= 0x7FFFFFFF;
131 subsize
= 0x7FFFFFFF;
133 payload
= sub
.recsize
- sizeof(sub
);
134 subsize
= (sub
.recsize
+ 7) & ~7;
136 if (sub
.rectype
& JMASK_NESTED
) {
137 *off
= base
+ sizeof(sub
);
138 error
= dump_mirror_subrecord(ss
->ss_direction
, js
, off
,
139 payload
, level
+ 1, &jattr
);
140 } else if (sub
.rectype
& JMASK_SUBRECORD
) {
141 *off
= base
+ sizeof(sub
) + payload
;
142 } else if ((sub
.rectype
& JTYPE_MASK
) == JLEAF_PAD
) {
145 if (ss
->ss_direction
== JD_FORWARDS
)
146 dump_mirror_rebuild_redo(sub
.rectype
, js
, &jattr
);
148 dump_mirror_rebuild_undo(sub
.rectype
, js
, &jattr
);
152 if (sub
.recsize
== -1) {
153 if ((sub
.rectype
& JMASK_NESTED
) == 0) {
154 printf("Record size of -1 only works for nested records\n");
158 recsize
-= ((*off
+ 7) & ~7) - base
;
159 base
= (*off
+ 7) & ~7;
162 subsize
= sizeof(sub
);
166 if (sub
.rectype
& JMASK_LAST
)
174 * Parse a meta-transaction's nested records. The highest subrecord layer
175 * starts at layer = 2 (the top layer specifying the command is layer = 1).
177 * The nested subrecord contains informational records containing primarily
178 * namespace data, and further subrecords containing nested
179 * audit, undo, and redo data.
182 dump_mirror_subrecord(enum jdirection direction
, struct jstream
*js
,
183 off_t
*off
, off_t recsize
, int level
,
186 struct journal_subrecord sub
;
195 while (recsize
> 0) {
196 if ((error
= jsread(js
, base
, &sub
, sizeof(sub
))) != 0)
198 rectype
= sub
.rectype
& JTYPE_MASK
; /* includes the nested bit */
199 if (sub
.recsize
== -1) {
200 payload
= 0x7FFFFFFF;
201 subsize
= 0x7FFFFFFF;
203 payload
= sub
.recsize
- sizeof(sub
);
204 subsize
= (sub
.recsize
+ 7) & ~7;
208 *off
= base
+ sizeof(sub
);
211 case JTYPE_REDO
: /* NESTED */
213 * Process redo information when scanning forwards.
215 if (direction
== JD_FORWARDS
) {
216 error
= dump_mirror_subrecord(direction
, js
, off
, payload
,
221 case JTYPE_UNDO
: /* NESTED */
223 * Process undo information when scanning backwards.
225 if (direction
== JD_BACKWARDS
) {
226 error
= dump_mirror_subrecord(direction
, js
, off
, payload
,
231 case JTYPE_CRED
: /* NESTED */
233 * Ignore audit information
236 default: /* NESTED or non-NESTED */
238 * Execute these. Nested records might contain attribute
239 * information under an UNDO or REDO parent, for example.
241 if (rectype
& JMASK_NESTED
) {
242 error
= dump_mirror_subrecord(direction
, js
, off
, payload
,
245 } else if (rectype
& JMASK_SUBRECORD
) {
246 error
= dump_mirror_payload(sub
.rectype
, js
, *off
, payload
,
255 * skip only applies to nested subrecords. If the record size
256 * is unknown the record MUST be a nested record, and if we have
257 * not processed it we must recurse to figure out the actual size.
259 if (sub
.recsize
== -1) {
260 assert(sub
.rectype
& JMASK_NESTED
);
262 error
= dump_mirror_subrecord(direction
, js
, off
, payload
,
265 recsize
-= ((*off
+ 7) & ~7) - base
;
266 base
= (*off
+ 7) & ~7;
269 subsize
= sizeof(sub
);
275 if (sub
.rectype
& JMASK_LAST
)
283 dump_mirror_payload(int16_t rectype
, struct jstream
*js
, off_t off
,
284 int recsize
, int level __unused
, struct jattr
*jattr
)
287 struct jattr_data
*data
;
293 if ((rectype
& ~JMASK_LAST
) != JLEAF_FILEDATA
) {
294 error
= jsreadp(js
, off
, (const void **)&buf
, recsize
);
302 switch(rectype
& ~JMASK_LAST
) {
306 case JLEAF_SYMLINKDATA
:
307 jattr
->symlinkdata
= dupdatastr(buf
, recsize
);
308 jattr
->symlinklen
= recsize
;
311 if ((data
= jattr
->last_data
) == NULL
) {
312 jattr
->data
.off
= off
;
313 jattr
->data
.bytes
= recsize
;
314 jattr
->last_data
= &jattr
->data
;
316 data
->next
= malloc(sizeof(jattr
->data
));
319 data
->bytes
= recsize
;
321 jattr
->last_data
= data
;
325 jattr
->path1
= dupdatapath(buf
, recsize
);
328 jattr
->path2
= dupdatapath(buf
, recsize
);
331 jattr
->path3
= dupdatapath(buf
, recsize
);
334 jattr
->path4
= dupdatapath(buf
, recsize
);
337 jattr
->uid
= buf_to_int64(buf
, recsize
);
340 jattr
->gid
= buf_to_int64(buf
, recsize
);
343 jattr
->vtype
= buf_to_int64(buf
, recsize
);
346 jattr
->modes
= buf_to_int64(buf
, recsize
);
349 jattr
->fflags
= buf_to_int64(buf
, recsize
);
352 jattr
->pid
= buf_to_int64(buf
, recsize
);
355 jattr
->ppid
= buf_to_int64(buf
, recsize
);
358 jattr
->comm
= dupdatastr(buf
, recsize
);
361 jattr
->attrname
= dupdatastr(buf
, recsize
);
364 jattr
->pathref
= dupdatapath(buf
, recsize
);
366 case JLEAF_RESERVED_0F
:
369 jattr
->seekpos
= buf_to_int64(buf
, recsize
);
372 jattr
->inum
= buf_to_int64(buf
, recsize
);
375 jattr
->nlink
= buf_to_int64(buf
, recsize
);
378 jattr
->fsid
= buf_to_int64(buf
, recsize
);
381 jattr
->size
= buf_to_int64(buf
, recsize
);
384 jattr
->atime
= *(const struct timeval
*)buf
;
387 jattr
->mtime
= *(const struct timeval
*)buf
;
390 jattr
->ctime
= *(const struct timeval
*)buf
;
393 jattr
->gen
= buf_to_int64(buf
, recsize
);
396 jattr
->flags
= buf_to_int64(buf
, recsize
);
399 jattr
->udev
= buf_to_int64(buf
, recsize
);
402 jattr
->filerev
= buf_to_int64(buf
, recsize
);
411 dump_mirror_rebuild_redo(u_int16_t rectype
, struct jstream
*js
,
414 struct jattr_data
*data
;
418 if (verbose_opt
> 2) {
419 fprintf(stderr
, "REDO %04x %s %s\n",
420 js
->js_head
->streamid
, type_to_name(rectype
),
421 jattr
->pathref
? jattr
->pathref
: jattr
->path1
);
425 if (jattr
->pathref
) {
426 if (jattr
->uid
!= (uid_t
)-1)
427 chown(jattr
->pathref
, jattr
->uid
, -1);
428 if (jattr
->gid
!= (gid_t
)-1)
429 chown(jattr
->pathref
, -1, jattr
->gid
);
430 if (jattr
->modes
!= (mode_t
)-1)
431 chmod(jattr
->pathref
, jattr
->modes
);
432 if (jattr
->fflags
!= -1)
433 chflags(jattr
->pathref
, jattr
->fflags
);
434 if (jattr
->size
!= -1)
435 truncate(jattr
->pathref
, jattr
->size
);
440 if (jattr
->pathref
&& jattr
->seekpos
!= -1) {
441 if ((fd
= open(jattr
->pathref
, O_RDWR
)) >= 0) {
442 lseek(fd
, jattr
->seekpos
, 0);
443 for (data
= &jattr
->data
; data
; data
= data
->next
) {
445 jsreadcallback(js
, write
, fd
, data
->off
, data
->bytes
);
453 case JTYPE_SETEXTATTR
:
457 * note: both path1 and pathref will exist.
459 if (jattr
->path1
&& jattr
->modes
!= (mode_t
)-1) {
460 if ((fd
= open(jattr
->path1
, O_CREAT
, jattr
->modes
)) >= 0) {
461 dosetattr(jattr
->path1
, fd
, jattr
);
470 if (jattr
->pathref
&& jattr
->path1
) {
471 link(jattr
->pathref
, jattr
->path1
);
475 if (jattr
->symlinkdata
&& jattr
->path1
) {
476 symlink(jattr
->symlinkdata
, jattr
->path1
);
483 remove(jattr
->path1
);
487 if (jattr
->path1
&& jattr
->modes
!= (mode_t
)-1) {
488 mkdir(jattr
->path1
, jattr
->modes
);
497 if (jattr
->path1
&& jattr
->path2
) {
498 rename(jattr
->path1
, jattr
->path2
);
506 * UNDO function using parsed primary data and parsed UNDO data. This
510 dump_mirror_rebuild_undo(u_int16_t rectype
, struct jstream
*js
,
513 struct jattr_data
*data
;
517 if (verbose_opt
> 2) {
518 fprintf(stderr
, "UNDO %04x %s %s\n",
519 js
->js_head
->streamid
, type_to_name(rectype
),
520 jattr
->pathref
? jattr
->pathref
: jattr
->path1
);
525 dosetattr(jattr
->pathref
, -1, jattr
);
529 if (jattr
->pathref
&& jattr
->seekpos
!= -1) {
530 if ((fd
= open(jattr
->pathref
, O_RDWR
)) >= 0) {
531 lseek(fd
, jattr
->seekpos
, 0);
532 for (data
= &jattr
->data
; data
; data
= data
->next
) {
534 jsreadcallback(js
, write
, fd
, data
->off
, data
->bytes
);
539 if (jattr
->size
!= -1)
540 truncate(jattr
->pathref
, jattr
->size
);
544 case JTYPE_SETEXTATTR
:
548 * note: both path1 and pathref will exist.
551 remove(jattr
->path1
);
555 remove(jattr
->path1
);
559 undo_recreate(jattr
->path1
, js
, jattr
);
563 if (jattr
->symlinkdata
&& jattr
->path1
) {
564 undo_recreate(jattr
->path1
, js
, jattr
);
572 undo_recreate(jattr
->path1
, js
, jattr
);
581 if (jattr
->path1
&& jattr
->modes
!= (mode_t
)-1) {
582 mkdir(jattr
->path1
, jattr
->modes
);
587 undo_recreate(jattr
->path2
, js
, jattr
);
595 * This is a helper function for undoing operations which completely destroy
596 * the file that had existed previously. The caller will clean up the
597 * attributes (including file truncations/extensions) after the fact.
600 undo_recreate(const char *filename
, struct jstream
*js
, struct jattr
*jattr
)
602 struct jattr_data
*data
;
606 fprintf(stderr
, "RECREATE %s (type %d)\n", filename
, jattr
->vtype
);
609 switch(jattr
->vtype
) {
611 if (jattr
->size
!= -1) {
612 if ((fd
= open(filename
, O_RDWR
|O_CREAT
|O_TRUNC
, 0600)) >= 0) {
613 if (jattr
->seekpos
!= -1) {
614 lseek(fd
, jattr
->seekpos
, 0);
615 for (data
= &jattr
->data
; data
; data
= data
->next
) {
617 jsreadcallback(js
, write
, fd
, data
->off
, data
->bytes
);
620 dosetattr(filename
, fd
, jattr
);
626 mkdir(filename
, 0600);
627 dosetattr(filename
, -1, jattr
);
632 mknod(filename
, S_IFBLK
|0666, jattr
->udev
);
633 dosetattr(filename
, -1, jattr
);
637 if (jattr
->symlinkdata
) {
638 symlink(jattr
->symlinkdata
, filename
);
639 dosetattr(filename
, -1, jattr
);
648 dosetattr(const char *filename
, int fd
, struct jattr
*jattr
)
651 if (jattr
->uid
!= (uid_t
)-1 && jattr
->gid
!= (gid_t
)-1)
652 fchown(fd
, jattr
->uid
, jattr
->gid
);
653 else if (jattr
->uid
!= (uid_t
)-1)
654 fchown(fd
, jattr
->uid
, -1);
655 else if (jattr
->gid
!= (gid_t
)-1)
656 fchown(fd
, -1, jattr
->gid
);
658 if (jattr
->modes
!= (mode_t
)-1)
659 fchmod(fd
, jattr
->modes
);
660 if (jattr
->fflags
!= -1)
661 fchflags(fd
, jattr
->fflags
);
662 if (jattr
->size
!= -1)
663 ftruncate(fd
, jattr
->size
);
665 if (jattr
->uid
!= (uid_t
)-1 && jattr
->gid
!= (gid_t
)-1)
666 lchown(filename
, jattr
->uid
, jattr
->gid
);
667 else if (jattr
->uid
!= (uid_t
)-1)
668 lchown(filename
, jattr
->uid
, -1);
669 else if (jattr
->gid
!= (gid_t
)-1)
670 lchown(filename
, -1, jattr
->gid
);
672 if (jattr
->modes
!= (mode_t
)-1)
673 lchmod(filename
, jattr
->modes
);
674 if (jattr
->fflags
!= -1)
675 chflags(filename
, jattr
->fflags
);
676 if (jattr
->size
!= -1)
677 truncate(filename
, jattr
->size
);