librsync  2.0.1
rdiff.c
1 /*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
2  *
3  * librsync -- the library for network deltas
4  *
5  * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@sourcefrog.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22  /*
23  | .. after a year and a day, mourning is
24  | dangerous to the survivor and troublesome
25  | to the dead.
26  | -- Harold Bloom
27  */
28 
29 /*
30  * rdiff.c -- Command-line network-delta tool.
31  *
32  * TODO: Add a -z option to gzip/gunzip patches. This would be
33  * somewhat useful, but more importantly a good test of the streaming
34  * API. Also add -I for bzip2.
35  *
36  * If built with debug support and we have mcheck, then turn it on.
37  * (Optionally?)
38  *
39  * FIXME: popt doesn't handle single dashes very well at the moment:
40  * we'd like to use them as arguments to indicate stdin/stdout, but it
41  * turns them into options. I sent a patch to the popt maintainers;
42  * hopefully it will be fixed in the future.
43  *
44  * TODO: Add an option for delta to check whether the files are
45  * identical.
46  */
47 
48 #include "config.h"
49 
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <fcntl.h>
54 #include <popt.h>
55 
56 #ifdef HAVE_ZLIB_H
57 #include <zlib.h>
58 #endif
59 
60 #ifdef HAVE_BZLIB_H
61 #include <bzlib.h>
62 #endif
63 
64 #include "librsync.h"
65 #include "fileutil.h"
66 #include "util.h"
67 #include "trace.h"
68 #include "isprefix.h"
69 #include "sumset.h"
70 
71 
72 #define PROGRAM "rdiff"
73 
74 static size_t block_len = RS_DEFAULT_BLOCK_LEN;
75 static size_t strong_len = 0;
76 
77 static int show_stats = 0;
78 
79 static int bzip2_level = 0;
80 static int gzip_level = 0;
81 static int file_force = 0;
82 
83 enum {
84  OPT_GZIP = 1069, OPT_BZIP2
85 };
86 
87 extern int rs_roll_paranoia;
88 char *rs_hash_name;
89 
90 const struct poptOption opts[] = {
91  { "verbose", 'v', POPT_ARG_NONE, 0, 'v' },
92  { "version", 'V', POPT_ARG_NONE, 0, 'V' },
93  { "input-size", 'I', POPT_ARG_INT, &rs_inbuflen },
94  { "output-size", 'O', POPT_ARG_INT, &rs_outbuflen },
95  { "hash", 'H', POPT_ARG_STRING, &rs_hash_name },
96  { "help", '?', POPT_ARG_NONE, 0, 'h' },
97  { 0, 'h', POPT_ARG_NONE, 0, 'h' },
98  { "block-size", 'b', POPT_ARG_INT, &block_len },
99  { "sum-size", 'S', POPT_ARG_INT, &strong_len },
100  { "statistics", 's', POPT_ARG_NONE, &show_stats },
101  { "stats", 0, POPT_ARG_NONE, &show_stats },
102  { "gzip", 'z', POPT_ARG_NONE, 0, OPT_GZIP },
103  { "bzip2", 'i', POPT_ARG_NONE, 0, OPT_BZIP2 },
104  { "force", 'f', POPT_ARG_NONE, &file_force },
105  { "paranoia", 0, POPT_ARG_NONE, &rs_roll_paranoia },
106  { 0 }
107 };
108 
109 
110 static void rdiff_usage(const char *error)
111 {
112  fprintf(stderr, "%s\n"
113  "Try `%s --help' for more information.\n",
114  error, PROGRAM);
115 }
116 
117 
118 static void rdiff_no_more_args(poptContext opcon)
119 {
120  if (poptGetArg(opcon)) {
121  rdiff_usage("rdiff: too many arguments");
122  exit(RS_SYNTAX_ERROR);
123  }
124 }
125 
126 
127 static void bad_option(poptContext opcon, int error)
128 {
129  fprintf(stderr, "%s: %s: %s",
130  PROGRAM, poptStrerror(error), poptBadOption(opcon, 0));
131  exit(RS_SYNTAX_ERROR);
132 }
133 
134 
135 static void help(void) {
136  printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n"
137  " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n"
138  " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n"
139  "\n"
140  "Options:\n"
141  " -v, --verbose Trace internal processing\n"
142  " -V, --version Show program version\n"
143  " -?, --help Show this help message\n"
144  " -s, --statistics Show performance statistics\n"
145  " -f, --force Force overwriting existing files\n"
146  "Signature generation options:\n"
147  " -H, --hash=ALG Hash algorithm: blake2 (default), md4\n"
148  "Delta-encoding options:\n"
149  " -b, --block-size=BYTES Signature block size\n"
150  " -S, --sum-size=BYTES Set signature strength\n"
151  " --paranoia Verify all rolling checksums\n"
152  "IO options:\n"
153  " -I, --input-size=BYTES Input buffer size\n"
154  " -O, --output-size=BYTES Output buffer size\n"
155  " -z, --gzip[=LEVEL] gzip-compress deltas\n"
156  " -i, --bzip2[=LEVEL] bzip2-compress deltas\n"
157  );
158 }
159 
160 
161 static void rdiff_show_version(void)
162 {
163  char const *bzlib = "", *zlib = "", *trace = "";
164 
165 #if 0
166  /* Compression isn't implemented so don't mention it. */
167 #ifdef HAVE_LIBZ
168  zlib = ", gzip";
169 #endif
170 
171 #ifdef HAVE_LIBBZ2
172  bzlib = ", bzip2";
173 #endif
174 #endif
175 
176 #ifndef DO_RS_TRACE
177  trace = ", trace disabled";
178 #endif
179 
180  printf("rdiff (%s)\n"
181  "Copyright (C) 1997-2016 by Martin Pool, Andrew Tridgell and others.\n"
182  "http://librsync.sourcefrog.net/\n"
183  "Capabilities: %ld bit files%s%s%s\n"
184  "\n"
185  "librsync comes with NO WARRANTY, to the extent permitted by law.\n"
186  "You may redistribute copies of librsync under the terms of the GNU\n"
187  "Lesser General Public License. For more information about these\n"
188  "matters, see the files named COPYING.\n",
190  (long) (8 * sizeof(rs_long_t)), zlib, bzlib, trace);
191 }
192 
193 
194 
195 static void rdiff_options(poptContext opcon)
196 {
197  int c;
198  char const *a;
199 
200  while ((c = poptGetNextOpt(opcon)) != -1) {
201  switch (c) {
202  case 'h':
203  help();
204  exit(RS_DONE);
205  case 'V':
206  rdiff_show_version();
207  exit(RS_DONE);
208  case 'v':
209  if (!rs_supports_trace()) {
210  rs_error("library does not support trace");
211  }
213  break;
214 
215  case OPT_GZIP:
216  case OPT_BZIP2:
217  if ((a = poptGetOptArg(opcon))) {
218  int l = atoi(a);
219  if (c == OPT_GZIP)
220  gzip_level = l;
221  else
222  bzip2_level = l;
223  } else {
224  if (c == OPT_GZIP)
225  gzip_level = -1; /* library default */
226  else
227  bzip2_level = 9; /* demand the best */
228  }
229  rs_error("sorry, compression is not really implemented yet");
230  exit(RS_UNIMPLEMENTED);
231 
232  default:
233  bad_option(opcon, c);
234  }
235  }
236 }
237 
238 
239 /**
240  * Generate signature from remaining command line arguments.
241  */
242 static rs_result rdiff_sig(poptContext opcon)
243 {
244  FILE *basis_file, *sig_file;
245  rs_stats_t stats;
246  rs_result result;
247  rs_long_t sig_magic;
248 
249  basis_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
250  sig_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
251 
252  rdiff_no_more_args(opcon);
253 
254  if (!rs_hash_name || !strcmp(rs_hash_name, "blake2")) {
255  sig_magic = RS_BLAKE2_SIG_MAGIC;
256  } else if (!strcmp(rs_hash_name, "md4")) {
257  /* By default, for compatibility with rdiff 0.9.8 and before, mdfour
258  * sums are truncated to only 8 bytes, making them even weaker, but
259  * making the signature file shorter.
260  */
261  if (!strong_len)
262  strong_len = 8;
263  sig_magic = RS_MD4_SIG_MAGIC;
264  } else {
265  rs_error("unknown hash algorithm %s", rs_hash_name);
266  return RS_PARAM_ERROR;
267  }
268 
269  result = rs_sig_file(basis_file, sig_file, block_len, strong_len,
270  sig_magic, &stats);
271 
272  rs_file_close(sig_file);
273  rs_file_close(basis_file);
274  if (result != RS_DONE)
275  return result;
276 
277  if (show_stats)
278  rs_log_stats(&stats);
279 
280  return result;
281 }
282 
283 
284 static rs_result rdiff_delta(poptContext opcon)
285 {
286  FILE *sig_file, *new_file, *delta_file;
287  char const *sig_name;
288  rs_result result;
289  rs_signature_t *sumset;
290  rs_stats_t stats;
291 
292  if (!(sig_name = poptGetArg(opcon))) {
293  rdiff_usage("Usage for delta: "
294  "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]");
295  return RS_SYNTAX_ERROR;
296  }
297 
298  sig_file = rs_file_open(sig_name, "rb", file_force);
299  new_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
300  delta_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
301 
302  rdiff_no_more_args(opcon);
303 
304  result = rs_loadsig_file(sig_file, &sumset, &stats);
305  if (result != RS_DONE)
306  return result;
307 
308  if (show_stats)
309  rs_log_stats(&stats);
310 
311  if ((result = rs_build_hash_table(sumset)) != RS_DONE)
312  return result;
313 
314  result = rs_delta_file(sumset, new_file, delta_file, &stats);
315 
316  rs_file_close(delta_file);
317  rs_file_close(new_file);
318  rs_file_close(sig_file);
319 
320  if (show_stats) {
321  rs_signature_log_stats(sumset);
322  rs_log_stats(&stats);
323  }
324 
325  rs_free_sumset(sumset);
326 
327  return result;
328 }
329 
330 
331 
332 static rs_result rdiff_patch(poptContext opcon)
333 {
334  /* patch BASIS [DELTA [NEWFILE]] */
335  FILE *basis_file, *delta_file, *new_file;
336  char const *basis_name;
337  rs_stats_t stats;
338  rs_result result;
339 
340  if (!(basis_name = poptGetArg(opcon))) {
341  rdiff_usage("Usage for patch: "
342  "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]");
343  return RS_SYNTAX_ERROR;
344  }
345 
346  basis_file = rs_file_open(basis_name, "rb", file_force);
347  delta_file = rs_file_open(poptGetArg(opcon), "rb", file_force);
348  new_file = rs_file_open(poptGetArg(opcon), "wb", file_force);
349 
350  rdiff_no_more_args(opcon);
351 
352  result = rs_patch_file(basis_file, delta_file, new_file, &stats);
353 
354  rs_file_close(new_file);
355  rs_file_close(delta_file);
356  rs_file_close(basis_file);
357 
358  if (show_stats)
359  rs_log_stats(&stats);
360 
361  return result;
362 }
363 
364 
365 
366 static rs_result rdiff_action(poptContext opcon)
367 {
368  const char *action;
369 
370  action = poptGetArg(opcon);
371  if (!action)
372  ;
373  else if (isprefix(action, "signature"))
374  return rdiff_sig(opcon);
375  else if (isprefix(action, "delta"))
376  return rdiff_delta(opcon);
377  else if (isprefix(action, "patch"))
378  return rdiff_patch(opcon);
379 
380  rdiff_usage("rdiff: You must specify an action: `signature', `delta', or `patch'.");
381  return RS_SYNTAX_ERROR;
382 }
383 
384 
385 int main(const int argc, const char *argv[])
386 {
387  poptContext opcon;
388  rs_result result;
389 
390  opcon = poptGetContext(PROGRAM, argc, argv, opts, 0);
391  rdiff_options(opcon);
392  result = rdiff_action(opcon);
393 
394  if (result != RS_DONE)
395  rs_log(RS_LOG_ERR|RS_LOG_NONAME, "%s", rs_strerror(result));
396 
397  poptFreeContext(opcon);
398  return result;
399 }
400 
401 /* vim: et sw=4
402  */
Command line syntax error.
Definition: librsync.h:204
rs_result rs_build_hash_table(rs_signature_t *sums)
Call this after loading a signature to index it.
Definition: sumset.c:188
A signature file using the BLAKE2 hash.
Definition: librsync.h:107
Bad value passed in to library, probably an application bug.
Definition: librsync.h:217
rs_result rs_delta_file(rs_signature_t *, FILE *new_file, FILE *delta_file, rs_stats_t *)
Generate a delta between a signature and a new file, int a delta file.
Definition: whole.c:135
long long rs_long_t
A long integer type that can handle the largest file offsets.
int rs_inbuflen
Buffer sizes for file IO.
Definition: buf.c:66
rs_result rs_loadsig_file(FILE *sig_file, rs_signature_t **sumset, rs_stats_t *stats)
Load signatures from a signature file into memory.
Definition: whole.c:114
void rs_free_sumset(rs_signature_t *)
Deep deallocation of checksums.
Definition: sumset.c:201
int rs_supports_trace(void)
Check whether the library was compiled with debugging trace.
Definition: trace.c:210
A signature file with MD4 signatures.
Definition: librsync.h:98
Error conditions.
Definition: librsync.h:123
Public header for librsync.
Signature of a whole file.
Definition: sumset.h:39
Author is lazy.
Definition: librsync.h:214
rs_result rs_sig_file(FILE *old_file, FILE *sig_file, size_t block_len, size_t strong_len, rs_magic_number sig_magic, rs_stats_t *stats)
Generate the signature of a basis file, and write it out to another.
Definition: whole.c:95
Performance statistics from a librsync encoding or decoding operation.
Definition: librsync.h:236
char const * rs_strerror(rs_result r)
Return an English description of a rs_result value.
Definition: msg.c:51
rs_result
Return codes from nonblocking rsync operations.
Definition: librsync.h:193
rs_result rs_patch_file(FILE *basis_file, FILE *delta_file, FILE *new_file, rs_stats_t *)
Apply a patch, relative to a basis, into a new file.
Definition: whole.c:155
#define RS_DEFAULT_BLOCK_LEN
Default block length, if not determined by any other factors.
Definition: librsync.h:406
char const rs_librsync_version[]
Library version string.
Definition: version.c:25
void rs_trace_set_level(rs_loglevel level)
Set the least important message severity that will be output.
Definition: trace.c:98
Completed successfully.
Definition: librsync.h:194
Debug-level messages.
Definition: librsync.h:127
int rs_log_stats(rs_stats_t const *stats)
Write statistics into the current log as text.
Definition: stats.c:47