summaryrefslogtreecommitdiffstats
path: root/usr.bin/csup/fattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/csup/fattr.c')
-rw-r--r--usr.bin/csup/fattr.c981
1 files changed, 981 insertions, 0 deletions
diff --git a/usr.bin/csup/fattr.c b/usr.bin/csup/fattr.c
new file mode 100644
index 0000000..b141c2c
--- /dev/null
+++ b/usr.bin/csup/fattr.c
@@ -0,0 +1,981 @@
+/*-
+ * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fattr.h"
+#include "idcache.h"
+#include "misc.h"
+
+/*
+ * Include the appropriate definition for the file attributes we support.
+ * There are two different files: fattr_bsd.h for BSD-like systems that
+ * support the extended file flags a la chflags() and fattr_posix.h for
+ * bare POSIX systems that don't.
+ */
+#ifdef HAVE_FFLAGS
+#include "fattr_bsd.h"
+#else
+#include "fattr_posix.h"
+#endif
+
+#ifdef __FreeBSD__
+#include <osreldate.h>
+#endif
+
+/* Define fflags_t if we're on a system that doesn't have it. */
+#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030
+typedef uint32_t fflags_t;
+#endif
+
+#define FA_MASKRADIX 16
+#define FA_FILETYPERADIX 10
+#define FA_MODTIMERADIX 10
+#define FA_SIZERADIX 10
+#define FA_RDEVRADIX 16
+#define FA_MODERADIX 8
+#define FA_FLAGSRADIX 16
+#define FA_LINKCOUNTRADIX 10
+#define FA_DEVRADIX 16
+#define FA_INODERADIX 10
+
+#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO)
+#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX)
+
+struct fattr {
+ int mask;
+ int type;
+ time_t modtime;
+ off_t size;
+ char *linktarget;
+ dev_t rdev;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ fflags_t flags;
+ nlink_t linkcount;
+ dev_t dev;
+ ino_t inode;
+};
+
+static const struct fattr bogus = {
+ FA_MODTIME | FA_SIZE | FA_MODE,
+ FT_UNKNOWN,
+ 1,
+ 0,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+static struct fattr *defaults[FT_NUMBER];
+
+void
+fattr_init(void)
+{
+ struct fattr *fa;
+ int i;
+
+ for (i = 0; i < FT_NUMBER; i++) {
+ fa = fattr_new(i, -1);
+ if (i == FT_DIRECTORY)
+ fa->mode = 0777;
+ else
+ fa->mode = 0666;
+ fa->mask |= FA_MODE;
+ defaults[i] = fa;
+ }
+ /* Initialize the uid/gid lookup cache. */
+ idcache_init();
+}
+
+void
+fattr_fini(void)
+{
+ int i;
+
+ idcache_fini();
+ for (i = 0; i < FT_NUMBER; i++)
+ fattr_free(defaults[i]);
+}
+
+const struct fattr *fattr_bogus = &bogus;
+
+static char *fattr_scanattr(struct fattr *, int, const char *);
+
+int
+fattr_supported(int type)
+{
+
+ return (fattr_support[type]);
+}
+
+struct fattr *
+fattr_new(int type, time_t modtime)
+{
+ struct fattr *new;
+
+ new = xmalloc(sizeof(struct fattr));
+ memset(new, 0, sizeof(struct fattr));
+ new->type = type;
+ if (type != FT_UNKNOWN)
+ new->mask |= FA_FILETYPE;
+ if (modtime != -1) {
+ new->modtime = modtime;
+ new->mask |= FA_MODTIME;
+ }
+ if (fattr_supported(new->type) & FA_LINKCOUNT) {
+ new->mask |= FA_LINKCOUNT;
+ new->linkcount = 1;
+ }
+ return (new);
+}
+
+/* Returns a new file attribute structure based on a stat structure. */
+struct fattr *
+fattr_fromstat(struct stat *sb)
+{
+ struct fattr *fa;
+
+ fa = fattr_new(FT_UNKNOWN, -1);
+ if (S_ISREG(sb->st_mode))
+ fa->type = FT_FILE;
+ else if (S_ISDIR(sb->st_mode))
+ fa->type = FT_DIRECTORY;
+ else if (S_ISCHR(sb->st_mode))
+ fa->type = FT_CDEV;
+ else if (S_ISBLK(sb->st_mode))
+ fa->type = FT_BDEV;
+ else if (S_ISLNK(sb->st_mode))
+ fa->type = FT_SYMLINK;
+ else
+ fa->type = FT_UNKNOWN;
+
+ fa->mask = FA_FILETYPE | fattr_supported(fa->type);
+ if (fa->mask & FA_MODTIME)
+ fa->modtime = sb->st_mtime;
+ if (fa->mask & FA_SIZE)
+ fa->size = sb->st_size;
+ if (fa->mask & FA_RDEV)
+ fa->rdev = sb->st_rdev;
+ if (fa->mask & FA_OWNER)
+ fa->uid = sb->st_uid;
+ if (fa->mask & FA_GROUP)
+ fa->gid = sb->st_gid;
+ if (fa->mask & FA_MODE)
+ fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK);
+#ifdef HAVE_FFLAGS
+ if (fa->mask & FA_FLAGS)
+ fa->flags = sb->st_flags;
+#endif
+ if (fa->mask & FA_LINKCOUNT)
+ fa->linkcount = sb->st_nlink;
+ if (fa->mask & FA_DEV)
+ fa->dev = sb->st_dev;
+ if (fa->mask & FA_INODE)
+ fa->inode = sb->st_ino;
+ return (fa);
+}
+
+struct fattr *
+fattr_frompath(const char *path, int nofollow)
+{
+ struct fattr *fa;
+ struct stat sb;
+ int error, len;
+
+ if (nofollow)
+ error = lstat(path, &sb);
+ else
+ error = stat(path, &sb);
+ if (error)
+ return (NULL);
+ fa = fattr_fromstat(&sb);
+ if (fa->mask & FA_LINKTARGET) {
+ char buf[1024];
+
+ len = readlink(path, buf, sizeof(buf));
+ if (len == -1) {
+ fattr_free(fa);
+ return (NULL);
+ }
+ if ((unsigned)len > sizeof(buf) - 1) {
+ fattr_free(fa);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ buf[len] = '\0';
+ fa->linktarget = xstrdup(buf);
+ }
+ return (fa);
+}
+
+struct fattr *
+fattr_fromfd(int fd)
+{
+ struct fattr *fa;
+ struct stat sb;
+ int error;
+
+ error = fstat(fd, &sb);
+ if (error)
+ return (NULL);
+ fa = fattr_fromstat(&sb);
+ return (fa);
+}
+
+int
+fattr_type(const struct fattr *fa)
+{
+
+ return (fa->type);
+}
+
+/* Returns a new file attribute structure from its encoded text form. */
+struct fattr *
+fattr_decode(char *attr)
+{
+ struct fattr *fa;
+ char *next;
+
+ fa = fattr_new(FT_UNKNOWN, -1);
+ next = fattr_scanattr(fa, FA_MASK, attr);
+ if (next == NULL || (fa->mask & ~FA_MASK) > 0)
+ goto bad;
+ if (fa->mask & FA_FILETYPE) {
+ next = fattr_scanattr(fa, FA_FILETYPE, next);
+ if (next == NULL)
+ goto bad;
+ if (fa->type < 0 || fa->type > FT_MAX)
+ fa->type = FT_UNKNOWN;
+ } else {
+ /* The filetype attribute is always valid. */
+ fa->mask |= FA_FILETYPE;
+ fa->type = FT_UNKNOWN;
+ }
+ fa->mask = fa->mask & fattr_supported(fa->type);
+ if (fa->mask & FA_MODTIME)
+ next = fattr_scanattr(fa, FA_MODTIME, next);
+ if (fa->mask & FA_SIZE)
+ next = fattr_scanattr(fa, FA_SIZE, next);
+ if (fa->mask & FA_LINKTARGET)
+ next = fattr_scanattr(fa, FA_LINKTARGET, next);
+ if (fa->mask & FA_RDEV)
+ next = fattr_scanattr(fa, FA_RDEV, next);
+ if (fa->mask & FA_OWNER)
+ next = fattr_scanattr(fa, FA_OWNER, next);
+ if (fa->mask & FA_GROUP)
+ next = fattr_scanattr(fa, FA_GROUP, next);
+ if (fa->mask & FA_MODE)
+ next = fattr_scanattr(fa, FA_MODE, next);
+ if (fa->mask & FA_FLAGS)
+ next = fattr_scanattr(fa, FA_FLAGS, next);
+ if (fa->mask & FA_LINKCOUNT) {
+ next = fattr_scanattr(fa, FA_LINKCOUNT, next);
+ } else if (fattr_supported(fa->type) & FA_LINKCOUNT) {
+ /* If the link count is missing but supported, fake it as 1. */
+ fa->mask |= FA_LINKCOUNT;
+ fa->linkcount = 1;
+ }
+ if (fa->mask & FA_DEV)
+ next = fattr_scanattr(fa, FA_DEV, next);
+ if (fa->mask & FA_INODE)
+ next = fattr_scanattr(fa, FA_INODE, next);
+ if (next == NULL)
+ goto bad;
+ return (fa);
+bad:
+ fattr_free(fa);
+ return (NULL);
+}
+
+char *
+fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
+{
+ struct {
+ char val[32];
+ char len[4];
+ int extval;
+ char *ext;
+ } pieces[FA_NUMBER], *piece;
+ char *cp, *s, *username, *groupname;
+ size_t len, vallen;
+ mode_t mode, modemask;
+ int mask, n, i;
+
+ username = NULL;
+ groupname = NULL;
+ if (support == NULL)
+ mask = fa->mask;
+ else
+ mask = fa->mask & support[fa->type];
+ mask &= ~ignore;
+ if (fa->mask & FA_OWNER) {
+ username = getuserbyid(fa->uid);
+ if (username == NULL)
+ mask &= ~FA_OWNER;
+ }
+ if (fa->mask & FA_GROUP) {
+ groupname = getgroupbyid(fa->gid);
+ if (groupname == NULL)
+ mask &= ~FA_GROUP;
+ }
+ if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
+ mask &= ~FA_LINKCOUNT;
+
+ memset(pieces, 0, FA_NUMBER * sizeof(*pieces));
+ len = 0;
+ piece = pieces;
+ vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ if (mask & FA_FILETYPE) {
+ vallen = snprintf(piece->val, sizeof(piece->val),
+ "%d", fa->type);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_MODTIME) {
+ vallen = snprintf(piece->val, sizeof(piece->val),
+ "%lld", (long long)fa->modtime);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_SIZE) {
+ vallen = snprintf(piece->val, sizeof(piece->val),
+ "%lld", (long long)fa->size);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_LINKTARGET) {
+ vallen = strlen(fa->linktarget);
+ piece->extval = 1;
+ piece->ext = fa->linktarget;
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_RDEV) {
+ vallen = snprintf(piece->val, sizeof(piece->val),
+ "%lld", (long long)fa->rdev);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_OWNER) {
+ vallen = strlen(username);
+ piece->extval = 1;
+ piece->ext = username;
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_GROUP) {
+ vallen = strlen(groupname);
+ piece->extval = 1;
+ piece->ext = groupname;
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_MODE) {
+ if (mask & FA_OWNER && mask & FA_GROUP)
+ modemask = FA_SETIDMASK | FA_PERMMASK;
+ else
+ modemask = FA_PERMMASK;
+ mode = fa->mode & modemask;
+ vallen = snprintf(piece->val, sizeof(piece->val),
+ "%o", mode);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_FLAGS) {
+ vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
+ (long long)fa->flags);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_LINKCOUNT) {
+ vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
+ (long long)fa->linkcount);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_DEV) {
+ vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
+ (long long)fa->dev);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+ if (mask & FA_INODE) {
+ vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
+ (long long)fa->inode);
+ len += snprintf(piece->len, sizeof(piece->len), "%lld",
+ (long long)vallen) + vallen + 1;
+ piece++;
+ }
+
+ s = xmalloc(len + 1);
+
+ n = piece - pieces;
+ piece = pieces;
+ cp = s;
+ for (i = 0; i < n; i++) {
+ if (piece->extval)
+ len = sprintf(cp, "%s#%s", piece->len, piece->ext);
+ else
+ len = sprintf(cp, "%s#%s", piece->len, piece->val);
+ cp += len;
+ piece++;
+ }
+ return (s);
+}
+
+struct fattr *
+fattr_dup(const struct fattr *from)
+{
+ struct fattr *fa;
+
+ fa = fattr_new(FT_UNKNOWN, -1);
+ fattr_override(fa, from, FA_MASK);
+ return (fa);
+}
+
+void
+fattr_free(struct fattr *fa)
+{
+
+ if (fa == NULL)
+ return;
+ if (fa->linktarget != NULL)
+ free(fa->linktarget);
+ free(fa);
+}
+
+void
+fattr_umask(struct fattr *fa, mode_t newumask)
+{
+
+ if (fa->mask & FA_MODE)
+ fa->mode = fa->mode & ~newumask;
+}
+
+void
+fattr_maskout(struct fattr *fa, int mask)
+{
+
+ /* Don't forget to free() the linktarget attribute if we remove it. */
+ if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) {
+ free(fa->linktarget);
+ fa->linktarget = NULL;
+ }
+ fa->mask &= ~mask;
+}
+
+int
+fattr_getmask(const struct fattr *fa)
+{
+
+ return (fa->mask);
+}
+
+nlink_t
+fattr_getlinkcount(const struct fattr *fa)
+{
+
+ return (fa->linkcount);
+}
+
+char *
+fattr_getlinktarget(const struct fattr *fa)
+{
+
+ return (fa->linktarget);
+}
+
+/*
+ * Eat the specified attribute and put it in the file attribute
+ * structure. Returns NULL on error, or a pointer to the next
+ * attribute to parse.
+ *
+ * This would be much prettier if we had strntol() so that we're
+ * not forced to write '\0' to the string before calling strtol()
+ * and then put back the old value...
+ *
+ * We need to use (unsigned) long long types here because some
+ * of the opaque types we're parsing (off_t, time_t...) may need
+ * 64bits to fit.
+ */
+static char *
+fattr_scanattr(struct fattr *fa, int type, const char *attr)
+{
+ char *attrend, *attrstart, *end;
+ size_t len;
+ unsigned long attrlen;
+ int error;
+ mode_t modemask;
+ char tmp;
+
+ if (attr == NULL)
+ return (NULL);
+ errno = 0;
+ attrlen = strtoul(attr, &end, 10);
+ if (errno || *end != '#')
+ return (NULL);
+ len = strlen(attr);
+ attrstart = end + 1;
+ attrend = attrstart + attrlen;
+ tmp = *attrend;
+ *attrend = '\0';
+ switch (type) {
+ /* Using FA_MASK here is a bit bogus semantically. */
+ case FA_MASK:
+ errno = 0;
+ fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_FILETYPE:
+ errno = 0;
+ fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_MODTIME:
+ errno = 0;
+ fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_SIZE:
+ errno = 0;
+ fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_LINKTARGET:
+ fa->linktarget = xstrdup(attrstart);
+ break;
+ case FA_RDEV:
+ errno = 0;
+ fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_OWNER:
+ error = getuidbyname(attrstart, &fa->uid);
+ if (error)
+ fa->mask &= ~FA_OWNER;
+ break;
+ case FA_GROUP:
+ error = getgidbyname(attrstart, &fa->gid);
+ if (error)
+ fa->mask &= ~FA_GROUP;
+ break;
+ case FA_MODE:
+ errno = 0;
+ fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
+ if (errno || end != attrend)
+ goto bad;
+ if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
+ modemask = FA_SETIDMASK | FA_PERMMASK;
+ else
+ modemask = FA_PERMMASK;
+ fa->mode &= modemask;
+ break;
+ case FA_FLAGS:
+ errno = 0;
+ fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_LINKCOUNT:
+ errno = 0;
+ fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_DEV:
+ errno = 0;
+ fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ case FA_INODE:
+ errno = 0;
+ fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
+ if (errno || end != attrend)
+ goto bad;
+ break;
+ }
+ *attrend = tmp;
+ return (attrend);
+bad:
+ *attrend = tmp;
+ return (NULL);
+}
+
+/* Return a file attribute structure built from the RCS file attributes. */
+struct fattr *
+fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
+{
+ struct fattr *fa;
+
+ fa = fattr_new(FT_FILE, -1);
+ if (rcsattr->mask & FA_MODE) {
+ if ((rcsattr->mode & 0111) > 0)
+ fa->mode = 0777;
+ else
+ fa->mode = 0666;
+ fa->mode &= ~mask;
+ fa->mask |= FA_MODE;
+ }
+ return (fa);
+}
+
+/* Merge attributes from "from" that aren't present in "fa". */
+void
+fattr_merge(struct fattr *fa, const struct fattr *from)
+{
+
+ fattr_override(fa, from, from->mask & ~fa->mask);
+}
+
+/* Merge default attributes. */
+void
+fattr_mergedefault(struct fattr *fa)
+{
+
+ fattr_merge(fa, defaults[fa->type]);
+}
+
+/* Override selected attributes of "fa" with values from "from". */
+void
+fattr_override(struct fattr *fa, const struct fattr *from, int mask)
+{
+
+ mask &= from->mask;
+ if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
+ free(fa->linktarget);
+ fa->mask |= mask;
+ if (mask & FA_FILETYPE)
+ fa->type = from->type;
+ if (mask & FA_MODTIME)
+ fa->modtime = from->modtime;
+ if (mask & FA_SIZE)
+ fa->size = from->size;
+ if (mask & FA_LINKTARGET)
+ fa->linktarget = xstrdup(from->linktarget);
+ if (mask & FA_RDEV)
+ fa->rdev = from->rdev;
+ if (mask & FA_OWNER)
+ fa->uid = from->uid;
+ if (mask & FA_GROUP)
+ fa->gid = from->gid;
+ if (mask & FA_MODE)
+ fa->mode = from->mode;
+ if (mask & FA_FLAGS)
+ fa->flags = from->flags;
+ if (mask & FA_LINKCOUNT)
+ fa->linkcount = from->linkcount;
+ if (mask & FA_DEV)
+ fa->dev = from->dev;
+ if (mask & FA_INODE)
+ fa->inode = from->inode;
+}
+
+/* Create a node. */
+int
+fattr_makenode(const struct fattr *fa, const char *path)
+{
+ mode_t modemask, mode;
+ int error;
+
+ error = 0;
+
+ if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
+ modemask = FA_SETIDMASK | FA_PERMMASK;
+ else
+ modemask = FA_PERMMASK;
+
+ /* We only implement fattr_makenode() for dirs for now. */
+ if (fa->mask & FA_MODE)
+ mode = fa->mode & modemask;
+ else
+ mode = 0700;
+
+ if (fa->type == FT_DIRECTORY)
+ error = mkdir(path, mode);
+ else if (fa->type == FT_SYMLINK) {
+ error = symlink(fa->linktarget, path);
+ } else if (fa->type == FT_CDEV) {
+ lprintf(-1, "Character devices not supported!\n");
+ } else if (fa->type == FT_BDEV) {
+ lprintf(-1, "Block devices not supported!\n");
+ }
+ return (error);
+}
+
+int
+fattr_delete(const char *path)
+{
+ struct fattr *fa;
+ int error;
+
+ fa = fattr_frompath(path, FATTR_NOFOLLOW);
+ if (fa == NULL) {
+ if (errno == ENOENT)
+ return (0);
+ return (-1);
+ }
+
+#ifdef HAVE_FFLAGS
+ /* Clear flags. */
+ if (fa->mask & FA_FLAGS && fa->flags != 0) {
+ fa->flags = 0;
+ (void)chflags(path, fa->flags);
+ }
+#endif
+
+ if (fa->type == FT_DIRECTORY)
+ error = rmdir(path);
+ else
+ error = unlink(path);
+ fattr_free(fa);
+ return (error);
+}
+
+/*
+ * Changes those attributes we can change. Returns -1 on error,
+ * 0 if no update was needed, and 1 if an update was needed and
+ * it has been applied successfully.
+ */
+int
+fattr_install(struct fattr *fa, const char *topath, const char *frompath)
+{
+ struct timeval tv[2];
+ struct fattr *old;
+ int error, inplace, mask;
+ mode_t modemask, newmode;
+ uid_t uid;
+ gid_t gid;
+
+ mask = fa->mask & fattr_supported(fa->type);
+ if (mask & FA_OWNER && mask & FA_GROUP)
+ modemask = FA_SETIDMASK | FA_PERMMASK;
+ else
+ modemask = FA_PERMMASK;
+
+ inplace = 0;
+ if (frompath == NULL) {
+ /* Changing attributes in place. */
+ frompath = topath;
+ inplace = 1;
+ }
+ old = fattr_frompath(topath, FATTR_NOFOLLOW);
+ if (old != NULL) {
+ if (inplace && fattr_equal(fa, old)) {
+ fattr_free(old);
+ return (0);
+ }
+
+#ifdef HAVE_FFLAGS
+ /*
+ * Determine whether we need to clear the flags of the target.
+ * This is bogus in that it assumes a value of 0 is safe and
+ * that non-zero is unsafe. I'm not really worried by that
+ * since as far as I know that's the way things are.
+ */
+ if ((old->mask & FA_FLAGS) && old->flags > 0) {
+ (void)chflags(topath, 0);
+ old->flags = 0;
+ }
+#endif
+
+ /*
+ * If it is changed from a file to a symlink, remove the file
+ * and create the symlink.
+ */
+ if (inplace && (fa->type == FT_SYMLINK) &&
+ (old->type == FT_FILE)) {
+ error = unlink(topath);
+ if (error)
+ goto bad;
+ error = symlink(fa->linktarget, topath);
+ if (error)
+ goto bad;
+ }
+ /* Determine whether we need to remove the target first. */
+ if (!inplace && (fa->type == FT_DIRECTORY) !=
+ (old->type == FT_DIRECTORY)) {
+ if (old->type == FT_DIRECTORY)
+ error = rmdir(topath);
+ else
+ error = unlink(topath);
+ if (error)
+ goto bad;
+ }
+ }
+
+ /* Change those attributes that we can before moving the file
+ * into place. That makes installation atomic in most cases. */
+ if (mask & FA_MODTIME) {
+ gettimeofday(tv, NULL); /* Access time. */
+ tv[1].tv_sec = fa->modtime; /* Modification time. */
+ tv[1].tv_usec = 0;
+ error = utimes(frompath, tv);
+ if (error)
+ goto bad;
+ }
+ if (mask & FA_OWNER || mask & FA_GROUP) {
+ uid = -1;
+ gid = -1;
+ if (mask & FA_OWNER)
+ uid = fa->uid;
+ if (mask & FA_GROUP)
+ gid = fa->gid;
+ error = chown(frompath, uid, gid);
+ if (error) {
+ goto bad;
+ }
+ }
+ if (mask & FA_MODE) {
+ newmode = fa->mode & modemask;
+ /* Merge in set*id bits from the old attribute. */
+ if (old != NULL && old->mask & FA_MODE) {
+ newmode |= (old->mode & ~modemask);
+ newmode &= (FA_SETIDMASK | FA_PERMMASK);
+ }
+ error = chmod(frompath, newmode);
+ if (error)
+ goto bad;
+ }
+
+ if (!inplace) {
+ error = rename(frompath, topath);
+ if (error)
+ goto bad;
+ }
+
+#ifdef HAVE_FFLAGS
+ /* Set the flags. */
+ if (mask & FA_FLAGS)
+ (void)chflags(topath, fa->flags);
+#endif
+ fattr_free(old);
+ return (1);
+bad:
+ fattr_free(old);
+ return (-1);
+}
+
+/*
+ * Returns 1 if both attributes are equal, 0 otherwise.
+ *
+ * This function only compares attributes that are valid in both
+ * files. A file of unknown type ("FT_UNKNOWN") is unequal to
+ * anything, including itself.
+ */
+int
+fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
+{
+ int mask;
+
+ mask = fa1->mask & fa2->mask;
+ if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
+ return (0);
+ if (mask & FA_FILETYPE)
+ if (fa1->type != fa2->type)
+ return (0);
+ if (mask & FA_MODTIME)
+ if (fa1->modtime != fa2->modtime)
+ return (0);
+ if (mask & FA_SIZE)
+ if (fa1->size != fa2->size)
+ return (0);
+ if (mask & FA_LINKTARGET)
+ if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
+ return (0);
+ if (mask & FA_RDEV)
+ if (fa1->rdev != fa2->rdev)
+ return (0);
+ if (mask & FA_OWNER)
+ if (fa1->uid != fa2->uid)
+ return (0);
+ if (mask & FA_GROUP)
+ if (fa1->gid != fa2->gid)
+ return (0);
+ if (mask & FA_MODE)
+ if (fa1->mode != fa2->mode)
+ return (0);
+ if (mask & FA_FLAGS)
+ if (fa1->flags != fa2->flags)
+ return (0);
+ if (mask & FA_LINKCOUNT)
+ if (fa1->linkcount != fa2->linkcount)
+ return (0);
+ if (mask & FA_DEV)
+ if (fa1->dev != fa2->dev)
+ return (0);
+ if (mask & FA_INODE)
+ if (fa1->inode != fa2->inode)
+ return (0);
+ return (1);
+}
+
+/*
+ * Must have to get the correct filesize sendt by the server.
+ */
+off_t
+fattr_filesize(const struct fattr *fa)
+{
+ return (fa->size);
+}
OpenPOWER on IntegriCloud