xref: /trueos/usr.bin/csup/fattr.c (revision 834fb25a9ed2240101506d137b5be7d71c75f306)
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