]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/csup/fattr.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.bin / 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 a 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), "%llx",
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 char *
538 fattr_getlinktarget(const struct fattr *fa)
539 {
540
541         return (fa->linktarget);
542 }
543
544 /*
545  * Eat the specified attribute and put it in the file attribute
546  * structure.  Returns NULL on error, or a pointer to the next
547  * attribute to parse.
548  *
549  * This would be much prettier if we had strntol() so that we're
550  * not forced to write '\0' to the string before calling strtol()
551  * and then put back the old value...
552  *
553  * We need to use (unsigned) long long types here because some
554  * of the opaque types we're parsing (off_t, time_t...) may need
555  * 64bits to fit.
556  */
557 static char *
558 fattr_scanattr(struct fattr *fa, int type, const char *attr)
559 {
560         char *attrend, *attrstart, *end;
561         size_t len;
562         unsigned long attrlen;
563         int error;
564         mode_t modemask;
565         char tmp;
566
567         if (attr == NULL)
568                 return (NULL);
569         errno = 0;
570         attrlen = strtoul(attr, &end, 10);
571         if (errno || *end != '#')
572                 return (NULL);
573         len = strlen(attr);
574         attrstart = end + 1;
575         attrend = attrstart + attrlen;
576         tmp = *attrend;
577         *attrend = '\0';
578         switch (type) {
579         /* Using FA_MASK here is a bit bogus semantically. */
580         case FA_MASK:
581                 errno = 0;
582                 fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
583                 if (errno || end != attrend)
584                         goto bad;
585                 break;
586         case FA_FILETYPE:
587                 errno = 0;
588                 fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
589                 if (errno || end != attrend)
590                         goto bad;
591                 break;
592         case FA_MODTIME:
593                 errno = 0;
594                 fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
595                 if (errno || end != attrend)
596                         goto bad;
597                 break;
598         case FA_SIZE:
599                 errno = 0;
600                 fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
601                 if (errno || end != attrend)
602                         goto bad;
603                 break;
604         case FA_LINKTARGET:
605                 fa->linktarget = xstrdup(attrstart);
606                 break;
607         case FA_RDEV:
608                 errno = 0;
609                 fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
610                 if (errno || end != attrend)
611                         goto bad;
612                 break;
613         case FA_OWNER:
614                 error = getuidbyname(attrstart, &fa->uid);
615                 if (error)
616                         fa->mask &= ~FA_OWNER;
617                 break;
618         case FA_GROUP:
619                 error = getgidbyname(attrstart, &fa->gid);
620                 if (error)
621                         fa->mask &= ~FA_GROUP;
622                 break;
623         case FA_MODE:
624                 errno = 0;
625                 fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
626                 if (errno || end != attrend)
627                         goto bad;
628                 if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
629                         modemask = FA_SETIDMASK | FA_PERMMASK;
630                 else
631                         modemask = FA_PERMMASK;
632                 fa->mode &= modemask;
633                 break;
634         case FA_FLAGS:
635                 errno = 0;
636                 fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
637                 if (errno || end != attrend)
638                         goto bad;
639                 break;
640         case FA_LINKCOUNT:
641                 errno = 0;
642                 fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
643                 if (errno || end != attrend)
644                         goto bad;
645                 break;
646         case FA_DEV:
647                 errno = 0;
648                 fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
649                 if (errno || end != attrend)
650                         goto bad;
651                 break;
652         case FA_INODE:
653                 errno = 0;
654                 fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
655                 if (errno || end != attrend)
656                         goto bad;
657                 break;
658         }
659         *attrend = tmp;
660         return (attrend);
661 bad:
662         *attrend = tmp;
663         return (NULL);
664 }
665
666 /* Return a file attribute structure built from the RCS file attributes. */
667 struct fattr *
668 fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
669 {
670         struct fattr *fa;
671
672         fa = fattr_new(FT_FILE, -1);
673         if (rcsattr->mask & FA_MODE) {
674                 if ((rcsattr->mode & 0111) > 0)
675                         fa->mode = 0777;
676                 else
677                         fa->mode = 0666;
678                 fa->mode &= ~mask;
679                 fa->mask |= FA_MODE;
680         }
681         return (fa);
682 }
683
684 /* Merge attributes from "from" that aren't present in "fa". */
685 void
686 fattr_merge(struct fattr *fa, const struct fattr *from)
687 {
688
689         fattr_override(fa, from, from->mask & ~fa->mask);
690 }
691
692 /* Merge default attributes. */
693 void
694 fattr_mergedefault(struct fattr *fa)
695 {
696
697         fattr_merge(fa, defaults[fa->type]);
698 }
699
700 /* Override selected attributes of "fa" with values from "from". */
701 void
702 fattr_override(struct fattr *fa, const struct fattr *from, int mask)
703 {
704
705         mask &= from->mask;
706         if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
707                 free(fa->linktarget);
708         fa->mask |= mask;
709         if (mask & FA_FILETYPE)
710                 fa->type = from->type;
711         if (mask & FA_MODTIME)
712                 fa->modtime = from->modtime;
713         if (mask & FA_SIZE)
714                 fa->size = from->size;
715         if (mask & FA_LINKTARGET)
716                 fa->linktarget = xstrdup(from->linktarget);
717         if (mask & FA_RDEV)
718                 fa->rdev = from->rdev;
719         if (mask & FA_OWNER)
720                 fa->uid = from->uid;
721         if (mask & FA_GROUP)
722                 fa->gid = from->gid;
723         if (mask & FA_MODE)
724                 fa->mode = from->mode;
725         if (mask & FA_FLAGS)
726                 fa->flags = from->flags;
727         if (mask & FA_LINKCOUNT)
728                 fa->linkcount = from->linkcount;
729         if (mask & FA_DEV)
730                 fa->dev = from->dev;
731         if (mask & FA_INODE)
732                 fa->inode = from->inode;
733 }
734
735 /* Create a node. */
736 int
737 fattr_makenode(const struct fattr *fa, const char *path)
738 {
739         mode_t modemask, mode;
740         int error;
741
742         error = 0;
743
744         if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
745                 modemask = FA_SETIDMASK | FA_PERMMASK;
746         else
747                 modemask = FA_PERMMASK;
748
749         /* We only implement fattr_makenode() for dirs for now. */
750         if (fa->mask & FA_MODE)
751                 mode = fa->mode & modemask;
752         else
753                 mode = 0700;
754
755         if (fa->type == FT_DIRECTORY)
756                 error = mkdir(path, mode);
757         else if (fa->type == FT_SYMLINK) {
758                 error = symlink(fa->linktarget, path);
759         } else if (fa->type == FT_CDEV) {
760                 lprintf(-1, "Character devices not supported!\n");
761         } else if (fa->type == FT_BDEV) {
762                 lprintf(-1, "Block devices not supported!\n");
763         }
764         return (error);
765 }
766
767 int
768 fattr_delete(const char *path)
769 {
770         struct fattr *fa;
771         int error;
772
773         fa = fattr_frompath(path, FATTR_NOFOLLOW);
774         if (fa == NULL) {
775                 if (errno == ENOENT)
776                         return (0);
777                 return (-1);
778         }
779
780 #ifdef HAVE_FFLAGS
781         /* Clear flags. */
782         if (fa->mask & FA_FLAGS && fa->flags != 0) {
783                 fa->flags = 0;
784                 (void)chflags(path, fa->flags);
785         }
786 #endif
787
788         if (fa->type == FT_DIRECTORY)
789                 error = rmdir(path);
790         else
791                 error = unlink(path);
792         fattr_free(fa);
793         return (error);
794 }
795
796 /*
797  * Changes those attributes we can change.  Returns -1 on error,
798  * 0 if no update was needed, and 1 if an update was needed and
799  * it has been applied successfully.
800  */
801 int
802 fattr_install(struct fattr *fa, const char *topath, const char *frompath)
803 {
804         struct timeval tv[2];
805         struct fattr *old;
806         int error, inplace, mask;
807         mode_t modemask, newmode;
808         uid_t uid;
809         gid_t gid;
810
811         mask = fa->mask & fattr_supported(fa->type);
812         if (mask & FA_OWNER && mask & FA_GROUP)
813                 modemask = FA_SETIDMASK | FA_PERMMASK;
814         else
815                 modemask = FA_PERMMASK;
816
817         inplace = 0;
818         if (frompath == NULL) {
819                 /* Changing attributes in place. */
820                 frompath = topath;
821                 inplace = 1;
822         }
823         old = fattr_frompath(topath, FATTR_NOFOLLOW);
824         if (old != NULL) {
825                 if (inplace && fattr_equal(fa, old)) {
826                         fattr_free(old);
827                         return (0);
828                 }
829
830 #ifdef HAVE_FFLAGS
831                 /*
832                  * Determine whether we need to clear the flags of the target.
833                  * This is bogus in that it assumes a value of 0 is safe and
834                  * that non-zero is unsafe.  I'm not really worried by that
835                  * since as far as I know that's the way things are.
836                  */
837                 if ((old->mask & FA_FLAGS) && old->flags > 0) {
838                         (void)chflags(topath, 0);
839                         old->flags = 0;
840                 }
841 #endif
842
843                 /*
844                  * If it is changed from a file to a symlink, remove the file
845                  * and create the symlink.
846                  */
847                 if (inplace && (fa->type == FT_SYMLINK) &&
848                     (old->type == FT_FILE)) {
849                         error = unlink(topath);
850                         if (error)
851                                 goto bad;
852                         error = symlink(fa->linktarget, topath);
853                         if (error)
854                                 goto bad;
855                 }
856                 /* Determine whether we need to remove the target first. */
857                 if (!inplace && (fa->type == FT_DIRECTORY) !=
858                     (old->type == FT_DIRECTORY)) {
859                         if (old->type == FT_DIRECTORY)
860                                 error = rmdir(topath);
861                         else
862                                 error = unlink(topath);
863                         if (error)
864                                 goto bad;
865                 }
866         }
867
868         /* Change those attributes that we can before moving the file
869          * into place.  That makes installation atomic in most cases. */
870         if (mask & FA_MODTIME) {
871                 gettimeofday(tv, NULL);         /* Access time. */
872                 tv[1].tv_sec = fa->modtime;     /* Modification time. */
873                 tv[1].tv_usec = 0;
874                 error = utimes(frompath, tv);
875                 if (error)
876                         goto bad;
877         }
878         if (mask & FA_OWNER || mask & FA_GROUP) {
879                 uid = -1;
880                 gid = -1;
881                 if (mask & FA_OWNER)
882                         uid = fa->uid;
883                 if (mask & FA_GROUP)
884                         gid = fa->gid;
885                 error = chown(frompath, uid, gid);
886                 if (error) {
887                         goto bad;
888                 }
889         }
890         if (mask & FA_MODE) {
891                 newmode = fa->mode & modemask;
892                 /* Merge in set*id bits from the old attribute. */
893                 if (old != NULL && old->mask & FA_MODE) {
894                         newmode |= (old->mode & ~modemask);
895                         newmode &= (FA_SETIDMASK | FA_PERMMASK);
896                 }
897                 error = chmod(frompath, newmode);
898                 if (error)
899                         goto bad;
900         }
901
902         if (!inplace) {
903                 error = rename(frompath, topath);
904                 if (error)
905                         goto bad;
906         }
907
908 #ifdef HAVE_FFLAGS
909         /* Set the flags. */
910         if (mask & FA_FLAGS)
911                 (void)chflags(topath, fa->flags);
912 #endif
913         fattr_free(old);
914         return (1);
915 bad:
916         fattr_free(old);
917         return (-1);
918 }
919
920 /*
921  * Returns 1 if both attributes are equal, 0 otherwise.
922  *
923  * This function only compares attributes that are valid in both
924  * files.  A file of unknown type ("FT_UNKNOWN") is unequal to
925  * anything, including itself.
926  */
927 int
928 fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
929 {
930         int mask;
931
932         mask = fa1->mask & fa2->mask;
933         if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
934                 return (0);
935         if (mask & FA_FILETYPE)
936                 if (fa1->type != fa2->type)
937                         return (0);
938         if (mask & FA_MODTIME)
939                 if (fa1->modtime != fa2->modtime)
940                         return (0);
941         if (mask & FA_SIZE)
942                 if (fa1->size != fa2->size)
943                         return (0);
944         if (mask & FA_LINKTARGET)
945                 if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
946                         return (0);
947         if (mask & FA_RDEV)
948                 if (fa1->rdev != fa2->rdev)
949                         return (0);
950         if (mask & FA_OWNER)
951                 if (fa1->uid != fa2->uid)
952                         return (0);
953         if (mask & FA_GROUP)
954                 if (fa1->gid != fa2->gid)
955                         return (0);
956         if (mask & FA_MODE)
957                 if (fa1->mode != fa2->mode)
958                         return (0);
959         if (mask & FA_FLAGS)
960                 if (fa1->flags != fa2->flags)
961                         return (0);
962         if (mask & FA_LINKCOUNT)
963                 if (fa1->linkcount != fa2->linkcount)
964                         return (0);
965         if (mask & FA_DEV)
966                 if (fa1->dev != fa2->dev)
967                         return (0);
968         if (mask & FA_INODE)
969                 if (fa1->inode != fa2->inode)
970                         return (0);
971         return (1);
972 }
973
974 /*
975  * Must have to get the correct filesize sendt by the server.
976  */
977 off_t
978 fattr_filesize(const struct fattr *fa)
979 {
980         return (fa->size);
981 }