Import the latest snapshot of csup (20060313).

This commit is contained in:
Maxime Henrion 2006-03-14 03:51:13 +00:00
parent 91be0d8ce3
commit 7d6ea92e92
18 changed files with 778 additions and 238 deletions

View File

@ -12,8 +12,8 @@ GROUP?= 0
UNAME= $(shell uname -s)
SRCS= attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
globtree.c keyword.c lister.c main.c misc.c mux.c pathcomp.c parse.c \
proto.c status.c stream.c threads.c token.c updater.c
globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c pathcomp.c \
parse.c proto.c status.c stream.c threads.c token.c updater.c
OBJS= $(SRCS:.c=.o)
WARNS= -Wall -W -Wno-unused-parameter -Wmissing-prototypes -Wpointer-arith \
@ -22,10 +22,10 @@ WARNS= -Wall -W -Wno-unused-parameter -Wmissing-prototypes -Wpointer-arith \
-Wnested-externs -Wredundant-decls -Wno-format-y2k
CFLAGS+= -g -O -pipe -DNDEBUG -I$(PREFIX)/include
ifeq ($(UNAME), "Linux")
ifeq ($(UNAME), Linux)
CFLAGS+= -D_XOPEN_SOURCE -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
endif
ifeq ($(UNAME), "Darwin")
ifeq ($(UNAME), Darwin)
CFLAGS+= -DHAVE_FFLAGS
endif
CFLAGS+= $(WARNS)

View File

@ -7,27 +7,9 @@ MANDIR?= ${PREFIX}/man/man
UNAME!= /usr/bin/uname -s
PROG= csup
SRCS= attrstack.c attrstack.h \
config.c config.h \
detailer.c detailer.h \
diff.c diff.h \
fattr.c fattr.h fattr_bsd.h \
fixups.c fixups.h \
fnmatch.c fnmatch.h \
globtree.c globtree.h \
keyword.c keyword.h \
lister.c lister.h \
main.c main.h \
misc.c misc.h \
mux.c mux.h \
parse.h parse.y \
pathcomp.c pathcomp.h \
proto.c proto.h \
status.c status.h \
stream.c stream.h \
threads.c threads.h \
token.h token.l \
updater.c updater.h
SRCS= attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c parse.y \
pathcomp.c proto.c status.c stream.c threads.c token.l updater.c
CFLAGS+= -I. -I${.CURDIR} -g -pthread -DHAVE_FFLAGS -DNDEBUG
WARNS?= 6

View File

@ -9,9 +9,6 @@ BUGS:
has global variables because of yacc, but I think it should be possible
to do it better by using YYFUNC_PROTOTYPE or something. I think it
should also be possible to completely get rid of the lex file.
- Some code uses getpwnam() while it should use the thread-safe variant,
getpwnam_r(). Same for getpwuid() and getgrgid(). We probably need a
UID/GID lookup cache here.
- The $Log$ CVS keyword is not supported.
- Add missing support for supfile keywords and add sanity checks for
some of them. Also, we're not supposed to choke on unknown keywords
@ -22,9 +19,9 @@ MISSING FEATURES:
- Add support for authentication.
- Add support for shell commands sent by the server.
- Add missing support for various CVSup options : -k, -d, -D,
-a (requires authentication support), -e and -E (requires shell
commands support) and the destDir parameter.
- Add missing support for various CVSup options : -D, -a (requires
authentication support), -e and -E (requires shell commands support)
and the destDir parameter.
- For now, this code should build fine on FreeBSD, NetBSD, OpenBSD,
Linux and Darwin. Solaris support would also be nice at some point.
- Implement some new useful options : the ability to generate CVS

View File

@ -53,6 +53,8 @@ extern FILE *yyin;
static STAILQ_HEAD(, coll) colls;
static struct coll *cur_coll;
static struct coll *defaults;
static struct coll *ovcoll;
static int ovmask;
static const char *cfgfile;
/*
@ -78,13 +80,14 @@ config_init(const char *file, struct coll *override, int overridemask)
mask = umask(0);
umask(mask);
defaults->co_umask = mask;
ovcoll = override;
ovmask = overridemask;
/* Extract a list of collections from the configuration file. */
cur_coll = coll_new(defaults);
yyin = fopen(file, "r");
if (yyin == NULL) {
lprintf(-1, "Cannot open \"%s\": %s\n", file,
strerror(errno));
lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno));
goto bad;
}
cfgfile = file;
@ -101,7 +104,6 @@ config_init(const char *file, struct coll *override, int overridemask)
/* Fixup the list of collections. */
STAILQ_FOREACH(coll, &config->colls, co_next) {
coll_override(coll, override, overridemask);
if (coll->co_base == NULL)
coll->co_base = xstrdup("/usr/local/etc/cvsup");
if (coll->co_colldir == NULL)
@ -229,13 +231,27 @@ static int
config_parse_refusefile(struct coll *coll, char *path)
{
struct stream *rd;
char *line;
char *cp, *line, *pat;
rd = stream_open_file(path, O_RDONLY);
if (rd == NULL)
return (0);
while ((line = stream_getln(rd, NULL)) != NULL)
pattlist_add(coll->co_refusals, line);
while ((line = stream_getln(rd, NULL)) != NULL) {
pat = line;
for (;;) {
/* Trim leading whitespace. */
pat += strspn(pat, " \t");
if (pat[0] == '\0')
break;
cp = strpbrk(pat, " \t");
if (cp != NULL)
*cp = '\0';
pattlist_add(coll->co_refusals, pat);
if (cp == NULL)
break;
pat = cp + 1;
}
}
if (!stream_eof(rd)) {
stream_close(rd);
lprintf(-1, "Read failure from \"%s\": %s\n", path,
@ -417,6 +433,7 @@ coll_add(char *name)
struct coll *coll;
cur_coll->co_name = name;
coll_override(cur_coll, ovcoll, ovmask);
if (cur_coll->co_release == NULL) {
lprintf(-1, "Release not specified for collection "
"\"%s\"\n", cur_coll->co_name);
@ -472,6 +489,8 @@ coll_free(struct coll *coll)
globtree_free(coll->co_dirfilter);
if (coll->co_dirfilter != NULL)
globtree_free(coll->co_filefilter);
if (coll->co_norsync != NULL)
globtree_free(coll->co_norsync);
if (coll->co_accepts != NULL)
pattlist_free(coll->co_accepts);
if (coll->co_refusals != NULL)
@ -483,6 +502,7 @@ void
coll_setopt(int opt, char *value)
{
struct coll *coll;
int error, mask;
coll = cur_coll;
switch (opt) {
@ -529,14 +549,14 @@ coll_setopt(int opt, char *value)
coll->co_listsuffix = value;
break;
case PT_UMASK:
errno = 0;
coll->co_umask = strtol(value, NULL, 8);
error = asciitoint(value, &mask, 8);
free(value);
if (errno) {
if (error) {
lprintf(-1, "Parse error in \"%s\": Invalid "
"umask value\n", cfgfile);
exit(1);
}
coll->co_umask = mask;
break;
case PT_USE_REL_SUFFIX:
coll->co_options |= CO_USERELSUFFIX;
@ -547,6 +567,9 @@ coll_setopt(int opt, char *value)
case PT_COMPRESS:
coll->co_options |= CO_COMPRESS;
break;
case PT_NORSYNC:
coll->co_options |= CO_NORSYNC;
break;
}
}

View File

@ -86,6 +86,7 @@ struct coll {
struct pattlist *co_refusals;
struct globtree *co_dirfilter;
struct globtree *co_filefilter;
struct globtree *co_norsync;
const char *co_colldir;
char *co_listsuffix;
time_t co_scantime; /* Set by the detailer thread. */
@ -101,6 +102,7 @@ struct config {
char *host;
struct sockaddr *laddr;
socklen_t laddrlen;
int deletelim;
int socket;
struct chan *chan0;
struct chan *chan1;

View File

@ -32,10 +32,11 @@
.Nd network distribution package for CVS repositories
.Sh SYNOPSIS
.Nm
.Op Fl 146svzZ
.Op Fl 146ksvzZ
.Op Fl A Ar addr
.Op Fl b Ar base
.Op Fl c Ar collDir
.Op Fl d Ar delLimit
.Op Fl h Ar host
.Op Fl i Ar pattern
.Op Fl l Ar lockfile
@ -123,6 +124,13 @@ Specifies the subdirectory of
where the information about the collections is maintained.
The default is
.Pa sup .
.It Fl d Ar delLimit
Specifies the maximum number of files that may be deleted in a
single update run.
Any attempt to exceed the limit results in a fatal error.
This can provide some protection against temporary configuration
mistakes on the server.
The default limit is infinity.
.It Fl h Ar host
Specifies the server host to contact, overriding any
.Cm host
@ -148,6 +156,17 @@ is a standard file name pattern.
It is interpreted relative to the collection's prefix directory.
Slash characters are matched only by explicit slashes in the pattern.
Leading periods in file name are not treated specially.
.It Fl k
Causes
.Nm
to keep the temporary copies of any incorrectly edited files, in the
event of checksum mismatches.
This option is for debugging, to help determine why the files were
edited incorrectly.
Regardless of whether this option is specified, the permanent versions
of faulty files are replaced with correct versions obtained by
transferring the files in their entirety.
Such transfers are called fixups.
.It Fl l Ar lockfile
Creates and locks the
.Ar lockfile

View File

@ -32,14 +32,13 @@
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fattr.h"
#include "idcache.h"
#include "misc.h"
/*
@ -126,6 +125,8 @@ fattr_init(void)
fa->mask |= FA_MODE;
defaults[i] = fa;
}
/* Initialize the uid/gid lookup cache. */
idcache_init();
}
void
@ -133,6 +134,7 @@ fattr_fini(void)
{
int i;
idcache_fini();
for (i = 0; i < FT_NUMBER; i++)
fattr_free(defaults[i]);
}
@ -336,29 +338,26 @@ fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
int extval;
char *ext;
} pieces[FA_NUMBER], *piece;
struct passwd *pw;
struct group *gr;
char *cp, *s;
char *cp, *s, *username, *groupname;
size_t len, vallen;
mode_t mode, modemask;
int mask, n, i;
pw = NULL;
gr = NULL;
username = NULL;
groupname = NULL;
if (support == NULL)
mask = fa->mask;
else
mask = fa->mask & support[fa->type];
mask &= ~ignore;
/* XXX - Use getpwuid_r() and getgrgid_r(). */
if (fa->mask & FA_OWNER) {
pw = getpwuid(fa->uid);
if (pw == NULL)
username = getuserbyid(fa->uid);
if (username == NULL)
mask &= ~FA_OWNER;
}
if (fa->mask & FA_GROUP) {
gr = getgrgid(fa->gid);
if (gr == NULL)
groupname = getgroupbyid(fa->gid);
if (groupname == NULL)
mask &= ~FA_GROUP;
}
if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
@ -408,17 +407,17 @@ fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
piece++;
}
if (mask & FA_OWNER) {
vallen = strlen(pw->pw_name);
vallen = strlen(username);
piece->extval = 1;
piece->ext = pw->pw_name;
piece->ext = username;
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_GROUP) {
vallen = strlen(gr->gr_name);
vallen = strlen(groupname);
piece->extval = 1;
piece->ext = gr->gr_name;
piece->ext = groupname;
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
@ -551,11 +550,10 @@ fattr_getlinkcount(const struct fattr *fa)
static char *
fattr_scanattr(struct fattr *fa, int type, const char *attr)
{
struct passwd *pw;
struct group *gr;
char *attrend, *attrstart, *end;
size_t len;
unsigned long attrlen;
int error;
mode_t modemask;
char tmp;
@ -606,21 +604,13 @@ fattr_scanattr(struct fattr *fa, int type, const char *attr)
goto bad;
break;
case FA_OWNER:
/*
* XXX - We need to use getpwnam_r() since getpwnam()
* is not thread-safe, and we also need to use a cache.
*/
pw = getpwnam(attrstart);
if (pw != NULL)
fa->uid = pw->pw_uid;
else
error = getuidbyname(attrstart, &fa->uid);
if (error)
fa->mask &= ~FA_OWNER;
break;
case FA_GROUP:
gr = getgrnam(attrstart);
if (gr != NULL)
fa->gid = gr->gr_gid;
else
error = getgidbyname(attrstart, &fa->gid);
if (error)
fa->mask &= ~FA_GROUP;
break;
case FA_MODE:
@ -770,11 +760,13 @@ fattr_delete(const char *path)
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);
@ -812,35 +804,35 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
inplace = 1;
}
old = fattr_frompath(topath, FATTR_NOFOLLOW);
if (old == NULL)
return (-1);
if (inplace && fattr_equal(fa, old)) {
fattr_free(old);
return (0);
}
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;
}
/*
* 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
/* 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;
/* 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
@ -866,8 +858,8 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
}
if (mask & FA_MODE) {
newmode = fa->mode & modemask;
/* Merge in set*id bits from the old attribute. XXX - Why? */
if (old->mask & FA_MODE) {
/* 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);
}

View File

@ -29,10 +29,10 @@
#include <sys/types.h>
#include <assert.h>
#include <fnmatch.h>
#include <regex.h>
#include <stdlib.h>
#include "fnmatch.h"
#include "globtree.h"
#include "misc.h"

421
contrib/csup/idcache.c Normal file
View File

@ -0,0 +1,421 @@
/*-
* Copyright (c) 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/types.h>
#include <assert.h>
#include <grp.h>
#include <pthread.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include "idcache.h"
#include "misc.h"
/*
* Constants and data structures used to implement the thread-safe
* group and password file caches. Cache sizes must be prime.
*/
#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */
#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */
#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */
#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */
/* Node structures used to cache lookups. */
struct uidc {
char *name; /* user name */
uid_t uid; /* cached uid */
int valid; /* is this a valid or a miss entry */
struct uidc *next; /* for collisions */
};
struct gidc {
char *name; /* group name */
gid_t gid; /* cached gid */
int valid; /* is this a valid or a miss entry */
struct gidc *next; /* for collisions */
};
static struct uidc **uidtoname; /* uid to user name cache */
static struct gidc **gidtoname; /* gid to group name cache */
static struct uidc **nametouid; /* user name to uid cache */
static struct gidc **nametogid; /* group name to gid cache */
static pthread_mutex_t uid_mtx;
static pthread_mutex_t gid_mtx;
static void uid_lock(void);
static void uid_unlock(void);
static void gid_lock(void);
static void gid_unlock(void);
static uint32_t hash(const char *);
/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
as used by ELF for hashing function names. */
static uint32_t
hash(const char *name)
{
uint32_t g, h;
h = 0;
while(*name != '\0') {
h = (h << 4) + *name++;
if ((g = h & 0xF0000000)) {
h ^= g >> 24;
h &= 0x0FFFFFFF;
}
}
return (h);
}
static void
uid_lock(void)
{
int error;
error = pthread_mutex_lock(&uid_mtx);
assert(!error);
}
static void
uid_unlock(void)
{
int error;
error = pthread_mutex_unlock(&uid_mtx);
assert(!error);
}
static void
gid_lock(void)
{
int error;
error = pthread_mutex_lock(&gid_mtx);
assert(!error);
}
static void
gid_unlock(void)
{
int error;
error = pthread_mutex_unlock(&gid_mtx);
assert(!error);
}
static void
uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key)
{
uidc->next = tbl[key];
tbl[key] = uidc;
}
static void
gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key)
{
gidc->next = tbl[key];
tbl[key] = gidc;
}
/* Return the user name for this uid, or NULL if it's not found. */
char *
getuserbyid(uid_t uid)
{
struct passwd *pw;
struct uidc *uidc, *uidc2;
uint32_t key, key2;
key = uid % UIDTONAME_SZ;
uid_lock();
uidc = uidtoname[key];
while (uidc != NULL) {
if (uidc->uid == uid)
break;
uidc = uidc->next;
}
if (uidc == NULL) {
/* We didn't find this uid, look it up and add it. */
uidc = xmalloc(sizeof(struct uidc));
uidc->uid = uid;
pw = getpwuid(uid);
if (pw != NULL) {
/* This uid is in the password file. */
uidc->name = xstrdup(pw->pw_name);
uidc->valid = 1;
/* Also add it to the name -> gid table. */
uidc2 = xmalloc(sizeof(struct uidc));
uidc2->uid = uid;
uidc2->name = uidc->name; /* We reuse the pointer. */
uidc2->valid = 1;
key2 = hash(uidc->name) % NAMETOUID_SZ;
uidc_insert(nametouid, uidc2, key2);
} else {
/* Add a miss entry for this uid. */
uidc->name = NULL;
uidc->valid = 0;
}
uidc_insert(uidtoname, uidc, key);
}
/* It is safe to unlock here since the cache structure
is not going to get freed or changed. */
uid_unlock();
return (uidc->name);
}
/* Return the group name for this gid, or NULL if it's not found. */
char *
getgroupbyid(gid_t gid)
{
struct group *gr;
struct gidc *gidc, *gidc2;
uint32_t key, key2;
key = gid % GIDTONAME_SZ;
gid_lock();
gidc = gidtoname[key];
while (gidc != NULL) {
if (gidc->gid == gid)
break;
gidc = gidc->next;
}
if (gidc == NULL) {
/* We didn't find this gid, look it up and add it. */
gidc = xmalloc(sizeof(struct gidc));
gidc->gid = gid;
gr = getgrgid(gid);
if (gr != NULL) {
/* This gid is in the group file. */
gidc->name = xstrdup(gr->gr_name);
gidc->valid = 1;
/* Also add it to the name -> gid table. */
gidc2 = xmalloc(sizeof(struct gidc));
gidc2->gid = gid;
gidc2->name = gidc->name; /* We reuse the pointer. */
gidc2->valid = 1;
key2 = hash(gidc->name) % NAMETOGID_SZ;
gidc_insert(nametogid, gidc2, key2);
} else {
/* Add a miss entry for this gid. */
gidc->name = NULL;
gidc->valid = 0;
}
gidc_insert(gidtoname, gidc, key);
}
/* It is safe to unlock here since the cache structure
is not going to get freed or changed. */
gid_unlock();
return (gidc->name);
}
/* Finds the uid for this user name. If it's found, the gid is stored
in *uid and 0 is returned. Otherwise, -1 is returned. */
int
getuidbyname(const char *name, uid_t *uid)
{
struct passwd *pw;
struct uidc *uidc, *uidc2;
uint32_t key, key2;
uid_lock();
key = hash(name) % NAMETOUID_SZ;
uidc = nametouid[key];
while (uidc != NULL) {
if (strcmp(uidc->name, name) == 0)
break;
uidc = uidc->next;
}
if (uidc == NULL) {
uidc = xmalloc(sizeof(struct uidc));
uidc->name = xstrdup(name);
pw = getpwnam(name);
if (pw != NULL) {
/* This user name is in the password file. */
uidc->valid = 1;
uidc->uid = pw->pw_uid;
/* Also add it to the uid -> name table. */
uidc2 = xmalloc(sizeof(struct uidc));
uidc2->name = uidc->name; /* We reuse the pointer. */
uidc2->uid = uidc->uid;
uidc2->valid = 1;
key2 = uidc2->uid % UIDTONAME_SZ;
uidc_insert(uidtoname, uidc2, key2);
} else {
/* Add a miss entry for this user name. */
uidc->valid = 0;
uidc->uid = (uid_t)-1; /* Should not be accessed. */
}
uidc_insert(nametouid, uidc, key);
}
/* It is safe to unlock here since the cache structure
is not going to get freed or changed. */
uid_unlock();
if (!uidc->valid)
return (-1);
*uid = uidc->uid;
return (0);
}
/* Finds the gid for this group name. If it's found, the gid is stored
in *gid and 0 is returned. Otherwise, -1 is returned. */
int
getgidbyname(const char *name, gid_t *gid)
{
struct group *gr;
struct gidc *gidc, *gidc2;
uint32_t key, key2;
gid_lock();
key = hash(name) % NAMETOGID_SZ;
gidc = nametogid[key];
while (gidc != NULL) {
if (strcmp(gidc->name, name) == 0)
break;
gidc = gidc->next;
}
if (gidc == NULL) {
gidc = xmalloc(sizeof(struct gidc));
gidc->name = xstrdup(name);
gr = getgrnam(name);
if (gr != NULL) {
/* This group name is in the group file. */
gidc->gid = gr->gr_gid;
gidc->valid = 1;
/* Also add it to the gid -> name table. */
gidc2 = xmalloc(sizeof(struct gidc));
gidc2->name = gidc->name; /* We reuse the pointer. */
gidc2->gid = gidc->gid;
gidc2->valid = 1;
key2 = gidc2->gid % GIDTONAME_SZ;
gidc_insert(gidtoname, gidc2, key2);
} else {
/* Add a miss entry for this group name. */
gidc->gid = (gid_t)-1; /* Should not be accessed. */
gidc->valid = 0;
}
gidc_insert(nametogid, gidc, key);
}
/* It is safe to unlock here since the cache structure
is not going to get freed or changed. */
gid_unlock();
if (!gidc->valid)
return (-1);
*gid = gidc->gid;
return (0);
}
/* Initialize the cache structures. */
void
idcache_init(void)
{
pthread_mutex_init(&uid_mtx, NULL);
pthread_mutex_init(&gid_mtx, NULL);
uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *));
gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *));
nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *));
nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *));
memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *));
memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *));
memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *));
memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *));
}
/* Cleanup the cache structures. */
void
idcache_fini(void)
{
struct uidc *uidc, *uidc2;
struct gidc *gidc, *gidc2;
size_t i;
for (i = 0; i < UIDTONAME_SZ; i++) {
uidc = uidtoname[i];
while (uidc != NULL) {
if (uidc->name != NULL) {
assert(uidc->valid);
free(uidc->name);
}
uidc2 = uidc->next;
free(uidc);
uidc = uidc2;
}
}
free(uidtoname);
for (i = 0; i < NAMETOUID_SZ; i++) {
uidc = nametouid[i];
while (uidc != NULL) {
assert(uidc->name != NULL);
/* If it's a valid entry, it has been added to both the
uidtoname and nametouid tables, and the name pointer
has been reused for both entries. Thus, the name
pointer has already been freed in the loop above. */
if (!uidc->valid)
free(uidc->name);
uidc2 = uidc->next;
free(uidc);
uidc = uidc2;
}
}
free(nametouid);
for (i = 0; i < GIDTONAME_SZ; i++) {
gidc = gidtoname[i];
while (gidc != NULL) {
if (gidc->name != NULL) {
assert(gidc->valid);
free(gidc->name);
}
gidc2 = gidc->next;
free(gidc);
gidc = gidc2;
}
}
free(gidtoname);
for (i = 0; i < NAMETOGID_SZ; i++) {
gidc = nametogid[i];
while (gidc != NULL) {
assert(gidc->name != NULL);
/* See above comment. */
if (!gidc->valid)
free(gidc->name);
gidc2 = gidc->next;
free(gidc);
gidc = gidc2;
}
}
free(nametogid);
pthread_mutex_destroy(&uid_mtx);
pthread_mutex_destroy(&gid_mtx);
}

41
contrib/csup/idcache.h Normal file
View File

@ -0,0 +1,41 @@
/*-
* Copyright (c) 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$
*/
#ifndef _IDCACHE_H_
#define _IDCACHE_H_
#include <sys/types.h>
void idcache_init(void);
void idcache_fini(void);
char *getuserbyid(uid_t);
char *getgroupbyid(gid_t);
int getuidbyname(const char *, uid_t *);
int getgidbyname(const char *, gid_t *);
#endif /* !_IDCACHE_H_ */

View File

@ -145,6 +145,7 @@ lister_coll(struct lister *l, struct coll *coll, struct status *st)
struct attrstack *as;
struct statusrec *sr;
struct fattr *fa;
size_t i;
int depth, error, ret, prunedepth;
wr = l->wr;
@ -203,7 +204,7 @@ lister_coll(struct lister *l, struct coll *coll, struct status *st)
return (LISTER_ERR_WRITE);
return (0);
bad:
while (depth-- > 0) {
for (i = 0; i < attrstack_size(as); i++) {
fa = attrstack_pop(as);
fattr_free(fa);
}

View File

@ -66,6 +66,8 @@ usage(char *argv0)
"Override supfile's \"base\" directory");
lprintf(-1, USAGE_OPTFMT, "-c collDir",
"Subdirectory of \"base\" for collections (default \"sup\")");
lprintf(-1, USAGE_OPTFMT, "-d delLimit",
"Allow at most \"delLimit\" file deletions (default unlimited)");
lprintf(-1, USAGE_OPTFMT, "-h host",
"Override supfile's \"host\" name");
lprintf(-1, USAGE_OPTFMT, "-i pattern",
@ -73,6 +75,8 @@ usage(char *argv0)
lprintf(-1, USAGE_OPTFMTSUB,
"May be repeated for an OR operation. Default is");
lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection.");
lprintf(-1, USAGE_OPTFMT, "-k",
"Keep bad temporary files when fixups are required");
lprintf(-1, USAGE_OPTFMT, "-l lockfile",
"Lock file during update; fail if already locked");
lprintf(-1, USAGE_OPTFMT, "-L n",
@ -102,13 +106,13 @@ main(int argc, char *argv[])
socklen_t laddrlen;
struct stream *lock;
char *argv0, *file, *lockfile;
uint16_t port;
int family, error, lockfd, lflag, overridemask;
int c, i, retries, status;
int c, i, deletelim, port, retries, status;
time_t nexttry;
error = 0;
family = PF_UNSPEC;
deletelim = -1;
port = 0;
lflag = 0;
lockfd = 0;
@ -121,7 +125,8 @@ main(int argc, char *argv[])
override = coll_new(NULL);
overridemask = 0;
while ((c = getopt(argc, argv, "146A:b:c:gh:i:l:L:p:P:r:svzZ")) != -1) {
while ((c = getopt(argc, argv,
"146A:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) {
switch (c) {
case '1':
retries = 0;
@ -152,6 +157,14 @@ main(int argc, char *argv[])
case 'c':
override->co_colldir = optarg;
break;
case 'd':
error = asciitoint(optarg, &deletelim, 0);
if (error || deletelim < 0) {
lprintf(-1, "Invalid deletion limit\n");
usage(argv0);
return (1);
}
break;
case 'g':
/* For compatibility. */
break;
@ -163,6 +176,10 @@ main(int argc, char *argv[])
case 'i':
pattlist_add(override->co_accepts, optarg);
break;
case 'k':
override->co_options |= CO_KEEPBADFILES;
overridemask |= CO_KEEPBADFILES;
break;
case 'l':
lockfile = optarg;
lflag = 1;
@ -191,9 +208,8 @@ main(int argc, char *argv[])
stream_close(lock);
break;
case 'L':
errno = 0;
verbose = strtol(optarg, NULL, 0);
if (errno == EINVAL) {
error = asciitoint(optarg, &verbose, 0);
if (error) {
lprintf(-1, "Invalid verbosity\n");
usage(argv0);
return (1);
@ -201,13 +217,21 @@ main(int argc, char *argv[])
break;
case 'p':
/* Use specified server port. */
errno = 0;
port = strtol(optarg, NULL, 0);
if (errno == EINVAL) {
error = asciitoint(optarg, &port, 0);
if (error) {
lprintf(-1, "Invalid server port\n");
usage(argv0);
return (1);
}
if (port <= 0 || port >= 65536) {
lprintf(-1, "Invalid port %d\n", port);
return (1);
}
if (port < 1024) {
lprintf(-1, "Reserved port %d not permitted\n",
port);
return (1);
}
break;
case 'P':
/* For compatibility. */
@ -218,9 +242,8 @@ main(int argc, char *argv[])
}
break;
case 'r':
errno = 0;
retries = strtol(optarg, NULL, 0);
if (errno == EINVAL || retries < 0) {
error = asciitoint(optarg, &retries, 0);
if (error || retries < 0) {
lprintf(-1, "Invalid retry limit\n");
usage(argv0);
return (1);
@ -270,15 +293,16 @@ main(int argc, char *argv[])
if (config == NULL)
return (1);
if (laddr != NULL) {
config->laddr = laddr;
config->laddrlen = laddrlen;
}
if (config_checkcolls(config) == 0) {
lprintf(-1, "No collections selected\n");
return (1);
}
if (laddr != NULL) {
config->laddr = laddr;
config->laddrlen = laddrlen;
}
config->deletelim = deletelim;
lprintf(2, "Connecting to %s\n", config->host);
i = 0;

View File

@ -34,6 +34,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
@ -63,6 +64,24 @@ struct backoff_timer {
static void bt_update(struct backoff_timer *);
static void bt_addjitter(struct backoff_timer *);
int
asciitoint(const char *s, int *val, int base)
{
char *end;
long longval;
errno = 0;
longval = strtol(s, &end, base);
if (errno || *end != '\0')
return (-1);
if (longval > INT_MAX || longval < INT_MIN) {
errno = ERANGE;
return (-1);
}
*val = longval;
return (0);
}
int
lprintf(int level, const char *fmt, ...)
{

View File

@ -99,6 +99,7 @@ struct backoff_timer;
struct pattlist;
struct tm;
int asciitoint(const char *, int *, int);
int lprintf(int, const char *, ...) __printflike(2, 3);
int MD5_File(char *, char *);
void MD5_End(char *, MD5_CTX *);
@ -125,4 +126,5 @@ struct backoff_timer *bt_new(time_t, time_t, float, float);
time_t bt_get(struct backoff_timer *);
void bt_pause(struct backoff_timer *);
void bt_free(struct backoff_timer *);
#endif /* !_MISC_H_ */

View File

@ -35,7 +35,6 @@
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
@ -228,7 +227,7 @@ proto_negproto(struct config *config)
stream_flush(s);
line = stream_getln(s, NULL);
cmd = proto_get_ascii(&line);
if (line == NULL)
if (cmd == NULL || line == NULL)
goto bad;
if (strcmp(cmd, "!") == 0) {
msg = proto_get_rest(&line);
@ -256,13 +255,20 @@ static int
proto_login(struct config *config)
{
struct stream *s;
char host[MAXHOSTNAMELEN];
char *line, *cmd, *realm, *challenge, *msg;
char hostbuf[MAXHOSTNAMELEN];
char *line, *login, *host, *cmd, *realm, *challenge, *msg;
int error;
s = config->server;
gethostname(host, sizeof(host));
host[sizeof(host) - 1] = '\0';
proto_printf(s, "USER %s %s\n", getlogin(), host);
error = gethostname(hostbuf, sizeof(hostbuf));
hostbuf[sizeof(hostbuf) - 1] = '\0';
if (error)
host = NULL;
else
host = hostbuf;
login = getlogin();
proto_printf(s, "USER %s %s\n", login != NULL ? login : "?",
host != NULL ? host : "?");
stream_flush(s);
line = stream_getln(s, NULL);
cmd = proto_get_ascii(&line);
@ -279,6 +285,8 @@ proto_login(struct config *config)
stream_flush(s);
line = stream_getln(s, NULL);
cmd = proto_get_ascii(&line);
if (cmd == NULL || line == NULL)
goto bad;
if (strcmp(cmd, "OK") == 0)
return (STATUS_SUCCESS);
if (strcmp(cmd, "!") == 0) {
@ -371,9 +379,11 @@ proto_xchgcoll(struct config *config)
}
proto_printf(s, ".\n");
stream_flush(s);
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
coll->co_norsync = globtree_false();
line = stream_getln(s, NULL);
if (line == NULL)
goto bad;
@ -430,7 +440,21 @@ proto_xchgcoll(struct config *config)
ident);
if (error)
goto bad;
}
} else if (strcmp(cmd, "NORS") == 0) {
pat = proto_get_ascii(&line);
if (pat == NULL || line != NULL)
goto bad;
coll->co_norsync = globtree_or(coll->co_norsync,
globtree_match(pat, FNM_PATHNAME));
} else if (strcmp(cmd, "RNORS") == 0) {
pat = proto_get_ascii(&line);
if (pat == NULL || line != NULL)
goto bad;
coll->co_norsync = globtree_or(coll->co_norsync,
globtree_match(pat, FNM_PATHNAME |
FNM_LEADING_DIR));
} else
goto bad;
}
if (line == NULL)
goto bad;
@ -904,22 +928,14 @@ proto_get_rest(char **s)
int
proto_get_int(char **s, int *val, int base)
{
char *cp, *end;
long longval;
char *cp;
int error;
cp = proto_get_ascii(s);
if (cp == NULL)
return (-1);
errno = 0;
longval = strtol(cp, &end, base);
if (errno || *end != '\0')
return (-1);
if (longval > INT_MAX || longval < INT_MIN) {
errno = ERANGE;
return (-1);
}
*val = longval;
return (0);
error = asciitoint(cp, val, base);
return (error);
}
/*

View File

@ -44,5 +44,6 @@ int yyparse(void);
#define PT_DELETE 8
#define PT_USE_REL_SUFFIX 9
#define PT_LIST 10
#define PT_NORSYNC 11
#endif /* !_TOKEN_H_ */

View File

@ -56,6 +56,7 @@ release { yylval.i = PT_RELEASE; return NAME; }
tag { yylval.i = PT_TAG; return NAME; }
umask { yylval.i = PT_UMASK; return NAME; }
list { yylval.i = PT_LIST; return NAME; }
norsync { yylval.i = PT_NORSYNC; return NAME; }
= { return EQUAL; }
compress { yylval.i = PT_COMPRESS; return BOOLEAN; }
delete { yylval.i = PT_DELETE; return BOOLEAN; }

View File

@ -54,11 +54,13 @@
#define UPDATER_ERR_PROTO (-1) /* Protocol error. */
#define UPDATER_ERR_MSG (-2) /* Error is in updater->errmsg. */
#define UPDATER_ERR_READ (-3) /* Error reading from server. */
#define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */
/* Everything needed to update a file. */
struct file_update {
struct statusrec srbuf;
char *destpath;
char *temppath;
char *coname; /* Points somewhere in destpath. */
char *wantmd5;
struct coll *coll;
@ -74,6 +76,7 @@ struct updater {
struct config *config;
struct stream *rd;
char *errmsg;
int deletecount;
};
static struct file_update *fup_new(struct coll *, struct status *);
@ -84,14 +87,13 @@ static void fup_free(struct file_update *);
static void updater_prunedirs(char *, char *);
static int updater_batch(struct updater *, int);
static int updater_docoll(struct updater *, struct file_update *, int);
static void updater_delete(struct file_update *);
static int updater_delete(struct updater *, struct file_update *);
static void updater_deletefile(const char *);
static int updater_checkout(struct updater *, struct file_update *, int);
static int updater_setattrs(struct updater *, struct file_update *,
char *, char *, char *, char *, char *, struct fattr *);
static void updater_checkmd5(struct updater *, struct file_update *,
const char *, int);
static int updater_updatefile(struct updater *, struct file_update *fup,
const char *, const char *);
const char *, int);
static int updater_diff(struct updater *, struct file_update *);
static int updater_diff_batch(struct updater *, struct file_update *);
static int updater_diff_apply(struct updater *, struct file_update *,
@ -134,6 +136,10 @@ fup_cleanup(struct file_update *fup)
free(fup->destpath);
fup->destpath = NULL;
}
if (fup->temppath != NULL) {
free(fup->temppath);
fup->temppath = NULL;
}
fup->coname = NULL;
if (fup->author != NULL) {
free(fup->author);
@ -188,6 +194,7 @@ updater(void *arg)
up->config = args->config;
up->rd = args->rd;
up->errmsg = NULL;
up->deletecount = 0;
error = updater_batch(up, 0);
@ -218,6 +225,11 @@ updater(void *arg)
}
args->status = STATUS_TRANSIENTFAILURE;
break;
case UPDATER_ERR_DELETELIM:
xasprintf(&args->errmsg, "Updater failed: "
"File deletion limit exceeded");
args->status = STATUS_FAILURE;
break;
default:
assert(error == 0);
args->status = STATUS_SUCCESS;
@ -261,12 +273,11 @@ updater_batch(struct updater *up, int isfixups)
stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
st = status_open(coll, coll->co_scantime, &errmsg);
fup = fup_new(coll, st);
if (st == NULL) {
fup_free(fup);
up->errmsg = errmsg;
return (UPDATER_ERR_MSG);
}
fup = fup_new(coll, st);
error = updater_docoll(up, fup, isfixups);
status_close(st, &errmsg);
fup_free(fup);
@ -360,8 +371,11 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
/* Theoritically, the file does not exist on the client.
Just to make sure, we'll delete it here, if it
exists. */
if (access(fup->destpath, F_OK) == 0)
updater_delete(fup);
if (access(fup->destpath, F_OK) == 0) {
error = updater_delete(up, fup);
if (error)
return (error);
}
sr = &srbuf;
sr->sr_type = SR_CHECKOUTDEAD;
@ -410,6 +424,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
return (UPDATER_ERR_PROTO);
fup->wantmd5 = xstrdup(wantmd5);
fup->temppath = tempname(fup->destpath);
error = updater_diff(up, fup);
if (error)
return (error);
@ -426,7 +441,9 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
error = fup_prepare(fup, name);
if (error)
return (UPDATER_ERR_PROTO);
updater_delete(fup);
error = updater_delete(up, fup);
if (error)
return (error);
sr = &srbuf;
sr->sr_type = SR_CHECKOUTDEAD;
sr->sr_file = name;
@ -478,6 +495,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
error = fup_prepare(fup, name);
if (error)
return (UPDATER_ERR_PROTO);
fup->temppath = tempname(fup->destpath);
if (*cmd == 'Y')
error = updater_checkout(up, fup, 1);
else
@ -493,7 +511,9 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
error = fup_prepare(fup, name);
if (error)
return (UPDATER_ERR_PROTO);
updater_delete(fup);
error = updater_delete(up, fup);
if (error)
return (error);
error = status_delete(fup->st, name, 0);
if (error) {
up->errmsg = status_errmsg(fup->st);
@ -518,27 +538,39 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
}
/* Delete file. */
static void
updater_delete(struct file_update *fup)
static int
updater_delete(struct updater *up, struct file_update *fup)
{
struct config *config;
struct coll *coll;
int error;
/* XXX - delete limit handling */
config = up->config;
coll = fup->coll;
if (coll->co_options & CO_DELETE) {
lprintf(1, " Delete %s\n", fup->coname);
error = fattr_delete(fup->destpath);
if (error) {
lprintf(-1, "Cannot delete \"%s\": %s\n",
fup->destpath, strerror(errno));
return;
}
if (config->deletelim >= 0 &&
up->deletecount >= config->deletelim)
return (UPDATER_ERR_DELETELIM);
up->deletecount++;
updater_deletefile(fup->destpath);
if (coll->co_options & CO_CHECKOUTMODE)
updater_prunedirs(coll->co_prefix, fup->destpath);
} else {
lprintf(1," NoDelete %s\n", fup->coname);
}
return (0);
}
static void
updater_deletefile(const char *path)
{
int error;
error = fattr_delete(path);
if (error && errno != ENOENT) {
lprintf(-1, "Cannot delete \"%s\": %s\n",
path, strerror(errno));
}
}
static int
@ -613,34 +645,9 @@ updater_setattrs(struct updater *up, struct file_update *fup, char *name,
return (0);
}
/*
* Check that the file we created/updated has a correct MD5 checksum.
* If it doesn't and that this is not a fixup update, add a fixup
* request to checkout the whole file. If it's already a fixup update,
* we just fail.
*/
static void
updater_checkmd5(struct updater *up, struct file_update *fup, const char *md5,
int isfixup)
{
struct statusrec *sr;
sr = &fup->srbuf;
if (strcmp(fup->wantmd5, md5) == 0)
return;
if (isfixup) {
lprintf(-1, "%s: Checksum mismatch -- file not updated\n",
fup->destpath);
return;
}
lprintf(-1, "%s: Checksum mismatch -- will transfer entire file\n",
fup->destpath);
fixups_put(up->config->fixups, fup->coll, sr->sr_file);
}
static int
updater_updatefile(struct updater *up, struct file_update *fup, const char *to,
const char *from)
updater_updatefile(struct updater *up, struct file_update *fup,
const char *md5, int isfixup)
{
struct coll *coll;
struct status *st;
@ -652,16 +659,27 @@ updater_updatefile(struct updater *up, struct file_update *fup, const char *to,
sr = &fup->srbuf;
st = fup->st;
fattr_umask(sr->sr_clientattr, coll->co_umask);
rv = fattr_install(sr->sr_clientattr, to, from);
if (rv == -1) {
if (from == NULL)
xasprintf(&up->errmsg, "Cannot install \"%s\": %s",
to, strerror(errno));
if (strcmp(fup->wantmd5, md5) != 0) {
if (isfixup) {
lprintf(-1, "%s: Checksum mismatch -- "
"file not updated\n", fup->destpath);
} else {
lprintf(-1, "%s: Checksum mismatch -- "
"will transfer entire file\n", fup->destpath);
fixups_put(up->config->fixups, fup->coll, sr->sr_file);
}
if (coll->co_options & CO_KEEPBADFILES)
lprintf(-1, "Bad version saved in %s\n", fup->temppath);
else
xasprintf(&up->errmsg,
"Cannot install \"%s\" to \"%s\": %s",
from, to, strerror(errno));
updater_deletefile(fup->temppath);
return (0);
}
fattr_umask(sr->sr_clientattr, coll->co_umask);
rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath);
if (rv == -1) {
xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
fup->temppath, fup->destpath, strerror(errno));
return (UPDATER_ERR_MSG);
}
@ -676,9 +694,9 @@ updater_updatefile(struct updater *up, struct file_update *fup, const char *to,
* server. This is important for preserving hard links in mirror
* mode.
*/
fileattr = fattr_frompath(to, FATTR_NOFOLLOW);
fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
if (fileattr == NULL) {
xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", to,
xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
strerror(errno));
return (UPDATER_ERR_MSG);
}
@ -715,10 +733,9 @@ updater_diff(struct updater *up, struct file_update *fup)
struct statusrec *sr;
struct fattr *fa, *tmp;
char *author, *path, *revnum, *revdate;
char *line, *cmd, *temppath;
char *line, *cmd;
int error;
temppath = NULL;
coll = fup->coll;
sr = &fup->srbuf;
path = fup->destpath;
@ -728,18 +745,14 @@ updater_diff(struct updater *up, struct file_update *fup)
if (strcmp(line, ".") == 0)
break;
cmd = proto_get_ascii(&line);
if (cmd == NULL || strcmp(cmd, "D") != 0) {
error = UPDATER_ERR_PROTO;
goto bad;
}
if (cmd == NULL || strcmp(cmd, "D") != 0)
return (UPDATER_ERR_PROTO);
revnum = proto_get_ascii(&line);
proto_get_ascii(&line); /* XXX - diffbase */
revdate = proto_get_ascii(&line);
author = proto_get_ascii(&line);
if (author == NULL || line != NULL) {
error = UPDATER_ERR_PROTO;
goto bad;
}
if (author == NULL || line != NULL)
return (UPDATER_ERR_PROTO);
if (sr->sr_revnum != NULL)
free(sr->sr_revnum);
if (sr->sr_revdate != NULL)
@ -755,36 +768,32 @@ updater_diff(struct updater *up, struct file_update *fup)
if (fup->orig == NULL) {
xasprintf(&up->errmsg, "%s: Cannot open: %s",
path, strerror(errno));
error = UPDATER_ERR_MSG;
goto bad;
return (UPDATER_ERR_MSG);
}
} else {
/* Subsequent patches. */
stream_close(fup->orig);
fup->orig = fup->to;
stream_rewind(fup->orig);
unlink(temppath);
free(temppath);
unlink(fup->temppath);
free(fup->temppath);
fup->temppath = tempname(path);
}
temppath = tempname(path);
fup->to = stream_open_file(temppath,
O_RDWR | O_CREAT | O_EXCL, 0600);
fup->to = stream_open_file(fup->temppath,
O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fup->to == NULL) {
xasprintf(&up->errmsg, "%s: Cannot open: %s",
temppath, strerror(errno));
error = UPDATER_ERR_MSG;
goto bad;
fup->temppath, strerror(errno));
return (UPDATER_ERR_MSG);
}
lprintf(2, " Add delta %s %s %s\n", sr->sr_revnum,
sr->sr_revdate, fup->author);
error = updater_diff_batch(up, fup);
if (error)
goto bad;
}
if (line == NULL) {
error = UPDATER_ERR_READ;
goto bad;
return (error);
}
if (line == NULL)
return (UPDATER_ERR_READ);
fa = fattr_frompath(path, FATTR_FOLLOW);
tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
@ -793,24 +802,13 @@ updater_diff(struct updater *up, struct file_update *fup)
fattr_maskout(fa, FA_MODTIME);
sr->sr_clientattr = fa;
error = updater_updatefile(up, fup, path, temppath);
if (error)
goto bad;
if (MD5_File(path, md5) == -1) {
if (MD5_File(fup->temppath, md5) == -1) {
xasprintf(&up->errmsg,
"Cannot calculate checksum for \"%s\": %s",
path, strerror(errno));
error = UPDATER_ERR_MSG;
goto bad;
return (UPDATER_ERR_MSG);
}
updater_checkmd5(up, fup, md5, 0);
free(temppath);
return (0);
bad:
assert(error);
if (temppath != NULL)
free(temppath);
error = updater_updatefile(up, fup, md5, 0);
return (error);
}
@ -934,10 +932,11 @@ updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
return (UPDATER_ERR_MSG);
}
to = stream_open_file(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
to = stream_open_file(fup->temppath,
O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (to == NULL) {
xasprintf(&up->errmsg, "%s: Cannot create: %s",
path, strerror(errno));
fup->temppath, strerror(errno));
return (UPDATER_ERR_MSG);
}
stream_filter_start(to, STREAM_FILTER_MD5, md5);
@ -980,14 +979,14 @@ updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
fup->wantmd5 = proto_get_ascii(&line);
if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
return (UPDATER_ERR_PROTO);
updater_checkmd5(up, fup, md5, isfixup);
error = updater_updatefile(up, fup, md5, isfixup);
fup->wantmd5 = NULL; /* So that it doesn't get freed. */
error = updater_updatefile(up, fup, path, NULL);
if (error)
return (error);
return (0);
bad:
xasprintf(&up->errmsg, "%s: Cannot write: %s", path, strerror(errno));
xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
strerror(errno));
return (UPDATER_ERR_MSG);
}