diff options
Diffstat (limited to 'contrib/csup/updater.c')
-rw-r--r-- | contrib/csup/updater.c | 2044 |
1 files changed, 0 insertions, 2044 deletions
diff --git a/contrib/csup/updater.c b/contrib/csup/updater.c deleted file mode 100644 index d9f4210b390b..000000000000 --- a/contrib/csup/updater.c +++ /dev/null @@ -1,2044 +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 <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "config.h" -#include "diff.h" -#include "fattr.h" -#include "fixups.h" -#include "keyword.h" -#include "updater.h" -#include "misc.h" -#include "mux.h" -#include "proto.h" -#include "rcsfile.h" -#include "status.h" -#include "stream.h" - -/* Internal error codes. */ -#define UPDATER_ERR_PROTO (-1) /* Protocol error. */ -#define UPDATER_ERR_MSG (-2) /* Error is in updater->errmsg. */ -#define UPDATER_ERR_READ (-3) /* Error reading from server. */ -#define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */ - -#define BUFSIZE 4096 - -/* Everything needed to update a file. */ -struct file_update { - struct statusrec srbuf; - char *destpath; - char *temppath; - char *origpath; - char *coname; /* Points somewhere in destpath. */ - char *wantmd5; - struct coll *coll; - struct status *st; - /* Those are only used for diff updating. */ - char *author; - struct stream *orig; - struct stream *to; - int attic; - int expand; -}; - -struct updater { - struct config *config; - struct stream *rd; - char *errmsg; - int deletecount; -}; - -static struct file_update *fup_new(struct coll *, struct status *); -static int fup_prepare(struct file_update *, char *, int); -static void fup_cleanup(struct file_update *); -static void fup_free(struct file_update *); - -static void updater_prunedirs(char *, char *); -static int updater_batch(struct updater *, int); -static int updater_docoll(struct updater *, struct file_update *, int); -static int updater_delete(struct updater *, struct file_update *); -static void updater_deletefile(const char *); -static int updater_checkout(struct updater *, struct file_update *, int); -static int updater_addfile(struct updater *, struct file_update *, - char *, int); -int updater_addelta(struct rcsfile *, struct stream *, char *); -static int updater_setattrs(struct updater *, struct file_update *, - char *, char *, char *, char *, char *, struct fattr *); -static int updater_setdirattrs(struct updater *, struct coll *, - struct file_update *, char *, char *); -static int updater_updatefile(struct updater *, struct file_update *fup, - const char *, int); -static int updater_updatenode(struct updater *, struct coll *, - struct file_update *, char *, char *); -static int updater_diff(struct updater *, struct file_update *); -static int updater_diff_batch(struct updater *, struct file_update *); -static int updater_diff_apply(struct updater *, struct file_update *, - char *); -static int updater_rcsedit(struct updater *, struct file_update *, char *, - char *); -int updater_append_file(struct updater *, struct file_update *, - off_t); -static int updater_rsync(struct updater *, struct file_update *, size_t); -static int updater_read_checkout(struct stream *, struct stream *); - -static struct file_update * -fup_new(struct coll *coll, struct status *st) -{ - struct file_update *fup; - - fup = xmalloc(sizeof(struct file_update)); - memset(fup, 0, sizeof(*fup)); - fup->coll = coll; - fup->st = st; - return (fup); -} - -static int -fup_prepare(struct file_update *fup, char *name, int attic) -{ - struct coll *coll; - - coll = fup->coll; - fup->attic = 0; - fup->origpath = NULL; - - if (coll->co_options & CO_CHECKOUTMODE) - fup->destpath = checkoutpath(coll->co_prefix, name); - else { - fup->destpath = cvspath(coll->co_prefix, name, attic); - fup->origpath = atticpath(coll->co_prefix, name); - /* If they're equal, we don't need special care. */ - if (fup->origpath != NULL && - strcmp(fup->origpath, fup->destpath) == 0) { - free(fup->origpath); - fup->origpath = NULL; - } - fup->attic = attic; - } - if (fup->destpath == NULL) - return (-1); - fup->coname = fup->destpath + coll->co_prefixlen + 1; - return (0); -} - -/* Called after each file update to reinit the structure. */ -static void -fup_cleanup(struct file_update *fup) -{ - struct statusrec *sr; - - sr = &fup->srbuf; - - if (fup->destpath != NULL) { - free(fup->destpath); - fup->destpath = NULL; - } - if (fup->temppath != NULL) { - free(fup->temppath); - fup->temppath = NULL; - } - if (fup->origpath != NULL) { - free(fup->origpath); - fup->origpath = NULL; - } - fup->coname = NULL; - if (fup->author != NULL) { - free(fup->author); - fup->author = NULL; - } - fup->expand = 0; - if (fup->wantmd5 != NULL) { - free(fup->wantmd5); - fup->wantmd5 = NULL; - } - if (fup->orig != NULL) { - stream_close(fup->orig); - fup->orig = NULL; - } - if (fup->to != NULL) { - stream_close(fup->to); - fup->to = NULL; - } - if (sr->sr_file != NULL) - free(sr->sr_file); - if (sr->sr_tag != NULL) - free(sr->sr_tag); - if (sr->sr_date != NULL) - free(sr->sr_date); - if (sr->sr_revnum != NULL) - free(sr->sr_revnum); - if (sr->sr_revdate != NULL) - free(sr->sr_revdate); - fattr_free(sr->sr_clientattr); - fattr_free(sr->sr_serverattr); - memset(sr, 0, sizeof(*sr)); -} - -static void -fup_free(struct file_update *fup) -{ - - fup_cleanup(fup); - free(fup); -} - -void * -updater(void *arg) -{ - struct thread_args *args; - struct updater upbuf, *up; - int error; - - args = arg; - - up = &upbuf; - up->config = args->config; - up->rd = args->rd; - up->errmsg = NULL; - up->deletecount = 0; - - error = updater_batch(up, 0); - - /* - * Make sure to close the fixups even in case of an error, - * so that the lister thread doesn't block indefinitely. - */ - fixups_close(up->config->fixups); - if (!error) - error = updater_batch(up, 1); - switch (error) { - case UPDATER_ERR_PROTO: - xasprintf(&args->errmsg, "Updater failed: Protocol error"); - args->status = STATUS_FAILURE; - break; - case UPDATER_ERR_MSG: - xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg); - free(up->errmsg); - args->status = STATUS_FAILURE; - break; - case UPDATER_ERR_READ: - if (stream_eof(up->rd)) { - xasprintf(&args->errmsg, "Updater failed: " - "Premature EOF from server"); - } else { - xasprintf(&args->errmsg, "Updater failed: " - "Network read failure: %s", strerror(errno)); - } - args->status = STATUS_TRANSIENTFAILURE; - break; - case UPDATER_ERR_DELETELIM: - xasprintf(&args->errmsg, "Updater failed: " - "File deletion limit exceeded"); - args->status = STATUS_FAILURE; - break; - default: - assert(error == 0); - args->status = STATUS_SUCCESS; - }; - return (NULL); -} - -static int -updater_batch(struct updater *up, int isfixups) -{ - struct stream *rd; - struct coll *coll; - struct status *st; - struct file_update *fup; - char *line, *cmd, *errmsg, *collname, *release; - int error; - - rd = up->rd; - STAILQ_FOREACH(coll, &up->config->colls, co_next) { - if (coll->co_options & CO_SKIP) - continue; - umask(coll->co_umask); - line = stream_getln(rd, NULL); - if (line == NULL) - return (UPDATER_ERR_READ); - cmd = proto_get_ascii(&line); - collname = proto_get_ascii(&line); - release = proto_get_ascii(&line); - if (release == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - if (strcmp(cmd, "COLL") != 0 || - strcmp(collname, coll->co_name) != 0 || - strcmp(release, coll->co_release) != 0) - return (UPDATER_ERR_PROTO); - - if (!isfixups) - lprintf(1, "Updating collection %s/%s\n", coll->co_name, - coll->co_release); - - if (coll->co_options & CO_COMPRESS) - stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); - - st = status_open(coll, coll->co_scantime, &errmsg); - if (st == NULL) { - up->errmsg = errmsg; - return (UPDATER_ERR_MSG); - } - fup = fup_new(coll, st); - error = updater_docoll(up, fup, isfixups); - status_close(st, &errmsg); - fup_free(fup); - if (errmsg != NULL) { - /* Discard previous error. */ - if (up->errmsg != NULL) - free(up->errmsg); - up->errmsg = errmsg; - return (UPDATER_ERR_MSG); - } - if (error) - return (error); - - if (coll->co_options & CO_COMPRESS) - stream_filter_stop(rd); - } - line = stream_getln(rd, NULL); - if (line == NULL) - return (UPDATER_ERR_READ); - if (strcmp(line, ".") != 0) - return (UPDATER_ERR_PROTO); - return (0); -} - -static int -updater_docoll(struct updater *up, struct file_update *fup, int isfixups) -{ - struct stream *rd; - struct coll *coll; - struct statusrec srbuf, *sr; - struct fattr *rcsattr, *tmp; - char *attr, *cmd, *blocksize, *line, *msg; - char *name, *tag, *date, *revdate; - char *expand, *wantmd5, *revnum; - char *optstr, *rcsopt, *pos; - time_t t; - off_t position; - int attic, error, needfixupmsg; - - error = 0; - rd = up->rd; - coll = fup->coll; - needfixupmsg = isfixups; - while ((line = stream_getln(rd, NULL)) != NULL) { - if (strcmp(line, ".") == 0) - break; - memset(&srbuf, 0, sizeof(srbuf)); - if (needfixupmsg) { - lprintf(1, "Applying fixups for collection %s/%s\n", - coll->co_name, coll->co_release); - needfixupmsg = 0; - } - cmd = proto_get_ascii(&line); - if (cmd == NULL || strlen(cmd) != 1) - return (UPDATER_ERR_PROTO); - switch (cmd[0]) { - case 'T': - /* Update recorded information for checked-out file. */ - name = proto_get_ascii(&line); - tag = proto_get_ascii(&line); - date = proto_get_ascii(&line); - revnum = proto_get_ascii(&line); - revdate = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - - rcsattr = fattr_decode(attr); - if (rcsattr == NULL) - return (UPDATER_ERR_PROTO); - - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - error = updater_setattrs(up, fup, name, tag, date, - revnum, revdate, rcsattr); - fattr_free(rcsattr); - if (error) - return (error); - break; - case 'c': - /* Checkout dead file. */ - name = proto_get_ascii(&line); - tag = proto_get_ascii(&line); - date = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - /* Theoritically, the file does not exist on the client. - Just to make sure, we'll delete it here, if it - exists. */ - if (access(fup->destpath, F_OK) == 0) { - error = updater_delete(up, fup); - if (error) - return (error); - } - - sr = &srbuf; - sr->sr_type = SR_CHECKOUTDEAD; - sr->sr_file = name; - sr->sr_tag = tag; - sr->sr_date = date; - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - - error = status_put(fup->st, sr); - fattr_free(sr->sr_serverattr); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'U': - /* Update live checked-out file. */ - name = proto_get_ascii(&line); - tag = proto_get_ascii(&line); - date = proto_get_ascii(&line); - proto_get_ascii(&line); /* XXX - oldRevNum */ - proto_get_ascii(&line); /* XXX - fromAttic */ - proto_get_ascii(&line); /* XXX - logLines */ - expand = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - wantmd5 = proto_get_ascii(&line); - if (wantmd5 == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - - sr = &fup->srbuf; - sr->sr_type = SR_CHECKOUTLIVE; - sr->sr_file = xstrdup(name); - sr->sr_date = xstrdup(date); - sr->sr_tag = xstrdup(tag); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - - fup->expand = keyword_decode_expand(expand); - if (fup->expand == -1) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - - fup->wantmd5 = xstrdup(wantmd5); - fup->temppath = tempname(fup->destpath); - error = updater_diff(up, fup); - if (error) - return (error); - break; - case 'u': - /* Update dead checked-out file. */ - name = proto_get_ascii(&line); - tag = proto_get_ascii(&line); - date = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - error = updater_delete(up, fup); - if (error) - return (error); - sr = &srbuf; - sr->sr_type = SR_CHECKOUTDEAD; - sr->sr_file = name; - sr->sr_tag = tag; - sr->sr_date = date; - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - error = status_put(fup->st, sr); - fattr_free(sr->sr_serverattr); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'C': - case 'Y': - /* Checkout file. */ - name = proto_get_ascii(&line); - tag = proto_get_ascii(&line); - date = proto_get_ascii(&line); - revnum = proto_get_ascii(&line); - revdate = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - - sr = &fup->srbuf; - sr->sr_type = SR_CHECKOUTLIVE; - sr->sr_file = xstrdup(name); - sr->sr_tag = xstrdup(tag); - sr->sr_date = xstrdup(date); - sr->sr_revnum = xstrdup(revnum); - sr->sr_revdate = xstrdup(revdate); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - - t = rcsdatetotime(revdate); - if (t == -1) - return (UPDATER_ERR_PROTO); - - sr->sr_clientattr = fattr_new(FT_FILE, t); - tmp = fattr_forcheckout(sr->sr_serverattr, - coll->co_umask); - fattr_override(sr->sr_clientattr, tmp, FA_MASK); - fattr_free(tmp); - fattr_mergedefault(sr->sr_clientattr); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - fup->temppath = tempname(fup->destpath); - if (*cmd == 'Y') - error = updater_checkout(up, fup, 1); - else - error = updater_checkout(up, fup, 0); - if (error) - return (error); - break; - case 'D': - /* Delete file. */ - name = proto_get_ascii(&line); - if (name == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - error = updater_delete(up, fup); - if (error) - return (error); - error = status_delete(fup->st, name, 0); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'A': - case 'a': - case 'R': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (name == NULL || attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - attic = (cmd[0] == 'a'); - error = fup_prepare(fup, name, attic); - if (error) - return (UPDATER_ERR_PROTO); - - fup->temppath = tempname(fup->destpath); - sr = &fup->srbuf; - sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - if (attic) - lprintf(1, " Create %s -> Attic\n", name); - else - lprintf(1, " Create %s\n", name); - error = updater_addfile(up, fup, attr, 0); - if (error) - return (error); - break; - case 'r': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - blocksize = proto_get_ascii(&line); - wantmd5 = proto_get_ascii(&line); - if (name == NULL || attr == NULL || blocksize == NULL || - wantmd5 == NULL) { - return (UPDATER_ERR_PROTO); - } - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - fup->wantmd5 = xstrdup(wantmd5); - fup->temppath = tempname(fup->destpath); - sr = &fup->srbuf; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - sr->sr_type = SR_FILELIVE; - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - error = updater_rsync(up, fup, strtol(blocksize, NULL, - 10)); - if (error) - return (error); - break; - case 'I': - /* - * Create directory and add DirDown entry in status - * file. - */ - name = proto_get_ascii(&line); - if (name == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - sr = &fup->srbuf; - sr->sr_type = SR_DIRDOWN; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = NULL; - sr->sr_clientattr = fattr_new(FT_DIRECTORY, -1); - fattr_mergedefault(sr->sr_clientattr); - - error = mkdirhier(fup->destpath, coll->co_umask); - if (error) - return (UPDATER_ERR_PROTO); - if (access(fup->destpath, F_OK) != 0) { - lprintf(1, " Mkdir %s\n", name); - error = fattr_makenode(sr->sr_clientattr, - fup->destpath); - if (error) - return (UPDATER_ERR_PROTO); - } - error = status_put(fup->st, sr); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'i': - /* Remove DirDown entry in status file. */ - name = proto_get_ascii(&line); - if (name == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - error = status_delete(fup->st, name, 0); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'J': - /* - * Set attributes of directory and update DirUp entry in - * status file. - */ - name = proto_get_ascii(&line); - if (name == NULL) - return (UPDATER_ERR_PROTO); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - error = updater_setdirattrs(up, coll, fup, name, attr); - if (error) - return (error); - break; - case 'j': - /* - * Remove directory and delete its DirUp entry in status - * file. - */ - name = proto_get_ascii(&line); - if (name == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - if (error) - return (UPDATER_ERR_PROTO); - lprintf(1, " Rmdir %s\n", name); - updater_deletefile(fup->destpath); - error = status_delete(fup->st, name, 0); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'L': - case 'l': - name = proto_get_ascii(&line); - if (name == NULL) - return (UPDATER_ERR_PROTO); - attr = proto_get_ascii(&line); - if (attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - attic = (cmd[0] == 'l'); - sr = &fup->srbuf; - sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - sr->sr_clientattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL || - sr->sr_clientattr == NULL) - return (UPDATER_ERR_PROTO); - - /* Save space. Described in detail in updatefile. */ - if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) - || fattr_getlinkcount(sr->sr_clientattr) <= 1) - fattr_maskout(sr->sr_clientattr, - FA_DEV | FA_INODE); - fattr_maskout(sr->sr_clientattr, FA_FLAGS); - error = status_put(fup->st, sr); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - break; - case 'N': - case 'n': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (name == NULL || attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - attic = (cmd[0] == 'n'); - error = fup_prepare(fup, name, attic); - if (error) - return (UPDATER_ERR_PROTO); - sr = &fup->srbuf; - sr->sr_type = (attic ? SR_FILEDEAD : SR_FILELIVE); - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - sr->sr_clientattr = fattr_new(FT_SYMLINK, -1); - fattr_mergedefault(sr->sr_clientattr); - fattr_maskout(sr->sr_clientattr, FA_FLAGS); - error = updater_updatenode(up, coll, fup, name, attr); - if (error) - return (error); - break; - case 'V': - case 'v': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - optstr = proto_get_ascii(&line); - wantmd5 = proto_get_ascii(&line); - rcsopt = NULL; /* XXX: Not supported. */ - if (attr == NULL || line != NULL || wantmd5 == NULL) - return (UPDATER_ERR_PROTO); - attic = (cmd[0] == 'v'); - error = fup_prepare(fup, name, attic); - if (error) - return (UPDATER_ERR_PROTO); - fup->temppath = tempname(fup->destpath); - fup->wantmd5 = xstrdup(wantmd5); - sr = &fup->srbuf; - sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - - error = 0; - error = updater_rcsedit(up, fup, name, rcsopt); - if (error) - return (error); - break; - case 'X': - case 'x': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - if (name == NULL || attr == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - attic = (cmd[0] == 'x'); - error = fup_prepare(fup, name, attic); - if (error) - return (UPDATER_ERR_PROTO); - - fup->temppath = tempname(fup->destpath); - sr = &fup->srbuf; - sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - lprintf(1, " Fixup %s\n", name); - error = updater_addfile(up, fup, attr, 1); - if (error) - return (error); - break; - case 'Z': - name = proto_get_ascii(&line); - attr = proto_get_ascii(&line); - pos = proto_get_ascii(&line); - if (name == NULL || attr == NULL || pos == NULL || - line != NULL) - return (UPDATER_ERR_PROTO); - error = fup_prepare(fup, name, 0); - fup->temppath = tempname(fup->destpath); - sr = &fup->srbuf; - sr->sr_type = SR_FILELIVE; - sr->sr_file = xstrdup(name); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - position = strtol(pos, NULL, 10); - lprintf(1, " Append to %s\n", name); - error = updater_append_file(up, fup, position); - if (error) - return (error); - break; - case '!': - /* Warning from server. */ - msg = proto_get_rest(&line); - if (msg == NULL) - return (UPDATER_ERR_PROTO); - lprintf(-1, "Server warning: %s\n", msg); - break; - default: - return (UPDATER_ERR_PROTO); - } - fup_cleanup(fup); - } - if (line == NULL) - return (UPDATER_ERR_READ); - return (0); -} - -/* Delete file. */ -static int -updater_delete(struct updater *up, struct file_update *fup) -{ - struct config *config; - struct coll *coll; - - config = up->config; - coll = fup->coll; - if (coll->co_options & CO_DELETE) { - lprintf(1, " Delete %s\n", fup->coname); - if (config->deletelim >= 0 && - up->deletecount >= config->deletelim) - return (UPDATER_ERR_DELETELIM); - up->deletecount++; - updater_deletefile(fup->destpath); - if (coll->co_options & CO_CHECKOUTMODE) - updater_prunedirs(coll->co_prefix, fup->destpath); - } else { - lprintf(1," NoDelete %s\n", fup->coname); - } - return (0); -} - -static void -updater_deletefile(const char *path) -{ - int error; - - error = fattr_delete(path); - if (error && errno != ENOENT) { - lprintf(-1, "Cannot delete \"%s\": %s\n", - path, strerror(errno)); - } -} - -static int -updater_setattrs(struct updater *up, struct file_update *fup, char *name, - char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr) -{ - struct statusrec sr; - struct status *st; - struct coll *coll; - struct fattr *fileattr, *fa; - char *path; - int error, rv; - - coll = fup->coll; - st = fup->st; - path = fup->destpath; - - fileattr = fattr_frompath(path, FATTR_NOFOLLOW); - if (fileattr == NULL) { - /* The file has vanished. */ - error = status_delete(st, name, 0); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - return (0); - } - fa = fattr_forcheckout(rcsattr, coll->co_umask); - fattr_override(fileattr, fa, FA_MASK); - fattr_free(fa); - - rv = fattr_install(fileattr, path, NULL); - if (rv == -1) { - lprintf(1, " SetAttrs %s\n", fup->coname); - fattr_free(fileattr); - xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s", - path, strerror(errno)); - return (UPDATER_ERR_MSG); - } - if (rv == 1) { - lprintf(1, " SetAttrs %s\n", fup->coname); - fattr_free(fileattr); - fileattr = fattr_frompath(path, FATTR_NOFOLLOW); - if (fileattr == NULL) { - /* We're being very unlucky. */ - error = status_delete(st, name, 0); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - return (0); - } - } - - fattr_maskout(fileattr, FA_COIGNORE); - - sr.sr_type = SR_CHECKOUTLIVE; - sr.sr_file = name; - sr.sr_tag = tag; - sr.sr_date = date; - sr.sr_revnum = revnum; - sr.sr_revdate = revdate; - sr.sr_clientattr = fileattr; - sr.sr_serverattr = rcsattr; - - error = status_put(st, &sr); - fattr_free(fileattr); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - return (0); -} - -static int -updater_updatefile(struct updater *up, struct file_update *fup, - const char *md5, int isfixup) -{ - struct coll *coll; - struct status *st; - struct statusrec *sr; - struct fattr *fileattr; - int error, rv; - - coll = fup->coll; - sr = &fup->srbuf; - st = fup->st; - - if (strcmp(fup->wantmd5, md5) != 0) { - if (isfixup) { - lprintf(-1, "%s: Checksum mismatch -- " - "file not updated\n", fup->destpath); - } else { - lprintf(-1, "%s: Checksum mismatch -- " - "will transfer entire file\n", fup->destpath); - fixups_put(up->config->fixups, fup->coll, sr->sr_file); - } - if (coll->co_options & CO_KEEPBADFILES) - lprintf(-1, "Bad version saved in %s\n", fup->temppath); - else - updater_deletefile(fup->temppath); - return (0); - } - - fattr_umask(sr->sr_clientattr, coll->co_umask); - rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath); - if (rv == -1) { - xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", - fup->temppath, fup->destpath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - - /* XXX Executes */ - /* - * We weren't necessarily able to set all the file attributes to the - * desired values, and any executes may have altered the attributes. - * To make sure we record the actual attribute values, we fetch - * them from the file. - * - * However, we preserve the link count as received from the - * server. This is important for preserving hard links in mirror - * mode. - */ - fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); - if (fileattr == NULL) { - xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); - fattr_free(sr->sr_clientattr); - sr->sr_clientattr = fileattr; - - /* - * To save space, don't write out the device and inode unless - * the link count is greater than 1. These attributes are used - * only for detecting hard links. If the link count is 1 then we - * know there aren't any hard links. - */ - if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || - fattr_getlinkcount(sr->sr_clientattr) <= 1) - fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); - - if (coll->co_options & CO_CHECKOUTMODE) - fattr_maskout(sr->sr_clientattr, FA_COIGNORE); - - error = status_put(st, sr); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - return (0); -} - -/* - * Update attributes of a directory. - */ -static int -updater_setdirattrs(struct updater *up, struct coll *coll, - struct file_update *fup, char *name, char *attr) -{ - struct statusrec *sr; - struct fattr *fa; - int error, rv; - - sr = &fup->srbuf; - sr->sr_type = SR_DIRUP; - sr->sr_file = xstrdup(name); - sr->sr_clientattr = fattr_decode(attr); - sr->sr_serverattr = fattr_decode(attr); - if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) - return (UPDATER_ERR_PROTO); - fattr_mergedefault(sr->sr_clientattr); - fattr_umask(sr->sr_clientattr, coll->co_umask); - rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL); - lprintf(1, " SetAttrs %s\n", name); - if (rv == -1) { - xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", - fup->temppath, fup->destpath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - /* - * Now, make sure they were set and record what was set in the status - * file. - */ - fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); - if (fa == NULL) { - xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - fattr_free(sr->sr_clientattr); - fattr_maskout(fa, FA_FLAGS); - sr->sr_clientattr = fa; - error = status_put(fup->st, sr); - if (error) { - up->errmsg = status_errmsg(fup->st); - return (UPDATER_ERR_MSG); - } - - return (0); -} - -static int -updater_diff(struct updater *up, struct file_update *fup) -{ - char md5[MD5_DIGEST_SIZE]; - struct coll *coll; - struct statusrec *sr; - struct fattr *fa, *tmp; - char *author, *path, *revnum, *revdate; - char *line, *cmd; - int error; - - coll = fup->coll; - sr = &fup->srbuf; - path = fup->destpath; - - lprintf(1, " Edit %s\n", fup->coname); - while ((line = stream_getln(up->rd, NULL)) != NULL) { - if (strcmp(line, ".") == 0) - break; - cmd = proto_get_ascii(&line); - if (cmd == NULL || strcmp(cmd, "D") != 0) - return (UPDATER_ERR_PROTO); - revnum = proto_get_ascii(&line); - proto_get_ascii(&line); /* XXX - diffbase */ - revdate = proto_get_ascii(&line); - author = proto_get_ascii(&line); - if (author == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - if (sr->sr_revnum != NULL) - free(sr->sr_revnum); - if (sr->sr_revdate != NULL) - free(sr->sr_revdate); - if (fup->author != NULL) - free(fup->author); - sr->sr_revnum = xstrdup(revnum); - sr->sr_revdate = xstrdup(revdate); - fup->author = xstrdup(author); - if (fup->orig == NULL) { - /* First patch, the "origin" file is the one we have. */ - fup->orig = stream_open_file(path, O_RDONLY); - if (fup->orig == NULL) { - xasprintf(&up->errmsg, "%s: Cannot open: %s", - path, strerror(errno)); - return (UPDATER_ERR_MSG); - } - } else { - /* Subsequent patches. */ - stream_close(fup->orig); - fup->orig = fup->to; - stream_rewind(fup->orig); - unlink(fup->temppath); - free(fup->temppath); - fup->temppath = tempname(path); - } - fup->to = stream_open_file(fup->temppath, - O_RDWR | O_CREAT | O_TRUNC, 0600); - if (fup->to == NULL) { - xasprintf(&up->errmsg, "%s: Cannot open: %s", - fup->temppath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - lprintf(2, " Add delta %s %s %s\n", sr->sr_revnum, - sr->sr_revdate, fup->author); - error = updater_diff_batch(up, fup); - if (error) - return (error); - } - if (line == NULL) - return (UPDATER_ERR_READ); - - fa = fattr_frompath(path, FATTR_FOLLOW); - tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask); - fattr_override(fa, tmp, FA_MASK); - fattr_free(tmp); - fattr_maskout(fa, FA_MODTIME); - sr->sr_clientattr = fa; - - if (MD5_File(fup->temppath, md5) == -1) { - xasprintf(&up->errmsg, - "Cannot calculate checksum for \"%s\": %s", - path, strerror(errno)); - return (UPDATER_ERR_MSG); - } - error = updater_updatefile(up, fup, md5, 0); - return (error); -} - -/* - * Edit a file and add delta. - */ -static int -updater_diff_batch(struct updater *up, struct file_update *fup) -{ - struct stream *rd; - char *cmd, *line, *state, *tok; - int error; - - state = NULL; - rd = up->rd; - while ((line = stream_getln(rd, NULL)) != NULL) { - if (strcmp(line, ".") == 0) - break; - cmd = proto_get_ascii(&line); - if (cmd == NULL || strlen(cmd) != 1) { - error = UPDATER_ERR_PROTO; - goto bad; - } - switch (cmd[0]) { - case 'L': - line = stream_getln(rd, NULL); - /* XXX - We're just eating the log for now. */ - while (line != NULL && strcmp(line, ".") != 0 && - strcmp(line, ".+") != 0) - line = stream_getln(rd, NULL); - if (line == NULL) { - error = UPDATER_ERR_READ; - goto bad; - } - break; - case 'S': - tok = proto_get_ascii(&line); - if (tok == NULL || line != NULL) { - error = UPDATER_ERR_PROTO; - goto bad; - } - if (state != NULL) - free(state); - state = xstrdup(tok); - break; - case 'T': - error = updater_diff_apply(up, fup, state); - if (error) - goto bad; - break; - default: - error = UPDATER_ERR_PROTO; - goto bad; - } - } - if (line == NULL) { - error = UPDATER_ERR_READ; - goto bad; - } - if (state != NULL) - free(state); - return (0); -bad: - if (state != NULL) - free(state); - return (error); -} - -int -updater_diff_apply(struct updater *up, struct file_update *fup, char *state) -{ - struct diffinfo dibuf, *di; - struct coll *coll; - struct statusrec *sr; - int error; - - coll = fup->coll; - sr = &fup->srbuf; - di = &dibuf; - - di->di_rcsfile = sr->sr_file; - di->di_cvsroot = coll->co_cvsroot; - di->di_revnum = sr->sr_revnum; - di->di_revdate = sr->sr_revdate; - di->di_author = fup->author; - di->di_tag = sr->sr_tag; - di->di_state = state; - di->di_expand = fup->expand; - - error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1); - if (error) { - /* XXX Bad error message */ - xasprintf(&up->errmsg, "Bad diff from server"); - return (UPDATER_ERR_MSG); - } - return (0); -} - -/* Update or create a node. */ -static int -updater_updatenode(struct updater *up, struct coll *coll, - struct file_update *fup, char *name, char *attr) -{ - struct fattr *fa, *fileattr; - struct status *st; - struct statusrec *sr; - int error, rv; - - sr = &fup->srbuf; - st = fup->st; - fa = fattr_decode(attr); - - if (fattr_type(fa) == FT_SYMLINK) { - lprintf(1, " Symlink %s -> %s\n", name, - fattr_getlinktarget(fa)); - } else { - lprintf(1, " Mknod %s\n", name); - } - - /* Create directory. */ - error = mkdirhier(fup->destpath, coll->co_umask); - if (error) - return (UPDATER_ERR_PROTO); - - /* If it does not exist, create it. */ - if (access(fup->destpath, F_OK) != 0) - fattr_makenode(fa, fup->destpath); - - /* - * Coming from attic? I don't think this is a problem since we have - * determined attic before we call this function (Look at UpdateNode in - * cvsup). - */ - fattr_umask(fa, coll->co_umask); - rv = fattr_install(fa, fup->destpath, fup->temppath); - if (rv == -1) { - xasprintf(&up->errmsg, "Cannot update attributes on " - "\"%s\": %s", fup->destpath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - /* - * XXX: Executes not implemented. Have not encountered much use for it - * yet. - */ - /* - * We weren't necessarily able to set all the file attributes to the - * desired values, and any executes may have altered the attributes. - * To make sure we record the actual attribute values, we fetch - * them from the file. - * - * However, we preserve the link count as received from the - * server. This is important for preserving hard links in mirror - * mode. - */ - fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); - if (fileattr == NULL) { - xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); - fattr_free(sr->sr_clientattr); - sr->sr_clientattr = fileattr; - - /* - * To save space, don't write out the device and inode unless - * the link count is greater than 1. These attributes are used - * only for detecting hard links. If the link count is 1 then we - * know there aren't any hard links. - */ - if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || - fattr_getlinkcount(sr->sr_clientattr) <= 1) - fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); - - /* If it is a symlink, write only out it's path. */ - if (fattr_type(fa) == FT_SYMLINK) { - fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE | - FA_LINKTARGET)); - } - fattr_maskout(sr->sr_clientattr, FA_FLAGS); - error = status_put(st, sr); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - fattr_free(fa); - - return (0); -} - -/* - * Fetches a new file in CVS mode. - */ -static int -updater_addfile(struct updater *up, struct file_update *fup, char *attr, - int isfixup) -{ - struct coll *coll; - struct stream *to; - struct statusrec *sr; - struct fattr *fa; - char buf[BUFSIZE]; - char md5[MD5_DIGEST_SIZE]; - ssize_t nread; - off_t fsize, remains; - char *cmd, *line, *path; - int error; - - coll = fup->coll; - path = fup->destpath; - sr = &fup->srbuf; - fa = fattr_decode(attr); - fsize = fattr_filesize(fa); - - error = mkdirhier(path, coll->co_umask); - if (error) - return (UPDATER_ERR_PROTO); - to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755); - if (to == NULL) { - xasprintf(&up->errmsg, "%s: Cannot create: %s", - fup->temppath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - stream_filter_start(to, STREAM_FILTER_MD5, md5); - remains = fsize; - do { - nread = stream_read(up->rd, buf, (BUFSIZE > remains ? - remains : BUFSIZE)); - if (nread == -1) - return (UPDATER_ERR_PROTO); - remains -= nread; - if (stream_write(to, buf, nread) == -1) - goto bad; - } while (remains > 0); - stream_close(to); - line = stream_getln(up->rd, NULL); - if (line == NULL) - return (UPDATER_ERR_PROTO); - /* Check for EOF. */ - if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) - return (UPDATER_ERR_PROTO); - line = stream_getln(up->rd, NULL); - if (line == NULL) - return (UPDATER_ERR_PROTO); - - cmd = proto_get_ascii(&line); - fup->wantmd5 = proto_get_ascii(&line); - if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) - return (UPDATER_ERR_PROTO); - - sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW); - if (sr->sr_clientattr == NULL) - return (UPDATER_ERR_PROTO); - fattr_override(sr->sr_clientattr, sr->sr_serverattr, - FA_MODTIME | FA_MASK); - error = updater_updatefile(up, fup, md5, isfixup); - fup->wantmd5 = NULL; /* So that it doesn't get freed. */ - return (error); -bad: - xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, - strerror(errno)); - return (UPDATER_ERR_MSG); -} - -static int -updater_checkout(struct updater *up, struct file_update *fup, int isfixup) -{ - char md5[MD5_DIGEST_SIZE]; - struct statusrec *sr; - struct coll *coll; - struct stream *to; - ssize_t nbytes; - size_t size; - char *cmd, *path, *line; - int error, first; - - coll = fup->coll; - sr = &fup->srbuf; - path = fup->destpath; - - if (isfixup) - lprintf(1, " Fixup %s\n", fup->coname); - else - lprintf(1, " Checkout %s\n", fup->coname); - error = mkdirhier(path, coll->co_umask); - if (error) { - xasprintf(&up->errmsg, - "Cannot create directories leading to \"%s\": %s", - path, strerror(errno)); - return (UPDATER_ERR_MSG); - } - - to = stream_open_file(fup->temppath, - O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (to == NULL) { - xasprintf(&up->errmsg, "%s: Cannot create: %s", - fup->temppath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - stream_filter_start(to, STREAM_FILTER_MD5, md5); - line = stream_getln(up->rd, &size); - first = 1; - while (line != NULL) { - if (line[size - 1] == '\n') - size--; - if ((size == 1 && *line == '.') || - (size == 2 && memcmp(line, ".+", 2) == 0)) - break; - if (size >= 2 && memcmp(line, "..", 2) == 0) { - size--; - line++; - } - if (!first) { - nbytes = stream_write(to, "\n", 1); - if (nbytes == -1) - goto bad; - } - nbytes = stream_write(to, line, size); - if (nbytes == -1) - goto bad; - line = stream_getln(up->rd, &size); - first = 0; - } - if (line == NULL) { - stream_close(to); - return (UPDATER_ERR_READ); - } - if (size == 1 && *line == '.') { - nbytes = stream_write(to, "\n", 1); - if (nbytes == -1) - goto bad; - } - stream_close(to); - /* Get the checksum line. */ - line = stream_getln(up->rd, NULL); - if (line == NULL) - return (UPDATER_ERR_READ); - cmd = proto_get_ascii(&line); - fup->wantmd5 = proto_get_ascii(&line); - if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) - return (UPDATER_ERR_PROTO); - error = updater_updatefile(up, fup, md5, isfixup); - fup->wantmd5 = NULL; /* So that it doesn't get freed. */ - if (error) - return (error); - return (0); -bad: - xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, - strerror(errno)); - return (UPDATER_ERR_MSG); -} - -/* - * Remove all empty directories below file. - * This function will trash the path passed to it. - */ -static void -updater_prunedirs(char *base, char *file) -{ - char *cp; - int error; - - while ((cp = strrchr(file, '/')) != NULL) { - *cp = '\0'; - if (strcmp(base, file) == 0) - return; - error = rmdir(file); - if (error) - return; - } -} - -/* - * Edit an RCS file. - */ -static int -updater_rcsedit(struct updater *up, struct file_update *fup, char *name, - char *rcsopt) -{ - struct coll *coll; - struct stream *dest; - struct statusrec *sr; - struct status *st; - struct rcsfile *rf; - struct fattr *oldfattr; - char md5[MD5_DIGEST_SIZE]; - char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath; - int error; - - coll = fup->coll; - sr = &fup->srbuf; - st = fup->st; - temppath = fup->temppath; - path = fup->origpath != NULL ? fup->origpath : fup->destpath; - error = 0; - - /* If the path is new, we must create the Attic dir if needed. */ - if (fup->origpath != NULL) { - error = mkdirhier(fup->destpath, coll->co_umask); - if (error) { - xasprintf(&up->errmsg, "Unable to create Attic dir for " - "%s\n", fup->origpath); - return (UPDATER_ERR_MSG); - } - } - /* - * XXX: we could avoid parsing overhead if we're reading ahead before we - * parse the file. - */ - oldfattr = fattr_frompath(path, FATTR_NOFOLLOW); - if (oldfattr == NULL) { - xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - fattr_merge(sr->sr_serverattr, oldfattr); - rf = NULL; - - /* Macro for making touching an RCS file faster. */ -#define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do { \ - if ((rf) == NULL) { \ - lprintf(1, " Edit %s", fup->coname); \ - if (fup->attic) \ - lprintf(1, " -> Attic"); \ - lprintf(1, "\n"); \ - (rf) = rcsfile_frompath((path), (name), (cvsroot), \ - (tag), 0); \ - if ((rf) == NULL) { \ - xasprintf(&(up)->errmsg, \ - "Error reading rcsfile %s\n", (name)); \ - return (UPDATER_ERR_MSG); \ - } \ - } \ -} while (0) - - while ((line = stream_getln(up->rd, NULL)) != NULL) { - if (strcmp(line, ".") == 0) - break; - cmd = proto_get_ascii(&line); - if (cmd == NULL) { - lprintf(-1, "Error editing %s\n", name); - return (UPDATER_ERR_PROTO); - } - switch(cmd[0]) { - case 'B': - branch = proto_get_ascii(&line); - if (branch == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - break; - case 'b': - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - rcsfile_setval(rf, RCSFILE_BRANCH, NULL); - break; - case 'D': - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - error = updater_addelta(rf, up->rd, line); - if (error) - return (error); - break; - case 'd': - revnum = proto_get_ascii(&line); - if (revnum == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - rcsfile_deleterev(rf, revnum); - break; - case 'E': - expand = proto_get_ascii(&line); - if (expand == NULL || line != NULL) - return (UPDATER_ERR_PROTO); - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - rcsfile_setval(rf, RCSFILE_EXPAND, expand); - break; - case 'T': - tag = proto_get_ascii(&line); - revnum = proto_get_ascii(&line); - if (tag == NULL || revnum == NULL || - line != NULL) - return (UPDATER_ERR_PROTO); - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - rcsfile_addtag(rf, tag, revnum); - break; - case 't': - tag = proto_get_ascii(&line); - revnum = proto_get_ascii(&line); - if (tag == NULL || revnum == NULL || - line != NULL) - return (UPDATER_ERR_PROTO); - UPDATER_OPENRCS(rf, up, path, name, - coll->co_cvsroot, coll->co_tag); - rcsfile_deletetag(rf, tag, revnum); - break; - default: - return (UPDATER_ERR_PROTO); - } - } - - if (rf == NULL) { - fattr_maskout(oldfattr, ~FA_MODTIME); - if (fattr_equal(oldfattr, sr->sr_serverattr)) - lprintf(1, " SetAttrs %s", fup->coname); - else - lprintf(1, " Touch %s", fup->coname); - /* Install new attributes. */ - fattr_umask(sr->sr_serverattr, coll->co_umask); - fattr_install(sr->sr_serverattr, fup->destpath, NULL); - if (fup->attic) - lprintf(1, " -> Attic"); - lprintf(1, "\n"); - fattr_free(oldfattr); - goto finish; - } - - /* Write and rename temp file. */ - dest = stream_open_file(fup->temppath, - O_RDWR | O_CREAT | O_TRUNC, 0600); - if (dest == NULL) { - xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", - fup->temppath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5); - error = rcsfile_write(rf, dest); - stream_close(dest); - rcsfile_free(rf); - if (error) { - xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - -finish: - sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW); - if (sr->sr_clientattr == NULL) { - xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", - fup->destpath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - fattr_override(sr->sr_clientattr, sr->sr_serverattr, - FA_MODTIME | FA_MASK); - if (rf != NULL) { - error = updater_updatefile(up, fup, md5, 0); - fup->wantmd5 = NULL; /* So that it doesn't get freed. */ - if (error) - return (error); - } else { - /* Record its attributes since we touched it. */ - if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || - fattr_getlinkcount(sr->sr_clientattr) <= 1) - fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); - error = status_put(st, sr); - if (error) { - up->errmsg = status_errmsg(st); - return (UPDATER_ERR_MSG); - } - } - - /* In this case, we need to remove the old file afterwards. */ - /* XXX: Can we be sure that a file not edited is moved? I don't think - * this is a problem, since if a file is moved, it should be edited to - * show if it's dead or not. - */ - if (fup->origpath != NULL) - updater_deletefile(fup->origpath); - return (0); -} - -/* - * Add a delta to a RCS file. - */ -int -updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline) -{ - struct delta *d; - size_t size; - char *author, *cmd, *diffbase, *line, *logline; - char *revdate, *revnum, *state, *textline; - - revnum = proto_get_ascii(&cmdline); - diffbase = proto_get_ascii(&cmdline); - revdate = proto_get_ascii(&cmdline); - author = proto_get_ascii(&cmdline); - size = 0; - - if (revnum == NULL || revdate == NULL || author == NULL) - return (UPDATER_ERR_PROTO); - - /* First add the delta so we have it. */ - d = rcsfile_addelta(rf, revnum, revdate, author, diffbase); - if (d == NULL) { - lprintf(-1, "Error adding delta %s\n", revnum); - return (UPDATER_ERR_READ); - } - while ((line = stream_getln(rd, NULL)) != NULL) { - if (strcmp(line, ".") == 0) - break; - cmd = proto_get_ascii(&line); - switch (cmd[0]) { - case 'L': - /* Do the same as in 'C' command. */ - logline = stream_getln(rd, &size); - while (logline != NULL) { - if (size == 2 && *logline == '.') - break; - if (size == 3 && - memcmp(logline, ".+", 2) == 0) { - rcsdelta_truncatelog(d, -1); - break; - } - if (size >= 3 && - memcmp(logline, "..", 2) == 0) { - size--; - logline++; - } - if (rcsdelta_appendlog(d, logline, size) - < 0) - return (-1); - logline = stream_getln(rd, &size); - } - break; - case 'N': - case 'n': - /* XXX: Not supported. */ - break; - case 'S': - state = proto_get_ascii(&line); - if (state == NULL) - return (UPDATER_ERR_PROTO); - rcsdelta_setstate(d, state); - break; - case 'T': - /* Do the same as in 'C' command. */ - textline = stream_getln(rd, &size); - while (textline != NULL) { - if (size == 2 && *textline == '.') - break; - if (size == 3 && - memcmp(textline, ".+", 2) == 0) { - /* Truncate newline. */ - rcsdelta_truncatetext(d, -1); - break; - } - if (size >= 3 && - memcmp(textline, "..", 2) == 0) { - size--; - textline++; - } - if (rcsdelta_appendtext(d, textline, - size) < 0) - return (-1); - textline = stream_getln(rd, &size); - } - break; - } - } - - return (0); -} - -int -updater_append_file(struct updater *up, struct file_update *fup, off_t pos) -{ - struct fattr *fa; - struct stream *to; - struct statusrec *sr; - ssize_t nread; - off_t bytes; - char buf[BUFSIZE], md5[MD5_DIGEST_SIZE]; - char *line, *cmd; - int error, fd; - - sr = &fup->srbuf; - fa = sr->sr_serverattr; - to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, - 0755); - if (to == NULL) { - xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - fd = open(fup->destpath, O_RDONLY); - if (fd < 0) { - xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - - stream_filter_start(to, STREAM_FILTER_MD5, md5); - /* First write the existing content. */ - while ((nread = read(fd, buf, BUFSIZE)) > 0) { - if (stream_write(to, buf, nread) == -1) - goto bad; - } - if (nread == -1) { - xasprintf(&up->errmsg, "%s: Error reading: %s", fup->destpath, - strerror(errno)); - return (UPDATER_ERR_MSG); - } - close(fd); - - bytes = fattr_filesize(fa) - pos; - /* Append the new data. */ - do { - nread = stream_read(up->rd, buf, - (BUFSIZE > bytes) ? bytes : BUFSIZE); - if (nread == -1) - return (UPDATER_ERR_PROTO); - bytes -= nread; - if (stream_write(to, buf, nread) == -1) - goto bad; - } while (bytes > 0); - stream_close(to); - - line = stream_getln(up->rd, NULL); - if (line == NULL) - return (UPDATER_ERR_PROTO); - /* Check for EOF. */ - if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) - return (UPDATER_ERR_PROTO); - line = stream_getln(up->rd, NULL); - if (line == NULL) - return (UPDATER_ERR_PROTO); - - cmd = proto_get_ascii(&line); - fup->wantmd5 = proto_get_ascii(&line); - if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) - return (UPDATER_ERR_PROTO); - - sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); - if (sr->sr_clientattr == NULL) - return (UPDATER_ERR_PROTO); - fattr_override(sr->sr_clientattr, sr->sr_serverattr, - FA_MODTIME | FA_MASK); - error = updater_updatefile(up, fup, md5, 0); - fup->wantmd5 = NULL; /* So that it doesn't get freed. */ - return (error); -bad: - xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, - strerror(errno)); - return (UPDATER_ERR_MSG); -} - -/* - * Read file data from stream of checkout commands, and write it to the - * destination. - */ -static int -updater_read_checkout(struct stream *src, struct stream *dest) -{ - ssize_t nbytes; - size_t size; - char *line; - int first; - - first = 1; - line = stream_getln(src, &size); - while (line != NULL) { - if (line[size - 1] == '\n') - size--; - if ((size == 1 && *line == '.') || - (size == 2 && strncmp(line, ".+", 2) == 0)) - break; - if (size >= 2 && strncmp(line, "..", 2) == 0) { - size--; - line++; - } - if (!first) { - nbytes = stream_write(dest, "\n", 1); - if (nbytes == -1) - return (UPDATER_ERR_MSG); - } - nbytes = stream_write(dest, line, size); - if (nbytes == -1) - return (UPDATER_ERR_MSG); - line = stream_getln(src, &size); - first = 0; - } - if (line == NULL) - return (UPDATER_ERR_READ); - if (size == 1 && *line == '.') { - nbytes = stream_write(dest, "\n", 1); - if (nbytes == -1) - return (UPDATER_ERR_MSG); - } - return (0); -} - -/* Update file using the rsync protocol. */ -static int -updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize) -{ - struct statusrec *sr; - struct stream *to; - char md5[MD5_DIGEST_SIZE]; - ssize_t nbytes; - size_t blocknum, blockstart, blockcount; - char *buf, *line; - int error, orig; - - sr = &fup->srbuf; - - lprintf(1, " Rsync %s\n", fup->coname); - /* First open all files that we are going to work on. */ - to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, - 0600); - if (to == NULL) { - xasprintf(&up->errmsg, "%s: Cannot create: %s", - fup->temppath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - orig = open(fup->destpath, O_RDONLY); - if (orig < 0) { - xasprintf(&up->errmsg, "%s: Cannot open: %s", - fup->destpath, strerror(errno)); - return (UPDATER_ERR_MSG); - } - stream_filter_start(to, STREAM_FILTER_MD5, md5); - - error = updater_read_checkout(up->rd, to); - if (error) { - xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, - strerror(errno)); - return (error); - } - - /* Buffer must contain blocksize bytes. */ - buf = xmalloc(blocksize); - /* Done with the initial text, read and write chunks. */ - line = stream_getln(up->rd, NULL); - while (line != NULL) { - if (strcmp(line, ".") == 0) - break; - error = UPDATER_ERR_PROTO; - if (proto_get_sizet(&line, &blockstart, 10) != 0) - goto bad; - if (proto_get_sizet(&line, &blockcount, 10) != 0) - goto bad; - /* Read blocks from original file. */ - lseek(orig, (blocksize * blockstart), SEEK_SET); - error = UPDATER_ERR_MSG; - for (blocknum = 0; blocknum < blockcount; blocknum++) { - nbytes = read(orig, buf, blocksize); - if (nbytes < 0) { - xasprintf(&up->errmsg, "%s: Cannot read: %s", - fup->destpath, strerror(errno)); - goto bad; - } - nbytes = stream_write(to, buf, nbytes); - if (nbytes == -1) { - xasprintf(&up->errmsg, "%s: Cannot write: %s", - fup->temppath, strerror(errno)); - goto bad; - } - } - /* Get the remaining text from the server. */ - error = updater_read_checkout(up->rd, to); - if (error) { - xasprintf(&up->errmsg, "%s: Cannot write: %s", - fup->temppath, strerror(errno)); - goto bad; - } - line = stream_getln(up->rd, NULL); - } - stream_close(to); - close(orig); - - sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); - if (sr->sr_clientattr == NULL) - return (UPDATER_ERR_PROTO); - fattr_override(sr->sr_clientattr, sr->sr_serverattr, - FA_MODTIME | FA_MASK); - - error = updater_updatefile(up, fup, md5, 0); - fup->wantmd5 = NULL; /* So that it doesn't get freed. */ -bad: - free(buf); - return (error); -} |