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
fattr_init(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
fattr_fini(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
fattr_supported(int type)147 fattr_supported(int type)
148 {
149
150 return (fattr_support[type]);
151 }
152
153 struct fattr *
fattr_new(int type,time_t modtime)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 *
fattr_fromstat(struct stat * sb)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 *
fattr_frompath(const char * path,int nofollow)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 *
fattr_fromfd(int fd)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
fattr_type(const struct fattr * fa)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 *
fattr_decode(char * attr)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 *
fattr_encode(const struct fattr * fa,fattr_support_t support,int ignore)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 *
fattr_dup(const struct fattr * from)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
fattr_free(struct fattr * fa)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
fattr_umask(struct fattr * fa,mode_t newumask)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
fattr_maskout(struct fattr * fa,int mask)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
fattr_getmask(const struct fattr * fa)524 fattr_getmask(const struct fattr *fa)
525 {
526
527 return (fa->mask);
528 }
529
530 nlink_t
fattr_getlinkcount(const struct fattr * fa)531 fattr_getlinkcount(const struct fattr *fa)
532 {
533
534 return (fa->linkcount);
535 }
536
537 char *
fattr_getlinktarget(const struct fattr * fa)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 *
fattr_scanattr(struct fattr * fa,int type,const char * attr)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 *
fattr_forcheckout(const struct fattr * rcsattr,mode_t mask)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
fattr_merge(struct fattr * fa,const struct fattr * from)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
fattr_mergedefault(struct fattr * fa)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
fattr_override(struct fattr * fa,const struct fattr * from,int mask)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
fattr_makenode(const struct fattr * fa,const char * path)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
fattr_delete(const char * path)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
fattr_install(struct fattr * fa,const char * topath,const char * frompath)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
fattr_equal(const struct fattr * fa1,const struct fattr * fa2)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
fattr_filesize(const struct fattr * fa)978 fattr_filesize(const struct fattr *fa)
979 {
980 return (fa->size);
981 }
982