Cleanup:
[dragonfly.git] / sbin / jscan / dump_mirror.c
1 /*
2  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
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
16  *    distribution.
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.
20  * 
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
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sbin/jscan/dump_mirror.c,v 1.8 2005/11/06 12:32:56 swildner Exp $
35  */
36
37 #include "jscan.h"
38 #include <sys/vfscache.h>
39
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,
45                                  struct jattr *jattr);
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);
55
56 void
57 dump_mirror(struct jsession *ss, struct jdata *jd)
58 {
59     struct jstream *js;
60
61     if ((js = jaddrecord(ss, jd)) != NULL) {
62         dump_mirror_stream(ss, js);
63         jscan_dispose(js);
64     }
65     jsession_update_transid(ss, jd->jd_transid);
66 }
67
68 static void
69 dump_mirror_stream(struct jsession *ss, struct jstream *js)
70 {
71         struct journal_rawrecbeg head;
72         int16_t sid;
73         mode_t save_umask;
74
75         save_umask = umask(0);
76         jsread(js, 0, &head, sizeof(head));
77
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), 
84                                   1);
85         } else {
86             switch(head.streamid & JREC_STREAMID_MASK) {
87             case JREC_STREAMID_SYNCPT & JREC_STREAMID_MASK:
88                 break;
89             case JREC_STREAMID_PAD & JREC_STREAMID_MASK:
90                 break;
91             case JREC_STREAMID_DISCONT & JREC_STREAMID_MASK:
92                 break;
93             case JREC_STREAMID_ANNOTATE & JREC_STREAMID_MASK:
94                 break;
95             default:
96                 break;
97             }
98         }
99         umask(save_umask);
100 }
101
102 /*
103  * Execute a meta-transaction, e.g. something like 'WRITE'.  Meta-transactions
104  * are almost universally nested.
105  */
106 static int
107 dump_mirror_toprecord(struct jsession *ss, struct jstream *js,
108                       off_t *off, off_t recsize, int level)
109 {
110     struct journal_subrecord sub;
111     struct jattr jattr;
112     int payload;
113     int subsize;
114     int error;
115     off_t base = *off;
116
117     error = 0;
118     bzero(&jattr, sizeof(jattr));
119     jattr_reset(&jattr);
120
121     while (recsize > 0) {
122         if ((error = jsread(js, base, &sub, sizeof(sub))) != 0)
123             break;
124         if (sub.recsize == -1) {
125             if ((sub.rectype & JMASK_NESTED) == 0) {
126                 printf("Record size of -1 only works for nested records\n");
127                 error = -1;
128                 break;
129             }
130             payload = 0x7FFFFFFF;
131             subsize = 0x7FFFFFFF;
132         } else {
133             payload = sub.recsize - sizeof(sub);
134             subsize = (sub.recsize + 7) & ~7;
135         }
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) {
143         } else {
144         }
145         if (ss->ss_direction == JD_FORWARDS)
146             dump_mirror_rebuild_redo(sub.rectype, js, &jattr);
147         else
148             dump_mirror_rebuild_undo(sub.rectype, js, &jattr);
149         jattr_reset(&jattr);
150         if (error)
151             break;
152         if (sub.recsize == -1) {
153             if ((sub.rectype & JMASK_NESTED) == 0) {
154                 printf("Record size of -1 only works for nested records\n");
155                 error = -1;
156                 break;
157             }
158             recsize -= ((*off + 7) & ~7) - base;
159             base = (*off + 7) & ~7;
160         } else {
161             if (subsize == 0)
162                 subsize = sizeof(sub);
163             recsize -= subsize;
164             base += subsize;
165         }
166         if (sub.rectype & JMASK_LAST)
167             break;
168     }
169     *off = base;
170     return(error);
171 }
172
173 /*
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).
176  *
177  * The nested subrecord contains informational records containing primarily
178  * namespace data, and further subrecords containing nested
179  * audit, undo, and redo data.
180  */
181 static int
182 dump_mirror_subrecord(enum jdirection direction, struct jstream *js,
183                       off_t *off, off_t recsize, int level,
184                       struct jattr *jattr)
185 {
186     struct journal_subrecord sub;
187     int payload;
188     int subsize;
189     int error;
190     int skip;
191     u_int16_t rectype;
192     off_t base = *off;
193
194     error = 0;
195     while (recsize > 0) {
196         if ((error = jsread(js, base, &sub, sizeof(sub))) != 0)
197             break;
198         rectype = sub.rectype & JTYPE_MASK;     /* includes the nested bit */
199         if (sub.recsize == -1) {
200             payload = 0x7FFFFFFF;
201             subsize = 0x7FFFFFFF;
202         } else {
203             payload = sub.recsize - sizeof(sub);
204             subsize = (sub.recsize + 7) & ~7;
205         }
206
207         skip = 1;
208         *off = base + sizeof(sub);
209
210         switch(rectype) {
211         case JTYPE_REDO:        /* NESTED */
212             /*
213              * Process redo information when scanning forwards.
214              */
215             if (direction == JD_FORWARDS) {
216                 error = dump_mirror_subrecord(direction, js, off, payload,
217                                               level + 1, jattr);
218                 skip = 0;
219             }
220             break;
221         case JTYPE_UNDO:        /* NESTED */
222             /*
223              * Process undo information when scanning backwards.
224              */
225             if (direction == JD_BACKWARDS) {
226                 error = dump_mirror_subrecord(direction, js, off, payload,
227                                               level + 1, jattr);
228                 skip = 0;
229             }
230             break;
231         case JTYPE_CRED:        /* NESTED */
232             /*
233              * Ignore audit information
234              */
235             break;
236         default:                /* NESTED or non-NESTED */
237             /*
238              * Execute these.  Nested records might contain attribute
239              * information under an UNDO or REDO parent, for example.
240              */
241             if (rectype & JMASK_NESTED) {
242                 error = dump_mirror_subrecord(direction, js, off, payload,
243                                               level + 1, jattr);
244                 skip = 0;
245             } else if (rectype & JMASK_SUBRECORD) {
246                 error = dump_mirror_payload(sub.rectype, js, *off, payload,
247                                             level, jattr);
248             }
249             break;
250         }
251         if (error)
252             break;
253
254         /*
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.
258          */
259         if (sub.recsize == -1) {
260             assert(sub.rectype & JMASK_NESTED);
261             if (skip) {
262                 error = dump_mirror_subrecord(direction, js, off, payload,
263                                               level + 1, NULL);
264             }
265             recsize -= ((*off + 7) & ~7) - base;
266             base = (*off + 7) & ~7;
267         } else {
268             if (subsize == 0)
269                 subsize = sizeof(sub);
270             recsize -= subsize;
271             base += subsize;
272         }
273         if (error)
274             break;
275         if (sub.rectype & JMASK_LAST)
276             break;
277     }
278     *off = base;
279     return(error);
280 }
281
282 static int
283 dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off, 
284              int recsize, int level __unused, struct jattr *jattr)
285 {
286     const char *buf;
287     struct jattr_data *data;
288     int error;
289
290     if (jattr == NULL)
291         return (0);
292
293     if ((rectype & ~JMASK_LAST) != JLEAF_FILEDATA) {
294         error = jsreadp(js, off, (const void **)&buf, recsize);
295         if (error)
296             return (error);
297     } else {
298         buf = NULL;
299         error = 0;
300     }
301
302     switch(rectype & ~JMASK_LAST) {
303     case JLEAF_PAD:
304     case JLEAF_ABORT:
305         break;
306     case JLEAF_SYMLINKDATA:
307         jattr->symlinkdata = dupdatastr(buf, recsize);
308         jattr->symlinklen = recsize;
309         break;
310     case JLEAF_FILEDATA:
311         if ((data = jattr->last_data) == NULL) {
312                 jattr->data.off = off;
313                 jattr->data.bytes = recsize;
314                 jattr->last_data = &jattr->data;
315         } else {
316                 data->next = malloc(sizeof(jattr->data));
317                 data = data->next;
318                 data->off = off;
319                 data->bytes = recsize;
320                 data->next = NULL;
321                 jattr->last_data = data;
322         }
323         break;
324     case JLEAF_PATH1:
325         jattr->path1 = dupdatapath(buf, recsize);
326         break;
327     case JLEAF_PATH2:
328         jattr->path2 = dupdatapath(buf, recsize);
329         break;
330     case JLEAF_PATH3:
331         jattr->path3 = dupdatapath(buf, recsize);
332         break;
333     case JLEAF_PATH4:
334         jattr->path4 = dupdatapath(buf, recsize);
335         break;
336     case JLEAF_UID:
337         jattr->uid = buf_to_int64(buf, recsize);
338         break;
339     case JLEAF_GID:
340         jattr->gid = buf_to_int64(buf, recsize);
341         break;
342     case JLEAF_VTYPE:
343         jattr->vtype = buf_to_int64(buf, recsize);
344         break;
345     case JLEAF_MODES:
346         jattr->modes = buf_to_int64(buf, recsize);
347         break;
348     case JLEAF_FFLAGS:
349         jattr->fflags = buf_to_int64(buf, recsize);
350         break;
351     case JLEAF_PID:
352         jattr->pid = buf_to_int64(buf, recsize);
353         break;
354     case JLEAF_PPID:
355         jattr->ppid = buf_to_int64(buf, recsize);
356         break;
357     case JLEAF_COMM:
358         jattr->comm = dupdatastr(buf, recsize);
359         break;
360     case JLEAF_ATTRNAME:
361         jattr->attrname = dupdatastr(buf, recsize);
362         break;
363     case JLEAF_PATH_REF:
364         jattr->pathref = dupdatapath(buf, recsize);
365         break;
366     case JLEAF_RESERVED_0F:
367         break;
368     case JLEAF_SEEKPOS:
369         jattr->seekpos = buf_to_int64(buf, recsize);
370         break;
371     case JLEAF_INUM:
372         jattr->inum = buf_to_int64(buf, recsize);
373         break;
374     case JLEAF_NLINK:
375         jattr->nlink = buf_to_int64(buf, recsize);
376         break;
377     case JLEAF_FSID:
378         jattr->fsid = buf_to_int64(buf, recsize);
379         break;
380     case JLEAF_SIZE:
381         jattr->size = buf_to_int64(buf, recsize);
382         break;
383     case JLEAF_ATIME:
384         jattr->atime = *(const struct timeval *)buf;
385         break;
386     case JLEAF_MTIME:
387         jattr->mtime = *(const struct timeval *)buf;
388         break;
389     case JLEAF_CTIME:
390         jattr->ctime = *(const struct timeval *)buf;
391         break;
392     case JLEAF_GEN:
393         jattr->gen = buf_to_int64(buf, recsize);
394         break;
395     case JLEAF_FLAGS:
396         jattr->flags = buf_to_int64(buf, recsize);
397         break;
398     case JLEAF_UDEV:
399         jattr->udev = buf_to_int64(buf, recsize);
400         break;
401     case JLEAF_FILEREV:
402         jattr->filerev = buf_to_int64(buf, recsize);
403         break;
404     default:
405         break;
406     }
407     return (0);
408 }
409
410 static int
411 dump_mirror_rebuild_redo(u_int16_t rectype, struct jstream *js,
412                         struct jattr *jattr)
413 {
414     struct jattr_data *data;
415     int error = 0;
416     int fd;
417
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);
422     }
423     switch(rectype) {
424     case JTYPE_SETATTR:
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);
436         }
437         break;
438     case JTYPE_WRITE:
439     case JTYPE_PUTPAGES:
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) {
444                     if (data->bytes)
445                         jsreadcallback(js, write, fd, data->off, data->bytes);
446                 }
447                 close(fd);
448             }
449         }
450         break;
451     case JTYPE_SETACL:
452         break;
453     case JTYPE_SETEXTATTR:
454         break;
455     case JTYPE_CREATE:
456         /*
457          * note: both path1 and pathref will exist.
458          */
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);
462                 close(fd);
463             }
464         }
465         break;
466     case JTYPE_MKNOD:
467         /* XXX */
468         break;
469     case JTYPE_LINK:
470         if (jattr->pathref && jattr->path1) {
471             link(jattr->pathref, jattr->path1);
472         }
473         break;
474     case JTYPE_SYMLINK:
475         if (jattr->symlinkdata && jattr->path1) {
476             symlink(jattr->symlinkdata, jattr->path1);
477         }
478         break;
479     case JTYPE_WHITEOUT:
480         break;
481     case JTYPE_REMOVE:
482         if (jattr->path1) {
483             remove(jattr->path1);
484         }
485         break;
486     case JTYPE_MKDIR:
487         if (jattr->path1 && jattr->modes != (mode_t)-1) {
488             mkdir(jattr->path1, jattr->modes);
489         }
490         break;
491     case JTYPE_RMDIR:
492         if (jattr->path1) {
493             rmdir(jattr->path1);
494         }
495         break;
496     case JTYPE_RENAME:
497         if (jattr->path1 && jattr->path2) {
498             rename(jattr->path1, jattr->path2);
499         }
500         break;
501     }
502     return(error);
503 }
504
505 /*
506  * UNDO function using parsed primary data and parsed UNDO data.  This
507  * must typically
508  */
509 static int
510 dump_mirror_rebuild_undo(u_int16_t rectype, struct jstream *js,
511                         struct jattr *jattr)
512 {
513     struct jattr_data *data;
514     int error = 0;
515     int fd;
516
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);
521     }
522     switch(rectype) {
523     case JTYPE_SETATTR:
524         if (jattr->pathref)
525             dosetattr(jattr->pathref, -1, jattr);
526         break;
527     case JTYPE_WRITE:
528     case JTYPE_PUTPAGES:
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) {
533                     if (data->bytes)
534                         jsreadcallback(js, write, fd, data->off, data->bytes);
535                 }
536                 close(fd);
537             }
538         }
539         if (jattr->size != -1)
540             truncate(jattr->pathref, jattr->size);
541         break;
542     case JTYPE_SETACL:
543         break;
544     case JTYPE_SETEXTATTR:
545         break;
546     case JTYPE_CREATE:
547         /*
548          * note: both path1 and pathref will exist.
549          */
550         if (jattr->path1)
551             remove(jattr->path1);
552         break;
553     case JTYPE_MKNOD:
554         if (jattr->path1)
555             remove(jattr->path1);
556         break;
557     case JTYPE_LINK:
558         if (jattr->path1) {
559             undo_recreate(jattr->path1, js, jattr);
560         }
561         break;
562     case JTYPE_SYMLINK:
563         if (jattr->symlinkdata && jattr->path1) {
564             undo_recreate(jattr->path1, js, jattr);
565         }
566         break;
567     case JTYPE_WHITEOUT:
568         /* XXX */
569         break;
570     case JTYPE_REMOVE:
571         if (jattr->path1) {
572             undo_recreate(jattr->path1, js, jattr);
573         }
574         break;
575     case JTYPE_MKDIR:
576         if (jattr->path1) {
577             rmdir(jattr->path1);
578         }
579         break;
580     case JTYPE_RMDIR:
581         if (jattr->path1 && jattr->modes != (mode_t)-1) {
582             mkdir(jattr->path1, jattr->modes);
583         }
584         break;
585     case JTYPE_RENAME:
586         if (jattr->path2) {
587             undo_recreate(jattr->path2, js, jattr);
588         }
589         break;
590     }
591     return(error);
592 }
593
594 /*
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.
598  */
599 static void
600 undo_recreate(const char *filename, struct jstream *js, struct jattr *jattr)
601 {
602     struct jattr_data *data;
603     int fd;
604
605     if (verbose_opt > 2)
606         fprintf(stderr, "RECREATE %s (type %d)\n", filename, jattr->vtype);
607
608     remove(filename);
609     switch(jattr->vtype) {
610     case VREG:
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) {
616                         if (data->bytes)
617                             jsreadcallback(js, write, fd, data->off, data->bytes);
618                     }
619                 }
620                 dosetattr(filename, fd, jattr);
621                 close(fd);
622             }
623         }
624         break;
625     case VDIR:
626         mkdir(filename, 0600);
627         dosetattr(filename, -1, jattr);
628         break;
629     case VBLK:
630     case VCHR:
631         if (jattr->udev) {
632             mknod(filename, S_IFBLK|0666, jattr->udev);
633             dosetattr(filename, -1, jattr);
634         }
635         break;
636     case VLNK:
637         if (jattr->symlinkdata) {
638             symlink(jattr->symlinkdata, filename);
639             dosetattr(filename, -1, jattr);
640         }
641         break;
642     default:
643         break;
644     }
645 }
646
647 static void
648 dosetattr(const char *filename, int fd, struct jattr *jattr)
649 {
650     if (fd >= 0) {
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);
657
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);
664     } else {
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);
671
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);
678     }
679 }
680