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 <[email protected]>
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 
70 
71 #define PROGRAM "rdiff"
72 
73 static size_t block_len = RS_DEFAULT_BLOCK_LEN;
74 static size_t strong_len = 0;
75 
76 static int show_stats = 0;
77 
78 static int bzip2_level = 0;
79 static int gzip_level = 0;
80 
81 
82 enum {
83  OPT_GZIP = 1069, OPT_BZIP2
84 };
85 
86 extern int rs_roll_paranoia;
87 char *rs_hash_name;
88 
89 const struct poptOption opts[] = {
90  { "verbose", 'v', POPT_ARG_NONE, 0, 'v' },
91  { "version", 'V', POPT_ARG_NONE, 0, 'V' },
92  { "input-size", 'I', POPT_ARG_INT, &rs_inbuflen },
93  { "output-size", 'O', POPT_ARG_INT, &rs_outbuflen },
94  { "hash", 'H', POPT_ARG_STRING, &rs_hash_name },
95  { "help", '?', POPT_ARG_NONE, 0, 'h' },
96  { 0, 'h', POPT_ARG_NONE, 0, 'h' },
97  { "block-size", 'b', POPT_ARG_INT, &block_len },
98  { "sum-size", 'S', POPT_ARG_INT, &strong_len },
99  { "statistics", 's', POPT_ARG_NONE, &show_stats },
100  { "stats", 0, POPT_ARG_NONE, &show_stats },
101  { "gzip", 'z', POPT_ARG_NONE, 0, OPT_GZIP },
102  { "bzip2", 'i', POPT_ARG_NONE, 0, OPT_BZIP2 },
103  { "paranoia", 0, POPT_ARG_NONE, &rs_roll_paranoia },
104  { 0 }
105 };
106 
107 
108 static void rdiff_usage(const char *error)
109 {
110  fprintf(stderr, "%s\n"
111  "Try `%s --help' for more information.\n",
112  error, PROGRAM);
113 }
114 
115 
116 static void rdiff_no_more_args(poptContext opcon)
117 {
118  if (poptGetArg(opcon)) {
119  rdiff_usage("rdiff: too many arguments");
120  exit(RS_SYNTAX_ERROR);
121  }
122 }
123 
124 
125 static void bad_option(poptContext opcon, int error)
126 {
127  fprintf(stderr, "%s: %s: %s",
128  PROGRAM, poptStrerror(error), poptBadOption(opcon, 0));
129  exit(RS_SYNTAX_ERROR);
130 }
131 
132 
133 static void help(void) {
134  printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n"
135  " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n"
136  " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n"
137  "\n"
138  "Options:\n"
139  " -v, --verbose Trace internal processing\n"
140  " -V, --version Show program version\n"
141  " -?, --help Show this help message\n"
142  " -s, --statistics Show performance statistics\n"
143  "Signature generation options:\n"
144  " -H, --hash=ALG Hash algorithm: blake2 (default), md4\n"
145  "Delta-encoding options:\n"
146  " -b, --block-size=BYTES Signature block size\n"
147  " -S, --sum-size=BYTES Set signature strength\n"
148  " --paranoia Verify all rolling checksums\n"
149  "IO options:\n"
150  " -I, --input-size=BYTES Input buffer size\n"
151  " -O, --output-size=BYTES Output buffer size\n"
152  " -z, --gzip[=LEVEL] gzip-compress deltas\n"
153  " -i, --bzip2[=LEVEL] bzip2-compress deltas\n"
154  );
155 }
156 
157 
158 static void rdiff_show_version(void)
159 {
160  char const *bzlib = "", *zlib = "", *trace = "";
161 
162 #if 0
163  /* Compression isn't implemented so don't mention it. */
164 #ifdef HAVE_LIBZ
165  zlib = ", gzip";
166 #endif
167 
168 #ifdef HAVE_LIBBZ2
169  bzlib = ", bzip2";
170 #endif
171 #endif
172 
173 #ifndef DO_RS_TRACE
174  trace = ", trace disabled";
175 #endif
176 
177  printf("rdiff (%s)\n"
178  "Copyright (C) 1997-2016 by Martin Pool, Andrew Tridgell and others.\n"
179  "http://librsync.sourcefrog.net/\n"
180  "Capabilities: %ld bit files%s%s%s\n"
181  "\n"
182  "librsync comes with NO WARRANTY, to the extent permitted by law.\n"
183  "You may redistribute copies of librsync under the terms of the GNU\n"
184  "Lesser General Public License. For more information about these\n"
185  "matters, see the files named COPYING.\n",
187  (long) (8 * sizeof(rs_long_t)), zlib, bzlib, trace);
188 }
189 
190 
191 
192 static void rdiff_options(poptContext opcon)
193 {
194  int c;
195  char const *a;
196 
197  while ((c = poptGetNextOpt(opcon)) != -1) {
198  switch (c) {
199  case 'h':
200  help();
201  exit(RS_DONE);
202  case 'V':
203  rdiff_show_version();
204  exit(RS_DONE);
205  case 'v':
206  if (!rs_supports_trace()) {
207  rs_error("library does not support trace");
208  }
210  break;
211 
212  case OPT_GZIP:
213  case OPT_BZIP2:
214  if ((a = poptGetOptArg(opcon))) {
215  int l = atoi(a);
216  if (c == OPT_GZIP)
217  gzip_level = l;
218  else
219  bzip2_level = l;
220  } else {
221  if (c == OPT_GZIP)
222  gzip_level = -1; /* library default */
223  else
224  bzip2_level = 9; /* demand the best */
225  }
226  rs_error("sorry, compression is not really implemented yet");
227  exit(RS_UNIMPLEMENTED);
228 
229  default:
230  bad_option(opcon, c);
231  }
232  }
233 }
234 
235 
236 /**
237  * Generate signature from remaining command line arguments.
238  */
239 static rs_result rdiff_sig(poptContext opcon)
240 {
241  FILE *basis_file, *sig_file;
242  rs_stats_t stats;
243  rs_result result;
244  rs_long_t sig_magic;
245 
246  basis_file = rs_file_open(poptGetArg(opcon), "rb");
247  sig_file = rs_file_open(poptGetArg(opcon), "wb");
248 
249  rdiff_no_more_args(opcon);
250 
251  if (!rs_hash_name || !strcmp(rs_hash_name, "blake2")) {
252  sig_magic = RS_BLAKE2_SIG_MAGIC;
253  } else if (!strcmp(rs_hash_name, "md4")) {
254  /* By default, for compatibility with rdiff 0.9.8 and before, mdfour
255  * sums are truncated to only 8 bytes, making them even weaker, but
256  * making the signature file shorter.
257  */
258  if (!strong_len)
259  strong_len = 8;
260  sig_magic = RS_MD4_SIG_MAGIC;
261  } else {
262  rs_error("unknown hash algorithm %s", rs_hash_name);
263  return RS_PARAM_ERROR;
264  }
265 
266  result = rs_sig_file(basis_file, sig_file, block_len, strong_len,
267  sig_magic, &stats);
268 
269  rs_file_close(sig_file);
270  rs_file_close(basis_file);
271  if (result != RS_DONE)
272  return result;
273 
274  if (show_stats)
275  rs_log_stats(&stats);
276 
277  return result;
278 }
279 
280 
281 static rs_result rdiff_delta(poptContext opcon)
282 {
283  FILE *sig_file, *new_file, *delta_file;
284  char const *sig_name;
285  rs_result result;
286  rs_signature_t *sumset;
287  rs_stats_t stats;
288 
289  if (!(sig_name = poptGetArg(opcon))) {
290  rdiff_usage("Usage for delta: "
291  "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]");
292  return RS_SYNTAX_ERROR;
293  }
294 
295  sig_file = rs_file_open(sig_name, "rb");
296  new_file = rs_file_open(poptGetArg(opcon), "rb");
297  delta_file = rs_file_open(poptGetArg(opcon), "wb");
298 
299  rdiff_no_more_args(opcon);
300 
301  result = rs_loadsig_file(sig_file, &sumset, &stats);
302  if (result != RS_DONE)
303  return result;
304 
305  if (show_stats)
306  rs_log_stats(&stats);
307 
308  if ((result = rs_build_hash_table(sumset)) != RS_DONE)
309  return result;
310 
311  result = rs_delta_file(sumset, new_file, delta_file, &stats);
312 
313  rs_free_sumset(sumset);
314 
315  rs_file_close(delta_file);
316  rs_file_close(new_file);
317  rs_file_close(sig_file);
318 
319  if (show_stats)
320  rs_log_stats(&stats);
321 
322  return result;
323 }
324 
325 
326 
327 static rs_result rdiff_patch(poptContext opcon)
328 {
329  /* patch BASIS [DELTA [NEWFILE]] */
330  FILE *basis_file, *delta_file, *new_file;
331  char const *basis_name;
332  rs_stats_t stats;
333  rs_result result;
334 
335  if (!(basis_name = poptGetArg(opcon))) {
336  rdiff_usage("Usage for patch: "
337  "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]");
338  return RS_SYNTAX_ERROR;
339  }
340 
341  basis_file = rs_file_open(basis_name, "rb");
342  delta_file = rs_file_open(poptGetArg(opcon), "rb");
343  new_file = rs_file_open(poptGetArg(opcon), "wb");
344 
345  rdiff_no_more_args(opcon);
346 
347  result = rs_patch_file(basis_file, delta_file, new_file, &stats);
348 
349  rs_file_close(new_file);
350  rs_file_close(delta_file);
351  rs_file_close(basis_file);
352 
353  if (show_stats)
354  rs_log_stats(&stats);
355 
356  return result;
357 }
358 
359 
360 
361 static rs_result rdiff_action(poptContext opcon)
362 {
363  const char *action;
364 
365  action = poptGetArg(opcon);
366  if (!action)
367  ;
368  else if (isprefix(action, "signature"))
369  return rdiff_sig(opcon);
370  else if (isprefix(action, "delta"))
371  return rdiff_delta(opcon);
372  else if (isprefix(action, "patch"))
373  return rdiff_patch(opcon);
374 
375  rdiff_usage("rdiff: You must specify an action: `signature', `delta', or `patch'.");
376  return RS_SYNTAX_ERROR;
377 }
378 
379 
380 int main(const int argc, const char *argv[])
381 {
382  poptContext opcon;
383  rs_result result;
384 
385  opcon = poptGetContext(PROGRAM, argc, argv, opts, 0);
386  rdiff_options(opcon);
387  result = rdiff_action(opcon);
388 
389  if (result != RS_DONE)
390  rs_log(RS_LOG_ERR|RS_LOG_NONAME, "%s", rs_strerror(result));
391 
392  poptFreeContext(opcon);
393  return result;
394 }
395 
396 /* vim: et sw=4
397  */
Command line syntax error.
Definition: librsync.h:203
rs_result rs_build_hash_table(rs_signature_t *sums)
Call this after loading a signature to index it.
Definition: search.c:111
A signature file using the BLAKE2 hash.
Definition: librsync.h:106
Bad value passed in to library, probably an application bug.
Definition: librsync.h:216
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:131
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:36
int rs_supports_trace(void)
Check whether the library was compiled with debugging trace.
Definition: trace.c:214
A signature file with MD4 signatures.
Definition: librsync.h:97
Error conditions.
Definition: librsync.h:122
Public header for librsync.
Author is lazy.
Definition: librsync.h:213
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:235
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:192
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:151
#define RS_DEFAULT_BLOCK_LEN
Default block length, if not determined by any other factors.
Definition: librsync.h:405
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:102
Completed successfully.
Definition: librsync.h:193
Debug-level messages.
Definition: librsync.h:126
int rs_log_stats(rs_stats_t const *stats)
Write statistics into the current log as text.
Definition: stats.c:47