1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-25 11:37:56 +00:00
freebsd/usr.bin/csup/fattr.c
Ulf Lilleengen 4b6675a6f8 - Move csup away from contrib/ and into usr.bin/. Software is no longer
contributed, and main development is happening in the FreeBSD repo.

Suggested by:	joel
2010-03-02 07:26:07 +00:00

982 lines
22 KiB
C

/*-
* 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);
}