$treeview $search $mathjax $extrastylesheet
librsync
2.0.2
$projectbrief
|
$projectbrief
|
$searchbox |
00001 /*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- 00002 * 00003 * librsync -- the library for network deltas 00004 * 00005 * Copyright (C) 1999, 2000, 2001 by Martin Pool <mbp@sourcefrog.net> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU Lesser General Public License as published by 00009 * the Free Software Foundation; either version 2.1 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00020 */ 00021 00022 /*= 00023 | .. after a year and a day, mourning is 00024 | dangerous to the survivor and troublesome 00025 | to the dead. 00026 | -- Harold Bloom 00027 */ 00028 00029 /** \file rdiff.c -- Command-line network-delta tool. 00030 * 00031 * \todo Add a -z option to gzip/gunzip patches. This would be somewhat useful, 00032 * but more importantly a good test of the streaming API. Also add -I for 00033 * bzip2. 00034 * 00035 * \todo If built with debug support and we have mcheck, then turn it on. 00036 * (Optionally?) 00037 * 00038 * \todo popt doesn't handle single dashes very well at the moment: we'd like 00039 * to use them as arguments to indicate stdin/stdout, but it turns them into 00040 * options. I sent a patch to the popt maintainers; hopefully it will be fixed 00041 * in the future. 00042 * 00043 * \todo Add an option for delta to check whether the files are identical. */ 00044 00045 #include "config.h" 00046 00047 #include <stdlib.h> 00048 #include <stdio.h> 00049 #include <string.h> 00050 #include <fcntl.h> 00051 #include <popt.h> 00052 00053 #ifdef HAVE_ZLIB_H 00054 # include <zlib.h> 00055 #endif 00056 00057 #ifdef HAVE_BZLIB_H 00058 # include <bzlib.h> 00059 #endif 00060 00061 #include "librsync.h" 00062 #include "fileutil.h" 00063 #include "util.h" 00064 #include "trace.h" 00065 #include "isprefix.h" 00066 #include "sumset.h" 00067 00068 #define PROGRAM "rdiff" 00069 00070 static size_t block_len = RS_DEFAULT_BLOCK_LEN; 00071 static size_t strong_len = 0; 00072 00073 static int show_stats = 0; 00074 00075 static int bzip2_level = 0; 00076 static int gzip_level = 0; 00077 static int file_force = 0; 00078 00079 enum { 00080 OPT_GZIP = 1069, OPT_BZIP2 00081 }; 00082 00083 extern int rs_roll_paranoia; 00084 char *rs_hash_name; 00085 00086 const struct poptOption opts[] = { 00087 {"verbose", 'v', POPT_ARG_NONE, 0, 'v'}, 00088 {"version", 'V', POPT_ARG_NONE, 0, 'V'}, 00089 {"input-size", 'I', POPT_ARG_INT, &rs_inbuflen}, 00090 {"output-size", 'O', POPT_ARG_INT, &rs_outbuflen}, 00091 {"hash", 'H', POPT_ARG_STRING, &rs_hash_name}, 00092 {"help", '?', POPT_ARG_NONE, 0, 'h'}, 00093 {0, 'h', POPT_ARG_NONE, 0, 'h'}, 00094 {"block-size", 'b', POPT_ARG_INT, &block_len}, 00095 {"sum-size", 'S', POPT_ARG_INT, &strong_len}, 00096 {"statistics", 's', POPT_ARG_NONE, &show_stats}, 00097 {"stats", 0, POPT_ARG_NONE, &show_stats}, 00098 {"gzip", 'z', POPT_ARG_NONE, 0, OPT_GZIP}, 00099 {"bzip2", 'i', POPT_ARG_NONE, 0, OPT_BZIP2}, 00100 {"force", 'f', POPT_ARG_NONE, &file_force}, 00101 {"paranoia", 0, POPT_ARG_NONE, &rs_roll_paranoia}, 00102 {0} 00103 }; 00104 00105 static void rdiff_usage(const char *error) 00106 { 00107 fprintf(stderr, "%s\n" "Try `%s --help' for more information.\n", error, 00108 PROGRAM); 00109 } 00110 00111 static void rdiff_no_more_args(poptContext opcon) 00112 { 00113 if (poptGetArg(opcon)) { 00114 rdiff_usage("rdiff: too many arguments"); 00115 exit(RS_SYNTAX_ERROR); 00116 } 00117 } 00118 00119 static void bad_option(poptContext opcon, int error) 00120 { 00121 fprintf(stderr, "%s: %s: %s", PROGRAM, poptStrerror(error), 00122 poptBadOption(opcon, 0)); 00123 exit(RS_SYNTAX_ERROR); 00124 } 00125 00126 static void help(void) 00127 { 00128 printf("Usage: rdiff [OPTIONS] signature [BASIS [SIGNATURE]]\n" 00129 " [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]\n" 00130 " [OPTIONS] patch BASIS [DELTA [NEWFILE]]\n" "\n" 00131 "Options:\n" 00132 " -v, --verbose Trace internal processing\n" 00133 " -V, --version Show program version\n" 00134 " -?, --help Show this help message\n" 00135 " -s, --statistics Show performance statistics\n" 00136 " -f, --force Force overwriting existing files\n" 00137 "Signature generation options:\n" 00138 " -H, --hash=ALG Hash algorithm: blake2 (default), md4\n" 00139 "Delta-encoding options:\n" 00140 " -b, --block-size=BYTES Signature block size\n" 00141 " -S, --sum-size=BYTES Set signature strength\n" 00142 " --paranoia Verify all rolling checksums\n" 00143 "IO options:\n" " -I, --input-size=BYTES Input buffer size\n" 00144 " -O, --output-size=BYTES Output buffer size\n" 00145 " -z, --gzip[=LEVEL] gzip-compress deltas\n" 00146 " -i, --bzip2[=LEVEL] bzip2-compress deltas\n"); 00147 } 00148 00149 static void rdiff_show_version(void) 00150 { 00151 char const *bzlib = "", *zlib = "", *trace = ""; 00152 00153 #if 0 00154 /* Compression isn't implemented so don't mention it. */ 00155 # ifdef HAVE_LIBZ 00156 zlib = ", gzip"; 00157 # endif 00158 00159 # ifdef HAVE_LIBBZ2 00160 bzlib = ", bzip2"; 00161 # endif 00162 #endif 00163 00164 #ifndef DO_RS_TRACE 00165 trace = ", trace disabled"; 00166 #endif 00167 00168 printf("rdiff (%s)\n" 00169 "Copyright (C) 1997-2016 by Martin Pool, Andrew Tridgell and others.\n" 00170 "http://librsync.sourcefrog.net/\n" 00171 "Capabilities: %ld bit files%s%s%s\n" "\n" 00172 "librsync comes with NO WARRANTY, to the extent permitted by law.\n" 00173 "You may redistribute copies of librsync under the terms of the GNU\n" 00174 "Lesser General Public License. For more information about these\n" 00175 "matters, see the files named COPYING.\n", rs_librsync_version, 00176 (long)(8 * sizeof(rs_long_t)), zlib, bzlib, trace); 00177 } 00178 00179 static void rdiff_options(poptContext opcon) 00180 { 00181 int c; 00182 char const *a; 00183 00184 while ((c = poptGetNextOpt(opcon)) != -1) { 00185 switch (c) { 00186 case 'h': 00187 help(); 00188 exit(RS_DONE); 00189 case 'V': 00190 rdiff_show_version(); 00191 exit(RS_DONE); 00192 case 'v': 00193 if (!rs_supports_trace()) { 00194 rs_error("library does not support trace"); 00195 } 00196 rs_trace_set_level(RS_LOG_DEBUG); 00197 break; 00198 00199 case OPT_GZIP: 00200 case OPT_BZIP2: 00201 if ((a = poptGetOptArg(opcon))) { 00202 int l = atoi(a); 00203 if (c == OPT_GZIP) 00204 gzip_level = l; 00205 else 00206 bzip2_level = l; 00207 } else { 00208 if (c == OPT_GZIP) 00209 gzip_level = -1; /* library default */ 00210 else 00211 bzip2_level = 9; /* demand the best */ 00212 } 00213 rs_error("sorry, compression is not really implemented yet"); 00214 exit(RS_UNIMPLEMENTED); 00215 00216 default: 00217 bad_option(opcon, c); 00218 } 00219 } 00220 } 00221 00222 /** Generate signature from remaining command line arguments. */ 00223 static rs_result rdiff_sig(poptContext opcon) 00224 { 00225 FILE *basis_file, *sig_file; 00226 rs_stats_t stats; 00227 rs_result result; 00228 rs_long_t sig_magic; 00229 00230 basis_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00231 sig_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00232 00233 rdiff_no_more_args(opcon); 00234 00235 if (!rs_hash_name || !strcmp(rs_hash_name, "blake2")) { 00236 sig_magic = RS_BLAKE2_SIG_MAGIC; 00237 } else if (!strcmp(rs_hash_name, "md4")) { 00238 /* By default, for compatibility with rdiff 0.9.8 and before, mdfour 00239 sums are truncated to only 8 bytes, making them even weaker, but 00240 making the signature file shorter. */ 00241 if (!strong_len) 00242 strong_len = 8; 00243 sig_magic = RS_MD4_SIG_MAGIC; 00244 } else { 00245 rs_error("unknown hash algorithm %s", rs_hash_name); 00246 return RS_PARAM_ERROR; 00247 } 00248 00249 result = 00250 rs_sig_file(basis_file, sig_file, block_len, strong_len, sig_magic, 00251 &stats); 00252 00253 rs_file_close(sig_file); 00254 rs_file_close(basis_file); 00255 if (result != RS_DONE) 00256 return result; 00257 00258 if (show_stats) 00259 rs_log_stats(&stats); 00260 00261 return result; 00262 } 00263 00264 static rs_result rdiff_delta(poptContext opcon) 00265 { 00266 FILE *sig_file, *new_file, *delta_file; 00267 char const *sig_name; 00268 rs_result result; 00269 rs_signature_t *sumset; 00270 rs_stats_t stats; 00271 00272 if (!(sig_name = poptGetArg(opcon))) { 00273 rdiff_usage("Usage for delta: " 00274 "rdiff [OPTIONS] delta SIGNATURE [NEWFILE [DELTA]]"); 00275 return RS_SYNTAX_ERROR; 00276 } 00277 00278 sig_file = rs_file_open(sig_name, "rb", file_force); 00279 new_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00280 delta_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00281 00282 rdiff_no_more_args(opcon); 00283 00284 result = rs_loadsig_file(sig_file, &sumset, &stats); 00285 if (result != RS_DONE) 00286 return result; 00287 00288 if (show_stats) 00289 rs_log_stats(&stats); 00290 00291 if ((result = rs_build_hash_table(sumset)) != RS_DONE) 00292 return result; 00293 00294 result = rs_delta_file(sumset, new_file, delta_file, &stats); 00295 00296 rs_file_close(delta_file); 00297 rs_file_close(new_file); 00298 rs_file_close(sig_file); 00299 00300 if (show_stats) { 00301 rs_signature_log_stats(sumset); 00302 rs_log_stats(&stats); 00303 } 00304 00305 rs_free_sumset(sumset); 00306 00307 return result; 00308 } 00309 00310 static rs_result rdiff_patch(poptContext opcon) 00311 { 00312 /* patch BASIS [DELTA [NEWFILE]] */ 00313 FILE *basis_file, *delta_file, *new_file; 00314 char const *basis_name; 00315 rs_stats_t stats; 00316 rs_result result; 00317 00318 if (!(basis_name = poptGetArg(opcon))) { 00319 rdiff_usage("Usage for patch: " 00320 "rdiff [OPTIONS] patch BASIS [DELTA [NEW]]"); 00321 return RS_SYNTAX_ERROR; 00322 } 00323 00324 basis_file = rs_file_open(basis_name, "rb", file_force); 00325 delta_file = rs_file_open(poptGetArg(opcon), "rb", file_force); 00326 new_file = rs_file_open(poptGetArg(opcon), "wb", file_force); 00327 00328 rdiff_no_more_args(opcon); 00329 00330 result = rs_patch_file(basis_file, delta_file, new_file, &stats); 00331 00332 rs_file_close(new_file); 00333 rs_file_close(delta_file); 00334 rs_file_close(basis_file); 00335 00336 if (show_stats) 00337 rs_log_stats(&stats); 00338 00339 return result; 00340 } 00341 00342 static rs_result rdiff_action(poptContext opcon) 00343 { 00344 const char *action; 00345 00346 action = poptGetArg(opcon); 00347 if (!action) ; 00348 else if (isprefix(action, "signature")) 00349 return rdiff_sig(opcon); 00350 else if (isprefix(action, "delta")) 00351 return rdiff_delta(opcon); 00352 else if (isprefix(action, "patch")) 00353 return rdiff_patch(opcon); 00354 00355 rdiff_usage 00356 ("rdiff: You must specify an action: `signature', `delta', or `patch'."); 00357 return RS_SYNTAX_ERROR; 00358 } 00359 00360 int main(const int argc, const char *argv[]) 00361 { 00362 poptContext opcon; 00363 rs_result result; 00364 00365 opcon = poptGetContext(PROGRAM, argc, argv, opts, 0); 00366 rdiff_options(opcon); 00367 result = rdiff_action(opcon); 00368 00369 if (result != RS_DONE) 00370 rs_log(RS_LOG_ERR | RS_LOG_NONAME, "%s", rs_strerror(result)); 00371 00372 poptFreeContext(opcon); 00373 return result; 00374 }