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