xref: /dragonfly/sbin/jscan/dump_mirror.c (revision 0bb9290e)
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 
681