Add support for mirroring symlinks and hardlinks.
[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.4 2005/07/05 06:20:07 dillon Exp $
35  */
36
37 #include "jscan.h"
38
39 static void dump_mirror_stream(struct jstream *js);
40 static int dump_mirror_toprecord(struct jstream *js, off_t *off, 
41                                  off_t recsize, int level);
42 static int dump_mirror_subrecord(struct jstream *js, off_t *off, 
43                                  off_t recsize, int level, struct jattr *jattr);
44 static int dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off,
45                                  int recsize, int level, struct jattr *jattr);
46 static int dump_mirror_rebuild(u_int16_t rectype, struct jstream *js, struct jattr *jattr);
47
48 void
49 dump_mirror(struct jfile *jf)
50 {
51     struct jstream *js;
52
53     while ((js = jscan_stream(jf)) != NULL) {
54         dump_mirror_stream(js);
55         jscan_dispose(js);
56     }
57 }
58
59 static void
60 dump_mirror_stream(struct jstream *js)
61 {
62         struct journal_rawrecbeg head;
63         int16_t sid;
64         mode_t save_umask;
65
66         save_umask = umask(0);
67         jsread(js, 0, &head, sizeof(head));
68
69         sid = head.streamid & JREC_STREAMID_MASK;
70         if (debug_opt) {
71             printf("STREAM %04x DATA (%lld) {\n",
72                 (int)(u_int16_t)head.streamid, js->js_normalized_total);
73         }
74         if (sid >= JREC_STREAMID_JMIN && sid < JREC_STREAMID_JMAX) {
75             off_t off = sizeof(head);
76             dump_mirror_toprecord(js, &off, js->js_normalized_total -
77                                   sizeof(struct journal_rawrecbeg), 
78                                   1);
79         } else {
80             switch(head.streamid & JREC_STREAMID_MASK) {
81             case JREC_STREAMID_SYNCPT & JREC_STREAMID_MASK:
82                 if (debug_opt)
83                     printf("    SYNCPT\n");
84                 break;
85             case JREC_STREAMID_PAD & JREC_STREAMID_MASK:
86                 if (debug_opt)
87                     printf("    PAD\n");
88                 break;
89             case JREC_STREAMID_DISCONT & JREC_STREAMID_MASK:
90                 if (debug_opt)
91                     printf("    DISCONT\n");
92                 break;
93             case JREC_STREAMID_ANNOTATE & JREC_STREAMID_MASK:
94                 if (debug_opt)
95                     printf("    ANNOTATION\n");
96                 break;
97             default:
98                 if (debug_opt)
99                     printf("    UNKNOWN\n");
100                 break;
101             }
102         }
103         umask(save_umask);
104         if (debug_opt) {
105             printf("}\n");
106             fflush(stdout);
107         }
108 }
109
110 static int
111 dump_mirror_toprecord(struct jstream *js, off_t *off, off_t recsize, int level)
112 {
113     struct journal_subrecord sub;
114     struct jattr jattr;
115     int payload;
116     int subsize;
117     int error;
118     off_t base = *off;
119
120     error = 0;
121     bzero(&jattr, sizeof(jattr));
122     jattr_reset(&jattr);
123
124     while (recsize > 0) {
125         if ((error = jsread(js, base, &sub, sizeof(sub))) != 0)
126             break;
127         if (debug_opt) {
128             printf("%*.*s", level * 4, level * 4, "");
129             printf("@%lld ", base);
130             printf("RECORD %s [%04x/%d]", type_to_name(sub.rectype), 
131                    (int)(u_int16_t)sub.rectype, sub.recsize);
132         }
133         if (sub.recsize == -1) {
134             if ((sub.rectype & JMASK_NESTED) == 0) {
135                 printf("Record size of -1 only works for nested records\n");
136                 error = -1;
137                 break;
138             }
139             payload = 0x7FFFFFFF;
140             subsize = 0x7FFFFFFF;
141         } else {
142             payload = sub.recsize - sizeof(sub);
143             subsize = (sub.recsize + 7) & ~7;
144         }
145         if (sub.rectype & JMASK_NESTED) {
146             if (debug_opt)
147                 printf(" {\n");
148             *off = base + sizeof(sub);
149             error = dump_mirror_subrecord(js, off,
150                                           payload, level + 1, &jattr);
151             if (debug_opt)
152                 printf("%*.*s}\n", level * 4, level * 4, "");
153         } else if (sub.rectype & JMASK_SUBRECORD) {
154             if (debug_opt) {
155                 printf(" DATA (%d)", payload);
156                 error = dump_debug_payload(sub.rectype, js, base + sizeof(sub), payload, level);
157             }
158             *off = base + sizeof(sub) + payload;
159             if (debug_opt)
160                 printf("\n");
161         } else if ((sub.rectype & JTYPE_MASK) == JLEAF_PAD) {
162             if (debug_opt) {
163                 if (payload)
164                     printf(" DATA (%d)", payload);
165                 printf("\n");
166             }
167         } else {
168             if (debug_opt)
169                 printf("[%d bytes of unknown content]\n", payload);
170         }
171         dump_mirror_rebuild(sub.rectype, js, &jattr);
172         jattr_reset(&jattr);
173         if (error)
174             break;
175         if (sub.recsize == -1) {
176             if ((sub.rectype & JMASK_NESTED) == 0) {
177                 printf("Record size of -1 only works for nested records\n");
178                 error = -1;
179                 break;
180             }
181             recsize -= ((*off + 7) & ~7) - base;
182             base = (*off + 7) & ~7;
183         } else {
184             if (subsize == 0)
185                 subsize = sizeof(sub);
186             recsize -= subsize;
187             base += subsize;
188         }
189         if (sub.rectype & JMASK_LAST)
190             break;
191     }
192     *off = base;
193     return(error);
194 }
195
196 static int
197 dump_mirror_subrecord(struct jstream *js, off_t *off, off_t recsize, int level,
198                       struct jattr *jattr)
199 {
200     struct journal_subrecord sub;
201     int payload;
202     int subsize;
203     int error;
204     u_int16_t rectype;
205     off_t base = *off;
206
207     error = 0;
208     while (recsize > 0) {
209         if ((error = jsread(js, base, &sub, sizeof(sub))) != 0)
210             break;
211         rectype = sub.rectype & JTYPE_MASK;
212         if (debug_opt) {
213             printf("%*.*s", level * 4, level * 4, "");
214             printf("@%lld ", base);
215             printf("SRECORD %s [%04x/%d]", type_to_name(sub.rectype), 
216                    (int)(u_int16_t)sub.rectype, sub.recsize);
217         }
218         if (sub.recsize == -1) {
219             payload = 0x7FFFFFFF;
220             subsize = 0x7FFFFFFF;
221         } else {
222             payload = sub.recsize - sizeof(sub);
223             subsize = (sub.recsize + 7) & ~7;
224         }
225         if (sub.rectype & JMASK_NESTED) {
226             if (debug_opt)
227                 printf(" {\n");
228
229             /*
230              * Only recurse through vattr records.  XXX currently assuming
231              * only on VATTR subrecord.
232              */
233             *off = base + sizeof(sub);
234             if (payload && rectype == JTYPE_VATTR) {
235                 error = dump_mirror_subrecord(js, off, 
236                                               payload, level + 1, jattr);
237             } else {
238                 error = dump_mirror_subrecord(js, off, 
239                                               payload, level + 1, NULL);
240             }
241             if (debug_opt)
242                 printf("%*.*s}\n", level * 4, level * 4, "");
243         } else if (sub.rectype & JMASK_SUBRECORD) {
244             if (debug_opt) {
245                 printf(" DATA (%d)", payload);
246                 dump_debug_payload(sub.rectype, js, base + sizeof(sub), 
247                                    payload, level);
248             }
249             error = dump_mirror_payload(sub.rectype, js, base + sizeof(sub),
250                                        payload, level, jattr);
251             *off = base + sizeof(sub) + payload;
252             if (debug_opt)
253                 printf("\n");
254         } else if ((sub.rectype & JTYPE_MASK) == JLEAF_PAD) {
255             if (debug_opt) {
256                 if (payload)
257                     printf(" DATA (%d)", payload);
258                 printf("\n");
259             }
260         } else {
261             if (debug_opt)
262                 printf("[%d bytes of unknown content]\n", sub.recsize);
263         }
264         if (error)
265             break;
266         if (sub.recsize == -1) {
267             recsize -= ((*off + 7) & ~7) - base;
268             base = (*off + 7) & ~7;
269         } else {
270             if (subsize == 0)
271                 subsize = sizeof(sub);
272             recsize -= subsize;
273             base += subsize;
274         }
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(u_int16_t rectype, struct jstream *js, struct jattr *jattr)
412 {
413     struct jattr_data *data;
414     int error = 0;
415     int fd;
416
417 again:
418     switch(rectype) {
419     case JTYPE_SETATTR:
420         if (jattr->pathref) {
421             if (jattr->uid != (uid_t)-1)
422                 chown(jattr->pathref, jattr->uid, -1);
423             if (jattr->gid != (gid_t)-1)
424                 chown(jattr->pathref, -1, jattr->gid);
425             if (jattr->modes != (mode_t)-1)
426                 chmod(jattr->pathref, jattr->modes);
427             if (jattr->fflags != -1)
428                 chflags(jattr->pathref, jattr->fflags);
429             if (jattr->size != -1)
430                 truncate(jattr->pathref, jattr->size);
431         }
432         break;
433     case JTYPE_WRITE:
434     case JTYPE_PUTPAGES:
435         if (jattr->pathref && jattr->seekpos != -1) {
436             if ((fd = open(jattr->pathref, O_RDWR)) >= 0) {
437                 lseek(fd, jattr->seekpos, 0);
438                 for (data = &jattr->data; data; data = data->next) {
439                     if (data->bytes)
440                         jsreadcallback(js, write, fd, data->off, data->bytes);
441                 }
442                 close(fd);
443             }
444         }
445         break;
446     case JTYPE_SETACL:
447         break;
448     case JTYPE_SETEXTATTR:
449         break;
450     case JTYPE_CREATE:
451         /*
452          * note: both path1 and pathref will exist.
453          */
454         if (jattr->path1 && jattr->modes != (mode_t)-1) {
455             if ((fd = open(jattr->path1, O_CREAT, jattr->modes)) >= 0) {
456                 close(fd);
457                 rectype = JTYPE_SETATTR;
458                 goto again;
459             }
460         }
461         break;
462     case JTYPE_MKNOD:
463         break;
464     case JTYPE_LINK:
465         if (jattr->pathref && jattr->path1) {
466             link(jattr->pathref, jattr->path1);
467         }
468         break;
469     case JTYPE_SYMLINK:
470         if (jattr->symlinkdata && jattr->path1) {
471             symlink(jattr->symlinkdata, jattr->path1);
472         }
473         break;
474     case JTYPE_WHITEOUT:
475         break;
476     case JTYPE_REMOVE:
477         if (jattr->path1) {
478             remove(jattr->path1);
479         }
480         break;
481     case JTYPE_MKDIR:
482         if (jattr->path1 && jattr->modes != (mode_t)-1) {
483             mkdir(jattr->path1, jattr->modes);
484         }
485         break;
486     case JTYPE_RMDIR:
487         if (jattr->path1) {
488             rmdir(jattr->path1);
489         }
490         break;
491     case JTYPE_RENAME:
492         if (jattr->path1 && jattr->path2) {
493             rename(jattr->path1, jattr->path2);
494         }
495         break;
496     }
497     return(error);
498 }
499