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 
67 static 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 
110 typedef struct _LRU LRU;
111 
112 struct _LRU
113 {
114   LRU *next;
115   LRU *prev;
116 
117   gint refs;
118   guint page;
119   guint8* buffer;
120 };
121 
122 typedef struct _XdFileHandle XdFileHandle;
123 
124 typedef 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 
151 struct _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$\"; " $ */
196 static const char xdelta_version[] = "1.1.4";
197 
198 typedef struct _Command Command;
199 
200 struct _Command {
201   gchar* name;
202   gint (* func) (gint argc, gchar** argv);
203   gint nargs;
204 };
205 
206 static gint    delta_command    (gint argc, gchar** argv);
207 static gint    patch_command    (gint argc, gchar** argv);
208 static gint    info_command     (gint argc, gchar** argv);
209 
210 static 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 
218 static 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 
231 static const gchar* program_name;
232 static gint         compress_level = Z_DEFAULT_COMPRESSION;
233 static gint         no_verify = FALSE;
234 static gint         pristine = FALSE;
235 static gint         verbose = FALSE;
236 static gint         max_mapped_pages = G_MAXINT;
237 static gint         quiet = FALSE;
238 
239 #define xd_error g_warning
240 
241 static void
usage()242 usage ()
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 
249 static void
help()250 help ()
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 
270 static void
version()271 version ()
272 {
273   xd_error ("version %s\n", xdelta_version);
274   exit (2);
275 }
276 
277 static FILE* xd_error_file = NULL;
278 
279 static void
xd_error_func(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)280 xd_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 
291 static gboolean
event_devel(void)292 event_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 
306 static gboolean
event_watch(GenericEvent * ev,GenericEventDef * def,const char * message)307 event_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 
320 gint
main(gint argc,gchar ** argv)321 main (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 
487 static gssize xd_handle_map_page (XdFileHandle *fh, guint pgno, const guint8** mem);
488 static gboolean xd_handle_unmap_page (XdFileHandle *fh, guint pgno, const guint8** mem);
489 
490 static gboolean
xd_fwrite(XdFileHandle * fh,const void * buf,gint nbyte)491 xd_fwrite (XdFileHandle* fh, const void* buf, gint nbyte)
492 {
493   return fwrite (buf, nbyte, 1, fh->out) == 1;
494 }
495 
496 static gboolean
xd_fread(XdFileHandle * fh,void * buf,gint nbyte)497 xd_fread (XdFileHandle* fh, void* buf, gint nbyte)
498 {
499   return fread (buf, nbyte, 1, fh->in) == 1;
500 }
501 
502 static gboolean
xd_fclose(XdFileHandle * fh)503 xd_fclose (XdFileHandle* fh)
504 {
505   return fclose (fh->out) == 0;
506 }
507 
508 static gboolean
xd_frclose(XdFileHandle * fh)509 xd_frclose (XdFileHandle* fh)
510 {
511   return fclose (fh->in) == 0;
512 }
513 
514 static gboolean
xd_gzwrite(XdFileHandle * fh,const void * buf,gint nbyte)515 xd_gzwrite (XdFileHandle* fh, const void* buf, gint nbyte)
516 {
517   return gzwrite (fh->out, (void*) buf, nbyte) == nbyte;
518 }
519 
520 static gboolean
xd_gzread(XdFileHandle * fh,void * buf,gint nbyte)521 xd_gzread (XdFileHandle* fh, void* buf, gint nbyte)
522 {
523   return gzread (fh->in, buf, nbyte) == nbyte;
524 }
525 
526 static gboolean
xd_gzclose(XdFileHandle * fh)527 xd_gzclose (XdFileHandle* fh)
528 {
529   return gzclose (fh->out) == Z_OK;
530 }
531 
532 static gboolean
xd_gzrclose(XdFileHandle * fh)533 xd_gzrclose (XdFileHandle* fh)
534 {
535   return gzclose (fh->in) == Z_OK;
536 }
537 
538 static void
init_table(XdFileHandle * fh)539 init_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 
547 static XdFileHandle*
open_common(const char * name,const char * real_name)548 open_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 
585 static gboolean
file_gzipped(const char * name,gboolean * is_compressed)586 file_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 
611 static const char*
xd_tmpname(void)612 xd_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 
635 static const char*
file_gunzip(const char * name)636 file_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 
670 static XdFileHandle*
open_read_noseek_handle(const char * name,gboolean * is_compressed,gboolean will_read,gboolean honor_pristine)671 open_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 
721 static void
xd_read_close(XdFileHandle * fh)722 xd_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 
738 static XdFileHandle*
open_read_seek_handle(const char * name,gboolean * is_compressed,gboolean honor_pristine)739 open_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 
770 static XdFileHandle*
open_write_handle(int fd,const char * name)771 open_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 
799 static gint
xd_begin_compression(XdFileHandle * fh)800 xd_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 
845 static gboolean
xd_end_compression(XdFileHandle * fh)846 xd_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 
877 static gssize
xd_handle_length(XdFileHandle * fh)878 xd_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 
886 static gssize
xd_handle_pages(XdFileHandle * fh)887 xd_handle_pages (XdFileHandle *fh)
888 {
889   g_assert (fh->type & READ_TYPE);
890   return xd_handle_length (fh) / XD_PAGE_SIZE;
891 }
892 
893 static gssize
xd_handle_pagesize(XdFileHandle * fh)894 xd_handle_pagesize (XdFileHandle *fh)
895 {
896   g_assert (fh->type & READ_TYPE);
897   return XD_PAGE_SIZE;
898 }
899 
900 static gint
on_page(XdFileHandle * fh,guint pgno)901 on_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 
912 static gboolean
xd_handle_close(XdFileHandle * fh,gint ignore)913 xd_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 
940 static const guint8*
xd_handle_checksum_md5(XdFileHandle * fh)941 xd_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 
977 static gboolean
xd_handle_set_pos(XdFileHandle * fh,guint pos)978 xd_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 
1000 static gboolean
xd_handle_narrow(XdFileHandle * fh,guint low,guint high,gboolean compressed)1001 xd_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 
1046 static guint
xd_handle_get_pos(XdFileHandle * fh)1047 xd_handle_get_pos (XdFileHandle* fh)
1048 {
1049   return fh->current_pos - fh->narrow_low;
1050 }
1051 
1052 static const gchar*
xd_handle_name(XdFileHandle * fh)1053 xd_handle_name (XdFileHandle *fh)
1054 {
1055   return g_strdup (fh->name);
1056 }
1057 
1058 static gssize
xd_handle_read(XdFileHandle * fh,guint8 * buf,gsize nbyte)1059 xd_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 
1078 static gboolean
xd_handle_write(XdFileHandle * fh,const guint8 * buf,gsize nbyte)1079 xd_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 
1106 static gboolean
xd_handle_really_close(XdFileHandle * fh)1107 xd_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 
1120 static LRU*
pull_lru(XdFileHandle * fh,LRU * lru)1121 pull_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 
1150 static gboolean
really_free_one_page(XdFileHandle * fh)1151 really_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
1193 static void
1194 print_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 
1208 static gboolean
make_lru_room(XdFileHandle * fh)1209 make_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 
1224 static gssize
xd_handle_map_page(XdFileHandle * fh,guint pgno,const guint8 ** mem)1225 xd_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 
1333 static gboolean
xd_handle_unmap_page(XdFileHandle * fh,guint pgno,const guint8 ** mem)1334 xd_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 
1373 static gboolean
xd_handle_copy(XdFileHandle * from,XdFileHandle * to,guint off,guint len)1374 xd_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 
1440 static gboolean
xd_handle_putui(XdFileHandle * fh,guint32 i)1441 xd_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 
1448 static gboolean
xd_handle_getui(XdFileHandle * fh,guint32 * i)1449 xd_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 
1459 static 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 
1479 static void
htonl_array(guint32 * array,gint len)1480 htonl_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 
1488 static void
ntohl_array(guint32 * array,gint len)1489 ntohl_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 
1497 static gint
delta_command(gint argc,gchar ** argv)1498 delta_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 
1632 static XdeltaPatch*
process_patch(const char * name)1633 process_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 
1783 static gint
info_command(gint argc,gchar ** argv)1784 info_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 
1839 static gint
patch_command(gint argc,gchar ** argv)1840 patch_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 }
1942