1156230Smux/*-
2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3156230Smux * All rights reserved.
4156230Smux *
5156230Smux * Redistribution and use in source and binary forms, with or without
6156230Smux * modification, are permitted provided that the following conditions
7156230Smux * are met:
8156230Smux * 1. Redistributions of source code must retain the above copyright
9156230Smux *    notice, this list of conditions and the following disclaimer.
10156230Smux * 2. Redistributions in binary form must reproduce the above copyright
11156230Smux *    notice, this list of conditions and the following disclaimer in the
12156230Smux *    documentation and/or other materials provided with the distribution.
13156230Smux *
14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156230Smux * SUCH DAMAGE.
25156230Smux *
26156230Smux * $FreeBSD$
27156230Smux */
28156230Smux
29156230Smux#include <sys/time.h>
30156230Smux#include <sys/types.h>
31156230Smux#include <sys/stat.h>
32156230Smux
33156230Smux#include <assert.h>
34156230Smux#include <errno.h>
35156230Smux#include <stdio.h>
36156230Smux#include <stdlib.h>
37156230Smux#include <string.h>
38156230Smux#include <unistd.h>
39156230Smux
40156230Smux#include "fattr.h"
41156701Smux#include "idcache.h"
42156230Smux#include "misc.h"
43156230Smux
44156230Smux/*
45156230Smux * Include the appropriate definition for the file attributes we support.
46156230Smux * There are two different files: fattr_bsd.h for BSD-like systems that
47186781Slulf * support the extended file flags a la chflags() and fattr_posix.h for
48156230Smux * bare POSIX systems that don't.
49156230Smux */
50156230Smux#ifdef HAVE_FFLAGS
51156230Smux#include "fattr_bsd.h"
52156230Smux#else
53156230Smux#include "fattr_posix.h"
54156230Smux#endif
55156230Smux
56156230Smux#ifdef __FreeBSD__
57156230Smux#include <osreldate.h>
58156230Smux#endif
59156230Smux
60156230Smux/* Define fflags_t if we're on a system that doesn't have it. */
61156230Smux#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030
62156230Smuxtypedef uint32_t fflags_t;
63156230Smux#endif
64156230Smux
65156230Smux#define	FA_MASKRADIX		16
66156230Smux#define	FA_FILETYPERADIX	10
67156230Smux#define	FA_MODTIMERADIX		10
68156230Smux#define	FA_SIZERADIX		10
69156230Smux#define	FA_RDEVRADIX		16
70156230Smux#define	FA_MODERADIX		8
71156230Smux#define	FA_FLAGSRADIX		16
72156230Smux#define	FA_LINKCOUNTRADIX	10
73156230Smux#define	FA_DEVRADIX		16
74156230Smux#define	FA_INODERADIX		10
75156230Smux
76156230Smux#define	FA_PERMMASK		(S_IRWXU | S_IRWXG | S_IRWXO)
77156230Smux#define	FA_SETIDMASK		(S_ISUID | S_ISGID | S_ISVTX)
78156230Smux
79156230Smuxstruct fattr {
80156230Smux	int		mask;
81156230Smux	int		type;
82156230Smux	time_t		modtime;
83156230Smux	off_t		size;
84156230Smux	char		*linktarget;
85156230Smux	dev_t		rdev;
86156230Smux	uid_t		uid;
87156230Smux	gid_t		gid;
88156230Smux	mode_t		mode;
89156230Smux	fflags_t	flags;
90156230Smux	nlink_t		linkcount;
91156230Smux	dev_t		dev;
92156230Smux	ino_t		inode;
93156230Smux};
94156230Smux
95156230Smuxstatic const struct fattr bogus = {
96156230Smux	FA_MODTIME | FA_SIZE | FA_MODE,
97156230Smux	FT_UNKNOWN,
98156230Smux	1,
99156230Smux	0,
100156230Smux	NULL,
101156230Smux	0,
102156230Smux	0,
103156230Smux	0,
104156230Smux	0,
105156230Smux	0,
106156230Smux	0,
107156230Smux	0,
108156230Smux	0
109156230Smux};
110156230Smux
111156230Smuxstatic struct fattr *defaults[FT_NUMBER];
112156230Smux
113156230Smuxvoid
114156230Smuxfattr_init(void)
115156230Smux{
116156230Smux	struct fattr *fa;
117156230Smux	int i;
118156230Smux
119156230Smux	for (i = 0; i < FT_NUMBER; i++) {
120156230Smux		fa = fattr_new(i, -1);
121156230Smux		if (i == FT_DIRECTORY)
122156230Smux			fa->mode = 0777;
123156230Smux		else
124156230Smux			fa->mode = 0666;
125156230Smux		fa->mask |= FA_MODE;
126156230Smux		defaults[i] = fa;
127156230Smux	}
128156701Smux	/* Initialize the uid/gid lookup cache. */
129156701Smux	idcache_init();
130156230Smux}
131156230Smux
132156230Smuxvoid
133156230Smuxfattr_fini(void)
134156230Smux{
135156230Smux	int i;
136156230Smux
137156701Smux	idcache_fini();
138156230Smux	for (i = 0; i < FT_NUMBER; i++)
139156230Smux		fattr_free(defaults[i]);
140156230Smux}
141156230Smux
142156230Smuxconst struct fattr *fattr_bogus = &bogus;
143156230Smux
144156230Smuxstatic char		*fattr_scanattr(struct fattr *, int, const char *);
145156230Smux
146156230Smuxint
147156230Smuxfattr_supported(int type)
148156230Smux{
149156230Smux
150156230Smux	return (fattr_support[type]);
151156230Smux}
152156230Smux
153156230Smuxstruct fattr *
154156230Smuxfattr_new(int type, time_t modtime)
155156230Smux{
156156230Smux	struct fattr *new;
157156230Smux
158156230Smux	new = xmalloc(sizeof(struct fattr));
159156230Smux	memset(new, 0, sizeof(struct fattr));
160156230Smux	new->type = type;
161156230Smux	if (type != FT_UNKNOWN)
162156230Smux		new->mask |= FA_FILETYPE;
163156230Smux	if (modtime != -1) {
164156230Smux		new->modtime = modtime;
165156230Smux		new->mask |= FA_MODTIME;
166156230Smux	}
167156230Smux	if (fattr_supported(new->type) & FA_LINKCOUNT) {
168156230Smux		new->mask |= FA_LINKCOUNT;
169156230Smux		new->linkcount = 1;
170156230Smux	}
171156230Smux	return (new);
172156230Smux}
173156230Smux
174156230Smux/* Returns a new file attribute structure based on a stat structure. */
175156230Smuxstruct fattr *
176156230Smuxfattr_fromstat(struct stat *sb)
177156230Smux{
178156230Smux	struct fattr *fa;
179156230Smux
180156230Smux	fa = fattr_new(FT_UNKNOWN, -1);
181156230Smux	if (S_ISREG(sb->st_mode))
182156230Smux		fa->type = FT_FILE;
183156230Smux	else if (S_ISDIR(sb->st_mode))
184156230Smux		fa->type = FT_DIRECTORY;
185156230Smux	else if (S_ISCHR(sb->st_mode))
186156230Smux		fa->type = FT_CDEV;
187156230Smux	else if (S_ISBLK(sb->st_mode))
188156230Smux		fa->type = FT_BDEV;
189156230Smux	else if (S_ISLNK(sb->st_mode))
190156230Smux		fa->type = FT_SYMLINK;
191156230Smux	else
192156230Smux		fa->type = FT_UNKNOWN;
193156230Smux
194156230Smux	fa->mask = FA_FILETYPE | fattr_supported(fa->type);
195156230Smux	if (fa->mask & FA_MODTIME)
196156230Smux		fa->modtime = sb->st_mtime;
197156230Smux	if (fa->mask & FA_SIZE)
198156230Smux		fa->size = sb->st_size;
199156230Smux	if (fa->mask & FA_RDEV)
200156230Smux		fa->rdev = sb->st_rdev;
201156230Smux	if (fa->mask & FA_OWNER)
202156230Smux		fa->uid = sb->st_uid;
203156230Smux	if (fa->mask & FA_GROUP)
204156230Smux		fa->gid = sb->st_gid;
205156230Smux	if (fa->mask & FA_MODE)
206156230Smux		fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK);
207156230Smux#ifdef HAVE_FFLAGS
208156230Smux	if (fa->mask & FA_FLAGS)
209156230Smux		fa->flags = sb->st_flags;
210156230Smux#endif
211156230Smux	if (fa->mask & FA_LINKCOUNT)
212156230Smux		fa->linkcount = sb->st_nlink;
213156230Smux	if (fa->mask & FA_DEV)
214156230Smux		fa->dev = sb->st_dev;
215156230Smux	if (fa->mask & FA_INODE)
216156230Smux		fa->inode = sb->st_ino;
217156230Smux	return (fa);
218156230Smux}
219156230Smux
220156230Smuxstruct fattr *
221156230Smuxfattr_frompath(const char *path, int nofollow)
222156230Smux{
223156230Smux	struct fattr *fa;
224156230Smux	struct stat sb;
225156230Smux	int error, len;
226156230Smux
227156230Smux	if (nofollow)
228156230Smux		error = lstat(path, &sb);
229156230Smux	else
230156230Smux		error = stat(path, &sb);
231156230Smux	if (error)
232156230Smux		return (NULL);
233156230Smux	fa = fattr_fromstat(&sb);
234156230Smux	if (fa->mask & FA_LINKTARGET) {
235156230Smux		char buf[1024];
236156230Smux
237156230Smux		len = readlink(path, buf, sizeof(buf));
238156230Smux		if (len == -1) {
239156230Smux			fattr_free(fa);
240156230Smux			return (NULL);
241156230Smux		}
242156230Smux		if ((unsigned)len > sizeof(buf) - 1) {
243156230Smux			fattr_free(fa);
244156230Smux			errno = ENAMETOOLONG;
245156230Smux			return (NULL);
246156230Smux		}
247156230Smux		buf[len] = '\0';
248156230Smux		fa->linktarget = xstrdup(buf);
249156230Smux	}
250156230Smux	return (fa);
251156230Smux}
252156230Smux
253156230Smuxstruct fattr *
254156230Smuxfattr_fromfd(int fd)
255156230Smux{
256156230Smux	struct fattr *fa;
257156230Smux	struct stat sb;
258156230Smux	int error;
259156230Smux
260156230Smux	error = fstat(fd, &sb);
261156230Smux	if (error)
262156230Smux		return (NULL);
263156230Smux	fa = fattr_fromstat(&sb);
264156230Smux	return (fa);
265156230Smux}
266156230Smux
267156230Smuxint
268156230Smuxfattr_type(const struct fattr *fa)
269156230Smux{
270156230Smux
271156230Smux	return (fa->type);
272156230Smux}
273156230Smux
274156230Smux/* Returns a new file attribute structure from its encoded text form. */
275156230Smuxstruct fattr *
276156230Smuxfattr_decode(char *attr)
277156230Smux{
278156230Smux	struct fattr *fa;
279156230Smux	char *next;
280156230Smux
281156230Smux	fa = fattr_new(FT_UNKNOWN, -1);
282156230Smux	next = fattr_scanattr(fa, FA_MASK, attr);
283156230Smux	if (next == NULL || (fa->mask & ~FA_MASK) > 0)
284156230Smux		goto bad;
285156230Smux	if (fa->mask & FA_FILETYPE) {
286156230Smux		next = fattr_scanattr(fa, FA_FILETYPE, next);
287156230Smux		if (next == NULL)
288156230Smux			goto bad;
289156230Smux		if (fa->type < 0 || fa->type > FT_MAX)
290156230Smux			fa->type = FT_UNKNOWN;
291156230Smux	} else {
292156230Smux		/* The filetype attribute is always valid. */
293156230Smux		fa->mask |= FA_FILETYPE;
294156230Smux		fa->type = FT_UNKNOWN;
295156230Smux	}
296156230Smux	fa->mask = fa->mask & fattr_supported(fa->type);
297156230Smux	if (fa->mask & FA_MODTIME)
298156230Smux		next = fattr_scanattr(fa, FA_MODTIME, next);
299156230Smux	if (fa->mask & FA_SIZE)
300156230Smux		next = fattr_scanattr(fa, FA_SIZE, next);
301156230Smux	if (fa->mask & FA_LINKTARGET)
302156230Smux		next = fattr_scanattr(fa, FA_LINKTARGET, next);
303156230Smux	if (fa->mask & FA_RDEV)
304156230Smux		next = fattr_scanattr(fa, FA_RDEV, next);
305156230Smux	if (fa->mask & FA_OWNER)
306156230Smux		next = fattr_scanattr(fa, FA_OWNER, next);
307156230Smux	if (fa->mask & FA_GROUP)
308156230Smux		next = fattr_scanattr(fa, FA_GROUP, next);
309156230Smux	if (fa->mask & FA_MODE)
310156230Smux		next = fattr_scanattr(fa, FA_MODE, next);
311156230Smux	if (fa->mask & FA_FLAGS)
312156230Smux		next = fattr_scanattr(fa, FA_FLAGS, next);
313156230Smux	if (fa->mask & FA_LINKCOUNT) {
314156230Smux		next = fattr_scanattr(fa, FA_LINKCOUNT, next);
315156230Smux	} else if (fattr_supported(fa->type) & FA_LINKCOUNT) {
316156230Smux		/* If the link count is missing but supported, fake it as 1. */
317156230Smux		fa->mask |= FA_LINKCOUNT;
318156230Smux		fa->linkcount = 1;
319156230Smux	}
320156230Smux	if (fa->mask & FA_DEV)
321156230Smux		next = fattr_scanattr(fa, FA_DEV, next);
322156230Smux	if (fa->mask & FA_INODE)
323156230Smux		next = fattr_scanattr(fa, FA_INODE, next);
324156230Smux	if (next == NULL)
325156230Smux		goto bad;
326156230Smux	return (fa);
327156230Smuxbad:
328156230Smux	fattr_free(fa);
329156230Smux	return (NULL);
330156230Smux}
331156230Smux
332156230Smuxchar *
333156230Smuxfattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
334156230Smux{
335156230Smux	struct {
336156230Smux		char val[32];
337156230Smux		char len[4];
338156230Smux		int extval;
339156230Smux		char *ext;
340156230Smux	} pieces[FA_NUMBER], *piece;
341156701Smux	char *cp, *s, *username, *groupname;
342156230Smux	size_t len, vallen;
343156230Smux	mode_t mode, modemask;
344156230Smux	int mask, n, i;
345156230Smux
346156701Smux	username = NULL;
347156701Smux	groupname = NULL;
348156230Smux	if (support == NULL)
349156230Smux		mask = fa->mask;
350156230Smux	else
351156230Smux		mask = fa->mask & support[fa->type];
352156230Smux	mask &= ~ignore;
353156230Smux	if (fa->mask & FA_OWNER) {
354156701Smux		username = getuserbyid(fa->uid);
355156701Smux		if (username == NULL)
356156230Smux			mask &= ~FA_OWNER;
357156230Smux	}
358156230Smux	if (fa->mask & FA_GROUP) {
359156701Smux		groupname = getgroupbyid(fa->gid);
360156701Smux		if (groupname == NULL)
361156230Smux			mask &= ~FA_GROUP;
362156230Smux	}
363156230Smux	if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
364156230Smux		mask &= ~FA_LINKCOUNT;
365156230Smux
366156230Smux	memset(pieces, 0, FA_NUMBER * sizeof(*pieces));
367156230Smux	len = 0;
368156230Smux	piece = pieces;
369156230Smux	vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask);
370156230Smux	len += snprintf(piece->len, sizeof(piece->len), "%lld",
371156230Smux	    (long long)vallen) + vallen + 1;
372156230Smux	piece++;
373156230Smux	if (mask & FA_FILETYPE) {
374156230Smux		vallen = snprintf(piece->val, sizeof(piece->val),
375156230Smux		    "%d", fa->type);
376156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
377156230Smux		    (long long)vallen) + vallen + 1;
378156230Smux		piece++;
379156230Smux	}
380156230Smux	if (mask & FA_MODTIME) {
381156230Smux		vallen = snprintf(piece->val, sizeof(piece->val),
382156230Smux		    "%lld", (long long)fa->modtime);
383156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
384156230Smux		    (long long)vallen) + vallen + 1;
385156230Smux		piece++;
386156230Smux	}
387156230Smux	if (mask & FA_SIZE) {
388156230Smux		vallen = snprintf(piece->val, sizeof(piece->val),
389156230Smux		    "%lld", (long long)fa->size);
390156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
391156230Smux		    (long long)vallen) + vallen + 1;
392156230Smux		piece++;
393156230Smux	}
394156230Smux	if (mask & FA_LINKTARGET) {
395156230Smux		vallen = strlen(fa->linktarget);
396156230Smux		piece->extval = 1;
397156230Smux		piece->ext = fa->linktarget;
398156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
399156230Smux		    (long long)vallen) + vallen + 1;
400156230Smux		piece++;
401156230Smux	}
402156230Smux	if (mask & FA_RDEV) {
403156230Smux		vallen = snprintf(piece->val, sizeof(piece->val),
404156230Smux		    "%lld", (long long)fa->rdev);
405156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
406156230Smux		    (long long)vallen) + vallen + 1;
407156230Smux		piece++;
408156230Smux	}
409156230Smux	if (mask & FA_OWNER) {
410156701Smux		vallen = strlen(username);
411156230Smux		piece->extval = 1;
412156701Smux		piece->ext = username;
413156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
414156230Smux		    (long long)vallen) + vallen + 1;
415156230Smux		piece++;
416156230Smux	}
417156230Smux	if (mask & FA_GROUP) {
418156701Smux		vallen = strlen(groupname);
419156230Smux		piece->extval = 1;
420156701Smux		piece->ext = groupname;
421156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
422156230Smux		    (long long)vallen) + vallen + 1;
423156230Smux		piece++;
424156230Smux	}
425156230Smux	if (mask & FA_MODE) {
426156230Smux		if (mask & FA_OWNER && mask & FA_GROUP)
427156230Smux			modemask = FA_SETIDMASK | FA_PERMMASK;
428156230Smux		else
429156230Smux			modemask = FA_PERMMASK;
430156230Smux		mode = fa->mode & modemask;
431156230Smux		vallen = snprintf(piece->val, sizeof(piece->val),
432156230Smux		    "%o", mode);
433156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
434156230Smux		    (long long)vallen) + vallen + 1;
435156230Smux		piece++;
436156230Smux	}
437156230Smux	if (mask & FA_FLAGS) {
438156230Smux		vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
439156230Smux		    (long long)fa->flags);
440156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
441156230Smux		    (long long)vallen) + vallen + 1;
442156230Smux		piece++;
443156230Smux	}
444156230Smux	if (mask & FA_LINKCOUNT) {
445156230Smux		vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
446156230Smux		    (long long)fa->linkcount);
447156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
448156230Smux		    (long long)vallen) + vallen + 1;
449156230Smux		piece++;
450156230Smux	}
451156230Smux	if (mask & FA_DEV) {
452186781Slulf		vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
453156230Smux		    (long long)fa->dev);
454156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
455156230Smux		    (long long)vallen) + vallen + 1;
456156230Smux		piece++;
457156230Smux	}
458156230Smux	if (mask & FA_INODE) {
459156230Smux		vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
460156230Smux		    (long long)fa->inode);
461156230Smux		len += snprintf(piece->len, sizeof(piece->len), "%lld",
462156230Smux		    (long long)vallen) + vallen + 1;
463156230Smux		piece++;
464156230Smux	}
465156230Smux
466156230Smux	s = xmalloc(len + 1);
467156230Smux
468156230Smux	n = piece - pieces;
469156230Smux	piece = pieces;
470156230Smux	cp = s;
471156230Smux	for (i = 0; i < n; i++) {
472156230Smux		if (piece->extval)
473156230Smux			len = sprintf(cp, "%s#%s", piece->len, piece->ext);
474156230Smux		else
475156230Smux			len = sprintf(cp, "%s#%s", piece->len, piece->val);
476156230Smux		cp += len;
477156230Smux	      	piece++;
478156230Smux	}
479156230Smux	return (s);
480156230Smux}
481156230Smux
482156230Smuxstruct fattr *
483156230Smuxfattr_dup(const struct fattr *from)
484156230Smux{
485156230Smux	struct fattr *fa;
486156230Smux
487156230Smux	fa = fattr_new(FT_UNKNOWN, -1);
488156230Smux	fattr_override(fa, from, FA_MASK);
489156230Smux	return (fa);
490156230Smux}
491156230Smux
492156230Smuxvoid
493156230Smuxfattr_free(struct fattr *fa)
494156230Smux{
495156230Smux
496156230Smux	if (fa == NULL)
497156230Smux		return;
498156230Smux	if (fa->linktarget != NULL)
499156230Smux		free(fa->linktarget);
500156230Smux	free(fa);
501156230Smux}
502156230Smux
503156230Smuxvoid
504156230Smuxfattr_umask(struct fattr *fa, mode_t newumask)
505156230Smux{
506156230Smux
507156230Smux	if (fa->mask & FA_MODE)
508156230Smux		fa->mode = fa->mode & ~newumask;
509156230Smux}
510156230Smux
511156230Smuxvoid
512156230Smuxfattr_maskout(struct fattr *fa, int mask)
513156230Smux{
514156230Smux
515156230Smux	/* Don't forget to free() the linktarget attribute if we remove it. */
516156230Smux	if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) {
517156230Smux		free(fa->linktarget);
518156230Smux		fa->linktarget = NULL;
519156230Smux	}
520156230Smux	fa->mask &= ~mask;
521156230Smux}
522156230Smux
523156230Smuxint
524156230Smuxfattr_getmask(const struct fattr *fa)
525156230Smux{
526156230Smux
527156230Smux	return (fa->mask);
528156230Smux}
529156230Smux
530156230Smuxnlink_t
531156230Smuxfattr_getlinkcount(const struct fattr *fa)
532156230Smux{
533156230Smux
534156230Smux	return (fa->linkcount);
535156230Smux}
536156230Smux
537186781Slulfchar *
538186781Slulffattr_getlinktarget(const struct fattr *fa)
539186781Slulf{
540186781Slulf
541186781Slulf	return (fa->linktarget);
542186781Slulf}
543186781Slulf
544156230Smux/*
545156230Smux * Eat the specified attribute and put it in the file attribute
546156230Smux * structure.  Returns NULL on error, or a pointer to the next
547156230Smux * attribute to parse.
548156230Smux *
549156230Smux * This would be much prettier if we had strntol() so that we're
550156230Smux * not forced to write '\0' to the string before calling strtol()
551156230Smux * and then put back the old value...
552156230Smux *
553156230Smux * We need to use (unsigned) long long types here because some
554156230Smux * of the opaque types we're parsing (off_t, time_t...) may need
555156230Smux * 64bits to fit.
556156230Smux */
557156230Smuxstatic char *
558156230Smuxfattr_scanattr(struct fattr *fa, int type, const char *attr)
559156230Smux{
560156230Smux	char *attrend, *attrstart, *end;
561156230Smux	size_t len;
562156230Smux	unsigned long attrlen;
563156701Smux	int error;
564156230Smux	mode_t modemask;
565156230Smux	char tmp;
566156230Smux
567156230Smux	if (attr == NULL)
568156230Smux		return (NULL);
569156230Smux	errno = 0;
570156230Smux	attrlen = strtoul(attr, &end, 10);
571156230Smux	if (errno || *end != '#')
572156230Smux		return (NULL);
573156230Smux	len = strlen(attr);
574156230Smux	attrstart = end + 1;
575156230Smux	attrend = attrstart + attrlen;
576156230Smux	tmp = *attrend;
577156230Smux	*attrend = '\0';
578156230Smux	switch (type) {
579156230Smux	/* Using FA_MASK here is a bit bogus semantically. */
580156230Smux	case FA_MASK:
581156230Smux		errno = 0;
582156230Smux		fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
583156230Smux		if (errno || end != attrend)
584156230Smux			goto bad;
585156230Smux		break;
586156230Smux	case FA_FILETYPE:
587156230Smux		errno = 0;
588156230Smux		fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
589156230Smux		if (errno || end != attrend)
590156230Smux			goto bad;
591156230Smux		break;
592156230Smux	case FA_MODTIME:
593156230Smux		errno = 0;
594156230Smux		fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
595156230Smux		if (errno || end != attrend)
596156230Smux			goto bad;
597156230Smux		break;
598156230Smux	case FA_SIZE:
599156230Smux		errno = 0;
600156230Smux		fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
601156230Smux		if (errno || end != attrend)
602156230Smux			goto bad;
603156230Smux		break;
604156230Smux	case FA_LINKTARGET:
605156230Smux		fa->linktarget = xstrdup(attrstart);
606156230Smux		break;
607156230Smux	case FA_RDEV:
608156230Smux		errno = 0;
609156230Smux		fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
610156230Smux		if (errno || end != attrend)
611156230Smux			goto bad;
612156230Smux		break;
613156230Smux	case FA_OWNER:
614156701Smux		error = getuidbyname(attrstart, &fa->uid);
615156701Smux		if (error)
616156230Smux			fa->mask &= ~FA_OWNER;
617156230Smux		break;
618156230Smux	case FA_GROUP:
619156701Smux		error = getgidbyname(attrstart, &fa->gid);
620156701Smux		if (error)
621156230Smux			fa->mask &= ~FA_GROUP;
622156230Smux		break;
623156230Smux	case FA_MODE:
624156230Smux		errno = 0;
625156230Smux		fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
626156230Smux		if (errno || end != attrend)
627156230Smux			goto bad;
628156230Smux		if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
629156230Smux			modemask = FA_SETIDMASK | FA_PERMMASK;
630156230Smux		else
631156230Smux			modemask = FA_PERMMASK;
632156230Smux		fa->mode &= modemask;
633156230Smux		break;
634156230Smux	case FA_FLAGS:
635156230Smux		errno = 0;
636156230Smux		fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
637156230Smux		if (errno || end != attrend)
638156230Smux			goto bad;
639156230Smux		break;
640156230Smux	case FA_LINKCOUNT:
641156230Smux		errno = 0;
642156230Smux		fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
643156230Smux		if (errno || end != attrend)
644156230Smux			goto bad;
645156230Smux		break;
646156230Smux	case FA_DEV:
647156230Smux		errno = 0;
648156230Smux		fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
649156230Smux		if (errno || end != attrend)
650156230Smux			goto bad;
651156230Smux		break;
652156230Smux	case FA_INODE:
653156230Smux		errno = 0;
654156230Smux		fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
655156230Smux		if (errno || end != attrend)
656156230Smux			goto bad;
657156230Smux		break;
658156230Smux	}
659156230Smux	*attrend = tmp;
660156230Smux	return (attrend);
661156230Smuxbad:
662156230Smux	*attrend = tmp;
663156230Smux	return (NULL);
664156230Smux}
665156230Smux
666156230Smux/* Return a file attribute structure built from the RCS file attributes. */
667156230Smuxstruct fattr *
668156230Smuxfattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
669156230Smux{
670156230Smux	struct fattr *fa;
671156230Smux
672156230Smux	fa = fattr_new(FT_FILE, -1);
673156230Smux	if (rcsattr->mask & FA_MODE) {
674156230Smux		if ((rcsattr->mode & 0111) > 0)
675156230Smux			fa->mode = 0777;
676156230Smux		else
677156230Smux			fa->mode = 0666;
678156230Smux		fa->mode &= ~mask;
679156230Smux		fa->mask |= FA_MODE;
680156230Smux	}
681156230Smux	return (fa);
682156230Smux}
683156230Smux
684156230Smux/* Merge attributes from "from" that aren't present in "fa". */
685156230Smuxvoid
686156230Smuxfattr_merge(struct fattr *fa, const struct fattr *from)
687156230Smux{
688156230Smux
689156230Smux	fattr_override(fa, from, from->mask & ~fa->mask);
690156230Smux}
691156230Smux
692156230Smux/* Merge default attributes. */
693156230Smuxvoid
694156230Smuxfattr_mergedefault(struct fattr *fa)
695156230Smux{
696156230Smux
697156230Smux	fattr_merge(fa, defaults[fa->type]);
698156230Smux}
699156230Smux
700156230Smux/* Override selected attributes of "fa" with values from "from". */
701156230Smuxvoid
702156230Smuxfattr_override(struct fattr *fa, const struct fattr *from, int mask)
703156230Smux{
704156230Smux
705156230Smux	mask &= from->mask;
706156230Smux	if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
707156230Smux		free(fa->linktarget);
708156230Smux	fa->mask |= mask;
709156230Smux	if (mask & FA_FILETYPE)
710156230Smux		fa->type = from->type;
711156230Smux	if (mask & FA_MODTIME)
712156230Smux		fa->modtime = from->modtime;
713156230Smux	if (mask & FA_SIZE)
714156230Smux		fa->size = from->size;
715156230Smux	if (mask & FA_LINKTARGET)
716156230Smux		fa->linktarget = xstrdup(from->linktarget);
717156230Smux	if (mask & FA_RDEV)
718156230Smux		fa->rdev = from->rdev;
719156230Smux	if (mask & FA_OWNER)
720156230Smux		fa->uid = from->uid;
721156230Smux	if (mask & FA_GROUP)
722156230Smux		fa->gid = from->gid;
723156230Smux	if (mask & FA_MODE)
724156230Smux		fa->mode = from->mode;
725156230Smux	if (mask & FA_FLAGS)
726156230Smux		fa->flags = from->flags;
727156230Smux	if (mask & FA_LINKCOUNT)
728156230Smux		fa->linkcount = from->linkcount;
729156230Smux	if (mask & FA_DEV)
730156230Smux		fa->dev = from->dev;
731156230Smux	if (mask & FA_INODE)
732156230Smux		fa->inode = from->inode;
733156230Smux}
734156230Smux
735156230Smux/* Create a node. */
736156230Smuxint
737156230Smuxfattr_makenode(const struct fattr *fa, const char *path)
738156230Smux{
739156230Smux	mode_t modemask, mode;
740156230Smux	int error;
741156230Smux
742186781Slulf	error = 0;
743186781Slulf
744156230Smux	if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
745156230Smux		modemask = FA_SETIDMASK | FA_PERMMASK;
746156230Smux	else
747156230Smux		modemask = FA_PERMMASK;
748156230Smux
749156230Smux	/* We only implement fattr_makenode() for dirs for now. */
750156230Smux	if (fa->mask & FA_MODE)
751156230Smux		mode = fa->mode & modemask;
752156230Smux	else
753156230Smux		mode = 0700;
754186781Slulf
755186781Slulf	if (fa->type == FT_DIRECTORY)
756186781Slulf		error = mkdir(path, mode);
757186781Slulf	else if (fa->type == FT_SYMLINK) {
758186781Slulf		error = symlink(fa->linktarget, path);
759186781Slulf	} else if (fa->type == FT_CDEV) {
760186781Slulf		lprintf(-1, "Character devices not supported!\n");
761186781Slulf	} else if (fa->type == FT_BDEV) {
762186781Slulf		lprintf(-1, "Block devices not supported!\n");
763186781Slulf	}
764156230Smux	return (error);
765156230Smux}
766156230Smux
767156230Smuxint
768156230Smuxfattr_delete(const char *path)
769156230Smux{
770156230Smux	struct fattr *fa;
771156230Smux	int error;
772156230Smux
773156230Smux	fa = fattr_frompath(path, FATTR_NOFOLLOW);
774156230Smux	if (fa == NULL) {
775156230Smux		if (errno == ENOENT)
776156230Smux			return (0);
777156230Smux		return (-1);
778156230Smux	}
779156230Smux
780156701Smux#ifdef HAVE_FFLAGS
781156230Smux	/* Clear flags. */
782156230Smux	if (fa->mask & FA_FLAGS && fa->flags != 0) {
783156230Smux		fa->flags = 0;
784156230Smux		(void)chflags(path, fa->flags);
785156230Smux	}
786156701Smux#endif
787156230Smux
788156230Smux	if (fa->type == FT_DIRECTORY)
789156230Smux		error = rmdir(path);
790156230Smux	else
791156230Smux		error = unlink(path);
792156230Smux	fattr_free(fa);
793156230Smux	return (error);
794156230Smux}
795156230Smux
796156230Smux/*
797156230Smux * Changes those attributes we can change.  Returns -1 on error,
798156230Smux * 0 if no update was needed, and 1 if an update was needed and
799156230Smux * it has been applied successfully.
800156230Smux */
801156230Smuxint
802156230Smuxfattr_install(struct fattr *fa, const char *topath, const char *frompath)
803156230Smux{
804156230Smux	struct timeval tv[2];
805156230Smux	struct fattr *old;
806156230Smux	int error, inplace, mask;
807156230Smux	mode_t modemask, newmode;
808156230Smux	uid_t uid;
809156230Smux	gid_t gid;
810156230Smux
811156230Smux	mask = fa->mask & fattr_supported(fa->type);
812156230Smux	if (mask & FA_OWNER && mask & FA_GROUP)
813156230Smux		modemask = FA_SETIDMASK | FA_PERMMASK;
814156230Smux	else
815156230Smux		modemask = FA_PERMMASK;
816156230Smux
817156230Smux	inplace = 0;
818156230Smux	if (frompath == NULL) {
819156230Smux		/* Changing attributes in place. */
820156230Smux		frompath = topath;
821156230Smux		inplace = 1;
822156230Smux	}
823156230Smux	old = fattr_frompath(topath, FATTR_NOFOLLOW);
824156701Smux	if (old != NULL) {
825156701Smux		if (inplace && fattr_equal(fa, old)) {
826156701Smux			fattr_free(old);
827156701Smux			return (0);
828156701Smux		}
829156230Smux
830156230Smux#ifdef HAVE_FFLAGS
831156701Smux		/*
832156701Smux		 * Determine whether we need to clear the flags of the target.
833156701Smux		 * This is bogus in that it assumes a value of 0 is safe and
834156701Smux		 * that non-zero is unsafe.  I'm not really worried by that
835156701Smux		 * since as far as I know that's the way things are.
836156701Smux		 */
837156701Smux		if ((old->mask & FA_FLAGS) && old->flags > 0) {
838156701Smux			(void)chflags(topath, 0);
839156701Smux			old->flags = 0;
840156701Smux		}
841156230Smux#endif
842156230Smux
843186781Slulf		/*
844186781Slulf		 * If it is changed from a file to a symlink, remove the file
845186781Slulf		 * and create the symlink.
846186781Slulf		 */
847186781Slulf		if (inplace && (fa->type == FT_SYMLINK) &&
848186781Slulf		    (old->type == FT_FILE)) {
849186781Slulf			error = unlink(topath);
850186781Slulf			if (error)
851186781Slulf				goto bad;
852186781Slulf			error = symlink(fa->linktarget, topath);
853186781Slulf			if (error)
854186781Slulf				goto bad;
855186781Slulf		}
856156701Smux		/* Determine whether we need to remove the target first. */
857156701Smux		if (!inplace && (fa->type == FT_DIRECTORY) !=
858156701Smux		    (old->type == FT_DIRECTORY)) {
859156701Smux			if (old->type == FT_DIRECTORY)
860156701Smux				error = rmdir(topath);
861156701Smux			else
862156701Smux				error = unlink(topath);
863156701Smux			if (error)
864156701Smux				goto bad;
865156701Smux		}
866156230Smux	}
867156230Smux
868156230Smux	/* Change those attributes that we can before moving the file
869156230Smux	 * into place.  That makes installation atomic in most cases. */
870156230Smux	if (mask & FA_MODTIME) {
871156230Smux		gettimeofday(tv, NULL);		/* Access time. */
872156230Smux		tv[1].tv_sec = fa->modtime;	/* Modification time. */
873156230Smux		tv[1].tv_usec = 0;
874156230Smux		error = utimes(frompath, tv);
875156230Smux		if (error)
876156230Smux			goto bad;
877156230Smux	}
878156230Smux	if (mask & FA_OWNER || mask & FA_GROUP) {
879156230Smux		uid = -1;
880156230Smux		gid = -1;
881156230Smux		if (mask & FA_OWNER)
882156230Smux			uid = fa->uid;
883156230Smux		if (mask & FA_GROUP)
884156230Smux			gid = fa->gid;
885156230Smux		error = chown(frompath, uid, gid);
886186781Slulf		if (error) {
887156230Smux			goto bad;
888186781Slulf		}
889156230Smux	}
890156230Smux	if (mask & FA_MODE) {
891156230Smux		newmode = fa->mode & modemask;
892156701Smux		/* Merge in set*id bits from the old attribute. */
893156701Smux		if (old != NULL && old->mask & FA_MODE) {
894156230Smux			newmode |= (old->mode & ~modemask);
895156230Smux			newmode &= (FA_SETIDMASK | FA_PERMMASK);
896156230Smux		}
897156230Smux		error = chmod(frompath, newmode);
898156230Smux		if (error)
899156230Smux			goto bad;
900156230Smux	}
901156230Smux
902156230Smux	if (!inplace) {
903156230Smux		error = rename(frompath, topath);
904156230Smux		if (error)
905156230Smux			goto bad;
906156230Smux	}
907156230Smux
908156230Smux#ifdef HAVE_FFLAGS
909156230Smux	/* Set the flags. */
910156230Smux	if (mask & FA_FLAGS)
911156230Smux		(void)chflags(topath, fa->flags);
912156230Smux#endif
913156230Smux	fattr_free(old);
914156230Smux	return (1);
915156230Smuxbad:
916156230Smux	fattr_free(old);
917156230Smux	return (-1);
918156230Smux}
919156230Smux
920156230Smux/*
921156230Smux * Returns 1 if both attributes are equal, 0 otherwise.
922156230Smux *
923156230Smux * This function only compares attributes that are valid in both
924156230Smux * files.  A file of unknown type ("FT_UNKNOWN") is unequal to
925156230Smux * anything, including itself.
926156230Smux */
927156230Smuxint
928156230Smuxfattr_equal(const struct fattr *fa1, const struct fattr *fa2)
929156230Smux{
930156230Smux	int mask;
931156230Smux
932156230Smux	mask = fa1->mask & fa2->mask;
933156230Smux	if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
934156230Smux		return (0);
935186781Slulf	if (mask & FA_FILETYPE)
936186781Slulf		if (fa1->type != fa2->type)
937186781Slulf			return (0);
938156230Smux	if (mask & FA_MODTIME)
939156230Smux		if (fa1->modtime != fa2->modtime)
940156230Smux			return (0);
941156230Smux	if (mask & FA_SIZE)
942156230Smux		if (fa1->size != fa2->size)
943156230Smux			return (0);
944156230Smux	if (mask & FA_LINKTARGET)
945156230Smux		if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
946156230Smux			return (0);
947156230Smux	if (mask & FA_RDEV)
948156230Smux		if (fa1->rdev != fa2->rdev)
949156230Smux			return (0);
950156230Smux	if (mask & FA_OWNER)
951156230Smux		if (fa1->uid != fa2->uid)
952156230Smux			return (0);
953156230Smux	if (mask & FA_GROUP)
954156230Smux		if (fa1->gid != fa2->gid)
955156230Smux			return (0);
956156230Smux	if (mask & FA_MODE)
957156230Smux		if (fa1->mode != fa2->mode)
958156230Smux			return (0);
959156230Smux	if (mask & FA_FLAGS)
960156230Smux		if (fa1->flags != fa2->flags)
961156230Smux			return (0);
962156230Smux	if (mask & FA_LINKCOUNT)
963156230Smux		if (fa1->linkcount != fa2->linkcount)
964156230Smux			return (0);
965156230Smux	if (mask & FA_DEV)
966156230Smux		if (fa1->dev != fa2->dev)
967156230Smux			return (0);
968156230Smux	if (mask & FA_INODE)
969156230Smux		if (fa1->inode != fa2->inode)
970156230Smux			return (0);
971156230Smux	return (1);
972156230Smux}
973186781Slulf
974186781Slulf/*
975186781Slulf * Must have to get the correct filesize sendt by the server.
976186781Slulf */
977186781Slulfoff_t
978186781Slulffattr_filesize(const struct fattr *fa)
979186781Slulf{
980186781Slulf	return (fa->size);
981186781Slulf}
982