summaryrefslogtreecommitdiff
path: root/xdelta1/xdmain.c
diff options
context:
space:
mode:
authordotdotisdead <dotdotisdead@a3eca27d-f21b-0410-9b4a-6511e771f64e>2006-09-30 04:47:47 +0000
committerdotdotisdead <dotdotisdead@a3eca27d-f21b-0410-9b4a-6511e771f64e>2006-09-30 04:47:47 +0000
commitad85653ca73c8126de516b9a4294e8f08577c00d (patch)
tree79fb4d644ccf6a4fe8dac146b801a21d63537b23 /xdelta1/xdmain.c
parent5a7c245871879325d7b05c06e0b2011203986ee8 (diff)
import 1.1.3
Diffstat (limited to 'xdelta1/xdmain.c')
-rwxr-xr-xxdelta1/xdmain.c1941
1 files changed, 1941 insertions, 0 deletions
diff --git a/xdelta1/xdmain.c b/xdelta1/xdmain.c
new file mode 100755
index 0000000..f0a61c8
--- /dev/null
+++ b/xdelta1/xdmain.c
@@ -0,0 +1,1941 @@
1/* -*- Mode: C;-*-
2 *
3 * This file is part of XDelta - A binary delta generator.
4 *
5 * Copyright (C) 1997, 1998, 1999, 2001 Josh MacDonald
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU 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 * Author: Josh MacDonald <jmacd@CS.Berkeley.EDU>
22 *
23 * $Id: xdmain.c 1.22.1.5 Fri, 29 Jun 2001 06:01:08 -0700 jmacd $
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30#include <ctype.h>
31
32#include <fcntl.h>
33#include <sys/stat.h>
34
35#if defined(_WIN32) || defined(__DJGPP__)
36#define WINHACK
37#endif
38
39#ifndef WINHACK
40#include <unistd.h>
41#include <sys/mman.h>
42#define O_BINARY 0
43
44#else /* WINHACK */
45
46#include <io.h>
47#include <process.h>
48#ifdef __DJGPP__
49#include <unistd.h>
50#include <dpmi.h>
51#endif
52
53#define STDOUT_FILENO 1
54#define lstat stat
55
56#ifndef __DJGPP__
57#define S_IFMT _S_IFMT
58#define S_IFREG _S_IFREG
59#endif /* !__DJGPP__ */
60
61#endif /* !WINHACK_ */
62
63#include <zlib.h>
64
65#include "xdelta.h"
66
67static HandleFuncTable xd_handle_table;
68
69#define XD_PAGE_SIZE (1<<20)
70
71#define XDELTA_110_PREFIX "%XDZ004%"
72#define XDELTA_104_PREFIX "%XDZ003%"
73#define XDELTA_100_PREFIX "%XDZ002%"
74#define XDELTA_020_PREFIX "%XDZ001%"
75#define XDELTA_018_PREFIX "%XDZ000%"
76#define XDELTA_014_PREFIX "%XDELTA%"
77#define XDELTA_PREFIX XDELTA_110_PREFIX
78#define XDELTA_PREFIX_LEN 8
79
80#define HEADER_WORDS (6)
81#define HEADER_SPACE (HEADER_WORDS*4)
82/* The header is composed of 4-byte words (network byte order) as follows:
83 * word 1: flags
84 * word 2: (from name length) << 16 | (to name length)
85 * word 3: (reserved)
86 * word 4: (reserved)
87 * word 5: (reserved)
88 * word 6: (reserved)
89 * flags are:
90 */
91#define FLAG_NO_VERIFY 1
92#define FLAG_FROM_COMPRESSED 2
93#define FLAG_TO_COMPRESSED 4
94#define FLAG_PATCH_COMPRESSED 8
95/* and, the header is follwed by the from file name, then the to file
96 * name, then the data. */
97
98#ifdef WINHACK
99#define FOPEN_READ_ARG "rb"
100#define FOPEN_WRITE_ARG "wb"
101#define FILE_SEPARATOR '\\'
102#else
103#define FOPEN_READ_ARG "r"
104#define FOPEN_WRITE_ARG "w"
105#define FILE_SEPARATOR '/'
106#endif /* WINHACK */
107
108#include "getopt.h"
109
110typedef struct _LRU LRU;
111
112struct _LRU
113{
114 LRU *next;
115 LRU *prev;
116
117 gint refs;
118 guint page;
119 guint8* buffer;
120};
121
122typedef struct _XdFileHandle XdFileHandle;
123
124typedef struct {
125 gboolean patch_is_compressed;
126 const gchar* patch_name;
127 guint patch_flags;
128 const gchar* patch_version;
129 gboolean has_trailer;
130
131 XdeltaSourceInfo* data_source;
132 XdeltaSourceInfo* from_source;
133
134 gchar *from_name;
135 gchar *to_name;
136
137 guint control_offset;
138 guint header_offset;
139
140 gint16 from_name_len;
141 gint16 to_name_len;
142
143 guint32 header_space[HEADER_WORDS];
144 guint8 magic_buf[XDELTA_PREFIX_LEN];
145
146 XdFileHandle *patch_in;
147
148 XdeltaControl *cont;
149} XdeltaPatch;
150
151struct _XdFileHandle
152{
153 FileHandle fh;
154
155 guint length;
156 guint real_length;
157 gint type;
158 const char* name;
159 const char* cleanup;
160
161 guint8 md5[16];
162 EdsioMD5Ctx ctx;
163
164 /* for write */
165 int out_fd;
166 void* out;
167 gboolean (* out_write) (XdFileHandle* handle, const void* buf, gint nbyte);
168 gboolean (* out_close) (XdFileHandle* handle);
169
170 /* for read */
171 GPtrArray *lru_table;
172 LRU *lru_head; /* most recently used. */
173 LRU *lru_tail; /* least recently used. */
174 GMemChunk *lru_chunk;
175 guint lru_count;
176 guint lru_outstanding_refs;
177
178 guint narrow_low;
179 guint narrow_high;
180 guint current_pos;
181 FILE* in;
182 gboolean (* in_read) (XdFileHandle* handle, void* buf, gint nbyte);
183 gboolean (* in_close) (XdFileHandle* handle);
184 gboolean in_compressed;
185
186 const guint8* copy_page;
187 guint copy_pgno;
188 gboolean md5_good;
189 gboolean reset_length_next_write;
190
191 gint md5_page;
192 gint fd;
193};
194
195/* $Format: "static const char xdelta_version[] = \"$ReleaseVersion$\"; " $ */
196static const char xdelta_version[] = "1.1.3";
197
198typedef struct _Command Command;
199
200struct _Command {
201 gchar* name;
202 gint (* func) (gint argc, gchar** argv);
203 gint nargs;
204};
205
206static gint delta_command (gint argc, gchar** argv);
207static gint patch_command (gint argc, gchar** argv);
208static gint info_command (gint argc, gchar** argv);
209
210static const Command commands[] =
211{
212 { "delta", delta_command, -1 },
213 { "patch", patch_command, -1 },
214 { "info", info_command, 1 },
215 { NULL, NULL, 0 }
216};
217
218static struct option const long_options[] =
219{
220 {"help", no_argument, 0, 'h'},
221 {"version", no_argument, 0, 'v'},
222 {"verbose", no_argument, 0, 'V'},
223 {"noverify", no_argument, 0, 'n'},
224 {"pristine", no_argument, 0, 'p'},
225 {"quiet", no_argument, 0, 'q'},
226 {"maxmem", required_argument, 0, 'm'},
227 {"blocksize", required_argument, 0, 's'},
228 {0,0,0,0}
229};
230
231static const gchar* program_name;
232static gint compress_level = Z_DEFAULT_COMPRESSION;
233static gint no_verify = FALSE;
234static gint pristine = FALSE;
235static gint verbose = FALSE;
236static gint max_mapped_pages = G_MAXINT;
237static gint quiet = FALSE;
238
239#define xd_error g_warning
240
241static void
242usage ()
243{
244 xd_error ("usage: %s COMMAND [OPTIONS] [ARG1 ...]\n", program_name);
245 xd_error ("use --help for more help\n");
246 exit (2);
247}
248
249static void
250help ()
251{
252 xd_error ("usage: %s COMMAND [OPTIONS] [ARG1 ...]\n", program_name);
253 xd_error ("COMMAND is one of:\n");
254 xd_error (" delta Produce a delta from ARG1 to ARG2 producing ARG3\n");
255 xd_error (" info List details about delta ARG1\n");
256 xd_error (" patch Apply patch ARG1 using file ARG2 producing ARG3\n");
257 xd_error ("OPTIONS are:\n");
258 xd_error (" -v, --version Print version information\n");
259 xd_error (" -V, --verbose Print verbose error messages\n");
260 xd_error (" -h, --help Print this summary\n");
261 xd_error (" -n, --noverify Disable automatic MD5 verification\n");
262 xd_error (" -p, --pristine Disable automatic GZIP decompression\n");
263 xd_error (" -m, --maxmem=SIZE Set the buffer size limit, e.g. 640K, 16M\n");
264 xd_error (" -[0-9] ZLIB compress level: 0=none, 1=fast, 6=default, 9=best\n");
265 xd_error (" -s=BLOCK_SIZE Sets block size (power of 2), minimum match length\n");
266 xd_error (" In-core memory requirement is (FROM_LEN * 8) / BLOCK_SIZE\n");
267 exit (2);
268}
269
270static void
271version ()
272{
273 xd_error ("version %s\n", xdelta_version);
274 exit (2);
275}
276
277static FILE* xd_error_file = NULL;
278
279static void
280xd_error_func (const gchar *log_domain,
281 GLogLevelFlags log_level,
282 const gchar *message,
283 gpointer user_data)
284{
285 if (! xd_error_file)
286 xd_error_file = stderr;
287
288 fprintf (xd_error_file, "%s: %s", program_name, message);
289}
290
291static gboolean
292event_devel (void)
293{
294 static gboolean once = FALSE;
295 static gboolean devel = FALSE;
296
297 if (! once)
298 {
299 devel = g_getenv ("EDSIO_DEVEL") != NULL;
300 once = TRUE;
301 }
302
303 return devel;
304}
305
306static gboolean
307event_watch (GenericEvent* ev, GenericEventDef* def, const char* message)
308{
309 if (quiet && def->level <= EL_Warning)
310 return TRUE;
311
312 if (event_devel ())
313 fprintf (stderr, "%s:%d: %s\n", ev->srcfile, ev->srcline, message);
314 else
315 fprintf (stderr, "%s: %s\n", program_name, message);
316
317 return TRUE;
318}
319
320gint
321main (gint argc, gchar** argv)
322{
323 const Command *cmd = NULL;
324 gint c;
325 gint longind;
326
327 eventdelivery_event_watch_all (event_watch);
328
329 if (! xd_edsio_init ())
330 return 2;
331
332#ifdef __DJGPP__
333 /*
334 * (richdawe@bigfoot.com): Limit maximum memory usage to 87.5% of available
335 * physical memory for DJGPP. Otherwise the replacement for mmap() will
336 * exhaust memory and XDelta will fail.
337 */
338 {
339 unsigned long phys_free = _go32_dpmi_remaining_physical_memory();
340
341 max_mapped_pages = phys_free / XD_PAGE_SIZE;
342 max_mapped_pages %= 8;
343 max_mapped_pages *= 7;
344 }
345
346 /*
347 * (richdawe@bigfoot.com): Strip off the file extension 'exe' from
348 * program_name, since it makes the help messages look ugly.
349 */
350 {
351 char strip_ext[PATH_MAX + 1];
352 char *p;
353
354 strcpy (strip_ext, argv[0]);
355 p = strrchr (strip_ext, '.');
356 if ((p != NULL) && (strncasecmp (p + 1, "exe", 3) == 0))
357 *p = '\0';
358 program_name = g_basename (strip_ext);
359 }
360#else /* !__DJGPP__ */
361 program_name = g_basename (argv[0]);
362#endif /* __DJGPP__ */
363
364 g_log_set_handler (G_LOG_DOMAIN,
365 G_LOG_LEVEL_WARNING,
366 xd_error_func,
367 NULL);
368
369 if (argc < 2)
370 usage ();
371
372 for (cmd = commands; cmd->name; cmd += 1)
373 if (strcmp (cmd->name, argv[1]) == 0)
374 break;
375
376 if (strcmp (argv[1], "-h") == 0 ||
377 strcmp (argv[1], "--help") == 0)
378 help ();
379
380 if (strcmp (argv[1], "-v") == 0 ||
381 strcmp (argv[1], "--version") == 0)
382 version ();
383
384 if (!cmd->name)
385 {
386 xd_error ("unrecognized command\n");
387 help ();
388 }
389
390 argc -= 1;
391 argv += 1;
392
393 while ((c = getopt_long(argc,
394 argv,
395 "+nqphvVs:m:0123456789",
396 long_options,
397 &longind)) != EOF)
398 {
399 switch (c)
400 {
401 case 'q': quiet = TRUE; break;
402 case 'n': no_verify = TRUE; break;
403 case 'p': pristine = TRUE; break;
404 case 'V': verbose = TRUE; break;
405 case 's':
406 {
407 int ret;
408 int s = atoi (optarg);
409
410 if ((ret = xdp_set_query_size_pow (s)) && ! quiet)
411 {
412 xd_error ("illegal query size: %s\n", xdp_errno (ret));
413 }
414 }
415 break;
416 case 'm':
417 {
418 gchar* end = NULL;
419 glong l = strtol (optarg, &end, 0);
420
421 if (end && g_strcasecmp (end, "M") == 0)
422 l <<= 20;
423 else if (end && g_strcasecmp (end, "K") == 0)
424 l <<= 10;
425 else if (end || l < 0)
426 {
427 xd_error ("illegal maxmem argument %s\n", optarg);
428 return 2;
429 }
430
431#ifdef __DJGPP__
432 /*
433 * (richdawe@bigfoot.com): Old MS-DOS systems may have a maximum
434 * of 8MB memory == XD_PAGE_SIZE * 8. Therefore, do what the user
435 * asks for on MS-DOS.
436 */
437#else /* !__DJGPP__ */
438 l = MAX (l, XD_PAGE_SIZE * 8);
439#endif /* __DJGPP__ */
440
441 max_mapped_pages = l / XD_PAGE_SIZE;
442 }
443 break;
444 case 'h': help (); break;
445 case 'v': version (); break;
446 case '0': case '1': case '2': case '3': case '4':
447 case '5': case '6': case '7': case '8': case '9':
448 compress_level = c - '0';
449 break;
450 case '?':
451 default:
452 xd_error ("illegal argument, use --help for help\n");
453 return 2;
454 }
455 }
456
457 if (verbose && max_mapped_pages < G_MAXINT)
458 xd_error ("using %d kilobytes of buffer space\n", (max_mapped_pages * XD_PAGE_SIZE) >> 10);
459
460 argc -= optind;
461 argv += optind;
462
463 if (cmd->nargs >= 0 && argc != cmd->nargs)
464 {
465 xd_error ("wrong number of arguments\n");
466 help ();
467 return 2;
468 }
469
470 return (* cmd->func) (argc, argv);
471}
472
473/* Commands */
474
475#define READ_TYPE 1
476#define READ_NOSEEK_TYPE 1
477#define READ_SEEK_TYPE 3
478#define WRITE_TYPE 4
479
480/* Note to the casual reader: the filehandle implemented here is
481 * highly aware of the calling patterns of the Xdelta library. It
482 * also plays games with narrowing to implement several files in the
483 * same handle. So, if you try to modify or use this code, BEWARE.
484 * See the repository package for a complete implementation.
485 * In fact, its really quite a hack. */
486
487static gssize xd_handle_map_page (XdFileHandle *fh, guint pgno, const guint8** mem);
488static gboolean xd_handle_unmap_page (XdFileHandle *fh, guint pgno, const guint8** mem);
489
490static gboolean
491xd_fwrite (XdFileHandle* fh, const void* buf, gint nbyte)
492{
493 return fwrite (buf, nbyte, 1, fh->out) == 1;
494}
495
496static gboolean
497xd_fread (XdFileHandle* fh, void* buf, gint nbyte)
498{
499 return fread (buf, nbyte, 1, fh->in) == 1;
500}
501
502static gboolean
503xd_fclose (XdFileHandle* fh)
504{
505 return fclose (fh->out) == 0;
506}
507
508static gboolean
509xd_frclose (XdFileHandle* fh)
510{
511 return fclose (fh->in) == 0;
512}
513
514static gboolean
515xd_gzwrite (XdFileHandle* fh, const void* buf, gint nbyte)
516{
517 return gzwrite (fh->out, (void*) buf, nbyte) == nbyte;
518}
519
520static gboolean
521xd_gzread (XdFileHandle* fh, void* buf, gint nbyte)
522{
523 return gzread (fh->in, buf, nbyte) == nbyte;
524}
525
526static gboolean
527xd_gzclose (XdFileHandle* fh)
528{
529 return gzclose (fh->out) == Z_OK;
530}
531
532static gboolean
533xd_gzrclose (XdFileHandle* fh)
534{
535 return gzclose (fh->in) == Z_OK;
536}
537
538static void
539init_table (XdFileHandle* fh)
540{
541 fh->lru_table = g_ptr_array_new ();
542 fh->lru_chunk = g_mem_chunk_create(LRU, 1<<9, G_ALLOC_ONLY);
543 fh->lru_head = NULL;
544 fh->lru_tail = NULL;
545}
546
547static XdFileHandle*
548open_common (const char* name, const char* real_name)
549{
550 XdFileHandle* fh;
551
552 gint fd;
553 struct stat buf;
554
555 if ((fd = open (name, O_RDONLY | O_BINARY, 0)) < 0)
556 {
557 xd_error ("open %s failed: %s\n", name, g_strerror (errno));
558 return NULL;
559 }
560
561 if (stat (name, &buf) < 0)
562 {
563 xd_error ("stat %s failed: %s\n", name, g_strerror (errno));
564 return NULL;
565 }
566
567 /* S_ISREG() is not on Windows */
568 if ((buf.st_mode & S_IFMT) != S_IFREG)
569 {
570 xd_error ("%s is not a regular file\n", name);
571 return NULL;
572 }
573
574 fh = g_new0 (XdFileHandle, 1);
575
576 fh->fh.table = & xd_handle_table;
577 fh->name = real_name;
578 fh->fd = fd;
579 fh->length = buf.st_size;
580 fh->narrow_high = buf.st_size;
581
582 return fh;
583}
584
585static gboolean
586file_gzipped (const char* name, gboolean *is_compressed)
587{
588 FILE* f = fopen (name, FOPEN_READ_ARG);
589 guint8 buf[2];
590
591 (*is_compressed) = FALSE;
592
593 if (! f)
594 {
595 xd_error ("open %s failed: %s\n", name, g_strerror (errno));
596 return FALSE;
597 }
598
599 if (fread (buf, 2, 1, f) != 1)
600 return TRUE;
601
602#define GZIP_MAGIC1 037
603#define GZIP_MAGIC2 0213
604
605 if (buf[0] == GZIP_MAGIC1 && buf[1] == GZIP_MAGIC2)
606 (* is_compressed) = TRUE;
607
608 return TRUE;
609}
610
611static const char*
612xd_tmpname (void)
613{
614 const char* tmpdir = g_get_tmp_dir ();
615 GString* s;
616 gint x = getpid ();
617 static gint seq = 0;
618 struct stat buf;
619
620 s = g_string_new (NULL);
621
622 do
623 {
624 /*
625 * (richdawe@bigfoot.com): Limit temporary filenames to
626 * the MS-DOS 8+3 convention for DJGPP.
627 */
628 g_string_sprintf (s, "%s/xd-%05d.%03d", tmpdir, x, seq++);
629 }
630 while (lstat (s->str, &buf) == 0);
631
632 return s->str;
633}
634
635static const char*
636file_gunzip (const char* name)
637{
638 const char* new_name = xd_tmpname ();
639 FILE* out = fopen (new_name, FOPEN_WRITE_ARG);
640 gzFile in = gzopen (name, "rb");
641 guint8 buf[1024];
642 int nread;
643
644 while ((nread = gzread (in, buf, 1024)) > 0)
645 {
646 if (fwrite (buf, nread, 1, out) != 1)
647 {
648 xd_error ("write %s failed (during uncompression): %s\n", new_name, g_strerror (errno));
649 return NULL;
650 }
651 }
652
653 if (nread < 0)
654 {
655 xd_error ("gzread %s failed\n", name);
656 return NULL;
657 }
658
659 gzclose (in);
660
661 if (fclose (out))
662 {
663 xd_error ("close %s failed (during uncompression): %s\n", new_name, g_strerror (errno));
664 return NULL;
665 }
666
667 return new_name;
668}
669
670static XdFileHandle*
671open_read_noseek_handle (const char* name, gboolean* is_compressed, gboolean will_read, gboolean honor_pristine)
672{
673 XdFileHandle* fh;
674 const char* name0 = name;
675
676 /* we _could_ stream-read this file if compressed, but it adds a
677 * lot of complexity. the library can handle it, just set the
678 * length to (XDELTA_MAX_FILE_LEN-1) and make sure that the end
679 * of file condition is set when on the last page. However, I
680 * don't feel like it. */
681 if (honor_pristine && pristine)
682 *is_compressed = FALSE;
683 else
684 {
685 if (! file_gzipped (name, is_compressed))
686 return NULL;
687 }
688
689 if ((* is_compressed) && ! (name = file_gunzip (name)))
690 return NULL;
691
692 if (! (fh = open_common (name, name0)))
693 return NULL;
694
695 fh->type = READ_NOSEEK_TYPE;
696
697 edsio_md5_init (&fh->ctx);
698
699 if (*is_compressed)
700 fh->cleanup = name;
701
702 if (will_read)
703 {
704 g_assert (fh->fd >= 0);
705 if (! (fh->in = fdopen (dup (fh->fd), FOPEN_READ_ARG)))
706 {
707 xd_error ("fdopen: %s\n", g_strerror (errno));
708 return NULL;
709 }
710 fh->in_read = &xd_fread;
711 fh->in_close = &xd_frclose;
712 }
713 else
714 {
715 init_table (fh);
716 }
717
718 return fh;
719}
720
721static void
722xd_read_close (XdFileHandle* fh)
723{
724 /*
725 * (richdawe@bigfoot.com): On Unix you can unlink a file while it is
726 * still open. On MS-DOS this can lead to filesystem corruption. Close
727 * the file before unlinking it.
728 */
729 close (fh->fd);
730
731 if (fh->cleanup)
732 unlink (fh->cleanup);
733
734 if (fh->in)
735 (*fh->in_close) (fh);
736}
737
738static XdFileHandle*
739open_read_seek_handle (const char* name, gboolean* is_compressed, gboolean honor_pristine)
740{
741 XdFileHandle* fh;
742 const char* name0 = name;
743
744 if (honor_pristine && pristine)
745 *is_compressed = FALSE;
746 else
747 {
748 if (! file_gzipped (name, is_compressed))
749 return NULL;
750 }
751
752 if ((* is_compressed) && ! (name = file_gunzip (name)))
753 return NULL;
754
755 if (! (fh = open_common (name, name0)))
756 return NULL;
757
758 fh->type = READ_SEEK_TYPE;
759
760 if (*is_compressed)
761 fh->cleanup = name;
762
763 init_table (fh);
764
765 edsio_md5_init (&fh->ctx);
766
767 return fh;
768}
769
770static XdFileHandle*
771open_write_handle (int fd, const char* name)
772{
773 XdFileHandle* fh = g_new0 (XdFileHandle, 1);
774 int nfd;
775
776 fh->fh.table = & xd_handle_table;
777 fh->out_fd = fd;
778 fh->out_write = &xd_fwrite;
779 fh->out_close = &xd_fclose;
780
781 g_assert (fh->out_fd >= 0);
782
783 nfd = dup (fh->out_fd);
784
785 if (! (fh->out = fdopen (nfd, FOPEN_WRITE_ARG)))
786 {
787 xd_error ("fdopen %s failed: %s\n", name, g_strerror (errno));
788 return NULL;
789 }
790
791 fh->type = WRITE_TYPE;
792 fh->name = name;
793
794 edsio_md5_init (&fh->ctx);
795
796 return fh;
797}
798
799static gint
800xd_begin_compression (XdFileHandle* fh)
801{
802 gint filepos, nfd;
803
804 if (compress_level == 0)
805 return fh->real_length;
806
807 if (! (fh->out_close) (fh))
808 {
809 xd_error ("fclose failed: %s\n", g_strerror (errno));
810 return -1;
811 }
812
813 filepos = lseek (fh->out_fd, 0, SEEK_END);
814
815 if (filepos < 0)
816 {
817 xd_error ("lseek failed: %s\n", g_strerror (errno));
818 return -1;
819 }
820
821 g_assert (fh->out_fd >= 0);
822
823 nfd = dup (fh->out_fd);
824
825 fh->out = gzdopen (nfd, "wb");
826 fh->out_write = &xd_gzwrite;
827 fh->out_close = &xd_gzclose;
828
829 if (! fh->out)
830 {
831 xd_error ("gzdopen failed: %s\n", g_strerror (errno));
832 return -1;
833 }
834
835 if (gzsetparams(fh->out, compress_level, Z_DEFAULT_STRATEGY) != Z_OK)
836 {
837 int foo;
838 xd_error ("gzsetparams failed: %s\n", gzerror (fh->out, &foo));
839 return -1;
840 }
841
842 return filepos;
843}
844
845static gboolean
846xd_end_compression (XdFileHandle* fh)
847{
848 if (compress_level == 0)
849 return TRUE;
850
851 if (! (fh->out_close) (fh))
852 {
853 xd_error ("fdclose failed: %s\n", g_strerror (errno));
854 return FALSE;
855 }
856
857 if (lseek (fh->out_fd, 0, SEEK_END) < 0)
858 {
859 xd_error ("lseek failed: %s\n", g_strerror (errno));
860 return FALSE;
861 }
862
863 g_assert (fh->out_fd >= 0);
864 fh->out = fdopen (dup (fh->out_fd), FOPEN_WRITE_ARG);
865 fh->out_write = &xd_fwrite;
866 fh->out_close = &xd_fclose;
867
868 if (! fh->out)
869 {
870 xd_error ("fdopen failed: %s\n", g_strerror (errno));
871 return FALSE;
872 }
873
874 return TRUE;
875}
876
877static gssize
878xd_handle_length (XdFileHandle *fh)
879{
880 if (fh->in_compressed)
881 return fh->current_pos;
882 else
883 return fh->narrow_high - fh->narrow_low;
884}
885
886static gssize
887xd_handle_pages (XdFileHandle *fh)
888{
889 g_assert (fh->type & READ_TYPE);
890 return xd_handle_length (fh) / XD_PAGE_SIZE;
891}
892
893static gssize
894xd_handle_pagesize (XdFileHandle *fh)
895{
896 g_assert (fh->type & READ_TYPE);
897 return XD_PAGE_SIZE;
898}
899
900static gint
901on_page (XdFileHandle* fh, guint pgno)
902{
903 if (pgno > xd_handle_pages (fh))
904 return -1;
905
906 if (pgno == xd_handle_pages (fh))
907 return xd_handle_length (fh) % XD_PAGE_SIZE;
908
909 return XD_PAGE_SIZE;
910}
911
912static gboolean
913xd_handle_close (XdFileHandle *fh, gint ignore)
914{
915 /* this is really a reset for writable files */
916
917 if (fh->type == WRITE_TYPE)
918 {
919 if (fh->reset_length_next_write)
920 {
921 fh->reset_length_next_write = FALSE;
922 fh->length = 0;
923 fh->narrow_high = 0;
924 }
925
926 fh->reset_length_next_write = TRUE;
927 edsio_md5_final (fh->md5, &fh->ctx);
928 edsio_md5_init (&fh->ctx);
929 }
930 else if (fh->in)
931 {
932 edsio_md5_final (fh->md5, &fh->ctx);
933 edsio_md5_init (&fh->ctx);
934 fh->md5_good = FALSE;
935 }
936
937 return TRUE;
938}
939
940static const guint8*
941xd_handle_checksum_md5 (XdFileHandle *fh)
942{
943 if (fh->in && ! fh->md5_good)
944 {
945 edsio_md5_final (fh->md5, &fh->ctx);
946 fh->md5_good = TRUE;
947 }
948 else if (fh->type != WRITE_TYPE && !fh->in)
949 {
950 const guint8* page;
951
952 while (fh->md5_page <= xd_handle_pages (fh))
953 {
954 gint pgno = fh->md5_page;
955 gint onpage;
956
957 if ((onpage = xd_handle_map_page (fh, pgno, &page)) < 0)
958 return NULL;
959
960 if (pgno == fh->md5_page)
961 {
962 fh->md5_page += 1;
963 edsio_md5_update (&fh->ctx, page, onpage);
964
965 if (fh->md5_page > xd_handle_pages (fh))
966 edsio_md5_final (fh->md5, &fh->ctx);
967 }
968
969 if (! xd_handle_unmap_page (fh, pgno, &page))
970 return NULL;
971 }
972 }
973
974 return g_memdup (fh->md5, 16);
975}
976
977static gboolean
978xd_handle_set_pos (XdFileHandle *fh, guint pos)
979{
980 if (fh->current_pos == pos + fh->narrow_low)
981 return TRUE;
982
983 if (pos + fh->narrow_low > fh->narrow_high)
984 {
985 xd_error ("unexpected EOF in %s\n", fh->name);
986 return FALSE;
987 }
988
989 fh->current_pos = pos + fh->narrow_low;
990
991 if (fseek (fh->in, fh->current_pos, SEEK_SET))
992 {
993 xd_error ("fseek failed: %s\n", g_strerror (errno));
994 return FALSE;
995 }
996
997 return TRUE;
998}
999
1000static gboolean
1001xd_handle_narrow (XdFileHandle* fh, guint low, guint high, gboolean compressed)
1002{
1003 if (high > fh->length)
1004 {
1005 xd_error ("%s: corrupt or truncated delta\n", fh->name);
1006 return FALSE;
1007 }
1008
1009 fh->narrow_low = low;
1010 fh->narrow_high = high;
1011
1012 edsio_md5_init (&fh->ctx);
1013
1014 if (compressed)
1015 {
1016 (* fh->in_close) (fh);
1017
1018 if (lseek (fh->fd, low, SEEK_SET) < 0)
1019 {
1020 xd_error ("%s: corrupt or truncated delta: cannot seek to %d: %s\n", fh->name, low, g_strerror (errno));
1021 return FALSE;
1022 }
1023
1024 g_assert (fh->fd >= 0);
1025 fh->in = gzdopen (dup (fh->fd), "rb");
1026 fh->in_read = &xd_gzread;
1027 fh->in_close = &xd_gzrclose;
1028 fh->in_compressed = TRUE;
1029 fh->current_pos = 0;
1030
1031 if (! fh->in)
1032 {
1033 xd_error ("gzdopen failed: %s\n", g_strerror (errno));
1034 return -1;
1035 }
1036 }
1037 else
1038 {
1039 if (! xd_handle_set_pos (fh, 0))
1040 return FALSE;
1041 }
1042
1043 return TRUE;
1044}
1045
1046static guint
1047xd_handle_get_pos (XdFileHandle* fh)
1048{
1049 return fh->current_pos - fh->narrow_low;
1050}
1051
1052static const gchar*
1053xd_handle_name (XdFileHandle *fh)
1054{
1055 return g_strdup (fh->name);
1056}
1057
1058static gssize
1059xd_handle_read (XdFileHandle *fh, guint8 *buf, gsize nbyte)
1060{
1061 if (nbyte == 0)
1062 return 0;
1063
1064 if (! (fh->in_read) (fh, buf, nbyte)) /* This is suspicious */
1065 {
1066 xd_error ("read failed: %s\n", g_strerror (errno));
1067 return -1;
1068 }
1069
1070 if (!no_verify)
1071 edsio_md5_update (&fh->ctx, buf, nbyte);
1072
1073 fh->current_pos += nbyte;
1074
1075 return nbyte;
1076}
1077
1078static gboolean
1079xd_handle_write (XdFileHandle *fh, const guint8 *buf, gsize nbyte)
1080{
1081 g_assert (fh->type == WRITE_TYPE);
1082
1083 if (fh->reset_length_next_write)
1084 {
1085 fh->reset_length_next_write = FALSE;
1086 fh->length = 0;
1087 fh->narrow_high = 0;
1088 }
1089
1090 if (! no_verify)
1091 edsio_md5_update (&fh->ctx, buf, nbyte);
1092
1093 if (! (*fh->out_write) (fh, buf, nbyte))
1094 {
1095 xd_error ("write failed: %s\n", g_strerror (errno));
1096 return FALSE;
1097 }
1098
1099 fh->length += nbyte;
1100 fh->real_length += nbyte;
1101 fh->narrow_high += nbyte;
1102
1103 return TRUE;
1104}
1105
1106static gboolean
1107xd_handle_really_close (XdFileHandle *fh)
1108{
1109 g_assert (fh->type == WRITE_TYPE);
1110
1111 if (! (* fh->out_close) (fh) || close (fh->out_fd) < 0)
1112 {
1113 xd_error ("write failed: %s\n", g_strerror (errno));
1114 return FALSE;
1115 }
1116
1117 return TRUE;
1118}
1119
1120static LRU*
1121pull_lru (XdFileHandle* fh, LRU* lru)
1122{
1123 if (lru->next && lru->prev)
1124 {
1125 lru->next->prev = lru->prev;
1126 lru->prev->next = lru->next;
1127 }
1128 else if (lru->next)
1129 {
1130 fh->lru_tail = lru->next;
1131 lru->next->prev = NULL;
1132 }
1133 else if (lru->prev)
1134 {
1135 fh->lru_head = lru->prev;
1136 lru->prev->next = NULL;
1137 }
1138 else
1139 {
1140 fh->lru_head = NULL;
1141 fh->lru_tail = NULL;
1142 }
1143
1144 lru->next = NULL;
1145 lru->prev = NULL;
1146
1147 return lru;
1148}
1149
1150static gboolean
1151really_free_one_page (XdFileHandle* fh)
1152{
1153 LRU *lru = fh->lru_tail;
1154
1155 for (; lru; lru = lru->prev)
1156 {
1157 gint to_unmap;
1158 LRU *lru_dead;
1159
1160 if (lru->refs > 0)
1161 continue;
1162
1163 lru_dead = pull_lru (fh, lru);
1164
1165 g_assert (lru_dead->buffer);
1166
1167 to_unmap = on_page (fh, lru_dead->page);
1168
1169 fh->lru_count -= 1;
1170
1171 if (to_unmap > 0)
1172 {
1173#ifdef WINHACK
1174 g_free (lru_dead->buffer);
1175#else
1176 if (munmap (lru_dead->buffer, to_unmap))
1177 {
1178 xd_error ("munmap failed: %s\n", g_strerror (errno));
1179 return FALSE;
1180 }
1181#endif /* WINHACK */
1182 }
1183
1184 lru_dead->buffer = NULL;
1185
1186 return TRUE;
1187 }
1188
1189 return TRUE;
1190}
1191
1192#if 0
1193static void
1194print_lru (XdFileHandle* fh)
1195{
1196 LRU* lru = fh->lru_head;
1197
1198 for (; lru; lru = lru->prev)
1199 {
1200 g_print ("page %d buffer %p\n", lru->page, lru->buffer);
1201
1202 if (! lru->prev && lru != fh->lru_tail)
1203 g_print ("incorrect lru_tail\n");
1204 }
1205}
1206#endif
1207
1208static gboolean
1209make_lru_room (XdFileHandle* fh)
1210{
1211 if (fh->lru_count == max_mapped_pages)
1212 {
1213 if (! really_free_one_page (fh))
1214 return FALSE;
1215 }
1216
1217 g_assert (fh->lru_count < max_mapped_pages);
1218
1219 return TRUE;
1220}
1221
1222/*#define DEBUG_MAP*/
1223
1224static gssize
1225xd_handle_map_page (XdFileHandle *fh, guint pgno, const guint8** mem)
1226{
1227 LRU* lru;
1228 guint to_map;
1229
1230#ifdef DEBUG_MAP
1231 g_print ("map %p:%d\n", fh, pgno);
1232#endif
1233
1234 g_assert (fh->type & READ_TYPE);
1235
1236 if (fh->lru_table->len < (pgno + 1))
1237 {
1238 gint olen = fh->lru_table->len;
1239
1240 g_ptr_array_set_size (fh->lru_table, pgno + 1);
1241
1242 while (olen <= pgno)
1243 fh->lru_table->pdata[olen++] = NULL;
1244 }
1245
1246 lru = fh->lru_table->pdata[pgno];
1247
1248 if (! lru)
1249 {
1250 lru = g_chunk_new0 (LRU, fh->lru_chunk);
1251 fh->lru_table->pdata[pgno] = lru;
1252 lru->page = pgno;
1253 }
1254 else if (lru->buffer)
1255 {
1256 pull_lru (fh, lru);
1257 }
1258
1259 lru->prev = fh->lru_head;
1260 lru->next = NULL;
1261
1262 fh->lru_head = lru;
1263
1264 if (lru->prev)
1265 lru->prev->next = lru;
1266
1267 if (! fh->lru_tail)
1268 fh->lru_tail = lru;
1269
1270 to_map = on_page (fh, pgno);
1271
1272 if (to_map < 0)
1273 {
1274 xd_error ("unexpected EOF in %s\n", fh->name);
1275 return -1;
1276 }
1277
1278 if (! lru->buffer)
1279 {
1280 if (! make_lru_room (fh))
1281 return -1;
1282
1283 fh->lru_count += 1;
1284
1285 if (to_map > 0)
1286 {
1287#ifdef WINHACK
1288 lru->buffer = g_malloc (to_map);
1289
1290 if (lseek (fh->fd, pgno * XD_PAGE_SIZE, SEEK_SET) < 0)
1291 {
1292 xd_error ("lseek failed: %s\n", g_strerror (errno));
1293 return -1;
1294 }
1295
1296 if (read (fh->fd, lru->buffer, to_map) != to_map)
1297 {
1298 xd_error ("read failed: %s\n", g_strerror (errno));
1299 return -1;
1300 }
1301#else
1302 if (! (lru->buffer = mmap (NULL, to_map, PROT_READ, MAP_PRIVATE, fh->fd, pgno * XD_PAGE_SIZE)))
1303 {
1304 xd_error ("mmap failed: %s\n", g_strerror (errno));
1305 return -1;
1306 }
1307#endif
1308 }
1309 else
1310 {
1311 lru->buffer = (void*) -1;
1312 }
1313
1314 if (pgno == fh->md5_page)
1315 {
1316 if (! no_verify)
1317 edsio_md5_update (&fh->ctx, lru->buffer, to_map);
1318 fh->md5_page += 1;
1319
1320 if (fh->md5_page > xd_handle_pages (fh))
1321 edsio_md5_final (fh->md5, &fh->ctx);
1322 }
1323 }
1324
1325 (*mem) = lru->buffer;
1326
1327 lru->refs += 1;
1328 fh->lru_outstanding_refs += 1;
1329
1330 return to_map;
1331}
1332
1333static gboolean
1334xd_handle_unmap_page (XdFileHandle *fh, guint pgno, const guint8** mem)
1335{
1336 LRU* lru;
1337
1338#ifdef DEBUG_MAP
1339 g_print ("unmap %p:%d\n", fh, pgno);
1340#endif
1341
1342 g_assert (fh->type & READ_TYPE);
1343
1344 g_assert (pgno < fh->lru_table->len);
1345
1346 lru = fh->lru_table->pdata[pgno];
1347
1348 g_assert (lru && lru->refs > 0);
1349
1350 g_assert (lru->buffer == (*mem));
1351
1352 (*mem) = NULL;
1353
1354 lru->refs -= 1;
1355 fh->lru_outstanding_refs += 1;
1356
1357 if (lru->refs == 0 && fh->type == READ_NOSEEK_TYPE)
1358 {
1359 pull_lru (fh, lru);
1360
1361 lru->next = fh->lru_tail;
1362 if (lru->next) lru->next->prev = lru;
1363 lru->prev = NULL;
1364 fh->lru_tail = lru;
1365
1366 if (! really_free_one_page (fh))
1367 return FALSE;
1368 }
1369
1370 return TRUE;
1371}
1372
1373static gboolean
1374xd_handle_copy (XdFileHandle *from, XdFileHandle *to, guint off, guint len)
1375{
1376 if (from->in)
1377 {
1378 guint8 buf[1024];
1379
1380 /*if (! xd_handle_set_pos (from, off))
1381 return FALSE;*/
1382
1383 while (len > 0)
1384 {
1385 guint r = MIN (1024, len);
1386
1387 if (xd_handle_read (from, buf, r) != r)
1388 return FALSE;
1389
1390 if (! xd_handle_write (to, buf, r))
1391 return FALSE;
1392
1393 len -= r;
1394 }
1395 }
1396 else
1397 {
1398 while (len > 0)
1399 {
1400 guint off_page = off / XD_PAGE_SIZE;
1401 guint off_off = off % XD_PAGE_SIZE;
1402
1403 gint on = on_page (from, off_page);
1404 guint rem;
1405 guint copy;
1406
1407 if (on <= 0)
1408 {
1409 xd_error ("unexpected EOF in %s\n", from->name);
1410 return FALSE;
1411 }
1412
1413 rem = on - off_off;
1414 copy = MIN (len, rem);
1415
1416 if (from->copy_pgno != off_page &&
1417 from->copy_page &&
1418 ! xd_handle_unmap_page (from, from->copy_pgno, &from->copy_page))
1419 return FALSE;
1420
1421 from->copy_pgno = off_page;
1422
1423 if (xd_handle_map_page (from, off_page, &from->copy_page) < 0)
1424 return FALSE;
1425
1426 if (! xd_handle_write (to, from->copy_page + off_off, copy))
1427 return FALSE;
1428
1429 if (! xd_handle_unmap_page (from, off_page, &from->copy_page))
1430 return FALSE;
1431
1432 len -= copy;
1433 off += copy;
1434 }
1435 }
1436
1437 return TRUE;
1438}
1439
1440static gboolean
1441xd_handle_putui (XdFileHandle *fh, guint32 i)
1442{
1443 guint32 hi = g_htonl (i);
1444
1445 return xd_handle_write (fh, (guint8*)&hi, 4);
1446}
1447
1448static gboolean
1449xd_handle_getui (XdFileHandle *fh, guint32* i)
1450{
1451 if (xd_handle_read (fh, (guint8*)i, 4) != 4)
1452 return FALSE;
1453
1454 *i = g_ntohl (*i);
1455
1456 return TRUE;
1457}
1458
1459static HandleFuncTable xd_handle_table =
1460{
1461 (gssize (*) (FileHandle *fh)) xd_handle_length,
1462 (gssize (*) (FileHandle *fh)) xd_handle_pages,
1463 (gssize (*) (FileHandle *fh)) xd_handle_pagesize,
1464 (gssize (*) (FileHandle *fh, guint pgno, const guint8** mem)) xd_handle_map_page,
1465 (gboolean (*) (FileHandle *fh, guint pgno, const guint8** mem)) xd_handle_unmap_page,
1466 (const guint8* (*) (FileHandle *fh)) xd_handle_checksum_md5,
1467
1468 (gboolean (*) (FileHandle *fh, gint flags)) xd_handle_close,
1469
1470 (gboolean (*) (FileHandle *fh, const guint8 *buf, gsize nbyte)) xd_handle_write,
1471 (gboolean (*) (FileHandle *from, FileHandle *to, guint off, guint len)) xd_handle_copy,
1472
1473 (gboolean (*) (FileHandle *fh, guint32* i)) xd_handle_getui,
1474 (gboolean (*) (FileHandle *fh, guint32 i)) xd_handle_putui,
1475 (gssize (*) (FileHandle *fh, guint8 *buf, gsize nbyte)) xd_handle_read,
1476 (const gchar* (*) (FileHandle *fh)) xd_handle_name,
1477};
1478
1479static void
1480htonl_array (guint32* array, gint len)
1481{
1482 gint i;
1483
1484 for (i = 0; i < len; i += 1)
1485 array[i] = g_htonl(array[i]);
1486}
1487
1488static void
1489ntohl_array (guint32* array, gint len)
1490{
1491 gint i;
1492
1493 for (i = 0; i < len; i += 1)
1494 array[i] = g_ntohl(array[i]);
1495}
1496
1497static gint
1498delta_command (gint argc, gchar** argv)
1499{
1500 gint patch_out_fd;
1501 const char* patch_out_name;
1502 XdFileHandle *from, *to, *out;
1503 XdeltaGenerator* gen;
1504 XdeltaSource* src;
1505 XdeltaControl* cont;
1506 gboolean from_is_compressed = FALSE, to_is_compressed = FALSE;
1507 guint32 control_offset, header_offset;
1508 const char* from_name, *to_name;
1509 guint32 header_space[HEADER_WORDS];
1510 int fd;
1511
1512 memset (header_space, 0, sizeof (header_space));
1513
1514 if (argc != 3)
1515 {
1516 xd_error ("usage: %s delta fromfile tofile patchfile\n", program_name);
1517 return 2;
1518 }
1519
1520 if (verbose)
1521 {
1522 xd_error ("using block size: %d bytes\n", xdp_blocksize ());
1523 }
1524
1525
1526 if (! (from = open_read_seek_handle (argv[0], &from_is_compressed, TRUE)))
1527 return 2;
1528
1529 if (! (to = open_read_noseek_handle (argv[1], &to_is_compressed, FALSE, TRUE)))
1530 return 2;
1531
1532 // Note: I tried support to patches to stdout, but it broke when
1533 // compression was added. Sigh
1534 fd = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
1535
1536 if (fd < 0)
1537 {
1538 xd_error ("open %s failed: %s\n", argv[2], g_strerror (errno));
1539 return 2;
1540 }
1541
1542 patch_out_fd = fd;
1543 patch_out_name = argv[2];
1544
1545 from_name = g_basename (argv[0]);
1546 to_name = g_basename (argv[1]);
1547
1548 if (! (out = open_write_handle (patch_out_fd, patch_out_name)))
1549 return 2;
1550
1551 if (! (gen = xdp_generator_new ()))
1552 return 2;
1553
1554 if (! (src = xdp_source_new (from_name, (FileHandle*) from, NULL, NULL)))
1555 return 2;
1556
1557 xdp_source_add (gen, src);
1558
1559 if (! xd_handle_write (out, XDELTA_PREFIX, XDELTA_PREFIX_LEN))
1560 return 2;
1561
1562 /* compute the header */
1563 header_space[0] = 0;
1564
1565 if (no_verify) header_space[0] |= FLAG_NO_VERIFY;
1566 if (from_is_compressed) header_space[0] |= FLAG_FROM_COMPRESSED;
1567 if (to_is_compressed) header_space[0] |= FLAG_TO_COMPRESSED;
1568 if (compress_level != 0) header_space[0] |= FLAG_PATCH_COMPRESSED;
1569
1570 header_space[1] = strlen (from_name) << 16 | strlen (to_name);
1571 /* end compute the header */
1572
1573 htonl_array (header_space, HEADER_WORDS);
1574
1575 if (! xd_handle_write (out, (guint8*) header_space, HEADER_SPACE))
1576 return 2;
1577
1578 if (! xd_handle_write (out, from_name, strlen (from_name)))
1579 return 2;
1580
1581 if (! xd_handle_write (out, to_name, strlen (to_name)))
1582 return 2;
1583
1584 if (! xd_handle_close (out, 0))
1585 return 2;
1586
1587 if ((header_offset = xd_begin_compression (out)) < 0)
1588 return 2;
1589
1590 if (! (cont = xdp_generate_delta (gen, (FileHandle*) to, NULL, (FileHandle*) out)))
1591 return 2;
1592
1593#if 0
1594 serializeio_print_xdeltacontrol_obj (cont, 0);
1595#endif
1596
1597 if (cont->has_data && cont->has_data == cont->source_info_len)
1598 {
1599 if (! quiet)
1600 xd_error ("warning: no matches found in from file, patch will apply without it\n");
1601 }
1602
1603 if (! xd_handle_close (out, 0))
1604 return 2;
1605
1606 if ((control_offset = xd_begin_compression (out)) < 0)
1607 return 2;
1608
1609 if (! xdp_control_write (cont, (FileHandle*) out))
1610 return 2;
1611
1612 if (! xd_end_compression (out))
1613 return 2;
1614
1615 if (! xd_handle_putui (out, control_offset))
1616 return 2;
1617
1618 if (! xd_handle_write (out, XDELTA_PREFIX, XDELTA_PREFIX_LEN))
1619 return 2;
1620
1621 xd_read_close (from);
1622 xd_read_close (to);
1623
1624 if (! xd_handle_really_close (out))
1625 return 2;
1626
1627 xdp_generator_free (gen);
1628
1629 return control_offset != header_offset;
1630}
1631
1632static XdeltaPatch*
1633process_patch (const char* name)
1634{
1635 XdeltaPatch* patch;
1636 guint total_trailer;
1637
1638 patch = g_new0 (XdeltaPatch, 1);
1639
1640 patch->patch_name = name;
1641
1642 /* Strictly speaking, I'm violating the intended semantics of noseek here.
1643 * It will seek the file, which is not in fact checked in the map/unmap
1644 * logic above. This only means that it will not cache pages of this file
1645 * since it will be read piecewise sequentially. */
1646 if (! (patch->patch_in = open_read_noseek_handle (name, &patch->patch_is_compressed, TRUE, TRUE)))
1647 return NULL;
1648
1649 if (xd_handle_read (patch->patch_in, patch->magic_buf, XDELTA_PREFIX_LEN) != XDELTA_PREFIX_LEN)
1650 return NULL;
1651
1652 if (xd_handle_read (patch->patch_in, (guint8*) patch->header_space, HEADER_SPACE) != HEADER_SPACE)
1653 return NULL;
1654
1655 ntohl_array (patch->header_space, HEADER_WORDS);
1656
1657 if (strncmp (patch->magic_buf, XDELTA_110_PREFIX, XDELTA_PREFIX_LEN) == 0)
1658 {
1659 patch->has_trailer = TRUE;
1660 patch->patch_version = "1.1";
1661 }
1662 else if (strncmp (patch->magic_buf, XDELTA_104_PREFIX, XDELTA_PREFIX_LEN) == 0)
1663 {
1664 patch->has_trailer = TRUE;
1665 patch->patch_version = "1.0.4";
1666 }
1667 else if (strncmp (patch->magic_buf, XDELTA_100_PREFIX, XDELTA_PREFIX_LEN) == 0)
1668 {
1669 patch->patch_version = "1.0";
1670 }
1671 else if (strncmp (patch->magic_buf, XDELTA_020_PREFIX, XDELTA_PREFIX_LEN) == 0)
1672 goto nosupport;
1673 else if (strncmp (patch->magic_buf, XDELTA_018_PREFIX, XDELTA_PREFIX_LEN) == 0)
1674 goto nosupport;
1675 else if (strncmp (patch->magic_buf, XDELTA_014_PREFIX, XDELTA_PREFIX_LEN) == 0)
1676 goto nosupport;
1677 else
1678 {
1679 xd_error ("%s: bad magic number: not a valid delta\n", name);
1680 return NULL;
1681 }
1682
1683 patch->patch_flags = patch->header_space[0];
1684
1685 if (no_verify)
1686 xd_error ("--noverify is only accepted when creating a delta\n");
1687
1688 if (patch->patch_flags & FLAG_NO_VERIFY)
1689 no_verify = TRUE;
1690 else
1691 no_verify = FALSE;
1692
1693 patch->from_name_len = patch->header_space[1] >> 16;
1694 patch->to_name_len = patch->header_space[1] & 0xffff;
1695
1696 patch->from_name = g_malloc (patch->from_name_len+1);
1697 patch->to_name = g_malloc (patch->to_name_len+1);
1698
1699 patch->from_name[patch->from_name_len] = 0;
1700 patch->to_name[patch->to_name_len] = 0;
1701
1702 if (xd_handle_read (patch->patch_in, patch->from_name, patch->from_name_len) != patch->from_name_len)
1703 return NULL;
1704
1705 if (xd_handle_read (patch->patch_in, patch->to_name, patch->to_name_len) != patch->to_name_len)
1706 return NULL;
1707
1708 patch->header_offset = xd_handle_get_pos (patch->patch_in);
1709
1710 total_trailer = 4 + (patch->has_trailer ? XDELTA_PREFIX_LEN : 0);
1711
1712 if (! xd_handle_set_pos (patch->patch_in, xd_handle_length (patch->patch_in) - total_trailer))
1713 return NULL;
1714
1715 if (! xd_handle_getui (patch->patch_in, &patch->control_offset))
1716 return NULL;
1717
1718 if (patch->has_trailer)
1719 {
1720 guint8 trailer_buf[XDELTA_PREFIX_LEN];
1721
1722 if (xd_handle_read (patch->patch_in, trailer_buf, XDELTA_PREFIX_LEN) != XDELTA_PREFIX_LEN)
1723 return NULL;
1724
1725 if (strncmp (trailer_buf, patch->magic_buf, XDELTA_PREFIX_LEN) != 0)
1726 {
1727 xd_error ("%s: bad trailing magic number, delta is corrupt\n", name);
1728 return NULL;
1729 }
1730 }
1731
1732 if (! xd_handle_narrow (patch->patch_in, patch->control_offset,
1733 xd_handle_length (patch->patch_in) - total_trailer,
1734 patch->patch_flags & FLAG_PATCH_COMPRESSED))
1735 return NULL;
1736
1737 if (! (patch->cont = xdp_control_read ((FileHandle*) patch->patch_in)))
1738 return NULL;
1739
1740 if (patch->cont->source_info_len > 0)
1741 {
1742 XdeltaSourceInfo* info = patch->cont->source_info[0];
1743
1744 if (info->isdata)
1745 patch->data_source = info;
1746 else
1747 {
1748 patch->from_source = info;
1749
1750 if (patch->cont->source_info_len > 1)
1751 {
1752 xd_generate_void_event (EC_XdIncompatibleDelta);
1753 return NULL;
1754 }
1755 }
1756 }
1757
1758 if (patch->cont->source_info_len > 1)
1759 {
1760 patch->from_source = patch->cont->source_info[1];
1761 }
1762
1763 if (patch->cont->source_info_len > 2)
1764 {
1765 xd_generate_void_event (EC_XdIncompatibleDelta);
1766 return NULL;
1767 }
1768
1769 if (! xd_handle_narrow (patch->patch_in,
1770 patch->header_offset,
1771 patch->control_offset,
1772 patch->patch_flags & FLAG_PATCH_COMPRESSED))
1773 return NULL;
1774
1775 return patch;
1776
1777 nosupport:
1778
1779 xd_error ("delta format is unsupported (too old)\n");
1780 return NULL;
1781}
1782
1783static gint
1784info_command (gint argc, gchar** argv)
1785{
1786 XdeltaPatch* patch;
1787 char buf[33];
1788 int i;
1789 XdeltaSourceInfo* si;
1790
1791 if (! (patch = process_patch (argv[0])))
1792 return 2;
1793
1794 xd_error_file = stdout;
1795
1796 xd_error ("version %s found patch version %s in %s%s\n",
1797 xdelta_version,
1798 patch->patch_version,
1799 patch->patch_name,
1800 patch->patch_flags & FLAG_PATCH_COMPRESSED ? " (compressed)" : "");
1801
1802 if (patch->patch_flags & FLAG_NO_VERIFY)
1803 xd_error ("generated with --noverify\n");
1804
1805 if (patch->patch_flags & FLAG_FROM_COMPRESSED)
1806 xd_error ("generated with a gzipped FROM file\n");
1807
1808 if (patch->patch_flags & FLAG_TO_COMPRESSED)
1809 xd_error ("generated with a gzipped TO file\n");
1810
1811 edsio_md5_to_string (patch->cont->to_md5, buf);
1812
1813 xd_error ("output name: %s\n", patch->to_name);
1814 xd_error ("output length: %d\n", patch->cont->to_len);
1815 xd_error ("output md5: %s\n", buf);
1816
1817 xd_error ("patch from segments: %d\n", patch->cont->source_info_len);
1818
1819 xd_error ("MD5\t\t\t\t\tLength\tCopies\tUsed\tSeq?\tName\n");
1820
1821 for (i = 0; i < patch->cont->source_info_len; i += 1)
1822 {
1823 si = patch->cont->source_info[i];
1824
1825 edsio_md5_to_string (si->md5, buf);
1826
1827 xd_error ("%s\t%d\t%d\t%d\t%s\t%s\n",
1828 buf,
1829 si->len,
1830 si->copies,
1831 si->copy_length,
1832 si->sequential ? "yes" : "no",
1833 si->name);
1834 }
1835
1836 return 0;
1837}
1838
1839static gint
1840patch_command (gint argc, gchar** argv)
1841{
1842 XdFileHandle* to_out;
1843 XdeltaPatch* patch;
1844 gint to_out_fd;
1845 int count = 1;
1846 int ret;
1847
1848 if (argc < 1 || argc > 3)
1849 {
1850 xd_error ("usage: %s patch patchfile [fromfile [tofile]]\n", program_name);
1851 return 2;
1852 }
1853
1854 if (! (patch = process_patch (argv[0])))
1855 return 2;
1856
1857 if (argc > 1)
1858 patch->from_name = argv[1];
1859 else if (verbose)
1860 xd_error ("using from file name: %s\n", patch->from_name);
1861
1862 if (argc > 2)
1863 patch->to_name = argv[2];
1864 else
1865 {
1866 struct stat sbuf;
1867 gchar *defname = g_strdup (patch->to_name);
1868
1869 while ((ret = stat (patch->to_name, & sbuf)) == 0)
1870 {
1871 if (verbose)
1872 xd_error ("to file exists: %s\n", patch->to_name);
1873 patch->to_name = g_strdup_printf ("%s.xdp%d", defname, count++);
1874 }
1875
1876 if (verbose || strcmp (defname, patch->to_name) != 0)
1877 xd_error ("using to file name: %s\n", patch->to_name);
1878 }
1879
1880 if (strcmp (patch->to_name, "-") == 0)
1881 {
1882 to_out_fd = STDOUT_FILENO;
1883 patch->to_name = "standard output";
1884 }
1885 else
1886 {
1887 to_out_fd = open (patch->to_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
1888
1889 if (to_out_fd < 0)
1890 {
1891 xd_error ("open %s failed: %s\n", patch->to_name, g_strerror (errno));
1892 return 2;
1893 }
1894 }
1895
1896 to_out = open_write_handle (to_out_fd, patch->to_name);
1897
1898 if ((patch->patch_flags & FLAG_TO_COMPRESSED) && (xd_begin_compression (to_out) < 0))
1899 return 2;
1900
1901 if (patch->from_source)
1902 {
1903 XdFileHandle* from_in;
1904 gboolean from_is_compressed = FALSE;
1905
1906 if (! (from_in = open_read_seek_handle (patch->from_name, &from_is_compressed, TRUE)))
1907 return 2;
1908
1909 if (from_is_compressed != ((patch->patch_flags & FLAG_FROM_COMPRESSED) && 1))
1910 xd_error ("warning: expected %scompressed from file (%s)\n",
1911 (patch->patch_flags & FLAG_FROM_COMPRESSED) ? "" : "un",
1912 patch->from_name);
1913
1914 if (xd_handle_length (from_in) != patch->from_source->len)
1915 {
1916 xd_error ("expected from file (%s) of %slength %d bytes\n",
1917 patch->from_name,
1918 from_is_compressed ? "uncompressed " : "",
1919 patch->from_source->len);
1920 return 2;
1921 }
1922
1923 patch->from_source->in = (XdeltaStream*) from_in;
1924 }
1925
1926 if (patch->data_source)
1927 patch->data_source->in = (XdeltaStream*) patch->patch_in;
1928
1929 if (! xdp_apply_delta (patch->cont, (FileHandle*) to_out))
1930 return 2;
1931
1932 if (patch->from_source)
1933 xd_read_close ((XdFileHandle*) patch->from_source->in);
1934
1935 xd_read_close (patch->patch_in);
1936
1937 if (! xd_handle_really_close (to_out))
1938 return 2;
1939
1940 return 0;
1941}