]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/csup/fattr.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / contrib / csup / fattr.c
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "fattr.h"
41 #include "idcache.h"
42 #include "misc.h"
43
44 /*
45  * Include the appropriate definition for the file attributes we support.
46  * There are two different files: fattr_bsd.h for BSD-like systems that
47  * support the extended file flags à la chflags() and fattr_posix.h for
48  * bare POSIX systems that don't.
49  */
50 #ifdef HAVE_FFLAGS
51 #include "fattr_bsd.h"
52 #else
53 #include "fattr_posix.h"
54 #endif
55
56 #ifdef __FreeBSD__
57 #include <osreldate.h>
58 #endif
59
60 /* Define fflags_t if we're on a system that doesn't have it. */
61 #if !defined(__FreeBSD_version) || __FreeBSD_version < 500030
62 typedef uint32_t fflags_t;
63 #endif
64
65 #define FA_MASKRADIX            16
66 #define FA_FILETYPERADIX        10
67 #define FA_MODTIMERADIX         10
68 #define FA_SIZERADIX            10
69 #define FA_RDEVRADIX            16
70 #define FA_MODERADIX            8
71 #define FA_FLAGSRADIX           16
72 #define FA_LINKCOUNTRADIX       10
73 #define FA_DEVRADIX             16
74 #define FA_INODERADIX           10
75
76 #define FA_PERMMASK             (S_IRWXU | S_IRWXG | S_IRWXO)
77 #define FA_SETIDMASK            (S_ISUID | S_ISGID | S_ISVTX)
78
79 struct fattr {
80         int             mask;
81         int             type;
82         time_t          modtime;
83         off_t           size;
84         char            *linktarget;
85         dev_t           rdev;
86         uid_t           uid;
87         gid_t           gid;
88         mode_t          mode;
89         fflags_t        flags;
90         nlink_t         linkcount;
91         dev_t           dev;
92         ino_t           inode;
93 };
94
95 static const struct fattr bogus = {
96         FA_MODTIME | FA_SIZE | FA_MODE,
97         FT_UNKNOWN,
98         1,
99         0,
100         NULL,
101         0,
102         0,
103         0,
104         0,
105         0,
106         0,
107         0,
108         0
109 };
110
111 static struct fattr *defaults[FT_NUMBER];
112
113 void
114 fattr_init(void)
115 {
116         struct fattr *fa;
117         int i;
118
119         for (i = 0; i < FT_NUMBER; i++) {
120                 fa = fattr_new(i, -1);
121                 if (i == FT_DIRECTORY)
122                         fa->mode = 0777;
123                 else
124                         fa->mode = 0666;
125                 fa->mask |= FA_MODE;
126                 defaults[i] = fa;
127         }
128         /* Initialize the uid/gid lookup cache. */
129         idcache_init();
130 }
131
132 void
133 fattr_fini(void)
134 {
135         int i;
136
137         idcache_fini();
138         for (i = 0; i < FT_NUMBER; i++)
139                 fattr_free(defaults[i]);
140 }
141
142 const struct fattr *fattr_bogus = &bogus;
143
144 static char             *fattr_scanattr(struct fattr *, int, const char *);
145
146 int
147 fattr_supported(int type)
148 {
149
150         return (fattr_support[type]);
151 }
152
153 struct fattr *
154 fattr_new(int type, time_t modtime)
155 {
156         struct fattr *new;
157
158         new = xmalloc(sizeof(struct fattr));
159         memset(new, 0, sizeof(struct fattr));
160         new->type = type;
161         if (type != FT_UNKNOWN)
162                 new->mask |= FA_FILETYPE;
163         if (modtime != -1) {
164                 new->modtime = modtime;
165                 new->mask |= FA_MODTIME;
166         }
167         if (fattr_supported(new->type) & FA_LINKCOUNT) {
168                 new->mask |= FA_LINKCOUNT;
169                 new->linkcount = 1;
170         }
171         return (new);
172 }
173
174 /* Returns a new file attribute structure based on a stat structure. */
175 struct fattr *
176 fattr_fromstat(struct stat *sb)
177 {
178         struct fattr *fa;
179
180         fa = fattr_new(FT_UNKNOWN, -1);
181         if (S_ISREG(sb->st_mode))
182                 fa->type = FT_FILE;
183         else if (S_ISDIR(sb->st_mode))
184                 fa->type = FT_DIRECTORY;
185         else if (S_ISCHR(sb->st_mode))
186                 fa->type = FT_CDEV;
187         else if (S_ISBLK(sb->st_mode))
188                 fa->type = FT_BDEV;
189         else if (S_ISLNK(sb->st_mode))
190                 fa->type = FT_SYMLINK;
191         else
192                 fa->type = FT_UNKNOWN;
193
194         fa->mask = FA_FILETYPE | fattr_supported(fa->type);
195         if (fa->mask & FA_MODTIME)
196                 fa->modtime = sb->st_mtime;
197         if (fa->mask & FA_SIZE)
198                 fa->size = sb->st_size;
199         if (fa->mask & FA_RDEV)
200                 fa->rdev = sb->st_rdev;
201         if (fa->mask & FA_OWNER)
202                 fa->uid = sb->st_uid;
203         if (fa->mask & FA_GROUP)
204                 fa->gid = sb->st_gid;
205         if (fa->mask & FA_MODE)
206                 fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK);
207 #ifdef HAVE_FFLAGS
208         if (fa->mask & FA_FLAGS)
209                 fa->flags = sb->st_flags;
210 #endif
211         if (fa->mask & FA_LINKCOUNT)
212                 fa->linkcount = sb->st_nlink;
213         if (fa->mask & FA_DEV)
214                 fa->dev = sb->st_dev;
215         if (fa->mask & FA_INODE)
216                 fa->inode = sb->st_ino;
217         return (fa);
218 }
219
220 struct fattr *
221 fattr_frompath(const char *path, int nofollow)
222 {
223         struct fattr *fa;
224         struct stat sb;
225         int error, len;
226
227         if (nofollow)
228                 error = lstat(path, &sb);
229         else
230                 error = stat(path, &sb);
231         if (error)
232                 return (NULL);
233         fa = fattr_fromstat(&sb);
234         if (fa->mask & FA_LINKTARGET) {
235                 char buf[1024];
236
237                 len = readlink(path, buf, sizeof(buf));
238                 if (len == -1) {
239                         fattr_free(fa);
240                         return (NULL);
241                 }
242                 if ((unsigned)len > sizeof(buf) - 1) {
243                         fattr_free(fa);
244                         errno = ENAMETOOLONG;
245                         return (NULL);
246                 }
247                 buf[len] = '\0';
248                 fa->linktarget = xstrdup(buf);
249         }
250         return (fa);
251 }
252
253 struct fattr *
254 fattr_fromfd(int fd)
255 {
256         struct fattr *fa;
257         struct stat sb;
258         int error;
259
260         error = fstat(fd, &sb);
261         if (error)
262                 return (NULL);
263         fa = fattr_fromstat(&sb);
264         return (fa);
265 }
266
267 int
268 fattr_type(const struct fattr *fa)
269 {
270
271         return (fa->type);
272 }
273
274 /* Returns a new file attribute structure from its encoded text form. */
275 struct fattr *
276 fattr_decode(char *attr)
277 {
278         struct fattr *fa;
279         char *next;
280
281         fa = fattr_new(FT_UNKNOWN, -1);
282         next = fattr_scanattr(fa, FA_MASK, attr);
283         if (next == NULL || (fa->mask & ~FA_MASK) > 0)
284                 goto bad;
285         if (fa->mask & FA_FILETYPE) {
286                 next = fattr_scanattr(fa, FA_FILETYPE, next);
287                 if (next == NULL)
288                         goto bad;
289                 if (fa->type < 0 || fa->type > FT_MAX)
290                         fa->type = FT_UNKNOWN;
291         } else {
292                 /* The filetype attribute is always valid. */
293                 fa->mask |= FA_FILETYPE;
294                 fa->type = FT_UNKNOWN;
295         }
296         fa->mask = fa->mask & fattr_supported(fa->type);
297         if (fa->mask & FA_MODTIME)
298                 next = fattr_scanattr(fa, FA_MODTIME, next);
299         if (fa->mask & FA_SIZE)
300                 next = fattr_scanattr(fa, FA_SIZE, next);
301         if (fa->mask & FA_LINKTARGET)
302                 next = fattr_scanattr(fa, FA_LINKTARGET, next);
303         if (fa->mask & FA_RDEV)
304                 next = fattr_scanattr(fa, FA_RDEV, next);
305         if (fa->mask & FA_OWNER)
306                 next = fattr_scanattr(fa, FA_OWNER, next);
307         if (fa->mask & FA_GROUP)
308                 next = fattr_scanattr(fa, FA_GROUP, next);
309         if (fa->mask & FA_MODE)
310                 next = fattr_scanattr(fa, FA_MODE, next);
311         if (fa->mask & FA_FLAGS)
312                 next = fattr_scanattr(fa, FA_FLAGS, next);
313         if (fa->mask & FA_LINKCOUNT) {
314                 next = fattr_scanattr(fa, FA_LINKCOUNT, next);
315         } else if (fattr_supported(fa->type) & FA_LINKCOUNT) {
316                 /* If the link count is missing but supported, fake it as 1. */
317                 fa->mask |= FA_LINKCOUNT;
318                 fa->linkcount = 1;
319         }
320         if (fa->mask & FA_DEV)
321                 next = fattr_scanattr(fa, FA_DEV, next);
322         if (fa->mask & FA_INODE)
323                 next = fattr_scanattr(fa, FA_INODE, next);
324         if (next == NULL)
325                 goto bad;
326         return (fa);
327 bad:
328         fattr_free(fa);
329         return (NULL);
330 }
331
332 char *
333 fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
334 {
335         struct {
336                 char val[32];
337                 char len[4];
338                 int extval;
339                 char *ext;
340         } pieces[FA_NUMBER], *piece;
341         char *cp, *s, *username, *groupname;
342         size_t len, vallen;
343         mode_t mode, modemask;
344         int mask, n, i;
345
346         username = NULL;
347         groupname = NULL;
348         if (support == NULL)
349                 mask = fa->mask;
350         else
351                 mask = fa->mask & support[fa->type];
352         mask &= ~ignore;
353         if (fa->mask & FA_OWNER) {
354                 username = getuserbyid(fa->uid);
355                 if (username == NULL)
356                         mask &= ~FA_OWNER;
357         }
358         if (fa->mask & FA_GROUP) {
359                 groupname = getgroupbyid(fa->gid);
360                 if (groupname == NULL)
361                         mask &= ~FA_GROUP;
362         }
363         if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
364                 mask &= ~FA_LINKCOUNT;
365
366         memset(pieces, 0, FA_NUMBER * sizeof(*pieces));
367         len = 0;
368         piece = pieces;
369         vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask);
370         len += snprintf(piece->len, sizeof(piece->len), "%lld",
371             (long long)vallen) + vallen + 1;
372         piece++;
373         if (mask & FA_FILETYPE) {
374                 vallen = snprintf(piece->val, sizeof(piece->val),
375                     "%d", fa->type);
376                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
377                     (long long)vallen) + vallen + 1;
378                 piece++;
379         }
380         if (mask & FA_MODTIME) {
381                 vallen = snprintf(piece->val, sizeof(piece->val),
382                     "%lld", (long long)fa->modtime);
383                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
384                     (long long)vallen) + vallen + 1;
385                 piece++;
386         }
387         if (mask & FA_SIZE) {
388                 vallen = snprintf(piece->val, sizeof(piece->val),
389                     "%lld", (long long)fa->size);
390                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
391                     (long long)vallen) + vallen + 1;
392                 piece++;
393         }
394         if (mask & FA_LINKTARGET) {
395                 vallen = strlen(fa->linktarget);
396                 piece->extval = 1;
397                 piece->ext = fa->linktarget;
398                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
399                     (long long)vallen) + vallen + 1;
400                 piece++;
401         }
402         if (mask & FA_RDEV) {
403                 vallen = snprintf(piece->val, sizeof(piece->val),
404                     "%lld", (long long)fa->rdev);
405                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
406                     (long long)vallen) + vallen + 1;
407                 piece++;
408         }
409         if (mask & FA_OWNER) {
410                 vallen = strlen(username);
411                 piece->extval = 1;
412                 piece->ext = username;
413                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
414                     (long long)vallen) + vallen + 1;
415                 piece++;
416         }
417         if (mask & FA_GROUP) {
418                 vallen = strlen(groupname);
419                 piece->extval = 1;
420                 piece->ext = groupname;
421                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
422                     (long long)vallen) + vallen + 1;
423                 piece++;
424         }
425         if (mask & FA_MODE) {
426                 if (mask & FA_OWNER && mask & FA_GROUP)
427                         modemask = FA_SETIDMASK | FA_PERMMASK;
428                 else
429                         modemask = FA_PERMMASK;
430                 mode = fa->mode & modemask;
431                 vallen = snprintf(piece->val, sizeof(piece->val),
432                     "%o", mode);
433                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
434                     (long long)vallen) + vallen + 1;
435                 piece++;
436         }
437         if (mask & FA_FLAGS) {
438                 vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
439                     (long long)fa->flags);
440                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
441                     (long long)vallen) + vallen + 1;
442                 piece++;
443         }
444         if (mask & FA_LINKCOUNT) {
445                 vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
446                     (long long)fa->linkcount);
447                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
448                     (long long)vallen) + vallen + 1;
449                 piece++;
450         }
451         if (mask & FA_DEV) {
452                 vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
453                     (long long)fa->dev);
454                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
455                     (long long)vallen) + vallen + 1;
456                 piece++;
457         }
458         if (mask & FA_INODE) {
459                 vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
460                     (long long)fa->inode);
461                 len += snprintf(piece->len, sizeof(piece->len), "%lld",
462                     (long long)vallen) + vallen + 1;
463                 piece++;
464         }
465
466         s = xmalloc(len + 1);
467
468         n = piece - pieces;
469         piece = pieces;
470         cp = s;
471         for (i = 0; i < n; i++) {
472                 if (piece->extval)
473                         len = sprintf(cp, "%s#%s", piece->len, piece->ext);
474                 else
475                         len = sprintf(cp, "%s#%s", piece->len, piece->val);
476                 cp += len;
477                 piece++;
478         }
479         return (s);
480 }
481
482 struct fattr *
483 fattr_dup(const struct fattr *from)
484 {
485         struct fattr *fa;
486
487         fa = fattr_new(FT_UNKNOWN, -1);
488         fattr_override(fa, from, FA_MASK);
489         return (fa);
490 }
491
492 void
493 fattr_free(struct fattr *fa)
494 {
495
496         if (fa == NULL)
497                 return;
498         if (fa->linktarget != NULL)
499                 free(fa->linktarget);
500         free(fa);
501 }
502
503 void
504 fattr_umask(struct fattr *fa, mode_t newumask)
505 {
506
507         if (fa->mask & FA_MODE)
508                 fa->mode = fa->mode & ~newumask;
509 }
510
511 void
512 fattr_maskout(struct fattr *fa, int mask)
513 {
514
515         /* Don't forget to free() the linktarget attribute if we remove it. */
516         if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) {
517                 free(fa->linktarget);
518                 fa->linktarget = NULL;
519         }
520         fa->mask &= ~mask;
521 }
522
523 int
524 fattr_getmask(const struct fattr *fa)
525 {
526
527         return (fa->mask);
528 }
529
530 nlink_t
531 fattr_getlinkcount(const struct fattr *fa)
532 {
533
534         return (fa->linkcount);
535 }
536
537 /*
538  * Eat the specified attribute and put it in the file attribute
539  * structure.  Returns NULL on error, or a pointer to the next
540  * attribute to parse.
541  *
542  * This would be much prettier if we had strntol() so that we're
543  * not forced to write '\0' to the string before calling strtol()
544  * and then put back the old value...
545  *
546  * We need to use (unsigned) long long types here because some
547  * of the opaque types we're parsing (off_t, time_t...) may need
548  * 64bits to fit.
549  */
550 static char *
551 fattr_scanattr(struct fattr *fa, int type, const char *attr)
552 {
553         char *attrend, *attrstart, *end;
554         size_t len;
555         unsigned long attrlen;
556         int error;
557         mode_t modemask;
558         char tmp;
559
560         if (attr == NULL)
561                 return (NULL);
562         errno = 0;
563         attrlen = strtoul(attr, &end, 10);
564         if (errno || *end != '#')
565                 return (NULL);
566         len = strlen(attr);
567         attrstart = end + 1;
568         attrend = attrstart + attrlen;
569         tmp = *attrend;
570         *attrend = '\0';
571         switch (type) {
572         /* Using FA_MASK here is a bit bogus semantically. */
573         case FA_MASK:
574                 errno = 0;
575                 fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
576                 if (errno || end != attrend)
577                         goto bad;
578                 break;
579         case FA_FILETYPE:
580                 errno = 0;
581                 fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
582                 if (errno || end != attrend)
583                         goto bad;
584                 break;
585         case FA_MODTIME:
586                 errno = 0;
587                 fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
588                 if (errno || end != attrend)
589                         goto bad;
590                 break;
591         case FA_SIZE:
592                 errno = 0;
593                 fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
594                 if (errno || end != attrend)
595                         goto bad;
596                 break;
597         case FA_LINKTARGET:
598                 fa->linktarget = xstrdup(attrstart);
599                 break;
600         case FA_RDEV:
601                 errno = 0;
602                 fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
603                 if (errno || end != attrend)
604                         goto bad;
605                 break;
606         case FA_OWNER:
607                 error = getuidbyname(attrstart, &fa->uid);
608                 if (error)
609                         fa->mask &= ~FA_OWNER;
610                 break;
611         case FA_GROUP:
612                 error = getgidbyname(attrstart, &fa->gid);
613                 if (error)
614                         fa->mask &= ~FA_GROUP;
615                 break;
616         case FA_MODE:
617                 errno = 0;
618                 fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
619                 if (errno || end != attrend)
620                         goto bad;
621                 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
622                         modemask = FA_SETIDMASK | FA_PERMMASK;
623                 else
624                         modemask = FA_PERMMASK;
625                 fa->mode &= modemask;
626                 break;
627         case FA_FLAGS:
628                 errno = 0;
629                 fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
630                 if (errno || end != attrend)
631                         goto bad;
632                 break;
633         case FA_LINKCOUNT:
634                 errno = 0;
635                 fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
636                 if (errno || end != attrend)
637                         goto bad;
638                 break;
639         case FA_DEV:
640                 errno = 0;
641                 fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
642                 if (errno || end != attrend)
643                         goto bad;
644                 break;
645         case FA_INODE:
646                 errno = 0;
647                 fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
648                 if (errno || end != attrend)
649                         goto bad;
650                 break;
651         }
652         *attrend = tmp;
653         return (attrend);
654 bad:
655         *attrend = tmp;
656         return (NULL);
657 }
658
659 /* Return a file attribute structure built from the RCS file attributes. */
660 struct fattr *
661 fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
662 {
663         struct fattr *fa;
664
665         fa = fattr_new(FT_FILE, -1);
666         if (rcsattr->mask & FA_MODE) {
667                 if ((rcsattr->mode & 0111) > 0)
668                         fa->mode = 0777;
669                 else
670                         fa->mode = 0666;
671                 fa->mode &= ~mask;
672                 fa->mask |= FA_MODE;
673         }
674         return (fa);
675 }
676
677 /* Merge attributes from "from" that aren't present in "fa". */
678 void
679 fattr_merge(struct fattr *fa, const struct fattr *from)
680 {
681
682         fattr_override(fa, from, from->mask & ~fa->mask);
683 }
684
685 /* Merge default attributes. */
686 void
687 fattr_mergedefault(struct fattr *fa)
688 {
689
690         fattr_merge(fa, defaults[fa->type]);
691 }
692
693 /* Override selected attributes of "fa" with values from "from". */
694 void
695 fattr_override(struct fattr *fa, const struct fattr *from, int mask)
696 {
697
698         mask &= from->mask;
699         if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
700                 free(fa->linktarget);
701         fa->mask |= mask;
702         if (mask & FA_FILETYPE)
703                 fa->type = from->type;
704         if (mask & FA_MODTIME)
705                 fa->modtime = from->modtime;
706         if (mask & FA_SIZE)
707                 fa->size = from->size;
708         if (mask & FA_LINKTARGET)
709                 fa->linktarget = xstrdup(from->linktarget);
710         if (mask & FA_RDEV)
711                 fa->rdev = from->rdev;
712         if (mask & FA_OWNER)
713                 fa->uid = from->uid;
714         if (mask & FA_GROUP)
715                 fa->gid = from->gid;
716         if (mask & FA_MODE)
717                 fa->mode = from->mode;
718         if (mask & FA_FLAGS)
719                 fa->flags = from->flags;
720         if (mask & FA_LINKCOUNT)
721                 fa->linkcount = from->linkcount;
722         if (mask & FA_DEV)
723                 fa->dev = from->dev;
724         if (mask & FA_INODE)
725                 fa->inode = from->inode;
726 }
727
728 /* Create a node. */
729 int
730 fattr_makenode(const struct fattr *fa, const char *path)
731 {
732         mode_t modemask, mode;
733         int error;
734
735         if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
736                 modemask = FA_SETIDMASK | FA_PERMMASK;
737         else
738                 modemask = FA_PERMMASK;
739
740         /* We only implement fattr_makenode() for dirs for now. */
741         assert(fa->type == FT_DIRECTORY);
742         if (fa->mask & FA_MODE)
743                 mode = fa->mode & modemask;
744         else
745                 mode = 0700;
746         error = mkdir(path, mode);
747         return (error);
748 }
749
750 int
751 fattr_delete(const char *path)
752 {
753         struct fattr *fa;
754         int error;
755
756         fa = fattr_frompath(path, FATTR_NOFOLLOW);
757         if (fa == NULL) {
758                 if (errno == ENOENT)
759                         return (0);
760                 return (-1);
761         }
762
763 #ifdef HAVE_FFLAGS
764         /* Clear flags. */
765         if (fa->mask & FA_FLAGS && fa->flags != 0) {
766                 fa->flags = 0;
767                 (void)chflags(path, fa->flags);
768         }
769 #endif
770
771         if (fa->type == FT_DIRECTORY)
772                 error = rmdir(path);
773         else
774                 error = unlink(path);
775         fattr_free(fa);
776         return (error);
777 }
778
779 /*
780  * Changes those attributes we can change.  Returns -1 on error,
781  * 0 if no update was needed, and 1 if an update was needed and
782  * it has been applied successfully.
783  */
784 int
785 fattr_install(struct fattr *fa, const char *topath, const char *frompath)
786 {
787         struct timeval tv[2];
788         struct fattr *old;
789         int error, inplace, mask;
790         mode_t modemask, newmode;
791         uid_t uid;
792         gid_t gid;
793
794         mask = fa->mask & fattr_supported(fa->type);
795         if (mask & FA_OWNER && mask & FA_GROUP)
796                 modemask = FA_SETIDMASK | FA_PERMMASK;
797         else
798                 modemask = FA_PERMMASK;
799
800         inplace = 0;
801         if (frompath == NULL) {
802                 /* Changing attributes in place. */
803                 frompath = topath;
804                 inplace = 1;
805         }
806         old = fattr_frompath(topath, FATTR_NOFOLLOW);
807         if (old != NULL) {
808                 if (inplace && fattr_equal(fa, old)) {
809                         fattr_free(old);
810                         return (0);
811                 }
812
813 #ifdef HAVE_FFLAGS
814                 /*
815                  * Determine whether we need to clear the flags of the target.
816                  * This is bogus in that it assumes a value of 0 is safe and
817                  * that non-zero is unsafe.  I'm not really worried by that
818                  * since as far as I know that's the way things are.
819                  */
820                 if ((old->mask & FA_FLAGS) && old->flags > 0) {
821                         (void)chflags(topath, 0);
822                         old->flags = 0;
823                 }
824 #endif
825
826                 /* Determine whether we need to remove the target first. */
827                 if (!inplace && (fa->type == FT_DIRECTORY) !=
828                     (old->type == FT_DIRECTORY)) {
829                         if (old->type == FT_DIRECTORY)
830                                 error = rmdir(topath);
831                         else
832                                 error = unlink(topath);
833                         if (error)
834                                 goto bad;
835                 }
836         }
837
838         /* Change those attributes that we can before moving the file
839          * into place.  That makes installation atomic in most cases. */
840         if (mask & FA_MODTIME) {
841                 gettimeofday(tv, NULL);         /* Access time. */
842                 tv[1].tv_sec = fa->modtime;     /* Modification time. */
843                 tv[1].tv_usec = 0;
844                 error = utimes(frompath, tv);
845                 if (error)
846                         goto bad;
847         }
848         if (mask & FA_OWNER || mask & FA_GROUP) {
849                 uid = -1;
850                 gid = -1;
851                 if (mask & FA_OWNER)
852                         uid = fa->uid;
853                 if (mask & FA_GROUP)
854                         gid = fa->gid;
855                 error = chown(frompath, uid, gid);
856                 if (error)
857                         goto bad;
858         }
859         if (mask & FA_MODE) {
860                 newmode = fa->mode & modemask;
861                 /* Merge in set*id bits from the old attribute. */
862                 if (old != NULL && old->mask & FA_MODE) {
863                         newmode |= (old->mode & ~modemask);
864                         newmode &= (FA_SETIDMASK | FA_PERMMASK);
865                 }
866                 error = chmod(frompath, newmode);
867                 if (error)
868                         goto bad;
869         }
870
871         if (!inplace) {
872                 error = rename(frompath, topath);
873                 if (error)
874                         goto bad;
875         }
876
877 #ifdef HAVE_FFLAGS
878         /* Set the flags. */
879         if (mask & FA_FLAGS)
880                 (void)chflags(topath, fa->flags);
881 #endif
882         fattr_free(old);
883         return (1);
884 bad:
885         fattr_free(old);
886         return (-1);
887 }
888
889 /*
890  * Returns 1 if both attributes are equal, 0 otherwise.
891  *
892  * This function only compares attributes that are valid in both
893  * files.  A file of unknown type ("FT_UNKNOWN") is unequal to
894  * anything, including itself.
895  */
896 int
897 fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
898 {
899         int mask;
900
901         mask = fa1->mask & fa2->mask;
902         if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
903                 return (0);
904         if (mask & FA_MODTIME)
905                 if (fa1->modtime != fa2->modtime)
906                         return (0);
907         if (mask & FA_SIZE)
908                 if (fa1->size != fa2->size)
909                         return (0);
910         if (mask & FA_LINKTARGET)
911                 if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
912                         return (0);
913         if (mask & FA_RDEV)
914                 if (fa1->rdev != fa2->rdev)
915                         return (0);
916         if (mask & FA_OWNER)
917                 if (fa1->uid != fa2->uid)
918                         return (0);
919         if (mask & FA_GROUP)
920                 if (fa1->gid != fa2->gid)
921                         return (0);
922         if (mask & FA_MODE)
923                 if (fa1->mode != fa2->mode)
924                         return (0);
925         if (mask & FA_FLAGS)
926                 if (fa1->flags != fa2->flags)
927                         return (0);
928         if (mask & FA_LINKCOUNT)
929                 if (fa1->linkcount != fa2->linkcount)
930                         return (0);
931         if (mask & FA_DEV)
932                 if (fa1->dev != fa2->dev)
933                         return (0);
934         if (mask & FA_INODE)
935                 if (fa1->inode != fa2->inode)
936                         return (0);
937         return (1);
938 }