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
62typedef 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
79struct 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
95static 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
111static struct fattr *defaults[FT_NUMBER];
112
113void
114fattr_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
132void
133fattr_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
142const struct fattr *fattr_bogus = &bogus;
143
144static char		*fattr_scanattr(struct fattr *, int, const char *);
145
146int
147fattr_supported(int type)
148{
149
150	return (fattr_support[type]);
151}
152
153struct fattr *
154fattr_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. */
175struct fattr *
176fattr_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
220struct fattr *
221fattr_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
253struct fattr *
254fattr_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
267int
268fattr_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. */
275struct fattr *
276fattr_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);
327bad:
328	fattr_free(fa);
329	return (NULL);
330}
331
332char *
333fattr_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
482struct fattr *
483fattr_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
492void
493fattr_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
503void
504fattr_umask(struct fattr *fa, mode_t newumask)
505{
506
507	if (fa->mask & FA_MODE)
508		fa->mode = fa->mode & ~newumask;
509}
510
511void
512fattr_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
523int
524fattr_getmask(const struct fattr *fa)
525{
526
527	return (fa->mask);
528}
529
530nlink_t
531fattr_getlinkcount(const struct fattr *fa)
532{
533
534	return (fa->linkcount);
535}
536
537char *
538fattr_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 */
557static char *
558fattr_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);
661bad:
662	*attrend = tmp;
663	return (NULL);
664}
665
666/* Return a file attribute structure built from the RCS file attributes. */
667struct fattr *
668fattr_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". */
685void
686fattr_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. */
693void
694fattr_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". */
701void
702fattr_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. */
736int
737fattr_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
767int
768fattr_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 */
801int
802fattr_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);
915bad:
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 */
927int
928fattr_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 */
977off_t
978fattr_filesize(const struct fattr *fa)
979{
980	return (fa->size);
981}
982