summaryrefslogtreecommitdiff
path: root/rsync/patch.c
authorkergoth <kergoth>2002-01-25 22:14:26 (UTC)
committer kergoth <kergoth>2002-01-25 22:14:26 (UTC)
commit15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff)
treec2fa0399a2c47fda8e2cd0092c73a809d17f68eb /rsync/patch.c
downloadopie-15318cad33835e4e2dc620d033e43cd930676cdd.zip
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz
opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2
Initial revision
Diffstat (limited to 'rsync/patch.c') (more/less context) (ignore whitespace changes)
-rw-r--r--rsync/patch.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/rsync/patch.c b/rsync/patch.c
new file mode 100644
index 0000000..d2daa85
--- a/dev/null
+++ b/rsync/patch.c
@@ -0,0 +1,317 @@
+/*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
+ *
+ * librsync -- the library for network deltas
+ * $Id$
+ *
+ * Copyright (C) 2000, 2001 by Martin Pool <mbp@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+ /*
+ | This is Tranquility Base.
+ */
+
+
+#include <config_rsync.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "rsync.h"
+#include "util.h"
+#include "trace.h"
+#include "protocol.h"
+#include "netint.h"
+#include "command.h"
+#include "sumset.h"
+#include "prototab.h"
+#include "stream.h"
+#include "job.h"
+
+
+
+static rs_result rs_patch_s_cmdbyte(rs_job_t *);
+static rs_result rs_patch_s_params(rs_job_t *);
+static rs_result rs_patch_s_run(rs_job_t *);
+static rs_result rs_patch_s_literal(rs_job_t *);
+static rs_result rs_patch_s_copy(rs_job_t *);
+static rs_result rs_patch_s_copying(rs_job_t *);
+
+
+/**
+ * State of trying to read the first byte of a command. Once we've
+ * taken that in, we can know how much data to read to get the
+ * arguments.
+ */
+static rs_result rs_patch_s_cmdbyte(rs_job_t *job)
+{
+ rs_result result;
+
+ if ((result = rs_suck_byte(job, &job->op)) != RS_DONE)
+ return result;
+
+ job->cmd = &rs_prototab[job->op];
+
+ rs_trace("got command byte 0x%02x (%s), len_1=%.0f", job->op,
+ rs_op_kind_name(job->cmd->kind),
+ (double) job->cmd->len_1);
+
+ if (job->cmd->len_1)
+ job->statefn = rs_patch_s_params;
+ else {
+ job->param1 = job->cmd->immediate;
+ job->statefn = rs_patch_s_run;
+ }
+
+ return RS_RUNNING;
+}
+
+
+/**
+ * Called after reading a command byte to pull in its parameters and
+ * then setup to execute the command.
+ */
+static rs_result rs_patch_s_params(rs_job_t *job)
+{
+ rs_result result;
+ int len = job->cmd->len_1 + job->cmd->len_2;
+ void *p;
+
+ assert(len);
+
+ result = rs_scoop_readahead(job, len, &p);
+ if (result != RS_DONE)
+ return result;
+
+ /* we now must have LEN bytes buffered */
+ result = rs_suck_netint(job, &job->param1, job->cmd->len_1);
+ /* shouldn't fail, since we already checked */
+ assert(result == RS_DONE);
+
+ if (job->cmd->len_2) {
+ result = rs_suck_netint(job, &job->param2, job->cmd->len_2);
+ assert(result == RS_DONE);
+ }
+
+ job->statefn = rs_patch_s_run;
+
+ return RS_RUNNING;
+}
+
+
+
+/**
+ * Called when we've read in the whole command and we need to execute it.
+ */
+static rs_result rs_patch_s_run(rs_job_t *job)
+{
+ rs_trace("running command 0x%x, kind %d", job->op, job->cmd->kind);
+
+ switch (job->cmd->kind) {
+ case RS_KIND_LITERAL:
+ job->statefn = rs_patch_s_literal;
+ return RS_RUNNING;
+
+ case RS_KIND_END:
+ return RS_DONE;
+ /* so we exit here; trying to continue causes an error */
+
+ case RS_KIND_COPY:
+ job->statefn = rs_patch_s_copy;
+ return RS_RUNNING;
+
+ default:
+ rs_error("bogus command 0x%02x", job->op);
+ return RS_CORRUPT;
+ }
+}
+
+
+/**
+ * Called when trying to copy through literal data.
+ */
+static rs_result rs_patch_s_literal(rs_job_t *job)
+{
+ rs_long_t len = job->param1;
+
+ rs_trace("LITERAL(len=%.0f)", (double) len);
+
+ job->stats.lit_cmds++;
+ job->stats.lit_bytes += len;
+ job->stats.lit_cmdbytes += 1 + job->cmd->len_1;
+
+ rs_tube_copy(job, len);
+
+ job->statefn = rs_patch_s_cmdbyte;
+ return RS_RUNNING;
+}
+
+
+
+static rs_result rs_patch_s_copy(rs_job_t *job)
+{
+ rs_long_t where, len;
+ rs_stats_t *stats;
+
+ where = job->param1;
+ len = job->param2;
+
+ rs_trace("COPY(where=%.0f, len=%.0f)", (double) where, (double) len);
+
+ if (len < 0) {
+ rs_log(RS_LOG_ERR, "invalid length=%.0f on COPY command", (double) len);
+ return RS_CORRUPT;
+ }
+
+ if (where < 0) {
+ rs_log(RS_LOG_ERR, "invalid where=%.0f on COPY command", (double) where);
+ return RS_CORRUPT;
+ }
+
+ job->basis_pos = where;
+ job->basis_len = len;
+
+ stats = &job->stats;
+
+ stats->copy_cmds++;
+ stats->copy_bytes += len;
+ stats->copy_cmdbytes += 1 + job->cmd->len_1 + job->cmd->len_2;
+
+ job->statefn = rs_patch_s_copying;
+ return RS_RUNNING;
+}
+
+
+/**
+ * Called when we're executing a COPY command and waiting for all the
+ * data to be retrieved from the callback.
+ */
+static rs_result rs_patch_s_copying(rs_job_t *job)
+{
+ rs_result result;
+ size_t len;
+ void *buf, *ptr;
+ rs_buffers_t *buffs = job->stream;
+
+ len = job->basis_len;
+
+ /* copy only as much as will fit in the output buffer, so that we
+ * don't have to block or store the input. */
+ if (len > buffs->avail_out)
+ len = buffs->avail_out;
+
+ if (!len)
+ return RS_BLOCKED;
+
+ rs_trace("copy %.0f bytes from basis at offset %.0f",
+ (double) len, (double) job->basis_pos);
+
+ ptr = buf = rs_alloc(len, "basis buffer");
+
+ result = (job->copy_cb)(job->copy_arg, job->basis_pos, &len, &ptr);
+ if (result != RS_DONE)
+ return result;
+ else
+ rs_trace("copy callback returned %s", rs_strerror(result));
+
+ rs_trace("got %.0f bytes back from basis callback", (double) len);
+
+ memcpy(buffs->next_out, ptr, len);
+
+ buffs->next_out += len;
+ buffs->avail_out -= len;
+
+ job->basis_pos += len;
+ job->basis_len -= len;
+
+ free(buf);
+
+ if (!job->basis_len) {
+ /* Done! */
+ job->statefn = rs_patch_s_cmdbyte;
+ }
+
+ return RS_RUNNING;
+}
+
+
+/**
+ * Called while we're trying to read the header of the patch.
+ */
+static rs_result rs_patch_s_header(rs_job_t *job)
+{
+ int v;
+ rs_result result;
+
+
+ if ((result = rs_suck_n4(job, &v)) != RS_DONE)
+ return result;
+
+ if (v != RS_DELTA_MAGIC) {
+ rs_log(RS_LOG_ERR,
+ "got magic number %#x rather than expected value %#x",
+ v, RS_DELTA_MAGIC);
+ return RS_BAD_MAGIC;
+ } else
+ rs_trace("got patch magic %#x", v);
+
+
+ job->statefn = rs_patch_s_cmdbyte;
+
+ return RS_RUNNING;
+}
+
+
+
+/**
+ * \brief Apply a \ref gloss_delta to a \ref gloss_basis to recreate
+ * the new file.
+ *
+ * This gives you back a ::rs_job_t object, which can be cranked by
+ * calling rs_job_iter() and updating the stream pointers. When
+ * finished, call rs_job_finish() to dispose of it.
+ *
+ * \param stream Contains pointers to input and output buffers, to be
+ * adjusted by caller on each iteration.
+ *
+ * \param copy_cb Callback used to retrieve content from the basis
+ * file.
+ *
+ * \param copy_arg Opaque environment pointer passed through to the
+ * callback.
+ *
+ * \todo As output is produced, accumulate the MD4 checksum of the
+ * output. Then if we find a CHECKSUM command we can check it's
+ * contents against the output.
+ *
+ * \todo Implement COPY commands.
+ *
+ * \sa rs_patch_file()
+ */
+rs_job_t *
+rs_patch_begin(rs_copy_cb *copy_cb, void *copy_arg)
+{
+ rs_job_t *job = rs_job_new("patch", rs_patch_s_header);
+
+ job->copy_cb = copy_cb;
+ job->copy_arg = copy_arg;
+
+ rs_mdfour_begin(&job->output_md4);
+
+ return job;
+}