Remove csup(1) and its associated cpasswd(1) tool.

With the move by the FreeBSD Project away from CVSup as a distribution
mechanism, there is no longer a need to keep this in base.

Approved by:	mux (around a year ago), silence on -hackers
X-MFC-after:	never
This commit is contained in:
Gavin Atkinson 2014-06-25 12:06:45 +00:00
parent 3f3ce2c44a
commit 2d4f49b998
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=267863
64 changed files with 5 additions and 20672 deletions

View File

@ -38,6 +38,11 @@
# xargs -n1 | sort | uniq -d;
# done
# 20140625: csup removal
OLD_FILES+=usr/bin/csup
OLD_FILES+=usr/bin/cpasswd
OLD_FILES+=usr/share/man/man1/csup.1.gz
OLD_FILES+=usr/share/man/man1/cpasswd.1.gz
# 20140614: send-pr removal
OLD_FILES+=usr/share/info/send-pr.info.gz
OLD_FILES+=usr/share/man/man1/send-pr.1.gz

View File

@ -266,10 +266,6 @@ SUBDIR+= drill
SUBDIR+= host
.endif
.if ${MK_LIBTHR} != "no"
SUBDIR+= csup
.endif
.if ${MK_LOCATE} != "no"
SUBDIR+= locate
.endif

View File

@ -1,42 +0,0 @@
# $FreeBSD$
PROG= csup
SRCS= attrstack.c \
auth.c \
config.c \
detailer.c \
diff.c \
fattr.c \
fixups.c \
fnmatch.c \
globtree.c \
idcache.c \
keyword.c \
lex.rcs.c \
lister.c \
main.c \
misc.c \
mux.c \
parse.y \
pathcomp.c \
proto.c \
rcsfile.c \
rcsparse.c \
rsyncfile.c \
status.c \
stream.c \
threads.c \
token.l \
updater.c
CFLAGS+= -I. -I${.CURDIR}
CFLAGS+= -DHAVE_FFLAGS -DNDEBUG
WARNS?= 1
DPADD= ${LIBMD} ${LIBZ} ${LIBPTHREAD}
LDADD= -lmd -lz -lpthread
SCRIPTS= cpasswd.sh
MAN= csup.1 cpasswd.1
.include <bsd.prog.mk>

View File

@ -1,39 +0,0 @@
$FreeBSD$
Authors
-------
CVSup was originally written in Modula-3 by
John Polstra <jdp@polstra.com>.
Csup is a rewrite of CVSup in C. It has been mostly written by
Maxime Henrion <mux@FreeBSD.org>.
A few contributors have helped him in his task and they are listed here in
alphabetical order :
Olivier Houchard <cognet@FreeBSD.org>
Ulf Lilleengen <lulf@kerneled.org>
Christoph Mathys <cmathys@bluewin.ch> (Google SoC Project)
Etienne Vidal <etienne.vidal@gmail.com>
Building & Installing
---------------------
Csup should build and run fine under any *BSD OS (that includes FreeBSD,
NetBSD, OpenBSD and DragonFlyBSD), as well as Linux and Darwin. If you
have a problem building from source, drop me a mail!
There is one Makefile specifically tailored for *BSD systems named
Makefile and another one that is gmake-specific for Darwin and Linux
users named GNUmakefile. You don't really need to worry about that
since whatever your "make" command is, it should pick up the correct
Makefile.
As usual, to build the source code, just run "make". Once this is done,
just run "make install" to install the binary and manual page.
Be warned however that if the packaging system of your OS knows about
csup, it is certainly better to install it from there rather than by
hand, so that it can then be properly deinstalled.

View File

@ -1,28 +0,0 @@
$FreeBSD$
BUGS:
- Fix every XXX in the code :-).
- The stream API needs some polishing. It needs proper error numbers
and a stream_error() function similar to the ferror() function.
- The yacc/lex code to parse the configuration file is sub-optimal. It
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.
- 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
to stay in line with CVSup, which just ignores them in order to
maintain compatibility with sup configuration files.
MISSING FEATURES:
- Add support for shell commands sent by the server.
- Add missing support for various CVSup options : -D, -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
checkout files (files in CVS/ subdirectores), a command line override
to only update a specific collection and a third verbosity level to
display commit log messages.

View File

@ -1,90 +0,0 @@
/*-
* 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.
*
* $Id$
*/
#include <assert.h>
#include <stdlib.h>
#include "attrstack.h"
#include "fattr.h"
#include "misc.h"
#define ATTRSTACK_DEFSIZE 16 /* Initial size of the stack. */
struct attrstack {
struct fattr **stack;
size_t cur;
size_t size;
};
struct attrstack *
attrstack_new(void)
{
struct attrstack *as;
as = xmalloc(sizeof(struct attrstack));
as->stack = xmalloc(sizeof(struct fattr *) * ATTRSTACK_DEFSIZE);
as->size = ATTRSTACK_DEFSIZE;
as->cur = 0;
return (as);
}
struct fattr *
attrstack_pop(struct attrstack *as)
{
assert(as->cur > 0);
return (as->stack[--as->cur]);
}
void
attrstack_push(struct attrstack *as, struct fattr *fa)
{
if (as->cur >= as->size) {
as->size *= 2;
as->stack = xrealloc(as->stack,
sizeof(struct fattr *) * as->size);
}
as->stack[as->cur++] = fa;
}
size_t
attrstack_size(struct attrstack *as)
{
return (as->cur);
}
void
attrstack_free(struct attrstack *as)
{
assert(as->cur == 0);
free(as->stack);
free(as);
}

View File

@ -1,40 +0,0 @@
/*-
* 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.
*
* $Id$
*/
#ifndef _ATTRSTACK_H_
#define _ATTRSTACK_H_
struct fattr;
struct attrstack;
struct attrstack *attrstack_new(void);
void attrstack_push(struct attrstack *, struct fattr *);
struct fattr *attrstack_pop(struct attrstack *);
size_t attrstack_size(struct attrstack *);
void attrstack_free(struct attrstack *);
#endif /* !_ATTRSTACK_H_ */

View File

@ -1,331 +0,0 @@
/*-
* Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com>
* 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/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "auth.h"
#include "config.h"
#include "misc.h"
#include "proto.h"
#include "stream.h"
#define MD5_BYTES 16
/* This should be at least 2 * MD5_BYTES + 6 (length of "$md5$" + 1) */
#define MD5_CHARS_MAX (2*(MD5_BYTES)+6)
struct srvrecord {
char server[MAXHOSTNAMELEN];
char client[256];
char password[256];
};
static int auth_domd5auth(struct config *);
static int auth_lookuprecord(char *, struct srvrecord *);
static int auth_parsetoken(char **, char *, int);
static void auth_makesecret(struct srvrecord *, char *);
static void auth_makeresponse(char *, char *, char *);
static void auth_readablesum(unsigned char *, char *);
static void auth_makechallenge(struct config *, char *);
static int auth_checkresponse(char *, char *, char *);
int auth_login(struct config *config)
{
struct stream *s;
char hostbuf[MAXHOSTNAMELEN];
char *login, *host;
int error;
s = config->server;
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);
error = auth_domd5auth(config);
return (error);
}
static int
auth_domd5auth(struct config *config)
{
struct stream *s;
char *line, *cmd, *challenge, *realm, *client, *srvresponse, *msg;
char shrdsecret[MD5_CHARS_MAX], response[MD5_CHARS_MAX];
char clichallenge[MD5_CHARS_MAX];
struct srvrecord auth;
int error;
lprintf(2, "MD5 authentication started\n");
s = config->server;
line = stream_getln(s, NULL);
cmd = proto_get_ascii(&line);
realm = proto_get_ascii(&line);
challenge = proto_get_ascii(&line);
if (challenge == NULL ||
line != NULL ||
(strcmp(cmd, "AUTHMD5") != 0)) {
lprintf(-1, "Invalid server reply to USER\n");
return (STATUS_FAILURE);
}
client = NULL;
response[0] = clichallenge[0] = '.';
response[1] = clichallenge[1] = 0;
if (config->reqauth || (strcmp(challenge, ".") != 0)) {
if (strcmp(realm, ".") == 0) {
lprintf(-1, "Authentication required, but not enabled on server\n");
return (STATUS_FAILURE);
}
error = auth_lookuprecord(realm, &auth);
if (error != STATUS_SUCCESS)
return (error);
client = auth.client;
auth_makesecret(&auth, shrdsecret);
}
if (strcmp(challenge, ".") != 0)
auth_makeresponse(challenge, shrdsecret, response);
if (config->reqauth)
auth_makechallenge(config, clichallenge);
proto_printf(s, "AUTHMD5 %s %s %s\n",
client == NULL ? "." : client, response, clichallenge);
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) {
srvresponse = proto_get_ascii(&line);
if (srvresponse == NULL)
goto bad;
if (config->reqauth &&
!auth_checkresponse(srvresponse, clichallenge, shrdsecret)) {
lprintf(-1, "Server failed to authenticate itself to client\n");
return (STATUS_FAILURE);
}
lprintf(2, "MD5 authentication successful\n");
return (STATUS_SUCCESS);
}
if (strcmp(cmd, "!") == 0) {
msg = proto_get_rest(&line);
if (msg == NULL)
goto bad;
lprintf(-1, "Server error: %s\n", msg);
return (STATUS_FAILURE);
}
bad:
lprintf(-1, "Invalid server reply to AUTHMD5\n");
return (STATUS_FAILURE);
}
static int
auth_lookuprecord(char *server, struct srvrecord *auth)
{
char *home, *line, authfile[FILENAME_MAX];
struct stream *s;
int linenum = 0, error;
home = getenv("HOME");
if (home == NULL) {
lprintf(-1, "Environment variable \"HOME\" is not set\n");
return (STATUS_FAILURE);
}
snprintf(authfile, sizeof(authfile), "%s/%s", home, AUTHFILE);
s = stream_open_file(authfile, O_RDONLY);
if (s == NULL) {
lprintf(-1, "Could not open file %s\n", authfile);
return (STATUS_FAILURE);
}
while ((line = stream_getln(s, NULL)) != NULL) {
linenum++;
if (line[0] == '#' || line[0] == '\0')
continue;
error = auth_parsetoken(&line, auth->server,
sizeof(auth->server));
if (error != STATUS_SUCCESS) {
lprintf(-1, "%s:%d Missing client name\n", authfile, linenum);
goto close;
}
/* Skip the rest of this line, it isn't what we are looking for. */
if (strcasecmp(auth->server, server) != 0)
continue;
error = auth_parsetoken(&line, auth->client,
sizeof(auth->client));
if (error != STATUS_SUCCESS) {
lprintf(-1, "%s:%d Missing password\n", authfile, linenum);
goto close;
}
error = auth_parsetoken(&line, auth->password,
sizeof(auth->password));
if (error != STATUS_SUCCESS) {
lprintf(-1, "%s:%d Missing comment\n", authfile, linenum);
goto close;
}
stream_close(s);
lprintf(2, "Found authentication record for server \"%s\"\n",
server);
return (STATUS_SUCCESS);
}
lprintf(-1, "Unknown server \"%s\". Fix your %s\n", server , authfile);
memset(auth->password, 0, sizeof(auth->password));
close:
stream_close(s);
return (STATUS_FAILURE);
}
static int
auth_parsetoken(char **line, char *buf, int len)
{
char *colon;
colon = strchr(*line, ':');
if (colon == NULL)
return (STATUS_FAILURE);
*colon = 0;
buf[len - 1] = 0;
strncpy(buf, *line, len - 1);
*line = colon + 1;
return (STATUS_SUCCESS);
}
static void
auth_makesecret(struct srvrecord *auth, char *secret)
{
char *s, ch;
const char *md5salt = "$md5$";
unsigned char md5sum[MD5_BYTES];
MD5_CTX md5;
MD5_Init(&md5);
for (s = auth->client; *s != 0; ++s) {
ch = tolower(*s);
MD5_Update(&md5, &ch, 1);
}
MD5_Update(&md5, ":", 1);
for (s = auth->server; *s != 0; ++s) {
ch = tolower(*s);
MD5_Update(&md5, &ch, 1);
}
MD5_Update(&md5, ":", 1);
MD5_Update(&md5, auth->password, strlen(auth->password));
MD5_Final(md5sum, &md5);
memset(secret, 0, MD5_CHARS_MAX);
strcpy(secret, md5salt);
auth_readablesum(md5sum, secret + strlen(md5salt));
}
static void
auth_makeresponse(char *challenge, char *sharedsecret, char *response)
{
MD5_CTX md5;
unsigned char md5sum[MD5_BYTES];
MD5_Init(&md5);
MD5_Update(&md5, sharedsecret, strlen(sharedsecret));
MD5_Update(&md5, ":", 1);
MD5_Update(&md5, challenge, strlen(challenge));
MD5_Final(md5sum, &md5);
auth_readablesum(md5sum, response);
}
/*
* Generates a challenge string which is an MD5 sum
* of a fairly random string. The purpose is to decrease
* the possibility of generating the same challenge
* string (even by different clients) more then once
* for the same server.
*/
static void
auth_makechallenge(struct config *config, char *challenge)
{
MD5_CTX md5;
unsigned char md5sum[MD5_BYTES];
char buf[128];
struct timeval tv;
struct sockaddr_in laddr;
pid_t pid, ppid;
int error, addrlen;
gettimeofday(&tv, NULL);
pid = getpid();
ppid = getppid();
srandom(tv.tv_usec ^ tv.tv_sec ^ pid);
addrlen = sizeof(laddr);
error = getsockname(config->socket, (struct sockaddr *)&laddr, &addrlen);
if (error < 0) {
memset(&laddr, 0, sizeof(laddr));
}
gettimeofday(&tv, NULL);
MD5_Init(&md5);
snprintf(buf, sizeof(buf), "%s:%jd:%ld:%ld:%d:%d",
inet_ntoa(laddr.sin_addr), (intmax_t)tv.tv_sec, tv.tv_usec,
random(), pid, ppid);
MD5_Update(&md5, buf, strlen(buf));
MD5_Final(md5sum, &md5);
auth_readablesum(md5sum, challenge);
}
static int
auth_checkresponse(char *response, char *challenge, char *secret)
{
char correctresponse[MD5_CHARS_MAX];
auth_makeresponse(challenge, secret, correctresponse);
return (strcmp(response, correctresponse) == 0);
}
static void
auth_readablesum(unsigned char *md5sum, char *readable)
{
unsigned int i;
char *s = readable;
for (i = 0; i < MD5_BYTES; ++i, s+=2) {
sprintf(s, "%.2x", md5sum[i]);
}
}

View File

@ -1,38 +0,0 @@
/*-
* Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com>
* 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 _AUTH_H_
#define _AUTH_H_
#define AUTHFILE ".csup/auth" /* user home relative */
struct config;
int auth_login(struct config *);
#endif /* !_AUTH_H_ */

View File

@ -1,579 +0,0 @@
/*-
* 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/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "globtree.h"
#include "keyword.h"
#include "misc.h"
#include "parse.h"
#include "stream.h"
#include "token.h"
static int config_parse_refusefiles(struct coll *);
static int config_parse_refusefile(struct coll *, char *);
extern FILE *yyin;
/* These are globals because I can't think of a better way with yacc. */
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;
/*
* Extract all the configuration information from the config
* file and some command line parameters.
*/
struct config *
config_init(const char *file, struct coll *override, int overridemask)
{
struct config *config;
struct coll *coll;
size_t slen;
char *prefix;
int error;
mode_t mask;
config = xmalloc(sizeof(struct config));
memset(config, 0, sizeof(struct config));
STAILQ_INIT(&colls);
defaults = coll_new(NULL);
/* Set the default umask. */
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));
goto bad;
}
cfgfile = file;
error = yyparse();
fclose(yyin);
if (error)
goto bad;
memcpy(&config->colls, &colls, sizeof(colls));
if (STAILQ_EMPTY(&config->colls)) {
lprintf(-1, "Empty supfile\n");
goto bad;
}
/* Fixup the list of collections. */
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_base == NULL)
coll->co_base = xstrdup("/usr/local/etc/cvsup");
if (coll->co_colldir == NULL)
coll->co_colldir = "sup";
if (coll->co_prefix == NULL) {
coll->co_prefix = xstrdup(coll->co_base);
/*
* If prefix is not an absolute pathname, it is
* interpreted relative to base.
*/
} else if (coll->co_prefix[0] != '/') {
slen = strlen(coll->co_base);
if (slen > 0 && coll->co_base[slen - 1] != '/')
xasprintf(&prefix, "%s/%s", coll->co_base,
coll->co_prefix);
else
xasprintf(&prefix, "%s%s", coll->co_base,
coll->co_prefix);
free(coll->co_prefix);
coll->co_prefix = prefix;
}
coll->co_prefixlen = strlen(coll->co_prefix);
/* Determine whether to checksum RCS files or not. */
if (coll->co_options & CO_EXACTRCS)
coll->co_options |= CO_CHECKRCS;
else
coll->co_options &= ~CO_CHECKRCS;
/* In recent versions, we always try to set the file modes. */
coll->co_options |= CO_SETMODE;
coll->co_options |= CO_NORSYNC;
error = config_parse_refusefiles(coll);
if (error)
goto bad;
}
coll_free(cur_coll);
coll_free(defaults);
config->host = STAILQ_FIRST(&config->colls)->co_host;
return (config);
bad:
coll_free(cur_coll);
coll_free(defaults);
config_free(config);
return (NULL);
}
int
config_checkcolls(struct config *config)
{
char linkname[4];
struct stat sb;
struct coll *coll;
int error, numvalid, ret;
numvalid = 0;
STAILQ_FOREACH(coll, &config->colls, co_next) {
error = stat(coll->co_prefix, &sb);
if (error || !S_ISDIR(sb.st_mode)) {
/* Skip this collection, and warn about it unless its
prefix is a symbolic link pointing to "SKIP". */
coll->co_options |= CO_SKIP;
ret = readlink(coll->co_prefix, linkname,
sizeof(linkname));
if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
lprintf(-1,"Nonexistent prefix \"%s\" for "
"%s/%s\n", coll->co_prefix, coll->co_name,
coll->co_release);
}
continue;
}
numvalid++;
}
return (numvalid);
}
static int
config_parse_refusefiles(struct coll *coll)
{
char *collstem, *suffix, *supdir, *path;
int error;
if (coll->co_colldir[0] == '/')
supdir = xstrdup(coll->co_colldir);
else
xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
/* First, the global refuse file that applies to all collections. */
xasprintf(&path, "%s/refuse", supdir);
error = config_parse_refusefile(coll, path);
free(path);
if (error) {
free(supdir);
return (error);
}
/* Next the per-collection refuse files that applies to all release/tag
combinations. */
xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
free(supdir);
error = config_parse_refusefile(coll, collstem);
if (error) {
free(collstem);
return (error);
}
/* Finally, the per-release and per-tag refuse file. */
suffix = coll_statussuffix(coll);
if (suffix != NULL) {
xasprintf(&path, "%s%s", collstem, suffix);
free(suffix);
error = config_parse_refusefile(coll, path);
free(path);
}
free(collstem);
return (error);
}
/*
* Parses a "refuse" file, and records the relevant information in
* coll->co_refusals. If the file does not exist, it is silently
* ignored.
*/
static int
config_parse_refusefile(struct coll *coll, char *path)
{
struct stream *rd;
char *cp, *line, *pat;
rd = stream_open_file(path, O_RDONLY);
if (rd == NULL)
return (0);
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,
strerror(errno));
return (-1);
}
stream_close(rd);
return (0);
}
void
config_free(struct config *config)
{
struct coll *coll;
while (!STAILQ_EMPTY(&config->colls)) {
coll = STAILQ_FIRST(&config->colls);
STAILQ_REMOVE_HEAD(&config->colls, co_next);
coll_free(coll);
}
if (config->server != NULL)
stream_close(config->server);
if (config->laddr != NULL)
free(config->laddr);
free(config);
}
/* Create a new collection, inheriting options from the default collection. */
struct coll *
coll_new(struct coll *def)
{
struct coll *new;
new = xmalloc(sizeof(struct coll));
memset(new, 0, sizeof(struct coll));
if (def != NULL) {
new->co_options = def->co_options;
new->co_umask = def->co_umask;
if (def->co_host != NULL)
new->co_host = xstrdup(def->co_host);
if (def->co_base != NULL)
new->co_base = xstrdup(def->co_base);
if (def->co_date != NULL)
new->co_date = xstrdup(def->co_date);
if (def->co_prefix != NULL)
new->co_prefix = xstrdup(def->co_prefix);
if (def->co_release != NULL)
new->co_release = xstrdup(def->co_release);
if (def->co_tag != NULL)
new->co_tag = xstrdup(def->co_tag);
if (def->co_listsuffix != NULL)
new->co_listsuffix = xstrdup(def->co_listsuffix);
} else {
new->co_tag = xstrdup(".");
new->co_date = xstrdup(".");
}
new->co_keyword = keyword_new();
new->co_accepts = pattlist_new();
new->co_refusals = pattlist_new();
new->co_attrignore = FA_DEV | FA_INODE;
return (new);
}
void
coll_override(struct coll *coll, struct coll *from, int mask)
{
size_t i;
int newoptions, oldoptions;
newoptions = from->co_options & mask;
oldoptions = coll->co_options & (CO_MASK & ~mask);
if (from->co_release != NULL) {
if (coll->co_release != NULL)
free(coll->co_release);
coll->co_release = xstrdup(from->co_release);
}
if (from->co_host != NULL) {
if (coll->co_host != NULL)
free(coll->co_host);
coll->co_host = xstrdup(from->co_host);
}
if (from->co_base != NULL) {
if (coll->co_base != NULL)
free(coll->co_base);
coll->co_base = xstrdup(from->co_base);
}
if (from->co_colldir != NULL)
coll->co_colldir = from->co_colldir;
if (from->co_prefix != NULL) {
if (coll->co_prefix != NULL)
free(coll->co_prefix);
coll->co_prefix = xstrdup(from->co_prefix);
}
if (newoptions & CO_CHECKOUTMODE) {
if (from->co_tag != NULL) {
if (coll->co_tag != NULL)
free(coll->co_tag);
coll->co_tag = xstrdup(from->co_tag);
}
if (from->co_date != NULL) {
if (coll->co_date != NULL)
free(coll->co_date);
coll->co_date = xstrdup(from->co_date);
}
}
if (from->co_listsuffix != NULL) {
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
coll->co_listsuffix = xstrdup(from->co_listsuffix);
}
for (i = 0; i < pattlist_size(from->co_accepts); i++) {
pattlist_add(coll->co_accepts,
pattlist_get(from->co_accepts, i));
}
for (i = 0; i < pattlist_size(from->co_refusals); i++) {
pattlist_add(coll->co_refusals,
pattlist_get(from->co_refusals, i));
}
coll->co_options = oldoptions | newoptions;
}
char *
coll_statussuffix(struct coll *coll)
{
const char *tag;
char *suffix;
if (coll->co_listsuffix != NULL) {
xasprintf(&suffix, ".%s", coll->co_listsuffix);
} else if (coll->co_options & CO_USERELSUFFIX) {
if (coll->co_tag == NULL)
tag = ".";
else
tag = coll->co_tag;
if (coll->co_release != NULL) {
if (coll->co_options & CO_CHECKOUTMODE) {
xasprintf(&suffix, ".%s:%s",
coll->co_release, tag);
} else {
xasprintf(&suffix, ".%s", coll->co_release);
}
} else if (coll->co_options & CO_CHECKOUTMODE) {
xasprintf(&suffix, ":%s", tag);
}
} else
suffix = NULL;
return (suffix);
}
char *
coll_statuspath(struct coll *coll)
{
char *path, *suffix;
suffix = coll_statussuffix(coll);
if (suffix != NULL) {
if (coll->co_colldir[0] == '/')
xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
coll->co_name, suffix);
else
xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
coll->co_colldir, coll->co_name, suffix);
} else {
if (coll->co_colldir[0] == '/')
xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
coll->co_name);
else
xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
coll->co_colldir, coll->co_name);
}
free(suffix);
return (path);
}
void
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);
exit(1);
}
if (cur_coll->co_host == NULL) {
lprintf(-1, "Host not specified for collection "
"\"%s\"\n", cur_coll->co_name);
exit(1);
}
if (!STAILQ_EMPTY(&colls)) {
coll = STAILQ_LAST(&colls, coll, co_next);
if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
lprintf(-1, "All \"host\" fields in the supfile "
"must be the same\n");
exit(1);
}
}
STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
cur_coll = coll_new(defaults);
}
void
coll_free(struct coll *coll)
{
if (coll == NULL)
return;
if (coll->co_host != NULL)
free(coll->co_host);
if (coll->co_base != NULL)
free(coll->co_base);
if (coll->co_date != NULL)
free(coll->co_date);
if (coll->co_prefix != NULL)
free(coll->co_prefix);
if (coll->co_release != NULL)
free(coll->co_release);
if (coll->co_tag != NULL)
free(coll->co_tag);
if (coll->co_cvsroot != NULL)
free(coll->co_cvsroot);
if (coll->co_name != NULL)
free(coll->co_name);
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
keyword_free(coll->co_keyword);
if (coll->co_dirfilter != NULL)
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)
pattlist_free(coll->co_refusals);
free(coll);
}
void
coll_setopt(int opt, char *value)
{
struct coll *coll;
int error, mask;
coll = cur_coll;
switch (opt) {
case PT_HOST:
if (coll->co_host != NULL)
free(coll->co_host);
coll->co_host = value;
break;
case PT_BASE:
if (coll->co_base != NULL)
free(coll->co_base);
coll->co_base = value;
break;
case PT_DATE:
if (coll->co_date != NULL)
free(coll->co_date);
coll->co_date = value;
coll->co_options |= CO_CHECKOUTMODE;
break;
case PT_PREFIX:
if (coll->co_prefix != NULL)
free(coll->co_prefix);
coll->co_prefix = value;
break;
case PT_RELEASE:
if (coll->co_release != NULL)
free(coll->co_release);
coll->co_release = value;
break;
case PT_TAG:
if (coll->co_tag != NULL)
free(coll->co_tag);
coll->co_tag = value;
coll->co_options |= CO_CHECKOUTMODE;
break;
case PT_LIST:
if (strchr(value, '/') != NULL) {
lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
"must not contain slashes\n", cfgfile);
exit(1);
}
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
coll->co_listsuffix = value;
break;
case PT_UMASK:
error = asciitoint(value, &mask, 8);
free(value);
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;
break;
case PT_DELETE:
coll->co_options |= CO_DELETE | CO_EXACTRCS;
break;
case PT_COMPRESS:
coll->co_options |= CO_COMPRESS;
break;
case PT_NORSYNC:
coll->co_options |= CO_NORSYNC;
break;
}
}
/* Set "coll" as being the default collection. */
void
coll_setdef(void)
{
coll_free(defaults);
defaults = cur_coll;
cur_coll = coll_new(defaults);
}

View File

@ -1,127 +0,0 @@
/*-
* 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$
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <time.h>
#include "fattr.h"
#include "misc.h"
/*
* Collection options.
*/
#define CO_BACKUP 0x00000001
#define CO_DELETE 0x00000002
#define CO_KEEP 0x00000004
#define CO_OLD 0x00000008
#define CO_UNLINKBUSY 0x00000010
#define CO_NOUPDATE 0x00000020
#define CO_COMPRESS 0x00000040
#define CO_USERELSUFFIX 0x00000080
#define CO_EXACTRCS 0x00000100
#define CO_CHECKRCS 0x00000200
#define CO_SKIP 0x00000400
#define CO_CHECKOUTMODE 0x00000800
#define CO_NORSYNC 0x00001000
#define CO_KEEPBADFILES 0x00002000
#define CO_EXECUTE 0x00004000
#define CO_SETOWNER 0x00008000
#define CO_SETMODE 0x00010000
#define CO_SETFLAGS 0x00020000
#define CO_NORCS 0x00040000
#define CO_STRICTCHECKRCS 0x00080000
#define CO_TRUSTSTATUSFILE 0x00100000
#define CO_DODELETESONLY 0x00200000
#define CO_DETAILALLRCSFILES 0x00400000
#define CO_MASK 0x007fffff
/* Options that the server is allowed to set. */
#define CO_SERVMAYSET (CO_SKIP | CO_NORSYNC | CO_NORCS)
/* Options that the server is allowed to clear. */
#define CO_SERVMAYCLEAR CO_CHECKRCS
struct coll {
char *co_name;
char *co_host;
char *co_base;
char *co_date;
char *co_prefix;
size_t co_prefixlen;
char *co_release;
char *co_tag;
char *co_cvsroot;
int co_attrignore;
struct pattlist *co_accepts;
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. */
int co_options;
mode_t co_umask;
struct keyword *co_keyword;
STAILQ_ENTRY(coll) co_next;
};
struct config {
STAILQ_HEAD(, coll) colls;
struct fixups *fixups;
char *host;
struct sockaddr *laddr;
socklen_t laddrlen;
int deletelim;
int socket;
struct chan *chan0;
struct chan *chan1;
struct stream *server;
fattr_support_t fasupport;
int reqauth;
};
struct config *config_init(const char *, struct coll *, int);
int config_checkcolls(struct config *);
void config_free(struct config *);
struct coll *coll_new(struct coll *);
void coll_override(struct coll *, struct coll *, int);
char *coll_statuspath(struct coll *);
char *coll_statussuffix(struct coll *);
void coll_add(char *);
void coll_free(struct coll *);
void coll_setdef(void);
void coll_setopt(int, char *);
#endif /* !_CONFIG_H_ */

View File

@ -1,119 +0,0 @@
.\" Copyright 1999-2003 John D. Polstra.
.\" 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.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgment:
.\" This product includes software developed by John D. Polstra.
.\" 4. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
.\"
.\" $Id: cvpasswd.1,v 1.4 2003/03/04 18:24:42 jdp Exp $
.\" $FreeBSD$
.\"
.Dd June 27, 2007
.Dt CPASSWD 1
.Os FreeBSD
.Sh NAME
.Nm cpasswd
.Nd scramble passwords for csup authentication
.Sh SYNOPSIS
.Nm
.Ar clientName
.Ar serverName
.Sh DESCRIPTION
The
.Nm
utility creates scrambled passwords for the
.Nm CVSup
server's authentication database. It is invoked with a client name
and a server name.
.Ar ClientName
is the name the client uses to gain access to the
server. By convention, e-mail addresses are used for all client
names, e.g.,
.Ql BillyJoe@FreeBSD.org .
Client names are case-insensitive.
.Pp
.Ar ServerName
is the name of the
.Nm CVSup
server which the client wishes to access. By convention,
it is the canonical fully-qualified domain name of the server, e.g.,
.Ql CVSup.FreeBSD.ORG .
This must agree with the server's own idea of its name. The name is
case-insensitive.
.Pp
To set up authentication for a given server, one must perform the
following steps:
.Bl -enum
.It
Obtain the official
.Ar serverName
from the administrator of the server or from some other source.
.It
Choose an appropriate
.Ar clientName .
It should be in the form of a valid e-mail address, to make it easy
for the server administrator to contact the user if necessary.
.It
Choose an arbitrary secret
.Ar password .
.It
Run
.Nm cpasswd ,
and type in the
.Ar password
when prompted for it. The utility will print out a line to send
to the server administrator, and instruct you how to modify your
.Li $ Ns Ev HOME Ns Pa /.csup/auth
file. You should use a secure channel to send the line to the
server administrator.
.El
.Pp
Since
.Li $ Ns Ev HOME Ns Pa /.csup/auth
contains passwords, you should ensure that it is not readable by
anyone except yourself.
.Sh FILES
.Bl -tag -width $HOME/.csup/authxx -compact
.It Li $ Ns Ev HOME Ns Pa /.csup/auth
Authentication password file.
.El
.Sh SEE ALSO
.Xr csup 1 ,
.Xr cvsup 1 ,
.Xr cvsupd 8 .
.Bd -literal
http://www.cvsup.org/
.Ed
.Sh AUTHORS
.An -nosplit
.An Petar Zhivkov Petrov Aq Mt pesho.petrov@gmail.com
is the author of
.Nm ,
the rewrite of
.Nm cvpasswd .
.An John Polstra Aq Mt jdp@polstra.com
is the author of
.Nm CVSup .
.Sh LEGALITIES
CVSup is a registered trademark of John D. Polstra.

View File

@ -1,135 +0,0 @@
#! /bin/sh
#
# Copyright 2007. Petar Zhivkov Petrov
# pesho.petrov@gmail.com
#
# $FreeBSD$
usage() {
echo "Usage: $0 clientName serverName"
echo " $0 -v"
}
countChars() {
_count="`echo "$1" | sed -e "s/[^$2]//g" | tr -d "\n" | wc -c`"
return 0
}
readPassword() {
while [ true ]; do
stty -echo
read -p "$1" _password
stty echo
echo ""
countChars "$_password" ":"
if [ $_count != 0 ]; then
echo "Sorry, password must not contain \":\" characters"
echo ""
else
break
fi
done
return 0
}
makeSecret() {
local clientLower="`echo "$1" | tr "[:upper:]" "[:lower:]"`"
local serverLower="`echo "$2" | tr "[:upper:]" "[:lower:]"`"
local secret="`md5 -qs "$clientLower:$serverLower:$3"`"
_secret="\$md5\$$secret"
}
if [ $# -eq 1 -a "X$1" = "X-v" ]; then
echo "Csup authentication key generator"
usage
exit
elif [ $# -ne 2 ]; then
usage
exit
fi
clientName=$1
serverName=$2
#
# Client name must contain exactly one '@' and at least one '.'.
# It must not contain a ':'.
#
countChars "$clientName" "@"
aCount=$_count
countChars "$clientName" "."
dotCount=$_count
if [ $aCount -ne 1 -o $dotCount -eq 0 ]; then
echo "Client name must have the form of an e-mail address,"
echo "e.g., \"user@domain.com\""
exit
fi
countChars "$clientName" ":"
colonCount=$_count
if [ $colonCount -gt 0 ]; then
echo "Client name must not contain \":\" characters"
exit
fi
#
# Server name must not contain '@' and must have at least one '.'.
# It also must not contain a ':'.
#
countChars "$serverName" "@"
aCount=$_count
countChars "$serverName" "."
dotCount=$_count
if [ $aCount != 0 -o $dotCount = 0 ]; then
echo "Server name must be a fully-qualified domain name."
echo "e.g., \"host.domain.com\""
exit
fi
countChars "$serverName" ":"
colonCount=$_count
if [ $colonCount -gt 0 ]; then
echo "Server name must not contain \":\" characters"
exit
fi
#
# Ask for password and generate secret.
#
while [ true ]; do
readPassword "Enter password: "
makeSecret "$clientName" "$serverName" "$_password"
secret=$_secret
readPassword "Enter same password again: "
makeSecret "$clientName" "$serverName" "$_password"
secret2=$_secret
if [ "X$secret" = "X$secret2" ]; then
break
else
echo "Passwords did not match. Try again."
echo ""
fi
done
echo ""
echo "Send this line to the server administrator at $serverName:"
echo "-------------------------------------------------------------------------------"
echo "$clientName:$secret::"
echo "-------------------------------------------------------------------------------"
echo "Be sure to send it using a secure channel!"
echo ""
echo "Add this line to your file \"$HOME/.csup/auth\", replacing \"XXX\""
echo "with the password you typed in:"
echo "-------------------------------------------------------------------------------"
echo "$serverName:$clientName:XXX:"
echo "-------------------------------------------------------------------------------"
echo "Make sure the file is readable and writable only by you!"
echo ""

View File

@ -1,996 +0,0 @@
.\" Copyright 1996-2003 John D. Polstra.
.\" 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 ``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 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.
.\"
.\" $Id: cvsup.1,v 1.70 2003/03/04 18:23:46 jdp Exp $
.\" $FreeBSD$
.\"
.Dd February 8, 2013
.Dt CSUP 1
.Os FreeBSD
.Sh NAME
.Nm csup
.Nd network distribution package for CVS repositories
.Sh SYNOPSIS
.Nm
.Op Fl 146aksvzZ
.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
.Op Fl L Ar verbosity
.Op Fl p Ar port
.Op Fl r Ar maxRetries
.Ar supfile
.Sh DESCRIPTION
.Nm
is a software package for updating collections of files across a network.
It is a rewrite of the
.Nm CVSup
software in C.
This manual page describes the usage of the
.Nm
client program.
.Pp
Unlike more traditional network distribution packages, such as
.Nm rdist
and
.Nm sup ,
.Nm
has specific optimizations for distributing CVS repositories.
.Nm
takes advantage of the properties of CVS repositories and the files they
contain (in particular, RCS files), enabling it to perform updates much
faster than traditional systems.
.Pp
.Nm
is a general-purpose network file updating package.
It is extremely fast,
even for collections of files which have nothing to do with CVS or
RCS.
.Sh OPTIONS
The client program
.Nm
requires at least a single argument,
.Ar supfile .
It names a file describing one or more collections of files to be
transferred and/or updated from the server.
The
.Ar supfile
has a format similar to the corresponding file used by
.Nm sup .
In most cases,
.Nm
can use existing
.Nm sup Ar supfiles .
.Pp
The following options are supported by
.Nm :
.Bl -tag -width Fl
.It Fl 1
Disables automatic retries when transient failures occur.
Without this option, a transient failure such as a dropped network
connection causes
.Nm
to retry repeatedly, using randomized exponential backoff to space the
retries.
This option is equivalent to
.Fl r Cm 0 .
.It Fl 4
Forces
.Nm
to use IPv4 addresses only.
.It Fl 6
Forces
.Nm
to use IPv6 addresses only.
.It Fl a
Requires the server to authenticate itself (prove its identity) to
the client. If authentication of the server fails, the update is
canceled. See
.Sx AUTHENTICATION ,
below.
.It Fl A Ar addr
Specifies a local address to bind to when connecting to the server.
The local address might be a hostname or a numeric host address string
consisting of a dotted decimal IPv4 address or an IPv6 address.
This may be useful on hosts which have multiple IP addresses.
.It Fl b Ar base
Specifies the base directory under which
.Nm
will maintain its bookkeeping files, overriding any
.Cm base
specifications in the
.Ar supfile .
.It Fl c Ar collDir
Specifies the subdirectory of
.Ar base
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
specifications in the
.Ar supfile .
.It Fl i Ar pattern
Causes
.Nm
to include only files and directories matching
.Ar pattern
in the update. If a directory matches the pattern, then the entire
subtree rooted at the directory is included. If this option is
specified multiple times, the patterns are combined using the
.Ql or
operation. If no
.Fl i
options are given, the default is to update all files in each
collection.
.Pp
The
.Ar pattern
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
while the update is in progress.
If
.Ar lockfile
is already locked,
.Nm
fails without performing automatic retries.
This option is useful when
.Nm
is executed periodically from
.Nm cron .
It prevents a job from interfering with an earlier job that is perhaps
taking extra long because of network problems.
.Pp
The process-ID is written to the lock file in text form when the lock
is successfully acquired.
Upon termination of the update, the lock file is removed.
.It Fl L Ar verbosity
Sets the verbosity level for output.
A level of 0 causes
.Nm
to be completely silent unless errors occur.
A level of 1 (the default) causes each updated file to be listed.
A level of 2 provides more detailed information about the updates
performed on each file.
All messages are directed to the standard output.
.It Fl p Ar port
Sets the TCP port to which
.Nm
attempts to connect on the server host.
The default port is 5999.
.It Fl r Ar maxRetries
Limits the number of automatic retries that will be attempted when
transient errors such as lost network connections are encountered.
By default,
.Nm
will retry indefinitely until an update is successfully completed.
The retries are spaced using randomized exponential backoff.
Note that
.Fl r Cm 0
is equivalent to the
.Fl 1
option.
.It Fl s
Suppresses the check of each client file's status against what is
recorded in the list file. Instead, the list file is assumed to be
accurate. This option greatly reduces the amount of disk activity and
results in faster updates with less load on the client host. However
it should only be used if client's files are never modified locally in
any way. Mirror sites may find this option beneficial to reduce the
disk load on their systems. For safety, even mirror sites should run
.Nm
occasionally (perhaps once a day) without the
.Fl s
option.
.Pp
Without the
.Fl s
option,
.Nm
performs a
.Xr stat 2
call on each file and verifies that its attributes match those
recorded in the list file. This ensures that any file changes made
outside of
.Nm
are detected and corrected.
.Pp
If the
.Fl s
option is used when one or more files have been modified locally, the
results are undefined. Local file damage may remain uncorrected,
updates may be missed, or
.Nm
may abort prematurely.
.It Fl v
Prints the version number and exits, without contacting the server.
.It Fl z
Enables compression for all collections, as if the
.Cm compress
keyword were added to every collection in the
.Ar supfile .
.It Fl Z
Disables compression for all collections, as if the
.Cm compress
keyword were removed from every collection in the
.Ar supfile .
.El
.Pp
The
.Ar supfile
is a text file which specifies the file collections to be updated.
Comments begin with
.Ql #
and extend to the end of the line. Lines that are empty except for
comments and white space are ignored. Each remaining line begins
with the name of a server-defined collection of files. Following the
collection name on the line are zero or more keywords or keyword=value
pairs.
.Pp
Default settings may be specified in lines whose collection name is
.Cm *default .
Such defaults will apply to subsequent lines in the
.Ar supfile .
Multiple
.Cm *default
lines may be present.
New values augment or override any defaults specified earlier in the
.Ar supfile .
Values specified explicitly for a collection override any default
values.
.Pp
The most commonly used keywords are:
.Bl -tag -width Fl
.It Cm release= Ns Ar releaseName
This specifies the release of the files within a collection.
Like collection names, release names are defined by the server
configuration files. Usually there is only one release in each
collection, but there may be any number. Collections which come from
a CVS repository often use
.Cm release=cvs
by convention. Non-CVS collections conventionally use
.Cm release=current .
.It Cm base= Ns Ar base
This specifies a directory under which
.Nm
will maintain its bookkeeping files, describing the state of each
collection on the client machine.
The
.Ar base
directory must already exist;
.Nm
will not create it.
The default
.Ar base
directory is
.Pa /usr/local/etc/cvsup .
.It Cm prefix= Ns Ar prefix
This is the directory under which updated files will be placed.
By default, it is the same as
.Ar base .
If it is not an absolute pathname, it is interpreted relative to
.Ar base .
The
.Ar prefix
directory must already exist;
.Nm
will not create it.
.Pp
As a special case, if
.Ar prefix
is a symbolic link pointing to a nonexistent file named
.Ql SKIP ,
then
.Nm
will skip the collection.
The parameters associated with the collection are still checked for
validity, but none of its files will be updated.
This feature allows a site to use a standard
.Ar supfile
on several machines, yet control which collections get updated on a
per-machine basis.
.It Cm host= Ns Ar hostname
This specifies the server machine from which all files will be taken.
.Nm
requires that all collections in a single run come from the same host.
If you wish to update collections from several different hosts, you must
run
.Nm
several times.
.It Cm delete
The presence of this keyword gives
.Nm
permission to delete files.
If it is missing, no files will be deleted.
.Pp
The presence of the
.Cm delete
keyword puts
.Nm
into so-called
.Em exact
mode. In exact mode,
.Nm
does its best to make the client's files correspond to those on the server.
This includes deleting individual deltas and symbolic tags from RCS
files, as well as deleting entire files.
In exact mode,
.Nm
verifies every edited file with a checksum, to ensure that the edits
have produced a file identical to the master copy on the server.
If the checksum test fails for a file, then
.Nm
falls back upon transferring the entire file.
.Pp
In general,
.Nm
deletes only files which are known to the server.
Extra files present in the client's tree are left alone, even in exact
mode.
More precisely,
.Nm
is willing to delete two classes of files:
.Bl -bullet -compact
.It
Files that were previously created or updated by
.Nm
itself.
.It
Checked-out versions of files which are marked as dead on the server.
.El
.It Cm use-rel-suffix
Causes
.Nm
to append a suffix constructed from the release and tag to the name of
each list file that it maintains.
See
.Sx THE LIST FILE
for details.
.It Cm compress
This enables compression of all data sent across the network.
Compression is quite effective, normally eliminating 65% to 75% of the
bytes that would otherwise need to be transferred.
However, it is costly in terms of CPU time on both the client and the
server.
On local area networks, compression is generally counter-productive; it
actually slows down file updates.
On links with speeds of 56K bits/second or less, compression is almost
always beneficial.
For network links with speeds between these two extremes, let
experimentation be your guide.
.Pp
The
.Fl z
command line option enables the
.Cm compress
keyword for all collections, regardless of what is specified in the supfile.
Likewise, the
.Fl Z
command line option disables the
.Cm compress
option for all collections.
.Nm
uses a looser checksum for RCS files, which ignores harmless
differences in white space. Different versions of CVS and RCS produce
a variety of differences in white space for the same RCS files. Thus
the strict checksum can report spurious mismatches for files which are
logically identical. This can lead to numerous unneeded
.Dq fixups ,
and thus to slow updates.
.It Cm umask= Ns Ar n
Causes
.Nm
to use a umask value of
.Ar n
(an octal number) when updating the files in the collection.
This option is ignored if
.Cm preserve
is specified.
.El
.Pp
Some additional, more specialized keywords are described below.
Unrecognized keywords are silently ignored for backward compatibility
with
.Nm sup .
.Sh CVS MODE
.Nm CVSup
supports two primary modes of operation.
They are called
.Em CVS
mode and
.Em checkout
mode.
.Pp
In CVS mode, the client receives copies of the actual RCS files making
up the master CVS repository. CVS mode is the default mode of operation.
It is appropriate when the user wishes to maintain a full copy of the
CVS repository on the client machine.
.Pp
CVS mode is also appropriate for file collections which are not
based upon a CVS repository. The files are simply transferred
verbatim, without interpretation.
.Sh CHECKOUT MODE
In checkout mode, the client receives specific revisions of files,
checked out directly from the server's CVS repository.
Checkout mode allows the client to receive any version from the
repository, without requiring any extra disk space on the server for
storing multiple versions in checked-out form.
Checkout mode provides much flexibility beyond that basic functionality,
however.
The client can specify any CVS symbolic tag, or any date, or both, and
.Nm
will provide the corresponding checked-out versions of the files in the
repository.
.Pp
Checkout mode is selected on a per-collection basis, by the presence of
one or both of the following keywords in the
.Ar supfile :
.Bl -tag -width Fl
.It Cm tag= Ns Ar tagname
This specifies a symbolic tag that should be used to select the
revisions that are checked out from the CVS repository.
The tag may refer to either a branch or a specific revision.
It must be symbolic; numeric revision numbers are not supported.
.Pp
For the FreeBSD source repository, the most commonly used tags will be:
.Bl -tag -width RELENG_6
.It Li RELENG_6
The
.Ql stable
branch.
.It Li \&.
The main branch (the
.Ql current
release).
This is the default, if only the
.Cm date
keyword is given.
.El
.Sm off
.It Xo Cm date=
.Op Ar cc
.Ar yy.mm.dd.hh.mm.ss
.Xc
.Sm on
This specifies a date that should be used to select the revisions that
are checked out from the CVS repository.
The client will receive the revisions that were in effect at the
specified date and time.
.Pp
At present, the date format is inflexible. All 17 or 19 characters must
be specified, exactly as shown.
For the years 2000 and beyond, specify the century
.Ar cc .
For earlier years, specify only the last two digits
.Ar yy .
Dates and times are considered to
be GMT.
The default date is
.Ql \&. ,
which means
.Dq as late as possible .
.El
.Pp
To enable checkout mode, you must specify at least one of these keywords.
If both are missing,
.Nm
defaults to CVS mode.
.Pp
If both a branch tag and a date are specified, then the revisions on the
given branch, as of the given date, will be checked out. It is
permitted, but not particularly useful, to specify a date with a
specific release tag.
.Pp
In checkout mode, the tag and/or date may be changed between updates.
For example, suppose that a collection has been transferred using the
specification
.Ql tag=. .
The user could later change the specification to
.Ql tag=RELENG_3 .
This would cause
.Nm
to edit the checked-out files in such a way as to transform them from the
.Ql current
versions to the
.Ql stable
versions.
In general,
.Nm
is willing to transform any tag/date combination into any other tag/date
combination, by applying the intervening RCS deltas to the existing files.
.Pp
When transforming a collection of checked-out files from one tag to
another, it is important to specify the
.Cm list
keyword in the
.Ar supfile ,
to ensure that the same list file is used both before and after the
transformation.
The list file is described in
.Sx THE LIST FILE ,
below.
.Sh THE LIST FILE
For efficiency,
.Nm
maintains a bookkeeping file for each collection, called the list file.
The list file contains information about which files and revisions the client
currently possesses.
It also contains information used for verifying that the list file
is consistent with the actual files in the client's tree.
.Pp
The list file is not strictly necessary. If it is deleted, or becomes
inconsistent with the actual client files,
.Nm
falls back upon a less efficient method of identifying the client's
files and performing its updates.
Depending on
.Nm csup Ns No 's
mode of operation, the fallback method employs time stamps, checksums, or
analysis of RCS files.
.Pp
Because the list file is not essential,
.Nm
is able to
.Dq adopt
an existing file tree acquired by FTP or from a CD-ROM.
.Nm
identifies the client's versions of the files, updates them as
necessary, and creates a list file for future use.
Adopting a foreign file tree is not as fast as performing a normal
update.
It also produces a heavier load on the server.
.Pp
The list file is stored in a collection-specific directory; see
.Sx FILES
for details.
Its name always begins with
.Ql checkouts .
If the keyword
.Cm use-rel-suffix
is specified in the
.Ar supfile ,
a suffix, formed from the release and tag, is appended to the name.
The default suffix can be overridden by specifying an explicit suffix in
the
.Ar supfile :
.Bl -tag -width Fl
.It Cm list= Ns Ar suffix
This specifies a suffix for the name of the list file. A leading dot is
provided automatically.
For example,
.Ql list=stable
would produce a list file named
.Pa checkouts.stable ,
regardless of the release, tag, or
.Cm use-rel-suffix
keyword.
.El
.Sh REFUSE FILES
The user can specify sets of files that he does not wish to receive.
The files are specified as file name patterns in so-called
.Em refuse
files.
The patterns are separated by whitespace, and multiple patterns are
permitted on each line.
Files and directories matching the patterns are neither updated nor
deleted; they are simply ignored.
.Pp
There is currently no provision for comments in refuse files.
.Pp
The patterns are similar to those of
.Xr sh 1 ,
except that there is no special treatment for slashes or for
filenames that begin with a period.
For example, the pattern
.Ql *.c
will match any file name ending with
.Ql \&.c
including those in subdirectories, such as
.Ql foo/bar/lam.c .
All patterns are interpreted relative to the collection's prefix
directory.
.Pp
If the files are coming from a CVS repository, as is usually
the case, then they will be RCS files. These have a
.Ql \&,v
suffix which must be taken into account in the patterns. For
example, the FreeBSD documentation files are in a sub-directory of
.Ar base
called
.Ql doc .
If
.Ql Makefile
from that directory is not required then the line
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/Makefile
.El
.Pp
will not work because the file on the server is called
.Ql Makefile,v .
A better solution would be
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/Makefile*
.El
.Pp
which will match whether
.Ql Makefile
is an RCS file or not.
.Pp
As another example, to receive the FreeBSD documentation files without
the Japanese, Russian, and Chinese translations, create a refuse file
containing the following lines:
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/ja*
.It
.Pa doc/ru*
.It
.Pa doc/zh*
.El
.Pp
As many as three refuse files are examined for each
.Ar supfile
line.
There can be a global refuse file named
.Sm off
.Ar base / Ar collDir Pa /refuse
.Sm on
which applies to all collections and releases.
There can be a per-collection refuse file named
.Sm off
.Xo Ar base / Ar collDir / Ar collection
.Pa /refuse
.Xc
.Sm on
which applies to a specific collection.
Finally, there can be a per-release and tag refuse file which applies only
to a given release/tag combination within a collection.
The name of the latter is formed by suffixing the name of the
per-collection refuse file in the same manner as described above for the
list file.
None of the refuse files are required to exist.
.Pp
.Nm
has a built-in default value of
.Ar /usr/local/etc/cvsup
for
.Ar base
and
.Ar sup
for
.Ar collDir
but it is possible to override both of these. The value of
.Ar base
can be changed using the
.Fl b
option or a
.Ar base=pathname
entry in the
.Ar supfile .
(If both are used the
.Fl b
option will override the
.Ar supfile
entry.) The value of
.Ar collDir
can only be changed with the
.Fl c
option; there is no
.Ar supfile
command to change it.
.Pp
As an example, suppose that the
.Ar base
and
.Ar collDir
both have their default values, and that the collection and release are
.Ql src-all
and
.Ql cvs ,
respectively.
Assume further that checkout mode is being used with
.Ql tag=RELENG_3 .
The three possible refuse files would then be named:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /usr/local/etc/cvsup/sup/refuse
.It
.Pa /usr/local/etc/cvsup/sup/src-all/refuse
.It
.Pa /usr/local/etc/cvsup/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
If the
.Ar supfile
includes the command
.Ar base=/foo
the refuse files would be:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /foo/sup/refuse
.It
.Pa /foo/sup/src-all/refuse
.It
.Pa /foo/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
If
.Fl b
.Ar /bar
is used (even with
.Ar base=/foo
in the
.Ar supfile ) :
.Pp
.Bl -item -compact -offset indent
.It
.Pa /bar/sup/refuse
.It
.Pa /bar/sup/src-all/refuse
.It
.Pa /bar/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
and with
.Fl c
.Ar stool
as well:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /bar/stool/refuse
.It
.Pa /bar/stool/src-all/refuse
.It
.Pa /bar/stool/src-all/refuse.cvs:RELENG_3
.El
.Sh AUTHENTICATION
.Nm
implements an optional authentication mechanism which can be used by the
client and server to verify each other's identities.
Public CVSup servers normally do not enable authentication.
.Nm
users may ignore this section unless they have been informed
that authentication is required by the administrator of their server.
.Pp
The authentication subsystem uses a
challenge-response protocol which is immune to packet sniffing and
replay attacks. No passwords are sent over the network in either
direction. Both the client and the server can independently verify
the identities of each other.
.Pp
The file
.Li $ Ns Ev HOME Ns Pa /.csup/auth
holds the information used for authentication. This file contains a
record for each server that the client is allowed to access. Each
record occupies one line in the file. Lines beginning with
.Ql #
are ignored, as are lines containing only white space. White space is
significant everywhere else in the file. Fields are separated by
.Ql \&:
characters.
.Pp
Each record of the file has the following form:
.Bd -literal -offset indent
.Sm off
.Xo Ar serverName No : Ar clientName No :
.Ar password No : Ar comment
.Xc
.Sm on
.Ed
.Pp
All fields must be present even if some of them are empty.
.Ar ServerName
is the name of the server to which the record applies. By convention,
it is the canonical fully-qualified domain name of the server, e.g.,
.Ql CVSup177.FreeBSD.ORG .
This must agree with the server's own idea of its name. The name is
case-insensitive.
.Pp
.Ar ClientName
is the name the client uses to gain access to the server. By
convention, e-mail addresses are used for all client names, e.g.,
.Ql BillyJoe@FreeBSD.org .
Client names are case-insensitive.
.Pp
.Ar Password
is a secret string of characters that the client uses to prove its
identity. It may not contain any
.Ql \&:
or newline characters.
.Pp
.Ar Comment
may contain any additional information to identify the record. It
is not interpreted by the program.
.Pp
To set up authentication for a given server, one must perform the
following steps:
.Bl -enum
.It
Obtain the official
.Ar serverName
from the administrator of the server or from some other source.
.It
Choose an appropriate
.Ar clientName .
It should be in the form of a valid e-mail address, to make it easy
for the server administrator to contact the user if necessary.
.It
Choose an arbitrary secret
.Ar password .
.It
Run the
.Nm cpasswd
utility, and type in the
.Ar password
when prompted for it. The utility will print out a line to send
to the server administrator, and instruct you how to modify your
.Li $ Ns Ev HOME Ns Pa /.csup/auth
file. You should use a secure channel to send the line to the
server administrator.
.El
.Pp
Since
.Li $ Ns Ev HOME Ns Pa /.csup/auth
contains passwords, you should ensure that it is not readable by
anyone except yourself.
.Pp
Authentication works independently in both directions. The server
administrator controls whether you must prove your identity.
You control whether to check the server's identity, by means of the
.Fl a
command line option.
.Sh csup AND FIREWALLS
In its default mode,
.Nm
will work through any firewall which permits outbound connections to
port 5999 of the server host.
.Sh USING csup WITH SOCKS
.Nm
can be used through a SOCKS proxy server with the
.Nm tsocks
command.
The
.Nm
executable needs to be dynamically-linked with the system
libraries for
.Nm tsocks
to work properly.
.Sh USING ssh PORT FORWARDING
As an alternative to SOCKS, a user behind a firewall can penetrate it
with the TCP port forwarding provided by the Secure Shell package
.Nm ssh .
The user must have a login account on the
.Nm CVSup
server host in order to do this.
The procedure is as follows:
.Bl -enum
.It
Establish a connection to the server host with
.Nm ssh ,
like this:
.Bd -literal
ssh -f -x -L 5999:localhost:5999 serverhost sleep 60
.Ed
.Pp
Replace
.Ar serverhost
with the hostname of the CVSup server, but type
.Ql localhost
literally.
This sets up the required port forwarding.
You must start
.Nm
before the 60-second
.Nm sleep
finishes.
Once the update has begun,
.Nm ssh
will keep the forwarded channels open as long as they are needed.
.It
Run
.Nm
on the local host, including the arguments
.Ql -h localhost
on the command line.
.El
.Sh FILES
.Bl -tag -width base/sup/collection/checkouts*xx -compact
.It Pa /usr/local/etc/cvsup
Default
.Ar base
directory.
.It Pa sup
Default
.Ar collDir
subdirectory.
.Sm off
.It Xo Ar base / Ar collDir / Ar collection
.Pa /checkouts*
.Xc
.Sm on
List files.
.El
.Sh SEE ALSO
.Xr cpasswd 1 ,
.Xr cvs 1 ,
.Xr rcsintro 1 ,
.Xr ssh 1 .
.Sh AUTHORS
.An -nosplit
.An Maxime Henrion Aq Mt mux@FreeBSD.org
is the author of
.Nm ,
the rewrite of
.Nm CVSup
in C.
.An John Polstra Aq Mt jdp@polstra.com
is the author of
.Nm CVSup .
.Sh LEGALITIES
CVSup is a registered trademark of John D. Polstra.
.Pp
.Nm
is released under a 2-clauses BSD license.
.Sh BUGS
An RCS file is not recognized as such unless its name ends with
.Ql \&,v .
.Pp
Any directory named
.Ql Attic
is assumed to be a CVS Attic, and is treated specially.

View File

@ -1,603 +0,0 @@
/*-
* 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 <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "config.h"
#include "detailer.h"
#include "fixups.h"
#include "globtree.h"
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "rcsfile.h"
#include "rsyncfile.h"
#include "status.h"
#include "stream.h"
/* Internal error codes. */
#define DETAILER_ERR_PROTO (-1) /* Protocol error. */
#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */
#define DETAILER_ERR_READ (-3) /* Error reading from server. */
#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */
struct detailer {
struct config *config;
struct stream *rd;
struct stream *wr;
char *errmsg;
};
static int detailer_batch(struct detailer *);
static int detailer_coll(struct detailer *, struct coll *,
struct status *);
static int detailer_dofile_co(struct detailer *, struct coll *,
struct status *, char *);
static int detailer_dofile_rcs(struct detailer *, struct coll *,
char *, char *);
static int detailer_dofile_regular(struct detailer *, char *, char *);
static int detailer_dofile_rsync(struct detailer *, char *, char *);
static int detailer_checkrcsattr(struct detailer *, struct coll *, char *,
struct fattr *, int);
int detailer_send_details(struct detailer *, struct coll *, char *,
char *, struct fattr *);
void *
detailer(void *arg)
{
struct thread_args *args;
struct detailer dbuf, *d;
int error;
args = arg;
d = &dbuf;
d->config = args->config;
d->rd = args->rd;
d->wr = args->wr;
d->errmsg = NULL;
error = detailer_batch(d);
switch (error) {
case DETAILER_ERR_PROTO:
xasprintf(&args->errmsg, "Detailer failed: Protocol error");
args->status = STATUS_FAILURE;
break;
case DETAILER_ERR_MSG:
xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg);
free(d->errmsg);
args->status = STATUS_FAILURE;
break;
case DETAILER_ERR_READ:
if (stream_eof(d->rd)) {
xasprintf(&args->errmsg, "Detailer failed: "
"Premature EOF from server");
} else {
xasprintf(&args->errmsg, "Detailer failed: "
"Network read failure: %s", strerror(errno));
}
args->status = STATUS_TRANSIENTFAILURE;
break;
case DETAILER_ERR_WRITE:
xasprintf(&args->errmsg, "Detailer failed: "
"Network write failure: %s", strerror(errno));
args->status = STATUS_TRANSIENTFAILURE;
break;
default:
assert(error == 0);
args->status = STATUS_SUCCESS;
}
return (NULL);
}
static int
detailer_batch(struct detailer *d)
{
struct config *config;
struct stream *rd, *wr;
struct coll *coll;
struct status *st;
struct fixup *fixup;
char *cmd, *collname, *line, *release;
int error, fixupseof;
config = d->config;
rd = d->rd;
wr = d->wr;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
line = stream_getln(rd, NULL);
cmd = proto_get_ascii(&line);
collname = proto_get_ascii(&line);
release = proto_get_ascii(&line);
error = proto_get_time(&line, &coll->co_scantime);
if (error || line != NULL || strcmp(cmd, "COLL") != 0 ||
strcmp(collname, coll->co_name) != 0 ||
strcmp(release, coll->co_release) != 0)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (DETAILER_ERR_WRITE);
stream_flush(wr);
if (coll->co_options & CO_COMPRESS) {
stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
}
st = status_open(coll, -1, &d->errmsg);
if (st == NULL)
return (DETAILER_ERR_MSG);
error = detailer_coll(d, coll, st);
status_close(st, NULL);
if (error)
return (error);
if (coll->co_options & CO_COMPRESS) {
stream_filter_stop(rd);
stream_filter_stop(wr);
}
stream_flush(wr);
}
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
if (strcmp(line, ".") != 0)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
stream_flush(wr);
/* Now send fixups if needed. */
fixup = NULL;
fixupseof = 0;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (DETAILER_ERR_WRITE);
if (coll->co_options & CO_COMPRESS)
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
while (!fixupseof) {
if (fixup == NULL)
fixup = fixups_get(config->fixups);
if (fixup == NULL) {
fixupseof = 1;
break;
}
if (fixup->f_coll != coll)
break;
if (coll->co_options & CO_CHECKOUTMODE)
error = proto_printf(wr, "Y %s %s %s\n",
fixup->f_name, coll->co_tag, coll->co_date);
else {
error = proto_printf(wr, "A %s\n",
fixup->f_name);
}
if (error)
return (DETAILER_ERR_WRITE);
fixup = NULL;
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
if (coll->co_options & CO_COMPRESS)
stream_filter_stop(wr);
stream_flush(wr);
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
static int
detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
{
struct fattr *rcsattr;
struct stream *rd, *wr;
char *attr, *cmd, *file, *line, *msg, *path, *target;
int error, attic;
rd = d->rd;
wr = d->wr;
attic = 0;
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
while (strcmp(line, ".") != 0) {
cmd = proto_get_ascii(&line);
if (cmd == NULL || strlen(cmd) != 1)
return (DETAILER_ERR_PROTO);
switch (cmd[0]) {
case 'D':
/* Delete file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "D %s\n", file);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'I':
case 'i':
case 'j':
/* Directory operations. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s\n", cmd, file);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'J':
/* Set directory attributes. */
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || line != NULL || attr == NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s %s\n", cmd, file, attr);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'H':
case 'h':
/* Create a hard link. */
file = proto_get_ascii(&line);
target = proto_get_ascii(&line);
if (file == NULL || target == NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s %s\n", cmd, file,
target);
break;
case 't':
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || attr == NULL || line != NULL) {
return (DETAILER_ERR_PROTO);
}
rcsattr = fattr_decode(attr);
if (rcsattr == NULL) {
return (DETAILER_ERR_PROTO);
}
error = detailer_checkrcsattr(d, coll, file, rcsattr,
1);
break;
case 'T':
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || attr == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
rcsattr = fattr_decode(attr);
if (rcsattr == NULL)
return (DETAILER_ERR_PROTO);
error = detailer_checkrcsattr(d, coll, file, rcsattr,
0);
break;
case 'U':
/* Add or update file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
if (coll->co_options & CO_CHECKOUTMODE) {
error = detailer_dofile_co(d, coll, st, file);
} else {
path = cvspath(coll->co_prefix, file, 0);
rcsattr = fattr_frompath(path, FATTR_NOFOLLOW);
error = detailer_send_details(d, coll, file,
path, rcsattr);
if (rcsattr != NULL)
fattr_free(rcsattr);
free(path);
}
if (error)
return (error);
break;
case '!':
/* Warning from server. */
msg = proto_get_rest(&line);
if (msg == NULL)
return (DETAILER_ERR_PROTO);
lprintf(-1, "Server warning: %s\n", msg);
break;
default:
return (DETAILER_ERR_PROTO);
}
stream_flush(wr);
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/*
* Tell the server to update a regular file.
*/
static int
detailer_dofile_regular(struct detailer *d, char *name, char *path)
{
struct stream *wr;
struct stat st;
char md5[MD5_DIGEST_SIZE];
int error;
wr = d->wr;
error = stat(path, &st);
/* If we don't have it or it's unaccessible, we want it again. */
if (error) {
proto_printf(wr, "A %s\n", name);
return (0);
}
/* If not, we want the file to be updated. */
error = MD5_File(path, md5);
if (error) {
lprintf(-1, "Error reading \"%s\"\n", name);
return (error);
}
error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/*
* Tell the server to update a file with the rsync algorithm.
*/
static int
detailer_dofile_rsync(struct detailer *d, char *name, char *path)
{
struct stream *wr;
struct rsyncfile *rf;
wr = d->wr;
rf = rsync_open(path, 0, 1);
if (rf == NULL) {
/* Fallback if we fail in opening it. */
proto_printf(wr, "A %s\n", name);
return (0);
}
proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf),
rsync_blocksize(rf));
/* Detail the blocks. */
while (rsync_nextblock(rf) != 0)
proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf));
proto_printf(wr, ".\n");
rsync_close(rf);
return (0);
}
/*
* Tell the server to update an RCS file that we have, or send it if we don't.
*/
static int
detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name,
char *path)
{
struct stream *wr;
struct fattr *fa;
struct rcsfile *rf;
int error;
wr = d->wr;
path = atticpath(coll->co_prefix, name);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* We don't have it, so send request to get it. */
error = proto_printf(wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
free(path);
return (0);
}
rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1);
free(path);
if (rf == NULL) {
error = proto_printf(wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/* Tell to update the RCS file. The client version details follow. */
rcsfile_send_details(rf, wr);
rcsfile_free(rf);
fattr_free(fa);
return (0);
}
static int
detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st,
char *file)
{
struct stream *wr;
struct fattr *fa;
struct statusrec *sr;
char md5[MD5_DIGEST_SIZE];
char *path;
int error, ret;
wr = d->wr;
path = checkoutpath(coll->co_prefix, file);
if (path == NULL)
return (DETAILER_ERR_PROTO);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* We don't have the file, so the only option at this
point is to tell the server to send it. The server
may figure out that the file is dead, in which case
it will tell us. */
error = proto_printf(wr, "C %s %s %s\n",
file, coll->co_tag, coll->co_date);
free(path);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
ret = status_get(st, file, 0, 0, &sr);
if (ret == -1) {
d->errmsg = status_errmsg(st);
free(path);
return (DETAILER_ERR_MSG);
}
if (ret == 0)
sr = NULL;
/* If our recorded information doesn't match the file that the
client has, then ignore the recorded information. */
if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE ||
!fattr_equal(sr->sr_clientattr, fa)))
sr = NULL;
fattr_free(fa);
if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) {
error = proto_printf(wr, "U %s %s %s %s %s\n", file,
coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate);
free(path);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/*
* We don't have complete and/or accurate recorded information
* about what version of the file we have. Compute the file's
* checksum as an aid toward identifying which version it is.
*/
error = MD5_File(path, md5);
if (error) {
xasprintf(&d->errmsg,
"Cannot calculate checksum for \"%s\": %s", path,
strerror(errno));
return (DETAILER_ERR_MSG);
}
free(path);
if (sr == NULL) {
error = proto_printf(wr, "S %s %s %s %s\n", file,
coll->co_tag, coll->co_date, md5);
} else {
error = proto_printf(wr, "s %s %s %s %s %s\n", file,
coll->co_tag, coll->co_date, sr->sr_revnum, md5);
}
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
int
detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name,
struct fattr *server_attr, int attic)
{
struct fattr *client_attr;
char *attr, *path;
int error;
/*
* I don't think we can use the status file, since it only records file
* attributes in cvsmode.
*/
client_attr = NULL;
path = cvspath(coll->co_prefix, name, attic);
if (path == NULL) {
return (DETAILER_ERR_PROTO);
}
if (access(path, F_OK) == 0 &&
((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) &&
fattr_equal(client_attr, server_attr)) {
attr = fattr_encode(client_attr, NULL, 0);
if (attic) {
error = proto_printf(d->wr, "l %s %s\n", name, attr);
} else {
error = proto_printf(d->wr, "L %s %s\n", name, attr);
}
free(attr);
free(path);
fattr_free(client_attr);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/* We don't have it, so tell the server to send it. */
error = detailer_send_details(d, coll, name, path, client_attr);
fattr_free(client_attr);
free(path);
return (error);
}
int
detailer_send_details(struct detailer *d, struct coll *coll, char *name,
char *path, struct fattr *fa)
{
int error;
size_t len;
/*
* Try to check if the file exists either live or dead to see if we can
* edit it and put it live or dead, rather than receiving the entire
* file.
*/
if (fa == NULL) {
path = atticpath(coll->co_prefix, name);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
}
if (fa == NULL) {
error = proto_printf(d->wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
} else if (fattr_type(fa) == FT_FILE) {
if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) {
detailer_dofile_rcs(d, coll, name, path);
} else if (!(coll->co_options & CO_NORSYNC) &&
!globtree_test(coll->co_norsync, name)) {
detailer_dofile_rsync(d, name, path);
} else {
detailer_dofile_regular(d, name, path);
}
} else {
error = proto_printf(d->wr, "N %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
}
return (0);
}

View File

@ -1,33 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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 _DETAILER_H_
#define _DETAILER_H_
void *detailer(void *);
#endif /* !_DETAILER_H_ */

View File

@ -1,438 +0,0 @@
/*-
* 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/queue.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "diff.h"
#include "keyword.h"
#include "misc.h"
#include "stream.h"
typedef long lineno_t;
#define EC_ADD 0
#define EC_DEL 1
#define MAXKEY LONG_MAX
/* Editing command and state. */
struct editcmd {
int cmd;
long key;
int havetext;
int offset;
lineno_t where;
lineno_t count;
lineno_t lasta;
lineno_t lastd;
lineno_t editline;
/* For convenience. */
struct keyword *keyword;
struct diffinfo *di;
struct stream *orig;
struct stream *dest;
LIST_ENTRY(editcmd) next;
};
struct diffstart {
LIST_HEAD(, editcmd) dhead;
};
static int diff_geteditcmd(struct editcmd *, char *);
static int diff_copyln(struct editcmd *, lineno_t);
static int diff_ignoreln(struct editcmd *, lineno_t);
static void diff_write(struct editcmd *, void *, size_t);
static int diff_insert_edit(struct diffstart *, struct editcmd *);
static void diff_free(struct diffstart *);
int
diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
struct keyword *keyword, struct diffinfo *di, int comode)
{
struct editcmd ec;
lineno_t i;
size_t size;
char *line;
int empty, error, noeol;
memset(&ec, 0, sizeof(ec));
empty = 0;
noeol = 0;
ec.di = di;
ec.keyword = keyword;
ec.orig = orig;
ec.dest = dest;
line = stream_getln(rd, NULL);
while (line != NULL && strcmp(line, ".") != 0 &&
strcmp(line, ".+") != 0) {
/*
* The server sends an empty line and then terminates
* with .+ for forced (and thus empty) commits.
*/
if (*line == '\0') {
if (empty)
return (-1);
empty = 1;
line = stream_getln(rd, NULL);
continue;
}
error = diff_geteditcmd(&ec, line);
if (error)
return (-1);
if (ec.cmd == EC_ADD) {
error = diff_copyln(&ec, ec.where);
if (error)
return (-1);
for (i = 0; i < ec.count; i++) {
line = stream_getln(rd, &size);
if (line == NULL)
return (-1);
if (comode && line[0] == '.') {
line++;
size--;
}
diff_write(&ec, line, size);
}
} else {
assert(ec.cmd == EC_DEL);
error = diff_copyln(&ec, ec.where - 1);
if (error)
return (-1);
for (i = 0; i < ec.count; i++) {
line = stream_getln(orig, NULL);
if (line == NULL)
return (-1);
ec.editline++;
}
}
line = stream_getln(rd, NULL);
}
if (comode && line == NULL)
return (-1);
/* If we got ".+", there's no ending newline. */
if (comode && strcmp(line, ".+") == 0 && !empty)
noeol = 1;
ec.where = 0;
while ((line = stream_getln(orig, &size)) != NULL)
diff_write(&ec, line, size);
stream_flush(dest);
if (noeol) {
error = stream_truncate_rel(dest, -1);
if (error) {
warn("stream_truncate_rel");
return (-1);
}
}
return (0);
}
/*
* Reverse a diff using the same algorithm as in cvsup.
*/
static int
diff_write_reverse(struct stream *dest, struct diffstart *ds)
{
struct editcmd *ec, *nextec;
long editline, endline, firstoutputlinedeleted;
long num_added, num_deleted, startline;
int num;
nextec = LIST_FIRST(&ds->dhead);
editline = 0;
num = 0;
while (nextec != NULL) {
ec = nextec;
nextec = LIST_NEXT(nextec, next);
if (nextec == NULL)
break;
num++;
num_deleted = 0;
if (ec->havetext)
num_deleted = ec->count;
num_added = num_deleted + nextec->offset - ec->offset;
if (num_deleted > 0) {
firstoutputlinedeleted = ec->key - num_deleted + 1;
stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted,
num_deleted);
if (num_added <= 0)
continue;
}
if (num_added > 0) {
stream_printf(dest, "a%ld %ld\n", ec->key, num_added);
startline = ec->key - num_deleted + 1 + ec->offset;
endline = startline + num_added - 1;
/* Copy lines from original file. First ignore some. */
ec->editline = editline;
diff_ignoreln(ec, startline - 1);
diff_copyln(ec, endline);
editline = ec->editline;
}
}
return (0);
}
/*
* Insert a diff into the list sorted on key. Should perhaps use quicker
* algorithms than insertion sort, but do this for now.
*/
static int
diff_insert_edit(struct diffstart *ds, struct editcmd *ec)
{
struct editcmd *curec;
if (ec == NULL)
return (0);
if (LIST_EMPTY(&ds->dhead)) {
LIST_INSERT_HEAD(&ds->dhead, ec, next);
return (0);
}
/* Insertion sort based on key. */
LIST_FOREACH(curec, &ds->dhead, next) {
if (ec->key < curec->key) {
LIST_INSERT_BEFORE(curec, ec, next);
return (0);
}
if (LIST_NEXT(curec, next) == NULL)
break;
}
/* Just insert it after. */
LIST_INSERT_AFTER(curec, ec, next);
return (0);
}
static void
diff_free(struct diffstart *ds)
{
struct editcmd *ec;
while(!LIST_EMPTY(&ds->dhead)) {
ec = LIST_FIRST(&ds->dhead);
LIST_REMOVE(ec, next);
free(ec);
}
}
/*
* Write the reverse diff from the diff in rd, and original file into
* destination. This algorithm is the same as used in cvsup.
*/
int
diff_reverse(struct stream *rd, struct stream *orig, struct stream *dest,
struct keyword *keyword, struct diffinfo *di)
{
struct diffstart ds;
struct editcmd ec, *addec, *delec;
lineno_t i;
char *line;
int error, offset;
memset(&ec, 0, sizeof(ec));
ec.orig = orig;
ec.dest = dest;
ec.keyword = keyword;
ec.di = di;
addec = NULL;
delec = NULL;
ec.havetext = 0;
offset = 0;
LIST_INIT(&ds.dhead);
/* Start with next since we need it. */
line = stream_getln(rd, NULL);
/* First we build up the list of diffs from input. */
while (line != NULL) {
error = diff_geteditcmd(&ec, line);
if (error)
break;
if (ec.cmd == EC_ADD) {
addec = xmalloc(sizeof(struct editcmd));
*addec = ec;
addec->havetext = 1;
/* Ignore the lines we was supposed to add. */
for (i = 0; i < ec.count; i++) {
line = stream_getln(rd, NULL);
if (line == NULL)
return (-1);
}
/* Get the next diff command if we have one. */
addec->key = addec->where + addec->count - offset;
if (delec != NULL &&
delec->key == addec->key - addec->count) {
delec->key = addec->key;
delec->havetext = addec->havetext;
delec->count = addec->count;
diff_insert_edit(&ds, delec);
free(addec);
delec = NULL;
addec = NULL;
} else {
if (delec != NULL) {
diff_insert_edit(&ds, delec);
}
delec = NULL;
addec->offset = offset;
diff_insert_edit(&ds, addec);
addec = NULL;
}
offset -= ec.count;
} else if (ec.cmd == EC_DEL) {
if (delec != NULL) {
/* Update offset to our next. */
diff_insert_edit(&ds, delec);
delec = NULL;
}
delec = xmalloc(sizeof(struct editcmd));
*delec = ec;
delec->key = delec->where - 1 - offset;
delec->offset = offset;
delec->count = 0;
delec->havetext = 0;
/* Important to use the count we had before reset.*/
offset += ec.count;
}
line = stream_getln(rd, NULL);
}
while (line != NULL)
line = stream_getln(rd, NULL);
if (delec != NULL) {
diff_insert_edit(&ds, delec);
delec = NULL;
}
addec = xmalloc(sizeof(struct editcmd));
/* Should be filesize, but we set it to max value. */
addec->key = MAXKEY;
addec->offset = offset;
addec->havetext = 0;
addec->count = 0;
diff_insert_edit(&ds, addec);
addec = NULL;
diff_write_reverse(dest, &ds);
diff_free(&ds);
stream_flush(dest);
return (0);
}
/* Get an editing command from the diff. */
static int
diff_geteditcmd(struct editcmd *ec, char *line)
{
char *end;
if (line[0] == 'a')
ec->cmd = EC_ADD;
else if (line[0] == 'd')
ec->cmd = EC_DEL;
else
return (-1);
errno = 0;
ec->where = strtol(line + 1, &end, 10);
if (errno || ec->where < 0 || *end != ' ')
return (-1);
line = end + 1;
errno = 0;
ec->count = strtol(line, &end, 10);
if (errno || ec->count <= 0 || *end != '\0')
return (-1);
if (ec->cmd == EC_ADD) {
if (ec->where < ec->lasta)
return (-1);
ec->lasta = ec->where + 1;
} else {
if (ec->where < ec->lasta || ec->where < ec->lastd)
return (-1);
ec->lasta = ec->where;
ec->lastd = ec->where + ec->count;
}
return (0);
}
/* Copy lines from the original version of the file up to line "to". */
static int
diff_copyln(struct editcmd *ec, lineno_t to)
{
size_t size;
char *line;
while (ec->editline < to) {
line = stream_getln(ec->orig, &size);
if (line == NULL)
return (-1);
ec->editline++;
diff_write(ec, line, size);
}
return (0);
}
/* Ignore lines from the original version of the file up to line "to". */
static int
diff_ignoreln(struct editcmd *ec, lineno_t to)
{
size_t size;
char *line;
while (ec->editline < to) {
line = stream_getln(ec->orig, &size);
if (line == NULL)
return (-1);
ec->editline++;
}
return (0);
}
/* Write a new line to the file, expanding RCS keywords appropriately. */
static void
diff_write(struct editcmd *ec, void *buf, size_t size)
{
size_t newsize;
char *line, *newline;
int ret;
line = buf;
ret = keyword_expand(ec->keyword, ec->di, line, size,
&newline, &newsize);
if (ret) {
stream_write(ec->dest, newline, newsize);
free(newline);
} else {
stream_write(ec->dest, buf, size);
}
}

View File

@ -1,52 +0,0 @@
/*-
* 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$
*/
#ifndef _DIFF_H_
#define _DIFF_H_
struct stream;
struct keyword;
struct file_update;
/* Description of an RCS delta. */
struct diffinfo {
char *di_rcsfile; /* RCS filename */
char *di_cvsroot; /* CVS root prefix */
char *di_revnum; /* Revision number */
char *di_revdate; /* Revision date */
char *di_author; /* Author of the delta */
char *di_tag; /* CVS tag, if any */
char *di_state; /* State of the branch */
int di_expand; /* CVS expansion mode */
};
int diff_apply(struct stream *, struct stream *, struct stream *,
struct keyword *, struct diffinfo *, int);
int diff_reverse(struct stream *, struct stream *,
struct stream *, struct keyword *, struct diffinfo *);
#endif /* !_DIFF_H_ */

View File

@ -1,981 +0,0 @@
/*-
* 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);
}

View File

@ -1,118 +0,0 @@
/*-
* 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$
*/
#ifndef _FATTR_H_
#define _FATTR_H_
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
/*
* File types.
*/
#define FT_UNKNOWN 0 /* Unknown file type. */
#define FT_FILE 1 /* Regular file. */
#define FT_DIRECTORY 2 /* Directory. */
#define FT_CDEV 3 /* Character device. */
#define FT_BDEV 4 /* Block device. */
#define FT_SYMLINK 5 /* Symbolic link. */
#define FT_MAX FT_SYMLINK /* Maximum file type number. */
#define FT_NUMBER (FT_MAX + 1) /* Number of file types. */
/*
* File attributes.
*/
#define FA_FILETYPE 0x0001 /* True for all supported file types. */
#define FA_MODTIME 0x0002 /* Last file modification time. */
#define FA_SIZE 0x0004 /* Size of the file. */
#define FA_LINKTARGET 0x0008 /* Target of a symbolic link. */
#define FA_RDEV 0x0010 /* Device for a device node. */
#define FA_OWNER 0x0020 /* Owner of the file. */
#define FA_GROUP 0x0040 /* Group of the file. */
#define FA_MODE 0x0080 /* File permissions. */
#define FA_FLAGS 0x0100 /* 4.4BSD flags, a la chflags(2). */
#define FA_LINKCOUNT 0x0200 /* Hard link count. */
#define FA_DEV 0x0400 /* Device holding the inode. */
#define FA_INODE 0x0800 /* Inode number. */
#define FA_MASK 0x0fff
#define FA_NUMBER 12 /* Number of file attributes. */
/* Attributes that we might be able to change. */
#define FA_CHANGEABLE (FA_MODTIME | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS)
/*
* Attributes that we don't want to save in the "checkouts" file
* when in checkout mode.
*/
#define FA_COIGNORE (FA_MASK & ~(FA_FILETYPE|FA_MODTIME|FA_SIZE|FA_MODE))
/* These are for fattr_frompath(). */
#define FATTR_FOLLOW 0
#define FATTR_NOFOLLOW 1
struct stat;
struct fattr;
typedef int fattr_support_t[FT_NUMBER];
extern const struct fattr *fattr_bogus;
void fattr_init(void);
void fattr_fini(void);
struct fattr *fattr_new(int, time_t);
struct fattr *fattr_default(int);
struct fattr *fattr_fromstat(struct stat *);
struct fattr *fattr_frompath(const char *, int);
struct fattr *fattr_fromfd(int);
struct fattr *fattr_decode(char *);
struct fattr *fattr_forcheckout(const struct fattr *, mode_t);
struct fattr *fattr_dup(const struct fattr *);
char *fattr_encode(const struct fattr *, fattr_support_t, int);
int fattr_type(const struct fattr *);
void fattr_maskout(struct fattr *, int);
int fattr_getmask(const struct fattr *);
nlink_t fattr_getlinkcount(const struct fattr *);
char *fattr_getlinktarget(const struct fattr *);
void fattr_umask(struct fattr *, mode_t);
void fattr_merge(struct fattr *, const struct fattr *);
void fattr_mergedefault(struct fattr *);
void fattr_override(struct fattr *, const struct fattr *, int);
int fattr_makenode(const struct fattr *, const char *);
int fattr_delete(const char *path);
int fattr_install(struct fattr *, const char *, const char *);
int fattr_equal(const struct fattr *, const struct fattr *);
void fattr_free(struct fattr *);
int fattr_supported(int);
off_t fattr_filesize(const struct fattr *);
#endif /* !_FATTR_H_ */

View File

@ -1,52 +0,0 @@
/*-
* 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.
*
* $Id$
*/
/*
* The file attributes we support in a BSD environment.
*
* This is similar to fattr_posix.h, except that we support the FA_FLAGS
* attribute when it makes sense. The FA_FLAGS attribute is for the
* extended BSD file flags, see chflags(2).
*/
fattr_support_t fattr_support = {
/* FT_UNKNOWN */
0,
/* FT_FILE */
FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE |
FA_FLAGS | FA_LINKCOUNT | FA_INODE | FA_DEV,
/* FT_DIRECTORY */
FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS,
/* FT_CDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS |
FA_LINKCOUNT | FA_DEV | FA_INODE,
/* FT_BDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS |
FA_LINKCOUNT | FA_DEV | FA_INODE,
/* FT_SYMLINK */
FA_FILETYPE | FA_LINKTARGET
};

View File

@ -1,48 +0,0 @@
/*-
* 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.
*
* $Id$
*/
/*
* The file attributes we support in a POSIX environment.
*/
fattr_support_t fattr_support = {
/* FT_UNKNOWN */
0,
/* FT_FILE */
FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE |
FA_LINKCOUNT | FA_INODE | FA_DEV,
/* FT_DIRECTORY */
FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE,
/* FT_CDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT |
FA_DEV | FA_INODE,
/* FT_BDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT |
FA_DEV | FA_INODE,
/* FT_SYMLINK */
FA_FILETYPE | FA_LINKTARGET
};

View File

@ -1,199 +0,0 @@
/*-
* 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/queue.h>
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "fixups.h"
#include "misc.h"
/*
* A synchronized queue to implement fixups. The updater thread adds
* fixup requests to the queue with fixups_put() when a checksum
* mismatch error occurred. It then calls fixups_close() when he's
* done requesting fixups. The detailer thread gets the fixups with
* fixups_get() and then send the requests to the server.
*
* The queue is synchronized with a mutex and a condition variable.
*/
struct fixups {
pthread_mutex_t lock;
pthread_cond_t cond;
STAILQ_HEAD(, fixup) fixupq;
struct fixup *cur;
size_t size;
int closed;
};
static void fixups_lock(struct fixups *);
static void fixups_unlock(struct fixups *);
static struct fixup *fixup_new(struct coll *, const char *);
static void fixup_free(struct fixup *);
static void
fixups_lock(struct fixups *f)
{
int error;
error = pthread_mutex_lock(&f->lock);
assert(!error);
}
static void
fixups_unlock(struct fixups *f)
{
int error;
error = pthread_mutex_unlock(&f->lock);
assert(!error);
}
static struct fixup *
fixup_new(struct coll *coll, const char *name)
{
struct fixup *fixup;
fixup = xmalloc(sizeof(struct fixup));
fixup->f_name = xstrdup(name);
fixup->f_coll = coll;
return (fixup);
}
static void
fixup_free(struct fixup *fixup)
{
free(fixup->f_name);
free(fixup);
}
/* Create a new fixup queue. */
struct fixups *
fixups_new(void)
{
struct fixups *f;
f = xmalloc(sizeof(struct fixups));
f->size = 0;
f->closed = 0;
f->cur = NULL;
STAILQ_INIT(&f->fixupq);
pthread_mutex_init(&f->lock, NULL);
pthread_cond_init(&f->cond, NULL);
return (f);
}
/* Add a fixup request to the queue. */
void
fixups_put(struct fixups *f, struct coll *coll, const char *name)
{
struct fixup *fixup;
int dosignal;
dosignal = 0;
fixup = fixup_new(coll, name);
fixups_lock(f);
assert(!f->closed);
STAILQ_INSERT_TAIL(&f->fixupq, fixup, f_link);
if (f->size++ == 0)
dosignal = 1;
fixups_unlock(f);
if (dosignal)
pthread_cond_signal(&f->cond);
}
/* Get a fixup request from the queue. */
struct fixup *
fixups_get(struct fixups *f)
{
struct fixup *fixup, *tofree;
fixups_lock(f);
while (f->size == 0 && !f->closed)
pthread_cond_wait(&f->cond, &f->lock);
if (f->closed && f->size == 0) {
fixups_unlock(f);
return (NULL);
}
assert(f->size > 0);
fixup = STAILQ_FIRST(&f->fixupq);
tofree = f->cur;
f->cur = fixup;
STAILQ_REMOVE_HEAD(&f->fixupq, f_link);
f->size--;
fixups_unlock(f);
if (tofree != NULL)
fixup_free(tofree);
return (fixup);
}
/* Close the writing end of the queue. */
void
fixups_close(struct fixups *f)
{
int dosignal;
dosignal = 0;
fixups_lock(f);
if (f->size == 0 && !f->closed)
dosignal = 1;
f->closed = 1;
fixups_unlock(f);
if (dosignal)
pthread_cond_signal(&f->cond);
}
/* Free a fixups queue. */
void
fixups_free(struct fixups *f)
{
struct fixup *fixup, *fixup2;
assert(f->closed);
/*
* Free any fixup that has been left on the queue.
* This can happen if we have been aborted prematurely.
*/
fixup = STAILQ_FIRST(&f->fixupq);
while (fixup != NULL) {
fixup2 = STAILQ_NEXT(fixup, f_link);
fixup_free(fixup);
fixup = fixup2;
}
if (f->cur != NULL)
fixup_free(f->cur);
pthread_cond_destroy(&f->cond);
pthread_mutex_destroy(&f->lock);
free(f);
}

View File

@ -1,48 +0,0 @@
/*-
* 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 _FIXUPS_H_
#define _FIXUPS_H_
#include <sys/queue.h>
struct coll;
struct fixups;
struct fixup {
struct coll *f_coll;
char *f_name;
STAILQ_ENTRY(fixup) f_link; /* Not for consumers. */
};
struct fixups *fixups_new(void);
void fixups_put(struct fixups *, struct coll *, const char *);
struct fixup *fixups_get(struct fixups *);
void fixups_close(struct fixups *);
void fixups_free(struct fixups *);
#endif /* !_FIXUPS_H_ */

View File

@ -1,197 +0,0 @@
/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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$
*
* From FreeBSD fnmatch.c 1.11
* $Id: fnmatch.c,v 1.3 1997/08/19 02:34:30 jdp Exp $
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
#endif /* LIBC_SCCS and not lint */
/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "fnmatch.h"
#define EOS '\0'
static const char *rangematch(const char *, char, int);
int
fnmatch(const char *pattern, const char *string, int flags)
{
const char *stringstart;
char c, test;
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if ((flags & FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
else if (c == '/' && flags & FNM_PATHNAME) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/* General case, use recursion. */
while ((test = *string) != EOS) {
if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
return (0);
if (test == '/' && flags & FNM_PATHNAME)
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && flags & FNM_PATHNAME)
return (FNM_NOMATCH);
if ((pattern =
rangematch(pattern, *string, flags)) == NULL)
return (FNM_NOMATCH);
++string;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
/* FALLTHROUGH */
default:
if (c == *string)
;
else if ((flags & FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string)))
;
else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
((c == '/' && string != stringstart) ||
(string == stringstart+1 && *stringstart == '/')))
return (0);
else
return (FNM_NOMATCH);
string++;
break;
}
/* NOTREACHED */
}
static const char *
rangematch(const char *pattern, char test, int flags)
{
int negate, ok;
char c, c2;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ( (negate = (*pattern == '!' || *pattern == '^')) )
++pattern;
if (flags & FNM_CASEFOLD)
test = tolower((unsigned char)test);
for (ok = 0; (c = *pattern++) != ']';) {
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (NULL);
if (flags & FNM_CASEFOLD)
c = tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !(flags & FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (NULL);
if (flags & FNM_CASEFOLD)
c2 = tolower((unsigned char)c2);
if ((unsigned char)c <= (unsigned char)test &&
(unsigned char)test <= (unsigned char)c2)
ok = 1;
} else if (c == test)
ok = 1;
}
return (ok == negate ? NULL : pattern);
}

View File

@ -1,56 +0,0 @@
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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$
*
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
*
* From FreeBSD fnmatch.h 1.7
* $Id: fnmatch.h,v 1.4 2001/10/04 02:46:21 jdp Exp $
*/
#ifndef _FNMATCH_H_
#define _FNMATCH_H_
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */
/* Make this compile successfully with "gcc -traditional" */
#ifndef __STDC__
#define const /* empty */
#endif
int fnmatch(const char *, const char *, int);
#endif /* !_FNMATCH_H_ */

View File

@ -1,393 +0,0 @@
/*-
* 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 <regex.h>
#include <stdlib.h>
#include "fnmatch.h"
#include "globtree.h"
#include "misc.h"
/*
* The "GlobTree" interface allows one to construct arbitrarily complex
* boolean expressions for evaluating whether to accept or reject a
* filename. The globtree_test() function returns true or false
* according to whether the name is accepted or rejected by the
* expression.
*
* Expressions are trees constructed from nodes representing either
* primitive matching operations (primaries) or operators that are
* applied to their subexpressions. The simplest primitives are
* globtree_false(), which matches nothing, and globtree_true(), which
* matches everything.
*
* A more useful primitive is the matching operation, constructed with
* globtree_match(). It will call fnmatch() with the suppliedi
* shell-style pattern to determine if the filename matches.
*
* Expressions can be combined with the boolean operators AND, OR, and
* NOT, to form more complex expressions.
*/
/* Node types. */
#define GLOBTREE_NOT 0
#define GLOBTREE_AND 1
#define GLOBTREE_OR 2
#define GLOBTREE_MATCH 3
#define GLOBTREE_REGEX 4
#define GLOBTREE_TRUE 5
#define GLOBTREE_FALSE 6
/* A node. */
struct globtree {
int type;
struct globtree *left;
struct globtree *right;
/* The "data" field points to the text pattern for GLOBTREE_MATCH
nodes, and to the regex_t for GLOBTREE_REGEX nodes. For any
other node, it is set to NULL. */
void *data;
/* The "flags" field contains the flags to pass to fnmatch() for
GLOBTREE_MATCH nodes. */
int flags;
};
static struct globtree *globtree_new(int);
static int globtree_eval(struct globtree *, const char *);
static struct globtree *
globtree_new(int type)
{
struct globtree *gt;
gt = xmalloc(sizeof(struct globtree));
gt->type = type;
gt->data = NULL;
gt->flags = 0;
gt->left = NULL;
gt->right = NULL;
return (gt);
}
struct globtree *
globtree_true(void)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_TRUE);
return (gt);
}
struct globtree *
globtree_false(void)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_FALSE);
return (gt);
}
struct globtree *
globtree_match(const char *pattern, int flags)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_MATCH);
gt->data = xstrdup(pattern);
gt->flags = flags;
return (gt);
}
struct globtree *
globtree_regex(const char *pattern)
{
struct globtree *gt;
int error;
gt = globtree_new(GLOBTREE_REGEX);
gt->data = xmalloc(sizeof(regex_t));
error = regcomp(gt->data, pattern, REG_NOSUB);
assert(!error);
return (gt);
}
struct globtree *
globtree_and(struct globtree *left, struct globtree *right)
{
struct globtree *gt;
if (left->type == GLOBTREE_FALSE || right->type == GLOBTREE_FALSE) {
globtree_free(left);
globtree_free(right);
gt = globtree_false();
return (gt);
}
if (left->type == GLOBTREE_TRUE) {
globtree_free(left);
return (right);
}
if (right->type == GLOBTREE_TRUE) {
globtree_free(right);
return (left);
}
gt = globtree_new(GLOBTREE_AND);
gt->left = left;
gt->right = right;
return (gt);
}
struct globtree *
globtree_or(struct globtree *left, struct globtree *right)
{
struct globtree *gt;
if (left->type == GLOBTREE_TRUE || right->type == GLOBTREE_TRUE) {
globtree_free(left);
globtree_free(right);
gt = globtree_true();
return (gt);
}
if (left->type == GLOBTREE_FALSE) {
globtree_free(left);
return (right);
}
if (right->type == GLOBTREE_FALSE) {
globtree_free(right);
return (left);
}
gt = globtree_new(GLOBTREE_OR);
gt->left = left;
gt->right = right;
return (gt);
}
struct globtree *
globtree_not(struct globtree *child)
{
struct globtree *gt;
if (child->type == GLOBTREE_TRUE) {
globtree_free(child);
gt = globtree_new(GLOBTREE_FALSE);
return (gt);
}
if (child->type == GLOBTREE_FALSE) {
globtree_free(child);
gt = globtree_new(GLOBTREE_TRUE);
return (gt);
}
gt = globtree_new(GLOBTREE_NOT);
gt->left = child;
return (gt);
}
/* Evaluate one node (must be a leaf node). */
static int
globtree_eval(struct globtree *gt, const char *path)
{
int rv;
switch (gt->type) {
case GLOBTREE_TRUE:
return (1);
case GLOBTREE_FALSE:
return (0);
case GLOBTREE_MATCH:
assert(gt->data != NULL);
rv = fnmatch(gt->data, path, gt->flags);
if (rv == 0)
return (1);
assert(rv == FNM_NOMATCH);
return (0);
case GLOBTREE_REGEX:
assert(gt->data != NULL);
rv = regexec(gt->data, path, 0, NULL, 0);
if (rv == 0)
return (1);
assert(rv == REG_NOMATCH);
return (0);
}
assert(0);
return (-1);
}
/* Small stack API to walk the tree iteratively. */
typedef enum {
STATE_DOINGLEFT,
STATE_DOINGRIGHT
} walkstate_t;
struct stack {
struct stackelem *stack;
size_t size;
size_t in;
};
struct stackelem {
struct globtree *node;
walkstate_t state;
};
static void
stack_init(struct stack *stack)
{
stack->in = 0;
stack->size = 8; /* Initial size. */
stack->stack = xmalloc(sizeof(struct stackelem) * stack->size);
}
static size_t
stack_size(struct stack *stack)
{
return (stack->in);
}
static void
stack_push(struct stack *stack, struct globtree *node, walkstate_t state)
{
struct stackelem *e;
if (stack->in == stack->size) {
stack->size *= 2;
stack->stack = xrealloc(stack->stack,
sizeof(struct stackelem) * stack->size);
}
e = stack->stack + stack->in++;
e->node = node;
e->state = state;
}
static void
stack_pop(struct stack *stack, struct globtree **node, walkstate_t *state)
{
struct stackelem *e;
assert(stack->in > 0);
e = stack->stack + --stack->in;
*node = e->node;
*state = e->state;
}
static void
stack_free(struct stack *s)
{
free(s->stack);
}
/* Tests if the supplied filename matches. */
int
globtree_test(struct globtree *gt, const char *path)
{
struct stack stack;
walkstate_t state;
int val;
stack_init(&stack);
for (;;) {
doleft:
/* Descend to the left until we hit bottom. */
while (gt->left != NULL) {
stack_push(&stack, gt, STATE_DOINGLEFT);
gt = gt->left;
}
/* Now we're at a leaf node. Evaluate it. */
val = globtree_eval(gt, path);
/* Ascend, propagating the value through operator nodes. */
for (;;) {
if (stack_size(&stack) == 0) {
stack_free(&stack);
return (val);
}
stack_pop(&stack, &gt, &state);
switch (gt->type) {
case GLOBTREE_NOT:
val = !val;
break;
case GLOBTREE_AND:
/* If we haven't yet evaluated the right subtree
and the partial result is true, descend to
the right. Otherwise the result is already
determined to be val. */
if (state == STATE_DOINGLEFT && val) {
stack_push(&stack, gt,
STATE_DOINGRIGHT);
gt = gt->right;
goto doleft;
}
break;
case GLOBTREE_OR:
/* If we haven't yet evaluated the right subtree
and the partial result is false, descend to
the right. Otherwise the result is already
determined to be val. */
if (state == STATE_DOINGLEFT && !val) {
stack_push(&stack, gt,
STATE_DOINGRIGHT);
gt = gt->right;
goto doleft;
}
break;
default:
/* We only push nodes that have children. */
assert(0);
return (-1);
}
}
}
}
/*
* We could de-recursify this function using a stack, but it would be
* overkill since it is never called from a thread context with a
* limited stack size nor used in a critical path, so I think we can
* afford keeping it recursive.
*/
void
globtree_free(struct globtree *gt)
{
if (gt->data != NULL) {
if (gt->type == GLOBTREE_REGEX)
regfree(gt->data);
free(gt->data);
}
if (gt->left != NULL)
globtree_free(gt->left);
if (gt->right != NULL)
globtree_free(gt->right);
free(gt);
}

View File

@ -1,45 +0,0 @@
/*-
* 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 _GLOBTREE_H_
#define _GLOBTREE_H_
#include "fnmatch.h"
struct globtree;
struct globtree *globtree_true(void);
struct globtree *globtree_false(void);
struct globtree *globtree_match(const char *, int);
struct globtree *globtree_regex(const char *);
struct globtree *globtree_and(struct globtree *, struct globtree *);
struct globtree *globtree_or(struct globtree *, struct globtree *);
struct globtree *globtree_not(struct globtree *);
int globtree_test(struct globtree *, const char *);
void globtree_free(struct globtree *);
#endif /* !_GLOBTREE_H_ */

View File

@ -1,421 +0,0 @@
/*-
* 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);
}

View File

@ -1,41 +0,0 @@
/*-
* 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

@ -1,526 +0,0 @@
/*-
* 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/queue.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "diff.h"
#include "keyword.h"
#include "misc.h"
#include "stream.h"
/*
* The keyword API is used to expand the CVS/RCS keywords in files,
* such as $Id$, $Revision$, etc. The server does it for us when it
* sends us entire files, but we need to handle the expansion when
* applying a diff update.
*/
enum rcskey {
RCSKEY_AUTHOR,
RCSKEY_CVSHEADER,
RCSKEY_DATE,
RCSKEY_HEADER,
RCSKEY_ID,
RCSKEY_LOCKER,
RCSKEY_LOG,
RCSKEY_NAME,
RCSKEY_RCSFILE,
RCSKEY_REVISION,
RCSKEY_SOURCE,
RCSKEY_STATE
};
typedef enum rcskey rcskey_t;
struct tag {
char *ident;
rcskey_t key;
int enabled;
STAILQ_ENTRY(tag) next;
};
static struct tag *tag_new(const char *, rcskey_t);
static char *tag_expand(struct tag *, struct diffinfo *);
static void tag_free(struct tag *);
struct keyword {
STAILQ_HEAD(, tag) keywords; /* Enabled keywords. */
size_t minkeylen;
size_t maxkeylen;
};
/* Default CVS keywords. */
static struct {
const char *ident;
rcskey_t key;
} tag_defaults[] = {
{ "Author", RCSKEY_AUTHOR },
{ "CVSHeader", RCSKEY_CVSHEADER },
{ "Date", RCSKEY_DATE },
{ "Header", RCSKEY_HEADER },
{ "Id", RCSKEY_ID },
{ "Locker", RCSKEY_LOCKER },
{ "Log", RCSKEY_LOG },
{ "Name", RCSKEY_NAME },
{ "RCSfile", RCSKEY_RCSFILE },
{ "Revision", RCSKEY_REVISION },
{ "Source", RCSKEY_SOURCE },
{ "State", RCSKEY_STATE },
{ NULL, 0, }
};
struct keyword *
keyword_new(void)
{
struct keyword *new;
struct tag *tag;
size_t len;
int i;
new = xmalloc(sizeof(struct keyword));
STAILQ_INIT(&new->keywords);
new->minkeylen = ~0;
new->maxkeylen = 0;
for (i = 0; tag_defaults[i].ident != NULL; i++) {
tag = tag_new(tag_defaults[i].ident, tag_defaults[i].key);
STAILQ_INSERT_TAIL(&new->keywords, tag, next);
len = strlen(tag->ident);
/*
* These values are only computed here and not updated when
* adding an alias. This is a bug, but CVSup has it and we
* need to be bug-to-bug compatible since the server will
* expect us to do the same, and we will fail with an MD5
* checksum mismatch if we don't.
*/
new->minkeylen = min(new->minkeylen, len);
new->maxkeylen = max(new->maxkeylen, len);
}
return (new);
}
int
keyword_decode_expand(const char *expand)
{
if (strcmp(expand, ".") == 0)
return (EXPAND_DEFAULT);
else if (strcmp(expand, "kv") == 0)
return (EXPAND_KEYVALUE);
else if (strcmp(expand, "kvl") == 0)
return (EXPAND_KEYVALUELOCKER);
else if (strcmp(expand, "k") == 0)
return (EXPAND_KEY);
else if (strcmp(expand, "o") == 0)
return (EXPAND_OLD);
else if (strcmp(expand, "b") == 0)
return (EXPAND_BINARY);
else if (strcmp(expand, "v") == 0)
return (EXPAND_VALUE);
else
return (-1);
}
const char *
keyword_encode_expand(int expand)
{
switch (expand) {
case EXPAND_DEFAULT:
return (".");
case EXPAND_KEYVALUE:
return ("kv");
case EXPAND_KEYVALUELOCKER:
return ("kvl");
case EXPAND_KEY:
return ("k");
case EXPAND_OLD:
return ("o");
case EXPAND_BINARY:
return ("b");
case EXPAND_VALUE:
return ("v");
}
return (NULL);
}
void
keyword_free(struct keyword *keyword)
{
struct tag *tag;
if (keyword == NULL)
return;
while (!STAILQ_EMPTY(&keyword->keywords)) {
tag = STAILQ_FIRST(&keyword->keywords);
STAILQ_REMOVE_HEAD(&keyword->keywords, next);
tag_free(tag);
}
free(keyword);
}
int
keyword_alias(struct keyword *keyword, const char *ident, const char *rcskey)
{
struct tag *new, *tag;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (strcmp(tag->ident, rcskey) == 0) {
new = tag_new(ident, tag->key);
STAILQ_INSERT_HEAD(&keyword->keywords, new, next);
return (0);
}
}
errno = ENOENT;
return (-1);
}
int
keyword_enable(struct keyword *keyword, const char *ident)
{
struct tag *tag;
int all;
all = 0;
if (strcmp(ident, ".") == 0)
all = 1;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (!all && strcmp(tag->ident, ident) != 0)
continue;
tag->enabled = 1;
if (!all)
return (0);
}
if (!all) {
errno = ENOENT;
return (-1);
}
return (0);
}
int
keyword_disable(struct keyword *keyword, const char *ident)
{
struct tag *tag;
int all;
all = 0;
if (strcmp(ident, ".") == 0)
all = 1;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (!all && strcmp(tag->ident, ident) != 0)
continue;
tag->enabled = 0;
if (!all)
return (0);
}
if (!all) {
errno = ENOENT;
return (-1);
}
return (0);
}
void
keyword_prepare(struct keyword *keyword)
{
struct tag *tag, *temp;
STAILQ_FOREACH_SAFE(tag, &keyword->keywords, next, temp) {
if (!tag->enabled) {
STAILQ_REMOVE(&keyword->keywords, tag, tag, next);
tag_free(tag);
continue;
}
}
}
/*
* Expand appropriate RCS keywords. If there's no tag to expand,
* keyword_expand() returns 0, otherwise it returns 1 and writes a
* pointer to the new line in *buf and the new len in *len. The
* new line is allocated with malloc() and needs to be freed by the
* caller after use.
*/
int
keyword_expand(struct keyword *keyword, struct diffinfo *di, char *line,
size_t size, char **buf, size_t *len)
{
struct tag *tag;
char *dollar, *keystart, *valstart, *vallim, *next;
char *linestart, *newline, *newval, *cp, *tmp;
size_t left, newsize, vallen;
if (di->di_expand == EXPAND_OLD || di->di_expand == EXPAND_BINARY)
return (0);
newline = NULL;
newsize = 0;
left = size;
linestart = cp = line;
again:
dollar = memchr(cp, '$', left);
if (dollar == NULL) {
if (newline != NULL) {
*buf = newline;
*len = newsize;
return (1);
}
return (0);
}
keystart = dollar + 1;
left -= keystart - cp;
vallim = memchr(keystart, '$', left);
if (vallim == NULL) {
if (newline != NULL) {
*buf = newline;
*len = newsize;
return (1);
}
return (0);
}
if (vallim == keystart) {
cp = keystart;
goto again;
}
valstart = memchr(keystart, ':', left);
if (valstart == keystart) {
cp = vallim;
left -= vallim - keystart;
goto again;
}
if (valstart == NULL || valstart > vallim)
valstart = vallim;
if (valstart < keystart + keyword->minkeylen ||
valstart > keystart + keyword->maxkeylen) {
cp = vallim;
left -= vallim -keystart;
goto again;
}
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (strncmp(tag->ident, keystart, valstart - keystart) == 0 &&
tag->ident[valstart - keystart] == '\0') {
if (newline != NULL)
tmp = newline;
else
tmp = NULL;
newval = NULL;
if (di->di_expand == EXPAND_KEY) {
newsize = dollar - linestart + 1 +
valstart - keystart + 1 +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
*cp++ = '$';
memcpy(cp, keystart, valstart - keystart);
cp += valstart - keystart;
*cp++ = '$';
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
} else if (di->di_expand == EXPAND_VALUE) {
newval = tag_expand(tag, di);
if (newval == NULL)
vallen = 0;
else
vallen = strlen(newval);
newsize = dollar - linestart +
vallen +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
if (newval != NULL) {
memcpy(cp, newval, vallen);
cp += vallen;
}
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
} else {
assert(di->di_expand == EXPAND_DEFAULT ||
di->di_expand == EXPAND_KEYVALUE ||
di->di_expand == EXPAND_KEYVALUELOCKER);
newval = tag_expand(tag, di);
if (newval == NULL)
vallen = 0;
else
vallen = strlen(newval);
newsize = dollar - linestart + 1 +
valstart - keystart + 2 +
vallen + 2 +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
*cp++ = '$';
memcpy(cp, keystart, valstart - keystart);
cp += valstart - keystart;
*cp++ = ':';
*cp++ = ' ';
if (newval != NULL) {
memcpy(cp, newval, vallen);
cp += vallen;
}
*cp++ = ' ';
*cp++ = '$';
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
}
if (newval != NULL)
free(newval);
if (tmp != NULL)
free(tmp);
/*
* Continue looking for tags in the rest of the line.
*/
cp = next;
size = newsize;
left = size - (cp - newline);
linestart = newline;
goto again;
}
}
cp = vallim;
left = size - (cp - linestart);
goto again;
}
static struct tag *
tag_new(const char *ident, rcskey_t key)
{
struct tag *new;
new = xmalloc(sizeof(struct tag));
new->ident = xstrdup(ident);
new->key = key;
new->enabled = 1;
return (new);
}
static void
tag_free(struct tag *tag)
{
free(tag->ident);
free(tag);
}
/*
* Expand a specific tag and return the new value. If NULL
* is returned, the tag is empty.
*/
static char *
tag_expand(struct tag *tag, struct diffinfo *di)
{
/*
* CVS formats dates as "XXXX/XX/XX XX:XX:XX". 32 bytes
* is big enough until year 10,000,000,000,000,000 :-).
*/
char cvsdate[32];
struct tm tm;
char *filename, *val;
int error;
error = rcsdatetotm(di->di_revdate, &tm);
if (error)
err(1, "strptime");
if (strftime(cvsdate, sizeof(cvsdate), "%Y/%m/%d %H:%M:%S", &tm) == 0)
err(1, "strftime");
filename = strrchr(di->di_rcsfile, '/');
if (filename == NULL)
filename = di->di_rcsfile;
else
filename++;
switch (tag->key) {
case RCSKEY_AUTHOR:
xasprintf(&val, "%s", di->di_author);
break;
case RCSKEY_CVSHEADER:
xasprintf(&val, "%s %s %s %s %s", di->di_rcsfile,
di->di_revnum, cvsdate, di->di_author, di->di_state);
break;
case RCSKEY_DATE:
xasprintf(&val, "%s", cvsdate);
break;
case RCSKEY_HEADER:
xasprintf(&val, "%s/%s %s %s %s %s", di->di_cvsroot,
di->di_rcsfile, di->di_revnum, cvsdate, di->di_author,
di->di_state);
break;
case RCSKEY_ID:
xasprintf(&val, "%s %s %s %s %s", filename, di->di_revnum,
cvsdate, di->di_author, di->di_state);
break;
case RCSKEY_LOCKER:
/*
* Unimplemented even in CVSup sources. It seems we don't
* even have this information sent by the server.
*/
return (NULL);
case RCSKEY_LOG:
/* XXX */
printf("%s: Implement Log keyword expansion\n", __func__);
return (NULL);
case RCSKEY_NAME:
if (di->di_tag != NULL)
xasprintf(&val, "%s", di->di_tag);
else
return (NULL);
break;
case RCSKEY_RCSFILE:
xasprintf(&val, "%s", filename);
break;
case RCSKEY_REVISION:
xasprintf(&val, "%s", di->di_revnum);
break;
case RCSKEY_SOURCE:
xasprintf(&val, "%s/%s", di->di_cvsroot, di->di_rcsfile);
break;
case RCSKEY_STATE:
xasprintf(&val, "%s", di->di_state);
break;
}
return (val);
}

View File

@ -1,54 +0,0 @@
/*-
* 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$
*/
#ifndef _KEYWORD_H_
#define _KEYWORD_H_
/* CVS expansion modes. */
#define EXPAND_DEFAULT 0
#define EXPAND_KEYVALUE 1
#define EXPAND_KEYVALUELOCKER 2
#define EXPAND_KEY 3
#define EXPAND_OLD 4
#define EXPAND_BINARY 5
#define EXPAND_VALUE 6
struct diffinfo;
struct keyword;
struct keyword *keyword_new(void);
int keyword_decode_expand(const char *);
const char *keyword_encode_expand(int);
int keyword_alias(struct keyword *, const char *, const char *);
int keyword_enable(struct keyword *, const char *);
int keyword_disable(struct keyword *, const char *);
void keyword_prepare(struct keyword *);
int keyword_expand(struct keyword *, struct diffinfo *, char *,
size_t, char **, size_t *);
void keyword_free(struct keyword *);
#endif /* !_KEYWORD_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,569 +0,0 @@
/*-
* 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 <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attrstack.h"
#include "config.h"
#include "fattr.h"
#include "globtree.h"
#include "lister.h"
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "status.h"
#include "stream.h"
/* Internal error codes. */
#define LISTER_ERR_WRITE (-1) /* Error writing to server. */
#define LISTER_ERR_STATUS (-2) /* Status file error in lstr->errmsg. */
struct lister {
struct config *config;
struct stream *wr;
char *errmsg;
};
static int lister_batch(struct lister *);
static int lister_coll(struct lister *, struct coll *, struct status *);
static int lister_dodirdown(struct lister *, struct coll *,
struct statusrec *, struct attrstack *as);
static int lister_dodirup(struct lister *, struct coll *,
struct statusrec *, struct attrstack *as);
static int lister_dofile(struct lister *, struct coll *,
struct statusrec *);
static int lister_dodead(struct lister *, struct coll *,
struct statusrec *);
static int lister_dorcsfile(struct lister *, struct coll *,
struct statusrec *);
static int lister_dorcsdead(struct lister *, struct coll *,
struct statusrec *);
void *
lister(void *arg)
{
struct thread_args *args;
struct lister lbuf, *l;
int error;
args = arg;
l = &lbuf;
l->config = args->config;
l->wr = args->wr;
l->errmsg = NULL;
error = lister_batch(l);
switch (error) {
case LISTER_ERR_WRITE:
xasprintf(&args->errmsg,
"TreeList failed: Network write failure: %s",
strerror(errno));
args->status = STATUS_TRANSIENTFAILURE;
break;
case LISTER_ERR_STATUS:
xasprintf(&args->errmsg,
"TreeList failed: %s. Delete it and try again.",
l->errmsg);
free(l->errmsg);
args->status = STATUS_FAILURE;
break;
default:
assert(error == 0);
args->status = STATUS_SUCCESS;
};
return (NULL);
}
static int
lister_batch(struct lister *l)
{
struct config *config;
struct stream *wr;
struct status *st;
struct coll *coll;
int error;
config = l->config;
wr = l->wr;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
st = status_open(coll, -1, &l->errmsg);
if (st == NULL)
return (LISTER_ERR_STATUS);
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (LISTER_ERR_WRITE);
stream_flush(wr);
if (coll->co_options & CO_COMPRESS)
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
error = lister_coll(l, coll, st);
status_close(st, NULL);
if (error)
return (error);
if (coll->co_options & CO_COMPRESS)
stream_filter_stop(wr);
stream_flush(wr);
}
error = proto_printf(wr, ".\n");
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* List a single collection based on the status file. */
static int
lister_coll(struct lister *l, struct coll *coll, struct status *st)
{
struct stream *wr;
struct attrstack *as;
struct statusrec *sr;
struct fattr *fa;
size_t i;
int depth, error, ret, prunedepth;
wr = l->wr;
depth = 0;
prunedepth = INT_MAX;
as = attrstack_new();
while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) {
switch (sr->sr_type) {
case SR_DIRDOWN:
depth++;
if (depth < prunedepth) {
error = lister_dodirdown(l, coll, sr, as);
if (error < 0)
goto bad;
if (error)
prunedepth = depth;
}
break;
case SR_DIRUP:
if (depth < prunedepth) {
error = lister_dodirup(l, coll, sr, as);
if (error)
goto bad;
} else if (depth == prunedepth) {
/* Finished pruning. */
prunedepth = INT_MAX;
}
depth--;
continue;
case SR_CHECKOUTLIVE:
if (depth < prunedepth) {
error = lister_dofile(l, coll, sr);
if (error)
goto bad;
}
break;
case SR_CHECKOUTDEAD:
if (depth < prunedepth) {
error = lister_dodead(l, coll, sr);
if (error)
goto bad;
}
break;
case SR_FILEDEAD:
if (depth < prunedepth) {
if (!(coll->co_options & CO_CHECKOUTMODE)) {
error = lister_dorcsdead(l, coll, sr);
if (error)
goto bad;
}
}
break;
case SR_FILELIVE:
if (depth < prunedepth) {
if (!(coll->co_options & CO_CHECKOUTMODE)) {
error = lister_dorcsfile(l, coll, sr);
if (error)
goto bad;
}
}
break;
}
}
if (ret == -1) {
l->errmsg = status_errmsg(st);
error = LISTER_ERR_STATUS;
goto bad;
}
assert(status_eof(st));
assert(depth == 0);
error = proto_printf(wr, ".\n");
attrstack_free(as);
if (error)
return (LISTER_ERR_WRITE);
return (0);
bad:
for (i = 0; i < attrstack_size(as); i++) {
fa = attrstack_pop(as);
fattr_free(fa);
}
attrstack_free(as);
return (error);
}
/* Handle a directory up entry found in the status file. */
static int
lister_dodirdown(struct lister *l, struct coll *coll, struct statusrec *sr,
struct attrstack *as)
{
struct config *config;
struct stream *wr;
struct fattr *fa, *fa2;
char *path;
int error;
config = l->config;
wr = l->wr;
if (!globtree_test(coll->co_dirfilter, sr->sr_file))
return (1);
if (coll->co_options & CO_TRUSTSTATUSFILE) {
fa = fattr_new(FT_DIRECTORY, -1);
} else {
xasprintf(&path, "%s/%s", coll->co_prefix, sr->sr_file);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* The directory doesn't exist, prune
* everything below it. */
free(path);
return (1);
}
if (fattr_type(fa) == FT_SYMLINK) {
fa2 = fattr_frompath(path, FATTR_FOLLOW);
if (fa2 != NULL && fattr_type(fa2) == FT_DIRECTORY) {
/* XXX - When not in checkout mode, CVSup warns
* here about the file being a symlink to a
* directory instead of a directory. */
fattr_free(fa);
fa = fa2;
} else {
fattr_free(fa2);
}
}
free(path);
}
if (fattr_type(fa) != FT_DIRECTORY) {
fattr_free(fa);
/* Report it as something bogus so
* that it will be replaced. */
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file),
fattr_bogus, config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (1);
}
/* It really is a directory. */
attrstack_push(as, fa);
error = proto_printf(wr, "D %s\n", pathlast(sr->sr_file));
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a directory up entry found in the status file. */
static int
lister_dodirup(struct lister *l, struct coll *coll, struct statusrec *sr,
struct attrstack *as)
{
struct config *config;
const struct fattr *sendattr;
struct stream *wr;
struct fattr *fa, *fa2;
int error;
config = l->config;
wr = l->wr;
fa = attrstack_pop(as);
if (coll->co_options & CO_TRUSTSTATUSFILE) {
fattr_free(fa);
fa = sr->sr_clientattr;
}
fa2 = sr->sr_clientattr;
if (fattr_equal(fa, fa2))
sendattr = fa;
else
sendattr = fattr_bogus;
error = proto_printf(wr, "U %F\n", sendattr, config->fasupport,
coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
if (!(coll->co_options & CO_TRUSTSTATUSFILE))
fattr_free(fa);
/* XXX CVSup flushes here for some reason with a comment saying
"Be smarter". We don't flush when listing other file types. */
stream_flush(wr);
return (0);
}
/* Handle a checkout live entry found in the status file. */
static int
lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr, *fa;
struct fattr *fa2, *rfa;
char *path, *spath;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
rfa = NULL;
sendattr = NULL;
error = 0;
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
path = checkoutpath(coll->co_prefix, sr->sr_file);
if (path == NULL) {
spath = coll_statuspath(coll);
xasprintf(&l->errmsg, "Error in \"%s\": "
"Invalid filename \"%s\"", spath, sr->sr_file);
free(spath);
return (LISTER_ERR_STATUS);
}
rfa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
if (rfa == NULL) {
/*
* According to the checkouts file we should have
* this file but we don't. Maybe the user deleted
* the file, or maybe the checkouts file is wrong.
* List the file with bogus attributes to cause the
* server to get things back in sync again.
*/
sendattr = fattr_bogus;
goto send;
}
fa = rfa;
} else {
fa = sr->sr_clientattr;
}
fa2 = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
if (!fattr_equal(fa, sr->sr_clientattr) || !fattr_equal(fa, fa2) ||
strcmp(coll->co_tag, sr->sr_tag) != 0 ||
strcmp(coll->co_date, sr->sr_date) != 0) {
/*
* The file corresponds to the information we have
* recorded about it, and its moded is correct for
* the requested umask setting.
*/
sendattr = fattr_bogus;
} else {
/*
* Either the file has been touched, or we are asking
* for a different revision than the one we recorded
* information about, or its mode isn't right (because
* it was last updated using a version of CVSup that
* wasn't so strict about modes).
*/
sendattr = sr->sr_serverattr;
}
fattr_free(fa2);
if (rfa != NULL)
fattr_free(rfa);
send:
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a rcs file live entry found in the status file. */
static int
lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
size_t len;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
path = cvspath(coll->co_prefix, sr->sr_file, 0);
if (path == NULL) {
spath = coll_statuspath(coll);
xasprintf(&l->errmsg, "Error in \"%s\": "
"Invalid filename \"%s\"", spath, sr->sr_file);
free(spath);
return (LISTER_ERR_STATUS);
}
fa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
} else
fa = sr->sr_clientattr;
if (fa != NULL && fattr_equal(fa, sr->sr_clientattr)) {
/*
* If the file is an RCS file, we use "loose" equality, so sizes
* may disagress because of differences in whitespace.
*/
if (isrcs(sr->sr_file, &len) &&
!(coll->co_options & CO_NORCS) &&
!(coll->co_options & CO_STRICTCHECKRCS)) {
fattr_maskout(fa, FA_SIZE);
}
sendattr = fa;
} else {
/*
* If different, the user may have changed it, so we report
* bogus attributes to force a full comparison.
*/
sendattr = fattr_bogus;
}
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a checkout dead entry found in the status file. */
static int
lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
path = checkoutpath(coll->co_prefix, sr->sr_file);
if (path == NULL) {
spath = coll_statuspath(coll);
xasprintf(&l->errmsg, "Error in \"%s\": "
"Invalid filename \"%s\"", spath, sr->sr_file);
free(spath);
return (LISTER_ERR_STATUS);
}
fa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
if (fa != NULL && fattr_type(fa) != FT_DIRECTORY) {
/*
* We shouldn't have this file but we do. Report
* it to the server, which will either send a
* deletion request, of (if the file has come alive)
* sent the correct version.
*/
fattr_free(fa);
error = proto_printf(wr, "F %s %F\n",
pathlast(sr->sr_file), fattr_bogus,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
fattr_free(fa);
}
if (strcmp(coll->co_tag, sr->sr_tag) != 0 ||
strcmp(coll->co_date, sr->sr_date) != 0)
sendattr = fattr_bogus;
else
sendattr = sr->sr_serverattr;
error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a rcs file dead entry found in the status file. */
static int
lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
size_t len;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
path = cvspath(coll->co_prefix, sr->sr_file, 1);
if (path == NULL) {
spath = coll_statuspath(coll);
xasprintf(&l->errmsg, "Error in \"%s\": "
"Invalid filename \"%s\"", spath, sr->sr_file);
free(spath);
return (LISTER_ERR_STATUS);
}
fa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
} else
fa = sr->sr_clientattr;
if (fattr_equal(fa, sr->sr_clientattr)) {
/*
* If the file is an RCS file, we use "loose" equality, so sizes
* may disagress because of differences in whitespace.
*/
if (isrcs(sr->sr_file, &len) &&
!(coll->co_options & CO_NORCS) &&
!(coll->co_options & CO_STRICTCHECKRCS)) {
fattr_maskout(fa, FA_SIZE);
}
sendattr = fa;
} else {
/*
* If different, the user may have changed it, so we report
* bogus attributes to force a full comparison.
*/
sendattr = fattr_bogus;
}
error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}

View File

@ -1,33 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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 _LISTER_H_
#define _LISTER_H_
void *lister(void *);
#endif /* !_LISTER_H_ */

View File

@ -1,346 +0,0 @@
/*-
* 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/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "fattr.h"
#include "misc.h"
#include "proto.h"
#include "stream.h"
#define USAGE_OPTFMT " %-12s %s\n"
#define USAGE_OPTFMTSUB " %-14s %s\n", ""
int verbose = 1;
static void
usage(char *argv0)
{
lprintf(-1, "Usage: %s [options] supfile\n", basename(argv0));
lprintf(-1, " Options:\n");
lprintf(-1, USAGE_OPTFMT, "-1", "Don't retry automatically on failure "
"(same as \"-r 0\")");
lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses");
lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses");
lprintf(-1, USAGE_OPTFMT, "-a",
"Require server to authenticate itself to us");
lprintf(-1, USAGE_OPTFMT, "-A addr",
"Bind local socket to a specific address");
lprintf(-1, USAGE_OPTFMT, "-b base",
"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",
"Include only files/directories matching pattern.");
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",
"Verbosity level (0..2, default 1)");
lprintf(-1, USAGE_OPTFMT, "-p port",
"Alternate server port (default 5999)");
lprintf(-1, USAGE_OPTFMT, "-r n",
"Maximum retries on transient errors (default unlimited)");
lprintf(-1, USAGE_OPTFMT, "-s",
"Don't stat client files; trust the checkouts file");
lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit");
lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all "
"collections");
lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all "
"collections");
}
int
main(int argc, char *argv[])
{
struct tm tm;
struct backoff_timer *timer;
struct config *config;
struct coll *override;
struct addrinfo *res;
struct sockaddr *laddr;
socklen_t laddrlen;
struct stream *lock;
char *argv0, *file, *lockfile;
int family, error, lockfd, lflag, overridemask;
int c, i, deletelim, port, retries, status, reqauth;
time_t nexttry;
error = 0;
family = PF_UNSPEC;
deletelim = -1;
port = 0;
lflag = 0;
lockfd = 0;
nexttry = 0;
retries = -1;
argv0 = argv[0];
laddr = NULL;
laddrlen = 0;
lockfile = NULL;
override = coll_new(NULL);
overridemask = 0;
reqauth = 0;
while ((c = getopt(argc, argv,
"146aA:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) {
switch (c) {
case '1':
retries = 0;
break;
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'a':
/* Require server authentication */
reqauth = 1;
break;
case 'A':
error = getaddrinfo(optarg, NULL, NULL, &res);
if (error) {
lprintf(-1, "%s: %s\n", optarg,
gai_strerror(error));
return (1);
}
laddrlen = res->ai_addrlen;
laddr = xmalloc(laddrlen);
memcpy(laddr, res->ai_addr, laddrlen);
freeaddrinfo(res);
break;
case 'b':
if (override->co_base != NULL)
free(override->co_base);
override->co_base = xstrdup(optarg);
break;
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;
case 'h':
if (override->co_host != NULL)
free(override->co_host);
override->co_host = xstrdup(optarg);
break;
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;
lockfd = open(lockfile,
O_CREAT | O_WRONLY | O_TRUNC, 0700);
if (lockfd != -1) {
error = flock(lockfd, LOCK_EX | LOCK_NB);
if (error == -1 && errno == EWOULDBLOCK) {
if (lockfd != -1)
close(lockfd);
lprintf(-1, "\"%s\" is already locked "
"by another process\n", lockfile);
return (1);
}
}
if (lockfd == -1 || error == -1) {
if (lockfd != -1)
close(lockfd);
lprintf(-1, "Error locking \"%s\": %s\n",
lockfile, strerror(errno));
return (1);
}
lock = stream_open_fd(lockfd,
NULL, stream_write_fd, NULL);
(void)stream_printf(lock, "%10ld\n", (long)getpid());
stream_close(lock);
break;
case 'L':
error = asciitoint(optarg, &verbose, 0);
if (error) {
lprintf(-1, "Invalid verbosity\n");
usage(argv0);
return (1);
}
break;
case 'p':
/* Use specified server port. */
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. */
if (strcmp(optarg, "m") != 0) {
lprintf(-1,
"Client only supports multiplexed mode\n");
return (1);
}
break;
case 'r':
error = asciitoint(optarg, &retries, 0);
if (error || retries < 0) {
lprintf(-1, "Invalid retry limit\n");
usage(argv0);
return (1);
}
break;
case 's':
override->co_options |= CO_TRUSTSTATUSFILE;
overridemask |= CO_TRUSTSTATUSFILE;
break;
case 'v':
lprintf(0, "CVSup client written in C\n");
lprintf(0, "Software version: %s\n", PROTO_SWVER);
lprintf(0, "Protocol version: %d.%d\n",
PROTO_MAJ, PROTO_MIN);
return (0);
break;
case 'z':
/* Force compression on all collections. */
override->co_options |= CO_COMPRESS;
overridemask |= CO_COMPRESS;
break;
case 'Z':
/* Disables compression on all collections. */
override->co_options &= ~CO_COMPRESS;
overridemask &= ~CO_COMPRESS;
break;
case '?':
default:
usage(argv0);
return (1);
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage(argv0);
return (1);
}
file = argv[0];
lprintf(2, "Parsing supfile \"%s\"\n", file);
config = config_init(file, override, overridemask);
coll_free(override);
if (config == NULL)
return (1);
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;
config->reqauth = reqauth;
lprintf(2, "Connecting to %s\n", config->host);
i = 0;
fattr_init(); /* Initialize the fattr API. */
timer = bt_new(300, 7200, 2.0, 0.1);
for (;;) {
status = proto_connect(config, family, port);
if (status == STATUS_SUCCESS) {
status = proto_run(config);
if (status != STATUS_TRANSIENTFAILURE)
break;
}
if (retries >= 0 && i >= retries)
break;
nexttry = time(0) + bt_get(timer);
localtime_r(&nexttry, &tm);
lprintf(1, "Will retry at %02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
bt_pause(timer);
lprintf(1, "Retrying\n");
i++;
}
bt_free(timer);
fattr_fini();
if (lflag) {
unlink(lockfile);
flock(lockfd, LOCK_UN);
close(lockfd);
}
config_free(config);
if (status != STATUS_SUCCESS)
return (1);
return (0);
}

View File

@ -1,29 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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.
*
* $Id$
*/
extern int verbose;

View File

@ -1,644 +0,0 @@
/*-
* 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/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "fattr.h"
#include "main.h"
#include "misc.h"
struct pattlist {
char **patterns;
size_t size;
size_t in;
};
struct backoff_timer {
time_t min;
time_t max;
time_t interval;
float backoff;
float jitter;
};
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, ...)
{
FILE *to;
va_list ap;
int ret;
if (level > verbose)
return (0);
if (level == -1)
to = stderr;
else
to = stdout;
va_start(ap, fmt);
ret = vfprintf(to, fmt, ap);
va_end(ap);
fflush(to);
return (ret);
}
/*
* Compute the MD5 checksum of a file. The md parameter must
* point to a buffer containing at least MD5_DIGEST_SIZE bytes.
*
* Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
* MD5_DIGEST_SIZE macro.
*/
int
MD5_File(char *path, char *md)
{
char buf[1024];
MD5_CTX ctx;
ssize_t n;
int fd;
fd = open(path, O_RDONLY);
if (fd == -1)
return (-1);
MD5_Init(&ctx);
while ((n = read(fd, buf, sizeof(buf))) > 0)
MD5_Update(&ctx, buf, n);
close(fd);
if (n == -1)
return (-1);
MD5_End(md, &ctx);
return (0);
}
/*
* Wrapper around MD5_Final() that converts the 128 bits MD5 hash
* to an ASCII string representing this value in hexadecimal.
*/
void
MD5_End(char *md, MD5_CTX *c)
{
unsigned char md5[MD5_DIGEST_LENGTH];
const char hex[] = "0123456789abcdef";
int i, j;
MD5_Final(md5, c);
j = 0;
for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
md[j++] = hex[md5[i] >> 4];
md[j++] = hex[md5[i] & 0xf];
}
md[j] = '\0';
}
int
pathcmp(const char *s1, const char *s2)
{
char c1, c2;
do {
c1 = *s1++;
if (c1 == '/')
c1 = 1;
c2 = *s2++;
if (c2 == '/')
c2 = 1;
} while (c1 == c2 && c1 != '\0');
return (c1 - c2);
}
size_t
commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
{
size_t i, minlen, lastslash;
minlen = min(alen, blen);
lastslash = 0;
for (i = 0; i < minlen; i++) {
if (a[i] != b[i])
return (lastslash);
if (a[i] == '/') {
if (i == 0) /* Include the leading slash. */
lastslash = 1;
else
lastslash = i;
}
}
/* One path is a prefix of the other/ */
if (alen > minlen) { /* Path "b" is a prefix of "a". */
if (a[minlen] == '/')
return (minlen);
else
return (lastslash);
} else if (blen > minlen) { /* Path "a" is a prefix of "b". */
if (b[minlen] == '/')
return (minlen);
else
return (lastslash);
}
/* The paths are identical. */
return (minlen);
}
const char *
pathlast(const char *path)
{
const char *s;
s = strrchr(path, '/');
if (s == NULL)
return (path);
return (++s);
}
int
rcsdatetotm(const char *revdate, struct tm *tm)
{
char *cp;
size_t len;
cp = strchr(revdate, '.');
if (cp == NULL)
return (-1);
len = cp - revdate;
if (len >= 4)
cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
else if (len == 2)
cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
else
return (-1);
if (cp == NULL || *cp != '\0')
return (-1);
return (0);
}
time_t
rcsdatetotime(const char *revdate)
{
struct tm tm;
time_t t;
int error;
error = rcsdatetotm(revdate, &tm);
if (error)
return (error);
t = timegm(&tm);
return (t);
}
/*
* Checks if a file is an RCS file.
*/
int
isrcs(const char *file, size_t *len)
{
const char *cp;
if (file[0] == '/')
return (0);
cp = file;
while ((cp = strstr(cp, "..")) != NULL) {
if (cp == file || cp[2] == '\0' ||
(cp[-1] == '/' && cp[2] == '/'))
return (0);
cp += 2;
}
*len = strlen(file);
if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
return (0);
}
return (1);
}
/*
* Returns a buffer allocated with malloc() containing the absolute
* pathname to the checkout file made from the prefix and the path
* of the corresponding RCS file relatively to the prefix. If the
* filename is not an RCS filename, NULL will be returned.
*/
char *
checkoutpath(const char *prefix, const char *file)
{
char *path;
size_t len;
if (!isrcs(file, &len))
return (NULL);
xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
return (path);
}
/*
* Returns a cvs path allocated with malloc() containing absolute pathname to a
* file in cvs mode which can reside in the attic. XXX: filename has really no
* restrictions.
*/
char *
cvspath(const char *prefix, const char *file, int attic)
{
const char *last;
char *path;
last = pathlast(file);
if (attic)
xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
file, last);
else
xasprintf(&path, "%s/%s", prefix, file);
return (path);
}
/*
* Regular or attic path if regular fails.
* XXX: This should perhaps also check if the Attic file exists too, and return
* NULL if not.
*/
char *
atticpath(const char *prefix, const char *file)
{
char *path;
path = cvspath(prefix, file, 0);
if (access(path, F_OK) != 0) {
free(path);
path = cvspath(prefix, file, 1);
}
return (path);
}
int
mkdirhier(char *path, mode_t mask)
{
struct fattr *fa;
size_t i, last, len;
int error, finish, rv;
finish = 0;
last = 0;
len = strlen(path);
for (i = len - 1; i > 0; i--) {
if (path[i] == '/') {
path[i] = '\0';
if (access(path, F_OK) == 0) {
path[i] = '/';
break;
}
if (errno != ENOENT) {
path[i] = '/';
if (last == 0)
return (-1);
finish = 1;
break;
}
last = i;
}
}
if (last == 0)
return (0);
i = strlen(path);
fa = fattr_new(FT_DIRECTORY, -1);
fattr_mergedefault(fa);
fattr_umask(fa, mask);
while (i < len) {
if (!finish) {
rv = 0;
error = fattr_makenode(fa, path);
if (!error)
rv = fattr_install(fa, path, NULL);
if (error || rv == -1)
finish = 1;
}
path[i] = '/';
i += strlen(path + i);
}
assert(i == len);
if (finish)
return (-1);
return (0);
}
/*
* Compute temporary pathnames.
* This can look a bit like overkill but we mimic CVSup's behaviour.
*/
#define TEMPNAME_PREFIX "#cvs.csup"
static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
static pid_t tempname_pid = -1;
static int tempname_count;
char *
tempname(const char *path)
{
char *cp, *temp;
int count, error;
error = pthread_mutex_lock(&tempname_mtx);
assert(!error);
if (tempname_pid == -1) {
tempname_pid = getpid();
tempname_count = 0;
}
count = tempname_count++;
error = pthread_mutex_unlock(&tempname_mtx);
assert(!error);
cp = strrchr(path, '/');
if (cp == NULL)
xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
(long)tempname_pid, count);
else
xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
TEMPNAME_PREFIX, (long)tempname_pid, count);
return (temp);
}
void *
xmalloc(size_t size)
{
void *buf;
buf = malloc(size);
if (buf == NULL)
err(1, "malloc");
return (buf);
}
void *
xrealloc(void *buf, size_t size)
{
buf = realloc(buf, size);
if (buf == NULL)
err(1, "realloc");
return (buf);
}
char *
xstrdup(const char *str)
{
char *buf;
buf = strdup(str);
if (buf == NULL)
err(1, "strdup");
return (buf);
}
int
xasprintf(char **ret, const char *format, ...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = vasprintf(ret, format, ap);
va_end(ap);
if (*ret == NULL)
err(1, "asprintf");
return (rv);
}
struct pattlist *
pattlist_new(void)
{
struct pattlist *p;
p = xmalloc(sizeof(struct pattlist));
p->size = 4; /* Initial size. */
p->patterns = xmalloc(p->size * sizeof(char *));
p->in = 0;
return (p);
}
void
pattlist_add(struct pattlist *p, const char *pattern)
{
if (p->in == p->size) {
p->size *= 2;
p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
}
assert(p->in < p->size);
p->patterns[p->in++] = xstrdup(pattern);
}
char *
pattlist_get(struct pattlist *p, size_t i)
{
assert(i < p->in);
return (p->patterns[i]);
}
size_t
pattlist_size(struct pattlist *p)
{
return (p->in);
}
void
pattlist_free(struct pattlist *p)
{
size_t i;
for (i = 0; i < p->in; i++)
free(p->patterns[i]);
free(p->patterns);
free(p);
}
/* Creates a backoff timer. */
struct backoff_timer *
bt_new(time_t min, time_t max, float backoff, float jitter)
{
struct backoff_timer *bt;
bt = xmalloc(sizeof(struct backoff_timer));
bt->min = min;
bt->max = max;
bt->backoff = backoff;
bt->jitter = jitter;
bt->interval = min;
bt_addjitter(bt);
srandom(time(0));
return (bt);
}
/* Updates the backoff timer. */
static void
bt_update(struct backoff_timer *bt)
{
bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
bt_addjitter(bt);
}
/* Adds some jitter. */
static void
bt_addjitter(struct backoff_timer *bt)
{
long mag;
mag = (long)(bt->jitter * bt->interval);
/* We want a random number between -mag and mag. */
bt->interval += (time_t)(random() % (2 * mag) - mag);
}
/* Returns the current timer value. */
time_t
bt_get(struct backoff_timer *bt)
{
return (bt->interval);
}
/* Times out for bt->interval seconds. */
void
bt_pause(struct backoff_timer *bt)
{
sleep(bt->interval);
bt_update(bt);
}
void
bt_free(struct backoff_timer *bt)
{
free(bt);
}
/* Compare two revisions. */
int
rcsnum_cmp(char *revision1, char *revision2)
{
char *ptr1, *ptr2, *dot1, *dot2;
int num1len, num2len, ret;
ptr1 = revision1;
ptr2 = revision2;
while (*ptr1 != '\0' && *ptr2 != '\0') {
dot1 = strchr(ptr1, '.');
dot2 = strchr(ptr2, '.');
if (dot1 == NULL)
dot1 = strchr(ptr1, '\0');
if (dot2 == NULL)
dot2 = strchr(ptr2, '\0');
num1len = dot1 - ptr1;
num2len = dot2 - ptr2;
/* Check the distance between each, showing how many digits */
if (num1len > num2len)
return (1);
else if (num1len < num2len)
return (-1);
/* Equal distance means we must check each character. */
ret = strncmp(ptr1, ptr2, num1len);
if (ret != 0)
return (ret);
ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
}
if (*ptr1 != '\0' && *ptr2 == '\0')
return (1);
if (*ptr1 == '\0' && *ptr2 != '\0')
return (-1);
return (0);
}
/* Returns 0 if a rcsrev is not a trunk revision number. */
int
rcsrev_istrunk(char *revnum)
{
char *tmp;
tmp = strchr(revnum, '.');
tmp++;
if (strchr(tmp, '.') != NULL)
return (0);
return (1);
}
/* Return prefix of rcsfile. */
char *
rcsrev_prefix(char *revnum)
{
char *modrev, *pos;
modrev = xstrdup(revnum);
pos = strrchr(modrev, '.');
if (pos == NULL) {
free(modrev);
return (NULL);
}
*pos = '\0';
return (modrev);
}

View File

@ -1,146 +0,0 @@
/*-
* 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$
*/
#ifndef _MISC_H_
#define _MISC_H_
#include <sys/types.h>
#ifdef __FreeBSD__
#include <md5.h>
#define MD5_DIGEST_LENGTH 16
#define MD5_Init MD5Init
#define MD5_Final MD5Final
#define MD5_Update MD5Update
#else
#include <openssl/md5.h>
#endif
/* If we're not compiling in a C99 environment, define the C99 types. */
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901
#ifdef uint32_t
#undef uint32_t
#endif
#define uint32_t u_int32_t
#ifdef uint16_t
#undef uint16_t
#endif
#define uint16_t u_int16_t
#ifdef uint8_t
#undef uint8_t
#endif
#define uint8_t u_int8_t
#else
#include <stdint.h>
#endif
/* This is a GCC-specific keyword but some other compilers (namely icc)
understand it, and the code won't work if we can't disable padding
anyways. */
#undef __packed
#define __packed __attribute__((__packed__))
/* We explicitely don't define this with icc because it defines __GNUC__
but doesn't support it. */
#undef __printflike
#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
(__GNUC__ > 2 || __GNUC__ == 2 && __GNUC__MINOR__ >= 7)
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#else
#define __printflike(fmtarg, firstvararg)
#endif
/* Exit codes. */
#define STATUS_SUCCESS 0
#define STATUS_FAILURE 1
#define STATUS_TRANSIENTFAILURE 2
#define STATUS_INTERRUPTED 3
struct config;
struct stream;
/* Thread parameters. */
struct thread_args {
struct config *config;
struct stream *rd;
struct stream *wr;
int status;
char *errmsg;
};
/* Minimum size for MD5_File() and MD5_End() buffers. */
#define MD5_DIGEST_SIZE 33
#define min(a, b) ((a) > (b) ? (b) : (a))
#define max(a, b) ((a) < (b) ? (b) : (a))
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 *);
int rcsdatetotm(const char *, struct tm *);
time_t rcsdatetotime(const char *);
int pathcmp(const char *, const char *);
size_t commonpathlength(const char *, size_t, const char *, size_t);
const char *pathlast(const char *);
int isrcs(const char *, size_t *);
char *checkoutpath(const char *, const char *);
char *cvspath(const char *, const char *, int);
char *atticpath(const char *, const char *);
char *path_prefix(char *);
char *path_first(char *);
int mkdirhier(char *, mode_t);
char *tempname(const char *);
void *xmalloc(size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *);
int xasprintf(char **, const char *, ...) __printflike(2, 3);
int rcsnum_cmp(char *, char *);
int rcsrev_istrunk(char *);
char *rcsrev_prefix(char *);
struct pattlist *pattlist_new(void);
void pattlist_add(struct pattlist *, const char *);
char *pattlist_get(struct pattlist *, size_t);
size_t pattlist_size(struct pattlist *);
void pattlist_free(struct pattlist *);
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_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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 _MUX_H_
#define _MUX_H_
struct mux;
struct chan;
struct mux *mux_open(int, struct chan **);
void mux_shutdown(struct mux *, const char *, int);
int mux_close(struct mux *);
void chan_wait(struct chan *);
int chan_listen(struct mux *);
struct chan *chan_accept(struct mux *, int);
ssize_t chan_read(struct chan *, void *, size_t);
ssize_t chan_write(struct chan *, const void *, size_t);
int chan_close(struct chan *);
#endif /* !_MUX_H_ */

View File

@ -1,91 +0,0 @@
%{
/*-
* Copyright (c) 2003-2004, 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 "config.h"
#include "token.h"
%}
%union {
char *str;
int i;
}
%token DEFAULT
%token <i> NAME
%token <i> BOOLEAN
%token EQUAL
%token <str> STRING
%%
config_file
: config_list
|
;
config_list
: config
| config_list config
;
config
: default_line
| collection
;
default_line
: DEFAULT options
{ coll_setdef(); }
;
collection
: STRING options
{ coll_add($1); }
;
options
:
| options option
;
option
: BOOLEAN
{ coll_setopt($1, NULL); }
| value
;
value
: NAME EQUAL STRING
{ coll_setopt($1, $3); }
;
%%

View File

@ -1,182 +0,0 @@
/*-
* 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 <assert.h>
#include <stdlib.h>
#include <string.h>
#include "misc.h"
#include "pathcomp.h"
struct pathcomp {
char *target;
size_t targetlen;
char *trashed;
char *prev;
size_t prevlen;
size_t goal;
size_t curlen;
};
struct pathcomp *
pathcomp_new(void)
{
struct pathcomp *pc;
pc = xmalloc(sizeof(struct pathcomp));
pc->curlen = 0;
pc->target = NULL;
pc->targetlen = 0;
pc->trashed = NULL;
pc->prev = NULL;
pc->prevlen = 0;
return (pc);
}
int
pathcomp_put(struct pathcomp *pc, int type, char *path)
{
char *cp;
assert(pc->target == NULL);
if (*path == '/')
return (-1);
switch (type) {
case PC_DIRDOWN:
pc->target = path;
pc->targetlen = strlen(path);
break;
case PC_FILE:
case PC_DIRUP:
cp = strrchr(path, '/');
pc->target = path;
if (cp != NULL)
pc->targetlen = cp - path;
else
pc->targetlen = 0;
break;
}
if (pc->prev != NULL)
pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target,
pc->targetlen);
else
pc->goal = 0;
if (pc->curlen == pc->goal) /* No need to go up. */
pc->goal = pc->targetlen;
return (0);
}
int
pathcomp_get(struct pathcomp *pc, int *type, char **name)
{
char *cp;
size_t slashpos, start;
if (pc->curlen > pc->goal) { /* Going up. */
assert(pc->prev != NULL);
pc->prev[pc->curlen] = '\0';
cp = pc->prev + pc->curlen - 1;
while (cp >= pc->prev) {
if (*cp == '/')
break;
cp--;
}
if (cp >= pc->prev)
slashpos = cp - pc->prev;
else
slashpos = 0;
pc->curlen = slashpos;
if (pc->curlen <= pc->goal) { /* Done going up. */
assert(pc->curlen == pc->goal);
pc->goal = pc->targetlen;
}
*type = PC_DIRUP;
*name = pc->prev;
return (1);
} else if (pc->curlen < pc->goal) { /* Going down. */
/* Restore the previously overwritten '/' character. */
if (pc->trashed != NULL) {
*pc->trashed = '/';
pc->trashed = NULL;
}
if (pc->curlen == 0)
start = pc->curlen;
else
start = pc->curlen + 1;
slashpos = start;
while (slashpos < pc->goal) {
if (pc->target[slashpos] == '/')
break;
slashpos++;
}
if (pc->target[slashpos] != '\0') {
assert(pc->target[slashpos] == '/');
pc->trashed = pc->target + slashpos;
pc->target[slashpos] = '\0';
}
pc->curlen = slashpos;
*type = PC_DIRDOWN;
*name = pc->target;
return (1);
} else { /* Done. */
if (pc->target != NULL) {
if (pc->trashed != NULL) {
*pc->trashed = '/';
pc->trashed = NULL;
}
if (pc->prev != NULL)
free(pc->prev);
pc->prev = xmalloc(pc->targetlen + 1);
memcpy(pc->prev, pc->target, pc->targetlen);
pc->prev[pc->targetlen] = '\0';
pc->prevlen = pc->targetlen;
pc->target = NULL;
pc->targetlen = 0;
}
return (0);
}
}
void
pathcomp_finish(struct pathcomp *pc)
{
pc->target = NULL;
pc->targetlen = 0;
pc->goal = 0;
}
void
pathcomp_free(struct pathcomp *pc)
{
if (pc->prev != NULL)
free(pc->prev);
free(pc);
}

View File

@ -1,44 +0,0 @@
/*-
* 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 _PATHCOMP_H
#define _PATHCOMP_H
/* File types */
#define PC_DIRDOWN 0
#define PC_FILE 1
#define PC_DIRUP 2
struct pathcomp;
struct pathcomp *pathcomp_new(void);
int pathcomp_put(struct pathcomp *, int, char *);
int pathcomp_get(struct pathcomp *, int *, char **);
void pathcomp_finish(struct pathcomp *);
void pathcomp_free(struct pathcomp *);
#endif /* !_PATHCOMP_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +0,0 @@
/*-
* 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$
*/
#ifndef _PROTO_H_
#define _PROTO_H_
#include <time.h>
#include "misc.h"
#define PROTO_MAJ 17
#define PROTO_MIN 0
#define PROTO_SWVER "CSUP_1_0"
struct stream;
int proto_connect(struct config *, int, uint16_t);
int proto_run(struct config *);
int proto_printf(struct stream *, const char *, ...);
char *proto_get_ascii(char **);
char *proto_get_rest(char **);
int proto_get_int(char **, int *, int);
int proto_get_sizet(char **, size_t *, int);
int proto_get_time(char **, time_t *);
#endif /* !_PROTO_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,74 +0,0 @@
/*-
* Copyright (c) 2007-2009, Ulf Lilleengen <lulf@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 _RCSFILE_H_
#define _RCSFILE_H_
/* RCSFILE fields. */
#define RCSFILE_HEAD 0
#define RCSFILE_BRANCH 1
#define RCSFILE_STRICT 2
#define RCSFILE_COMMENT 3
#define RCSFILE_EXPAND 4
#define RCSFILE_DESC 5
struct rcsfile;
struct delta;
struct stream;
/* Fetching, sending and writing an RCS file. */
struct rcsfile *rcsfile_frompath(const char *, const char *, const char *,
const char *, int);
int rcsfile_send_details(struct rcsfile *, struct stream *);
int rcsfile_write(struct rcsfile *, struct stream *);
void rcsfile_print(struct rcsfile *);
void rcsfile_free(struct rcsfile *);
/* Used for adding and setting rcsfile values. */
void rcsfile_addaccess(struct rcsfile *, char *);
void rcsfile_addtag(struct rcsfile *, char *, char *);
void rcsfile_importtag(struct rcsfile *, char *, char *);
void rcsfile_deleterev(struct rcsfile *, char *);
void rcsfile_deletetag(struct rcsfile *, char *, char *);
struct delta *rcsfile_getdelta(struct rcsfile *, char *);
void rcsfile_setval(struct rcsfile *, int, char *);
/* Functions used for operating on RCS deltas. */
struct delta *rcsfile_addelta(struct rcsfile *, char *, char *, char *,
char *);
void rcsfile_importdelta(struct rcsfile *, char *, char *, char *,
char *, char *);
int rcsdelta_addlog(struct delta *, char *, int);
int rcsdelta_addtext(struct delta *, char *, int);
int rcsdelta_appendlog(struct delta *, char *, size_t);
int rcsdelta_appendtext(struct delta *, char *, size_t);
void rcsdelta_setstate(struct delta *, char *);
void rcsdelta_truncatetext(struct delta *, off_t);
void rcsdelta_truncatelog(struct delta *, off_t);
#endif /* !_RCSFILE_H_ */

View File

@ -1,366 +0,0 @@
/*-
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@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/queue.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "rcsfile.h"
#include "rcsparse.h"
#include "rcstokenizer.h"
/*
* This is an RCS-parser using lex for tokenizing and makes sure the RCS syntax
* is correct as it constructs an RCS file that is used by csup.
*/
static void asserttoken(yyscan_t *, int);
static int parse_admin(struct rcsfile *, yyscan_t *);
static int parse_deltas(struct rcsfile *, yyscan_t *, int);
static int parse_deltatexts(struct rcsfile *, yyscan_t *, int);
static char *duptext(yyscan_t *, int *);
struct string {
char *str;
STAILQ_ENTRY(string) next;
};
static void
asserttoken(yyscan_t *sp, int token)
{
int t;
t = token;
t = rcslex(*sp);
assert(t == token);
}
static char *
duptext(yyscan_t *sp, int *arglen)
{
char *tmp, *val;
int len;
tmp = rcsget_text(*sp);
len = rcsget_leng(*sp);
val = xmalloc(len + 1);
memcpy(val, tmp, len);
val[len] = '\0';
if (arglen != NULL)
*arglen = len;
return (val);
}
/*
* Start up parser, and use the rcsfile hook to add objects.
*/
int
rcsparse_run(struct rcsfile *rf, FILE *infp, int ro)
{
yyscan_t scanner;
char *desc;
int error, tok;
error = 0;
rcslex_init(&scanner);
rcsset_in(infp, scanner);
tok = parse_admin(rf, &scanner);
tok = parse_deltas(rf, &scanner, tok);
assert(tok == KEYWORD);
asserttoken(&scanner, STRING);
desc = duptext(&scanner, NULL);
rcsfile_setval(rf, RCSFILE_DESC, desc);
free(desc);
tok = rcslex(scanner);
/* Parse deltatexts if we need to edit. */
if (!ro) {
error = parse_deltatexts(rf, &scanner, tok);
if (error)
return (error);
}
rcslex_destroy(scanner);
return (0);
}
/*
* Parse the admin part of a RCS file.
*/
static int
parse_admin(struct rcsfile *rf, yyscan_t *sp)
{
char *branch, *comment, *expand, *head, *id, *revnum, *tag, *tmp;
int strict, token;
strict = 0;
branch = NULL;
/* head {num}; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
head = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_HEAD, head);
free(head);
asserttoken(sp, SEMIC);
/* { branch {num}; } */
token = rcslex(*sp);
if (token == KEYWORD_TWO) {
asserttoken(sp, NUM);
branch = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_BRANCH, branch);
free(branch);
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
/* access {id]*; */
assert(token == KEYWORD);
token = rcslex(*sp);
while (token == ID) {
id = duptext(sp, NULL);
rcsfile_addaccess(rf, id);
free(id);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* symbols {sym : num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
while (token == ID) {
tag = duptext(sp, NULL);
asserttoken(sp, COLON);
asserttoken(sp, NUM);
revnum = duptext(sp, NULL);
rcsfile_importtag(rf, tag, revnum);
free(tag);
free(revnum);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* locks {id : num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
while (token == ID) {
/* XXX: locks field is skipped */
asserttoken(sp, COLON);
asserttoken(sp, NUM);
token = rcslex(*sp);
}
assert(token == SEMIC);
token = rcslex(*sp);
while (token == KEYWORD) {
tmp = rcsget_text(*sp);
/* {strict ;} */
if (!strcmp(tmp, "strict")) {
rcsfile_setval(rf, RCSFILE_STRICT, tmp);
asserttoken(sp, SEMIC);
/* { comment {string}; } */
} else if (!strcmp(tmp, "comment")) {
token = rcslex(*sp);
if (token == STRING) {
comment = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_COMMENT, comment);
free(comment);
}
asserttoken(sp, SEMIC);
/* { expand {string}; } */
} else if (!strcmp(tmp, "expand")) {
token = rcslex(*sp);
if (token == STRING) {
expand = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_EXPAND, expand);
free(expand);
}
asserttoken(sp, SEMIC);
}
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
}
return (token);
}
/*
* Parse RCS deltas.
*/
static int
parse_deltas(struct rcsfile *rf, yyscan_t *sp, int token)
{
STAILQ_HEAD(, string) branchlist;
char *revnum, *revdate, *author, *state, *next;
/* In case we don't have deltas. */
if (token != NUM)
return (token);
do {
next = NULL;
state = NULL;
/* num */
assert(token == NUM);
revnum = duptext(sp, NULL);
/* date num; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
revdate = duptext(sp, NULL);
asserttoken(sp, SEMIC);
/* author id; */
asserttoken(sp, KEYWORD);
asserttoken(sp, ID);
author = duptext(sp, NULL);
asserttoken(sp, SEMIC);
/* state {id}; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
if (token == ID) {
state = duptext(sp, NULL);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* branches {num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
STAILQ_INIT(&branchlist);
while (token == NUM)
token = rcslex(*sp);
assert(token == SEMIC);
/* next {num}; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
if (token == NUM) {
next = duptext(sp, NULL);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored. */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
rcsfile_importdelta(rf, revnum, revdate, author, state, next);
free(revnum);
free(revdate);
free(author);
if (state != NULL)
free(state);
if (next != NULL)
free(next);
} while (token == NUM);
return (token);
}
/*
* Parse RCS deltatexts.
*/
static int
parse_deltatexts(struct rcsfile *rf, yyscan_t *sp, int token)
{
struct delta *d;
char *log, *revnum, *text;
int error, len;
error = 0;
/* In case we don't have deltatexts. */
if (token != NUM)
return (-1);
do {
/* num */
assert(token == NUM);
revnum = duptext(sp, NULL);
/* Get delta we're adding text to. */
d = rcsfile_getdelta(rf, revnum);
free(revnum);
/*
* XXX: The RCS file is corrupt, but lie and say it is ok.
* If it is actually broken, then the MD5 mismatch will
* trigger a fixup.
*/
if (d == NULL)
return (0);
/* log string */
asserttoken(sp, KEYWORD);
asserttoken(sp, STRING);
log = duptext(sp, &len);
error = rcsdelta_addlog(d, log, len);
free(log);
if (error)
return (-1);
/* { newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored. */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
/* text string */
assert(token == KEYWORD);
asserttoken(sp, STRING);
text = duptext(sp, &len);
error = rcsdelta_addtext(d, text, len);
/*
* If this happens, something is wrong with the RCS file, and it
* should be resent.
*/
free(text);
if (error)
return (-1);
token = rcslex(*sp);
} while (token == NUM);
return (0);
}

View File

@ -1,41 +0,0 @@
/*-
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@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 _RCSPARSE_H_
#define _RCSPARSE_H_
#define ID 0
#define NUM 1
#define KEYWORD 2
#define KEYWORD_TWO 3
#define STRING 4
#define SEMIC 5
#define COLON 6
struct rcsfile;
int rcsparse_run(struct rcsfile *, FILE *, int);
#endif /* !_RCSPARSE_H_ */

View File

@ -1,333 +0,0 @@
#ifndef rcsHEADER_H
#define rcsHEADER_H 1
#define rcsIN_HEADER 1
#line 6 "rcstokenizer.h"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* An opaque pointer. */
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
/* For convenience, these vars (plus the bison vars far below)
are macros in the reentrant scanner. */
#define yyin yyg->yyin_r
#define yyout yyg->yyout_r
#define yyextra yyg->yyextra_r
#define yyleng yyg->yyleng_r
#define yytext yyg->yytext_r
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
#define yy_flex_debug yyg->yy_flex_debug_r
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void rcsrestart (FILE *input_file ,yyscan_t yyscanner );
void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
void rcspop_buffer_state (yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *rcsalloc (yy_size_t ,yyscan_t yyscanner );
void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner );
void rcsfree (void * ,yyscan_t yyscanner );
/* Begin user sect3 */
#define rcswrap(n) 1
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include <unistd.h>
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
int rcslex_init (yyscan_t* scanner);
int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int rcslex_destroy (yyscan_t yyscanner );
int rcsget_debug (yyscan_t yyscanner );
void rcsset_debug (int debug_flag ,yyscan_t yyscanner );
YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner );
void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
FILE *rcsget_in (yyscan_t yyscanner );
void rcsset_in (FILE * in_str ,yyscan_t yyscanner );
FILE *rcsget_out (yyscan_t yyscanner );
void rcsset_out (FILE * out_str ,yyscan_t yyscanner );
int rcsget_leng (yyscan_t yyscanner );
char *rcsget_text (yyscan_t yyscanner );
int rcsget_lineno (yyscan_t yyscanner );
void rcsset_lineno (int line_number ,yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int rcswrap (yyscan_t yyscanner );
#else
extern int rcswrap (yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 8192
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int rcslex (yyscan_t yyscanner);
#define YY_DECL int rcslex (yyscan_t yyscanner)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#line 73 "rcstokenizer.l"
#line 332 "rcstokenizer.h"
#undef rcsIN_HEADER
#endif /* rcsHEADER_H */

View File

@ -1,73 +0,0 @@
/*-
* Copyright (c) 2007-2009, Ulf Lilleengen <lulf@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$
*/
/*
* This tokenizer must be generated by a lexxer with support for reentrancy.
*/
%{
#include <string.h>
#include "misc.h"
#include "rcsparse.h"
%}
%option reentrant noyywrap
%option header-file="rcstokenizer.h"
everything (.|\n)*
num [0-9\.]+
whitespace [\t\n ]
digit [0-9]
idchar [^$,.:;\t\n ]
string @([^@]|\n|"@@")*@
keyword head|access|symbols|locks|comment|expand|strict|date|author|state|branches|next|desc|log|text
keyword2 branch
newline \n
%%
{keyword2} {
return (KEYWORD_TWO);
}
{keyword} {
return (KEYWORD);
}
{string} {
return (STRING);
}
{num} {
return (NUM);
}
{num}?{idchar}({idchar}|{num})* {
/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/
return (ID);
}
; { return (SEMIC); }
: { return (COLON); }
\n ;
[ \t]+ ;
%%

View File

@ -1,223 +0,0 @@
/*-
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@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 <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "misc.h"
#include "rsyncfile.h"
#define MINBLOCKSIZE 1024
#define MAXBLOCKSIZE (16 * 1024)
#define RECEIVEBUFFERSIZE (15 * 1024)
#define BLOCKINFOSIZE 26
#define SEARCHREGION 10
#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE)
#define CHAR_OFFSET 3
#define RSUM_SIZE 9
struct rsyncfile {
char *start;
char *buf;
char *end;
size_t blocksize;
size_t fsize;
int fd;
char *blockptr;
int blocknum;
char blockmd5[MD5_DIGEST_SIZE];
char rsumstr[RSUM_SIZE];
uint32_t rsum;
};
static size_t rsync_chooseblocksize(size_t);
static uint32_t rsync_rollsum(char *, size_t);
/* Open a file and initialize variable for rsync operation. */
struct rsyncfile *
rsync_open(char *path, size_t blocksize, int rdonly)
{
struct rsyncfile *rf;
struct stat st;
int error;
rf = xmalloc(sizeof(*rf));
error = stat(path, &st);
if (error) {
free(rf);
return (NULL);
}
rf->fsize = st.st_size;
rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR);
if (rf->fd < 0) {
free(rf);
return (NULL);
}
rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0);
if (rf->buf == MAP_FAILED) {
free(rf);
return (NULL);
}
rf->start = rf->buf;
rf->end = rf->buf + rf->fsize;
rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) :
blocksize);
rf->blockptr = rf->buf;
rf->blocknum = 0;
return (rf);
}
/* Close and free all resources related to an rsync file transfer. */
int
rsync_close(struct rsyncfile *rf)
{
int error;
error = munmap(rf->buf, rf->fsize);
if (error)
return (error);
close(rf->fd);
free(rf);
return (0);
}
/*
* Choose the most appropriate block size for an rsync transfer. Modeled
* algorithm after cvsup.
*/
static size_t
rsync_chooseblocksize(size_t fsize)
{
size_t bestrem, blocksize, bs, hisearch, losearch, rem;
blocksize = fsize / MAXBLOCKS;
losearch = blocksize - SEARCHREGION;
hisearch = blocksize + SEARCHREGION;
if (losearch < MINBLOCKSIZE) {
losearch = MINBLOCKSIZE;
hisearch = losearch + (2 * SEARCHREGION);
} else if (hisearch > MAXBLOCKSIZE) {
hisearch = MAXBLOCKSIZE;
losearch = hisearch - (2 * SEARCHREGION);
}
bestrem = MAXBLOCKSIZE;
for (bs = losearch; bs <= hisearch; bs++) {
rem = fsize % bs;
if (rem < bestrem) {
bestrem = rem;
blocksize = bs;
}
}
return (bestrem);
}
/* Get the next rsync block of a file. */
int
rsync_nextblock(struct rsyncfile *rf)
{
MD5_CTX ctx;
size_t blocksize;
if (rf->blockptr >= rf->end)
return (0);
blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize);
/* Calculate MD5 of the block. */
MD5_Init(&ctx);
MD5_Update(&ctx, rf->blockptr, blocksize);
MD5_End(rf->blockmd5, &ctx);
rf->rsum = rsync_rollsum(rf->blockptr, blocksize);
snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum);
rf->blocknum++;
rf->blockptr += blocksize;
return (1);
}
/* Get the rolling checksum of a file. */
static uint32_t
rsync_rollsum(char *buf, size_t len)
{
uint32_t a, b;
char *ptr, *limit;
a = b = 0;
ptr = buf;
limit = buf + len;
while (ptr < limit) {
a += *ptr + CHAR_OFFSET;
b += a;
ptr++;
}
return ((b << 16) | a);
}
/* Get running sum so far. */
char *
rsync_rsum(struct rsyncfile *rf)
{
return (rf->rsumstr);
}
/* Get MD5 of current block. */
char *
rsync_blockmd5(struct rsyncfile *rf)
{
return (rf->blockmd5);
}
/* Accessor for blocksize. */
size_t
rsync_blocksize(struct rsyncfile *rf)
{
return (rf->blocksize);
}
/* Accessor for filesize. */
size_t
rsync_filesize(struct rsyncfile *rf)
{
return (rf->fsize);
}

View File

@ -1,41 +0,0 @@
/*-
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@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 _RSYNCFILE_H_
#define _RSYNCFILE_H_
struct rsyncfile;
struct rsyncfile *rsync_open(char *, size_t, int);
int rsync_nextblock(struct rsyncfile *);
char *rsync_rsum(struct rsyncfile *);
char *rsync_blockmd5(struct rsyncfile *);
int rsync_close(struct rsyncfile *);
size_t rsync_blocksize(struct rsyncfile *);
size_t rsync_filesize(struct rsyncfile *);
#endif /* !_RSYNCFILE_H_ */

View File

@ -1,875 +0,0 @@
/*-
* 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/queue.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "fattr.h"
#include "misc.h"
#include "pathcomp.h"
#include "proto.h"
#include "status.h"
#include "stream.h"
#define STATUS_VERSION 5
/* Internal error codes. */
#define STATUS_ERR_READ (-1)
#define STATUS_ERR_WRITE (-2)
#define STATUS_ERR_PARSE (-3)
#define STATUS_ERR_UNSORTED (-4)
#define STATUS_ERR_TRUNC (-5)
#define STATUS_ERR_BOGUS_DIRUP (-6)
#define STATUS_ERR_BAD_TYPE (-7)
#define STATUS_ERR_RENAME (-8)
static struct status *status_new(char *, time_t, struct stream *);
static struct statusrec *status_rd(struct status *);
static struct statusrec *status_rdraw(struct status *, char **);
static int status_wr(struct status *, struct statusrec *);
static int status_wrraw(struct status *, struct statusrec *,
char *);
static struct status *status_fromrd(char *, struct stream *);
static struct status *status_fromnull(char *);
static void status_free(struct status *);
static void statusrec_init(struct statusrec *);
static void statusrec_fini(struct statusrec *);
static int statusrec_cook(struct statusrec *, char *);
static int statusrec_cmp(struct statusrec *, struct statusrec *);
struct status {
char *path;
char *tempfile;
int error;
int suberror;
struct pathcomp *pc;
struct statusrec buf;
struct statusrec *previous;
struct statusrec *current;
struct stream *rd;
struct stream *wr;
time_t scantime;
int eof;
int linenum;
int depth;
int dirty;
};
static void
statusrec_init(struct statusrec *sr)
{
memset(sr, 0, sizeof(*sr));
}
static int
statusrec_cook(struct statusrec *sr, char *line)
{
char *clientattr, *serverattr;
switch (sr->sr_type) {
case SR_FILEDEAD:
case SR_FILELIVE:
clientattr = proto_get_ascii(&line);
if (clientattr == NULL || line != NULL)
return (-1);
sr->sr_clientattr = fattr_decode(clientattr);
if (sr->sr_clientattr == NULL)
return (-1);
break;
case SR_DIRDOWN:
/* Nothing to do. */
if (line != NULL)
return (-1);
break;
case SR_CHECKOUTLIVE:
sr->sr_tag = proto_get_ascii(&line);
sr->sr_date = proto_get_ascii(&line);
serverattr = proto_get_ascii(&line);
sr->sr_revnum = proto_get_ascii(&line);
sr->sr_revdate = proto_get_ascii(&line);
clientattr = proto_get_ascii(&line);
if (clientattr == NULL || line != NULL)
return (-1);
sr->sr_serverattr = fattr_decode(serverattr);
if (sr->sr_serverattr == NULL)
return (-1);
sr->sr_clientattr = fattr_decode(clientattr);
if (sr->sr_clientattr == NULL) {
fattr_free(sr->sr_serverattr);
return (-1);
}
break;
case SR_CHECKOUTDEAD:
sr->sr_tag = proto_get_ascii(&line);
sr->sr_date = proto_get_ascii(&line);
serverattr = proto_get_ascii(&line);
if (serverattr == NULL || line != NULL)
return (-1);
sr->sr_serverattr = fattr_decode(serverattr);
if (sr->sr_serverattr == NULL)
return (-1);
break;
case SR_DIRUP:
clientattr = proto_get_ascii(&line);
if (clientattr == NULL || line != NULL)
return (-1);
sr->sr_clientattr = fattr_decode(clientattr);
if (sr->sr_clientattr == NULL)
return (-1);
break;
default:
return (-1);
}
return (0);
}
static struct statusrec *
status_rd(struct status *st)
{
struct statusrec *sr;
char *line;
int error;
sr = status_rdraw(st, &line);
if (sr == NULL)
return (NULL);
error = statusrec_cook(sr, line);
if (error) {
st->error = STATUS_ERR_PARSE;
return (NULL);
}
return (sr);
}
static struct statusrec *
status_rdraw(struct status *st, char **linep)
{
struct statusrec sr;
char *cmd, *line, *file;
if (st->rd == NULL || st->eof)
return (NULL);
line = stream_getln(st->rd, NULL);
if (line == NULL) {
if (stream_eof(st->rd)) {
if (st->depth != 0) {
st->error = STATUS_ERR_TRUNC;
return (NULL);
}
st->eof = 1;
return (NULL);
}
st->error = STATUS_ERR_READ;
st->suberror = errno;
return (NULL);
}
st->linenum++;
cmd = proto_get_ascii(&line);
file = proto_get_ascii(&line);
if (file == NULL || strlen(cmd) != 1) {
st->error = STATUS_ERR_PARSE;
return (NULL);
}
switch (cmd[0]) {
case 'A':
sr.sr_type = SR_FILELIVE;
break;
case 'D':
sr.sr_type = SR_DIRDOWN;
st->depth++;
break;
case 'C':
sr.sr_type = SR_CHECKOUTLIVE;
break;
case 'c':
sr.sr_type = SR_CHECKOUTDEAD;
break;
case 'U':
sr.sr_type = SR_DIRUP;
if (st->depth <= 0) {
st->error = STATUS_ERR_BOGUS_DIRUP;
return (NULL);
}
st->depth--;
break;
case 'V':
sr.sr_type = SR_FILELIVE;
break;
case 'v':
sr.sr_type = SR_FILEDEAD;
break;
default:
st->error = STATUS_ERR_BAD_TYPE;
st->suberror = cmd[0];
return (NULL);
}
sr.sr_file = xstrdup(file);
if (st->previous != NULL &&
statusrec_cmp(st->previous, &sr) >= 0) {
st->error = STATUS_ERR_UNSORTED;
free(sr.sr_file);
return (NULL);
}
if (st->previous == NULL) {
st->previous = &st->buf;
} else {
statusrec_fini(st->previous);
statusrec_init(st->previous);
}
st->previous->sr_type = sr.sr_type;
st->previous->sr_file = sr.sr_file;
*linep = line;
return (st->previous);
}
static int
status_wr(struct status *st, struct statusrec *sr)
{
struct pathcomp *pc;
const struct fattr *fa;
char *name;
int error, type, usedirupattr;
pc = st->pc;
error = 0;
usedirupattr = 0;
if (sr->sr_type == SR_DIRDOWN) {
pathcomp_put(pc, PC_DIRDOWN, sr->sr_file);
} else if (sr->sr_type == SR_DIRUP) {
pathcomp_put(pc, PC_DIRUP, sr->sr_file);
usedirupattr = 1;
} else {
pathcomp_put(pc, PC_FILE, sr->sr_file);
}
while (pathcomp_get(pc, &type, &name)) {
if (type == PC_DIRDOWN) {
error = proto_printf(st->wr, "D %s\n", name);
} else if (type == PC_DIRUP) {
if (usedirupattr)
fa = sr->sr_clientattr;
else
fa = fattr_bogus;
usedirupattr = 0;
error = proto_printf(st->wr, "U %s %f\n", name, fa);
}
if (error)
goto bad;
}
switch (sr->sr_type) {
case SR_DIRDOWN:
case SR_DIRUP:
/* Already emitted above. */
break;
case SR_CHECKOUTLIVE:
error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n",
sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr,
sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr);
break;
case SR_CHECKOUTDEAD:
error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file,
sr->sr_tag, sr->sr_date, sr->sr_serverattr);
break;
case SR_FILELIVE:
error = proto_printf(st->wr, "V %s %f\n", sr->sr_file,
sr->sr_clientattr);
break;
case SR_FILEDEAD:
error = proto_printf(st->wr, "v %s %f\n", sr->sr_file,
sr->sr_clientattr);
break;
}
if (error)
goto bad;
return (0);
bad:
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
return (-1);
}
static int
status_wrraw(struct status *st, struct statusrec *sr, char *line)
{
char *name;
char cmd;
int error, ret, type;
if (st->wr == NULL)
return (0);
/*
* Keep the compressor in sync. At this point, the necessary
* DirDowns and DirUps should have already been emitted, so the
* compressor should return exactly one value in the PC_DIRDOWN
* and PC_DIRUP case and none in the PC_FILE case.
*/
if (sr->sr_type == SR_DIRDOWN)
pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file);
else if (sr->sr_type == SR_DIRUP)
pathcomp_put(st->pc, PC_DIRUP, sr->sr_file);
else
pathcomp_put(st->pc, PC_FILE, sr->sr_file);
if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) {
ret = pathcomp_get(st->pc, &type, &name);
assert(ret);
if (sr->sr_type == SR_DIRDOWN)
assert(type == PC_DIRDOWN);
else
assert(type == PC_DIRUP);
}
ret = pathcomp_get(st->pc, &type, &name);
assert(!ret);
switch (sr->sr_type) {
case SR_DIRDOWN:
cmd = 'D';
break;
case SR_DIRUP:
cmd = 'U';
break;
case SR_CHECKOUTLIVE:
cmd = 'C';
break;
case SR_CHECKOUTDEAD:
cmd = 'c';
break;
case SR_FILELIVE:
cmd = 'V';
break;
case SR_FILEDEAD:
cmd = 'v';
break;
default:
assert(0);
return (-1);
}
if (sr->sr_type == SR_DIRDOWN)
error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file);
else
error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file,
line);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
return (-1);
}
return (0);
}
static void
statusrec_fini(struct statusrec *sr)
{
fattr_free(sr->sr_serverattr);
fattr_free(sr->sr_clientattr);
free(sr->sr_file);
}
static int
statusrec_cmp(struct statusrec *a, struct statusrec *b)
{
size_t lena, lenb;
if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) {
lena = strlen(a->sr_file);
lenb = strlen(b->sr_file);
if (a->sr_type == SR_DIRUP &&
((lena < lenb && b->sr_file[lena] == '/') || lena == lenb)
&& strncmp(a->sr_file, b->sr_file, lena) == 0)
return (1);
if (b->sr_type == SR_DIRUP &&
((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena)
&& strncmp(a->sr_file, b->sr_file, lenb) == 0)
return (-1);
}
return (pathcmp(a->sr_file, b->sr_file));
}
static struct status *
status_new(char *path, time_t scantime, struct stream *file)
{
struct status *st;
st = xmalloc(sizeof(struct status));
st->path = path;
st->error = 0;
st->suberror = 0;
st->tempfile = NULL;
st->scantime = scantime;
st->rd = file;
st->wr = NULL;
st->previous = NULL;
st->current = NULL;
st->dirty = 0;
st->eof = 0;
st->linenum = 0;
st->depth = 0;
st->pc = pathcomp_new();
statusrec_init(&st->buf);
return (st);
}
static void
status_free(struct status *st)
{
if (st->previous != NULL)
statusrec_fini(st->previous);
if (st->rd != NULL)
stream_close(st->rd);
if (st->wr != NULL)
stream_close(st->wr);
if (st->tempfile != NULL)
free(st->tempfile);
free(st->path);
pathcomp_free(st->pc);
free(st);
}
static struct status *
status_fromrd(char *path, struct stream *file)
{
struct status *st;
char *id, *line;
time_t scantime;
int error, ver;
/* Get the first line of the file and validate it. */
line = stream_getln(file, NULL);
if (line == NULL) {
stream_close(file);
return (NULL);
}
id = proto_get_ascii(&line);
error = proto_get_int(&line, &ver, 10);
if (error) {
stream_close(file);
return (NULL);
}
error = proto_get_time(&line, &scantime);
if (error || line != NULL) {
stream_close(file);
return (NULL);
}
if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) {
stream_close(file);
return (NULL);
}
st = status_new(path, scantime, file);
st->linenum = 1;
return (st);
}
static struct status *
status_fromnull(char *path)
{
struct status *st;
st = status_new(path, -1, NULL);
st->eof = 1;
return (st);
}
/*
* Open the status file. If scantime is not -1, the file is opened
* for updating, otherwise, it is opened read-only. If the status file
* couldn't be opened, NULL is returned and errmsg is set to the error
* message.
*/
struct status *
status_open(struct coll *coll, time_t scantime, char **errmsg)
{
struct status *st;
struct stream *file;
struct fattr *fa;
char *destpath, *path;
int error, rv;
path = coll_statuspath(coll);
file = stream_open_file(path, O_RDONLY);
if (file == NULL) {
if (errno != ENOENT) {
xasprintf(errmsg, "Could not open \"%s\": %s\n",
path, strerror(errno));
free(path);
return (NULL);
}
st = status_fromnull(path);
} else {
st = status_fromrd(path, file);
if (st == NULL) {
xasprintf(errmsg, "Error in \"%s\": Bad header line",
path);
free(path);
return (NULL);
}
}
if (scantime != -1) {
/* Open for writing too. */
xasprintf(&destpath, "%s/%s/%s/", coll->co_base,
coll->co_colldir, coll->co_name);
st->tempfile = tempname(destpath);
if (mkdirhier(destpath, coll->co_umask) != 0) {
xasprintf(errmsg, "Cannot create directories leading "
"to \"%s\": %s", destpath, strerror(errno));
free(destpath);
status_free(st);
return (NULL);
}
free(destpath);
st->wr = stream_open_file(st->tempfile,
O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (st->wr == NULL) {
xasprintf(errmsg, "Cannot create \"%s\": %s",
st->tempfile, strerror(errno));
status_free(st);
return (NULL);
}
fa = fattr_new(FT_FILE, -1);
fattr_mergedefault(fa);
fattr_umask(fa, coll->co_umask);
rv = fattr_install(fa, st->tempfile, NULL);
fattr_free(fa);
if (rv == -1) {
xasprintf(errmsg,
"Cannot set attributes for \"%s\": %s",
st->tempfile, strerror(errno));
status_free(st);
return (NULL);
}
if (scantime != st->scantime)
st->dirty = 1;
error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION,
scantime);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
*errmsg = status_errmsg(st);
status_free(st);
return (NULL);
}
}
return (st);
}
/*
* Get an entry from the status file. If name is NULL, the next entry
* is returned. If name is not NULL, the entry matching this name is
* returned, or NULL if it couldn't be found. If deleteto is set to 1,
* all the entries read from the status file while looking for the
* given name are deleted.
*/
int
status_get(struct status *st, char *name, int isdirup, int deleteto,
struct statusrec **psr)
{
struct statusrec key;
struct statusrec *sr;
char *line;
int c, error;
if (st->eof)
return (0);
if (st->error)
return (-1);
if (name == NULL) {
sr = status_rd(st);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
*psr = sr;
return (1);
}
if (st->current != NULL) {
sr = st->current;
st->current = NULL;
} else {
sr = status_rd(st);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
}
key.sr_file = name;
if (isdirup)
key.sr_type = SR_DIRUP;
else
key.sr_type = SR_CHECKOUTLIVE;
c = statusrec_cmp(sr, &key);
if (c < 0) {
if (st->wr != NULL && !deleteto) {
error = status_wr(st, sr);
if (error)
return (-1);
}
/* Loop until we find the good entry. */
for (;;) {
sr = status_rdraw(st, &line);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
c = statusrec_cmp(sr, &key);
if (c >= 0)
break;
if (st->wr != NULL && !deleteto) {
error = status_wrraw(st, sr, line);
if (error)
return (-1);
}
}
error = statusrec_cook(sr, line);
if (error) {
st->error = STATUS_ERR_PARSE;
return (-1);
}
}
st->current = sr;
if (c != 0)
return (0);
*psr = sr;
return (1);
}
/*
* Put this entry into the status file. If an entry with the same name
* existed in the status file, it is replaced by this one, otherwise,
* the entry is added to the status file.
*/
int
status_put(struct status *st, struct statusrec *sr)
{
struct statusrec *old;
int error, ret;
ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old);
if (ret == -1)
return (-1);
if (ret) {
if (old->sr_type == SR_DIRDOWN) {
/* DirUp should never match DirDown */
assert(old->sr_type != SR_DIRUP);
if (sr->sr_type == SR_CHECKOUTLIVE ||
sr->sr_type == SR_CHECKOUTDEAD) {
/* We are replacing a directory with a file.
Delete all entries inside the directory we
are replacing. */
ret = status_get(st, sr->sr_file, 1, 1, &old);
if (ret == -1)
return (-1);
assert(ret);
}
} else
st->current = NULL;
}
st->dirty = 1;
error = status_wr(st, sr);
if (error)
return (-1);
return (0);
}
/*
* Delete the specified entry from the status file.
*/
int
status_delete(struct status *st, char *name, int isdirup)
{
struct statusrec *sr;
int ret;
ret = status_get(st, name, isdirup, 0, &sr);
if (ret == -1)
return (-1);
if (ret) {
st->current = NULL;
st->dirty = 1;
}
return (0);
}
/*
* Check whether we hit the end of file.
*/
int
status_eof(struct status *st)
{
return (st->eof);
}
/*
* Returns the error message if there was an error, otherwise returns
* NULL. The error message is allocated dynamically and needs to be
* freed by the caller after use.
*/
char *
status_errmsg(struct status *st)
{
char *errmsg;
if (!st->error)
return (NULL);
switch (st->error) {
case STATUS_ERR_READ:
xasprintf(&errmsg, "Read failure on \"%s\": %s",
st->path, strerror(st->suberror));
break;
case STATUS_ERR_WRITE:
xasprintf(&errmsg, "Write failure on \"%s\": %s",
st->tempfile, strerror(st->suberror));
break;
case STATUS_ERR_PARSE:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"Could not parse status record", st->path, st->linenum);
break;
case STATUS_ERR_UNSORTED:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"File is not sorted properly", st->path, st->linenum);
break;
case STATUS_ERR_TRUNC:
xasprintf(&errmsg, "Error in \"%s\": "
"File is truncated", st->path);
break;
case STATUS_ERR_BOGUS_DIRUP:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"\"U\" entry has no matching \"D\"", st->path, st->linenum);
break;
case STATUS_ERR_BAD_TYPE:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"Invalid file type \"%c\"", st->path, st->linenum,
st->suberror);
break;
case STATUS_ERR_RENAME:
xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s",
st->tempfile, st->path, strerror(st->suberror));
break;
default:
assert(0);
return (NULL);
}
return (errmsg);
}
/*
* Close the status file and free any resource associated with it.
* It is OK to pass NULL for errmsg only if the status file was
* opened read-only. If it wasn't opened read-only, status_close()
* can produce an error and errmsg is not allowed to be NULL. If
* there was no errors, errmsg is set to NULL.
*/
void
status_close(struct status *st, char **errmsg)
{
struct statusrec *sr;
char *line, *name;
int error, type;
if (st->wr != NULL) {
if (st->dirty) {
if (st->current != NULL) {
error = status_wr(st, st->current);
if (error) {
*errmsg = status_errmsg(st);
goto bad;
}
st->current = NULL;
}
/* Copy the rest of the file. */
while ((sr = status_rdraw(st, &line)) != NULL) {
error = status_wrraw(st, sr, line);
if (error) {
*errmsg = status_errmsg(st);
goto bad;
}
}
if (st->error) {
*errmsg = status_errmsg(st);
goto bad;
}
/* Close off all the open directories. */
pathcomp_finish(st->pc);
while (pathcomp_get(st->pc, &type, &name)) {
assert(type == PC_DIRUP);
error = proto_printf(st->wr, "U %s %f\n",
name, fattr_bogus);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
*errmsg = status_errmsg(st);
goto bad;
}
}
/* Rename tempfile. */
error = rename(st->tempfile, st->path);
if (error) {
st->error = STATUS_ERR_RENAME;
st->suberror = errno;
*errmsg = status_errmsg(st);
goto bad;
}
} else {
/* Just discard the tempfile. */
unlink(st->tempfile);
}
*errmsg = NULL;
}
status_free(st);
return;
bad:
status_free(st);
}

View File

@ -1,72 +0,0 @@
/*-
* 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 _STATUS_H_
#define _STATUS_H_
#include <time.h>
struct coll;
struct fattr;
struct status;
#define SR_DIRDOWN 0
#define SR_CHECKOUTLIVE 1
#define SR_CHECKOUTDEAD 2
#define SR_FILELIVE 3
#define SR_FILEDEAD 4
#define SR_DIRUP 5
struct statusrec {
int sr_type;
char *sr_file;
char *sr_tag;
char *sr_date;
char *sr_revnum;
char *sr_revdate;
/*
* "clientrttr" contains the attributes of the client's file if there
* is one. "serverattr" contains the attributes of the corresponding
* file on the server. In CVS mode, these are identical. But in
* checkout mode, "clientattr" represents the checked-out file while
* "serverattr" represents the corresponding RCS file on the server.
*/
struct fattr *sr_serverattr;
struct fattr *sr_clientattr;
};
struct status *status_open(struct coll *, time_t, char **);
int status_get(struct status *, char *, int, int,
struct statusrec **);
int status_put(struct status *, struct statusrec *);
int status_eof(struct status *);
char *status_errmsg(struct status *);
int status_delete(struct status *, char *, int);
void status_close(struct status *, char **);
#endif /* !_STATUS_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
/*-
* 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$
*/
#ifndef _STREAM_H_
#define _STREAM_H_
#include "misc.h"
/* Stream filters. */
typedef enum {
STREAM_FILTER_NULL,
STREAM_FILTER_ZLIB,
STREAM_FILTER_MD5,
STREAM_FILTER_MD5RCS
} stream_filter_t;
struct stream;
struct buf;
typedef ssize_t stream_readfn_t(void *, void *, size_t);
typedef ssize_t stream_writefn_t(void *, const void *, size_t);
typedef int stream_closefn_t(void *);
/* Convenience functions for handling file descriptors. */
stream_readfn_t stream_read_fd;
stream_writefn_t stream_write_fd;
stream_closefn_t stream_close_fd;
/* Convenience functions for handling character buffers. */
stream_readfn_t stream_read_buf;
stream_writefn_t stream_append_buf;
stream_closefn_t stream_close_buf;
struct stream *stream_open(void *, stream_readfn_t *, stream_writefn_t *,
stream_closefn_t *);
struct stream *stream_open_fd(int, stream_readfn_t *, stream_writefn_t *,
stream_closefn_t *);
struct stream *stream_open_buf(struct buf *);
struct stream *stream_open_file(const char *, int, ...);
int stream_fileno(struct stream *);
ssize_t stream_read(struct stream *, void *, size_t);
ssize_t stream_read_blocking(struct stream *, void *, size_t);
ssize_t stream_write(struct stream *, const void *, size_t);
char *stream_getln(struct stream *, size_t *);
int stream_printf(struct stream *, const char *, ...)
__printflike(2, 3);
int stream_flush(struct stream *);
int stream_sync(struct stream *);
int stream_truncate(struct stream *, off_t);
void stream_truncate_buf(struct buf *, off_t);
int stream_truncate_rel(struct stream *, off_t);
int stream_rewind(struct stream *);
int stream_eof(struct stream *);
int stream_close(struct stream *);
int stream_filter_start(struct stream *, stream_filter_t, void *);
void stream_filter_stop(struct stream *);
struct buf *buf_new(size_t);
void buf_free(struct buf *);
#endif /* !_STREAM_H_ */

View File

@ -1,177 +0,0 @@
/*-
* Copyright (c) 2004-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/queue.h>
#include <assert.h>
#include <err.h>
#include <pthread.h>
#include <stdlib.h>
#include "misc.h"
#include "threads.h"
/*
* This API is a wrapper around the pthread(3) API, which mainly
* allows me to wait for multiple threads to exit. We use a
* condition variable to signal a thread's death. All threads
* created with this API have a common entry/exit point, so we
* don't need to add any code in the threads themselves.
*/
/* Structure describing a thread. */
struct thread {
pthread_t thread;
void *(*start)(void *);
void *data;
struct threads *threads;
LIST_ENTRY(thread) runlist;
STAILQ_ENTRY(thread) deadlist;
};
/* A set of threads. */
struct threads {
pthread_mutex_t threads_mtx;
pthread_cond_t thread_exited;
LIST_HEAD(, thread) threads_running;
STAILQ_HEAD(, thread) threads_dead;
};
static void *thread_start(void *); /* Common entry point for threads. */
static void threads_lock(struct threads *);
static void threads_unlock(struct threads *);
static void
threads_lock(struct threads *tds)
{
int error;
error = pthread_mutex_lock(&tds->threads_mtx);
assert(!error);
}
static void
threads_unlock(struct threads *tds)
{
int error;
error = pthread_mutex_unlock(&tds->threads_mtx);
assert(!error);
}
/* Create a new set of threads. */
struct threads *
threads_new(void)
{
struct threads *tds;
tds = xmalloc(sizeof(struct threads));
pthread_mutex_init(&tds->threads_mtx, NULL);
pthread_cond_init(&tds->thread_exited, NULL);
LIST_INIT(&tds->threads_running);
STAILQ_INIT(&tds->threads_dead);
return (tds);
}
/* Create a new thread in this set. */
void
threads_create(struct threads *tds, void *(*start)(void *), void *data)
{
pthread_attr_t attr;
struct thread *td;
int error;
td = xmalloc(sizeof(struct thread));
td->threads = tds;
td->start = start;
td->data = data;
/* We don't use pthread_join() to wait for the threads to finish. */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
threads_lock(tds);
error = pthread_create(&td->thread, &attr, thread_start, td);
if (error)
err(1, "pthread_create");
LIST_INSERT_HEAD(&tds->threads_running, td, runlist);
threads_unlock(tds);
}
/* Wait for a thread in the set to exit, and return its data pointer. */
void *
threads_wait(struct threads *tds)
{
struct thread *td;
void *data;
threads_lock(tds);
while (STAILQ_EMPTY(&tds->threads_dead)) {
assert(!LIST_EMPTY(&tds->threads_running));
pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx);
}
td = STAILQ_FIRST(&tds->threads_dead);
STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist);
threads_unlock(tds);
data = td->data;
free(td);
return (data);
}
/* Free a threads set. */
void
threads_free(struct threads *tds)
{
assert(LIST_EMPTY(&tds->threads_running));
assert(STAILQ_EMPTY(&tds->threads_dead));
pthread_cond_destroy(&tds->thread_exited);
pthread_mutex_destroy(&tds->threads_mtx);
free(tds);
}
/*
* Common entry point for threads. This just calls the real start
* routine, and then signals the thread's death, after having
* removed the thread from the list.
*/
static void *
thread_start(void *data)
{
struct threads *tds;
struct thread *td;
td = data;
tds = td->threads;
td->start(td->data);
threads_lock(tds);
LIST_REMOVE(td, runlist);
STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist);
pthread_cond_signal(&tds->thread_exited);
threads_unlock(tds);
return (NULL);
}

View File

@ -1,38 +0,0 @@
/*-
* Copyright (c) 2004, 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 _THREADS_H_
#define _THREADS_H_
struct threads;
struct threads *threads_new(void);
void threads_create(struct threads *, void *(*)(void *), void *);
void *threads_wait(struct threads *);
void threads_free(struct threads *);
#endif /* !_THREADS_H_ */

View File

@ -1,49 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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 _TOKEN_H_
#define _TOKEN_H_
void yyerror(const char *);
int yylex(void);
int yyparse(void);
/* Parsing tokens. */
#define PT_BASE 0
#define PT_DATE 1
#define PT_HOST 2
#define PT_PREFIX 3
#define PT_RELEASE 4
#define PT_TAG 5
#define PT_UMASK 6
#define PT_COMPRESS 7
#define PT_DELETE 8
#define PT_USE_REL_SUFFIX 9
#define PT_LIST 10
#define PT_NORSYNC 11
#endif /* !_TOKEN_H_ */

View File

@ -1,79 +0,0 @@
%{
/*-
* Copyright (c) 2003-2004, 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 <err.h>
#include <stdlib.h>
#include <string.h>
#include "parse.h"
#include "misc.h"
#include "token.h"
int lineno = 1;
%}
%option nounput
%option noyywrap
%%
[ \t]+ ;
#.* ;
\*default { return DEFAULT; }
base { yylval.i = PT_BASE; return NAME; }
date { yylval.i = PT_DATE; return NAME; }
host { yylval.i = PT_HOST; return NAME; }
prefix { yylval.i = PT_PREFIX; return NAME; }
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; }
use-rel-suffix { yylval.i = PT_USE_REL_SUFFIX; return BOOLEAN; }
[a-zA-Z0-9./_-]+ {
yylval.str = strdup(yytext);
if (yylval.str == NULL)
err(1, "strdup");
return STRING;
}
\n lineno++;
%%
void
yyerror(const char *s)
{
lprintf(-1, "Parse error line %d: %s: %s\n", lineno, s, yytext);
exit(1);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +0,0 @@
/*-
* Copyright (c) 2003-2004, 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 _UPDATER_H_
#define _UPDATER_H_
void *updater(void *);
#endif /* !_UPDATER_H_ */