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