diff options
Diffstat (limited to 'usr.bin/csup/fattr.c')
-rw-r--r-- | usr.bin/csup/fattr.c | 981 |
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); +} |