Extend SHA256 support elsewhere
[jigdo.git] / src / mkimage.cc
1 /* $Id: mkimage.cc,v 1.15 2005/07/09 19:14:46 atterer Exp $ -*- C++ -*-
2   __   _
3   |_) /|  Copyright (C) 2001-2003  |  richard@
4   | \/¯|  Richard Atterer          |  atterer.org
5   ¯ '` ¯
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License, version 2. See
8   the file COPYING for details.
9
10   Create image from template / merge new files into image.tmp
11
12 */
13
14 #include <config.h>
15
16 #include <errno.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd-jigdo.h>
22
23 #include <iomanip>
24 #include <iostream>
25 #include <fstream>
26 #include <memory>
27
28 #include <compat.hh>
29 #include <log.hh>
30 #include <mkimage.hh>
31 #include <scan.hh>
32 #include <serialize.hh>
33 #include <string.hh>
34 #include <zstream-gz.hh>
35 #include <mktemplate.hh>
36
37 //______________________________________________________________________
38
39 DEBUG_UNIT("make-image")
40
41 namespace {
42
43 typedef JigdoDesc::ProgressReporter ProgressReporter;
44
45 // memset() is not portable enough...
46 void memClear(byte* buf, size_t size) {
47   while (size > 8) {
48     *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0;
49     *buf++ = 0; *buf++ = 0; *buf++ = 0; *buf++ = 0;
50     size -= 8;
51   }
52   while (size > 0) {
53     *buf++ = 0;
54     --size;
55   }
56 }
57
58 } // local namespace
59 //______________________________________________________________________
60
61 JigdoDesc::~JigdoDesc() { }
62 //______________________________________________________________________
63
64 bool JigdoDesc::isTemplate(bistream& file) {
65   if (!file.seekg(0, ios::beg)) return false;
66   string l;
67   getline(file, l); // "JigsawDownload template 1.0 jigdo-file/0.0.1"
68   string templHdr = TEMPLATE_HDR;
69   if (compat_compare(l, 0, templHdr.length(), templHdr) != 0) return false;
70   getline(file, l); // Ignore comment line
71   getline(file, l); // Empty line, except for CR
72   if (l != "\r") return false;
73   return true;
74 }
75 //______________________________________________________________________
76
77 void JigdoDesc::seekFromEnd(bistream& file) {
78   file.seekg(-6, ios::end);
79   debug("JigdoDesc::seekFromEnd0: now at file offset %1",
80         static_cast<uint64>(file.tellg()));
81   uint64 descLen;
82   SerialIstreamIterator f(file);
83   unserialize6(descLen, f);
84   if (static_cast<uint64>(file.tellg()) < descLen) { // Is this cast correct?
85     debug("JigdoDesc::seekFromEnd1 descLen=%1", descLen);
86     throw JigdoDescError(_("Invalid template data - corrupted file?"));
87   }
88
89   file.seekg(-descLen, ios::end);
90   debug("JigdoDesc::seekFromEnd2: now at file offset %1",
91         static_cast<uint64>(file.tellg()));
92
93   size_t toRead = 4;
94   byte buf[4];
95   buf[3] = '\0';
96   byte* b = buf;
97   do {
98     readBytes(file, b, toRead);
99     size_t n = file.gcount();
100     debug("JigdoDesc::seekFromEnd3: read %1, now at file offset %2",
101           n, static_cast<uint64>(file.tellg()));
102     //cerr<<"read "<<n<<' '<<file.tellg()<<endl;
103     b += n;
104     toRead -= n;
105   } while (file.good() && toRead > 0);
106   if (buf[0] != 'D' || buf[1] != 'E' || buf[2] != 'S' || buf[3] != 'C') {
107     debug("JigdoDesc::seekFromEnd4 %1 %2 %3 %4",
108           int(buf[0]), int(buf[1]), int(buf[2]), int(buf[3]));
109     throw JigdoDescError(_("Invalid template data - corrupted file?"));
110   }
111 }
112 //______________________________________________________________________
113
114 bistream& JigdoDescVec::get(bistream& file) {
115   /* Need auto_ptr: If we did a direct push_back(new JigdoDesc), the
116      "new" might succeed, but the push_back() fail with bad_alloc =>
117      mem leak */
118   auto_ptr<JigdoDesc> desc;
119   clear();
120
121   SerialIstreamIterator f(file);
122   uint64 len;
123   unserialize6(len, f); // descLen - 16, i.e. length of entries
124   if (len < 45 || len > 256*1024*1024) {
125     debug("JigdoDescVec::get: DESC section too small/large");
126     throw JigdoDescError(_("Invalid template data - corrupted file?"));
127   }
128   len -= 16;
129   //____________________
130
131   uint64 off = 0; // Offset in image
132   uint64 read = 0; // Nr of bytes read
133   MD5 entryMd5;
134   SHA256 entrySha256;
135   uint64 entryLen;
136   RsyncSum64 rsum;
137   size_t blockLength;
138   while (file && read < len) {
139     byte type = *f;
140     ++f;
141     switch (type) {
142
143     case JigdoDesc::IMAGE_INFO_MD5:
144       unserialize6(entryLen, f);
145       unserialize(entryMd5, f);
146       unserialize4(blockLength, f);
147       if (!file) break;
148       debug("JigdoDesc::read: ImageInfoMD5 %1 %2",
149             entryLen, entryMd5.toString());
150       desc.reset(new JigdoDesc::ImageInfoMD5(entryLen, entryMd5, blockLength));
151       push_back(desc.release());
152       read += 1 + 6 + entryMd5.serialSizeOf() + 4;
153       break;
154
155     case JigdoDesc::IMAGE_INFO_SHA256:
156       unserialize6(entryLen, f);
157       unserialize(entrySha256, f);
158       unserialize4(blockLength, f);
159       if (!file) break;
160       debug("JigdoDesc::read: ImageInfoSHA256 %1 %2",
161             entryLen, entrySha256.toString());
162       desc.reset(new JigdoDesc::ImageInfoSHA256(entryLen, entrySha256, blockLength));
163       push_back(desc.release());
164       read += 1 + 6 + entrySha256.serialSizeOf() + 4;
165       break;
166
167     case JigdoDesc::UNMATCHED_DATA:
168       unserialize6(entryLen, f);
169       if (!file) break;
170       debug("JigdoDesc::read: %1 UnmatchedData %2", off, entryLen);
171       desc.reset(new JigdoDesc::UnmatchedData(off, entryLen));
172       push_back(desc.release());
173       read += 1 + 6;
174       off += entryLen;
175       break;
176
177     case JigdoDesc::MATCHED_FILE_MD5:
178     case JigdoDesc::WRITTEN_FILE_MD5:
179       unserialize6(entryLen, f);
180       unserialize(rsum, f);
181       unserialize(entryMd5, f);
182       if (!file) break;
183       debug("JigdoDesc::read: %1 %2File %3 %4",
184             off, (type == JigdoDesc::MATCHED_FILE_MD5 ? "Matched" : "Written"),
185             entryLen, entryMd5.toString());
186       if (type == JigdoDesc::MATCHED_FILE_MD5)
187         desc.reset(new JigdoDesc::MatchedFileMD5(off, entryLen, rsum,entryMd5));
188       else
189         desc.reset(new JigdoDesc::WrittenFileMD5(off, entryLen, rsum,entryMd5));
190       push_back(desc.release());
191       read += 1 + 6 + rsum.serialSizeOf() + entryMd5.serialSizeOf();
192       off += entryLen;
193       break;
194
195     case JigdoDesc::MATCHED_FILE_SHA256:
196     case JigdoDesc::WRITTEN_FILE_SHA256:
197       unserialize6(entryLen, f);
198       unserialize(rsum, f);
199       unserialize(entrySha256, f);
200       if (!file) break;
201       debug("JigdoDesc::read: %1 %2File %3 %4",
202             off, (type == JigdoDesc::MATCHED_FILE_SHA256 ? "Matched" : "Written"),
203             entryLen, entrySha256.toString());
204       if (type == JigdoDesc::MATCHED_FILE_SHA256)
205         desc.reset(new JigdoDesc::MatchedFileSHA256(off, entryLen, rsum,entrySha256));
206       else
207         desc.reset(new JigdoDesc::WrittenFileSHA256(off, entryLen, rsum,entrySha256));
208       push_back(desc.release());
209       read += 1 + 6 + rsum.serialSizeOf() + entrySha256.serialSizeOf();
210       off += entryLen;
211       break;
212
213       // Template entry types that were obsoleted with version 0.6.3:
214
215     case JigdoDesc::OBSOLETE_IMAGE_INFO:
216       unserialize6(entryLen, f);
217       unserialize(entryMd5, f);
218       if (!file) break;
219       debug("JigdoDesc::read: old ImageInfoMD5 %1 %2",
220             entryLen, entryMd5.toString());
221       // Special case: passing blockLength==0, which is otherwise impossible
222       desc.reset(new JigdoDesc::ImageInfoMD5(entryLen, entryMd5, 0));
223       push_back(desc.release());
224       read += 1 + 6 + entryMd5.serialSizeOf();
225       break;
226
227     case JigdoDesc::OBSOLETE_MATCHED_FILE:
228     case JigdoDesc::OBSOLETE_WRITTEN_FILE:
229       unserialize6(entryLen, f);
230       unserialize(entryMd5, f);
231       if (!file) break;
232       debug("JigdoDesc::read: %1 old %2File %3 %4", off,
233             (type == JigdoDesc::OBSOLETE_MATCHED_FILE ? "Matched" :
234              "Written"), entryLen, entryMd5.toString());
235       /* Value of rsum is "don't care" because the ImageInfo's
236          blockLength will be zero. */
237       rsum.reset();
238       if (type == JigdoDesc::OBSOLETE_MATCHED_FILE)
239         desc.reset(new JigdoDesc::MatchedFileMD5(off, entryLen, rsum,entryMd5));
240       else
241         desc.reset(new JigdoDesc::WrittenFileMD5(off, entryLen, rsum,entryMd5));
242       push_back(desc.release());
243       read += 1 + 6 + entryMd5.serialSizeOf();
244       off += entryLen;
245       break;
246
247     default:
248       debug("JigdoDesc::read: unknown type %1", type);
249       throw JigdoDescError(_("Invalid template data - corrupted file?"));
250     }
251   }
252   //____________________
253
254   if (read < len) {
255     string err = subst(_("Error reading template data (%1)"),
256                        strerror(errno));
257     throw JigdoDescError(err);
258   }
259
260   if (empty())
261     throw JigdoDescError(_("Invalid template data - corrupted file?"));
262
263   // Sanity check for an imageInfoMD5/imageInfoSHA256 entry on the end of
264   // the chain
265   JigdoDesc::ImageInfoMD5* ii = dynamic_cast<JigdoDesc::ImageInfoMD5*>(back());
266   JigdoDesc::ImageInfoSHA256* ii2 = dynamic_cast<JigdoDesc::ImageInfoSHA256*>(back());
267   if ((ii == 0 && ii2 == 0)
268       || (ii != 0 && ii->size() != off)
269       || (ii2 != 0 && ii2->size() != off)) {
270     throw JigdoDescError(_("Invalid template data - corrupted file?"));
271   }
272   return file;
273 }
274 //______________________________________________________________________
275
276 bostream& JigdoDescVec::put(bostream& file, MD5Sum* md, SHA256Sum* sd, int checksumChoice) const {
277   // Pass 1: Accumulate sizes of entries, calculate descLen
278   // 4 for DESC, 6 each for length of part at start & end
279   uint64 descLen = 4 + 6*2; // Length of DESC part
280   unsigned bufLen = 4 + 6;
281   for (const_iterator i = begin(), e = end(); i != e; ++i) {
282     unsigned s = (unsigned)(*i)->serialSizeOf();
283     bufLen = max(bufLen, s);
284     descLen += s;
285   }
286   if (DEBUG) bufLen += 1;
287
288   // Pass 2: Write DESC part
289   byte buf[bufLen];
290   if (DEBUG) buf[bufLen - 1] = 0xa5;
291   byte* p;
292   p = serialize4(0x43534544, buf); // "DESC" in little-endian order
293   p = serialize6(descLen, p);
294   writeBytes(file, buf, 4 + 6);
295   if (md != 0 && checksumChoice == MkTemplate::CHECK_MD5)
296     md->update(buf, 4 + 6);
297   if (sd != 0 && checksumChoice == MkTemplate::CHECK_SHA256)
298     sd->update(buf, 4 + 6);
299   for (const_iterator i = begin(), e = end(); i != e; ++i) {
300     JigdoDesc::UnmatchedData* unm;
301     JigdoDesc::ImageInfoMD5* infomd5;
302     JigdoDesc::ImageInfoSHA256* infosha;
303     JigdoDesc::MatchedFileMD5* matchedmd5;
304     JigdoDesc::WrittenFileMD5* writtenmd5;
305     JigdoDesc::MatchedFileSHA256* matchedsha;
306     JigdoDesc::WrittenFileSHA256* writtensha;
307
308     if ((unm = dynamic_cast<JigdoDesc::UnmatchedData*>(*i)) != 0)
309       p = unm->serialize(buf);
310
311     if (checksumChoice == MkTemplate::CHECK_MD5) {
312       if ((infomd5 = dynamic_cast<JigdoDesc::ImageInfoMD5*>(*i)) != 0)
313         p = infomd5->serialize(buf);
314       /* NB we must first try to cast to WrittenFile*, then to
315          MatchedFile*, because WrittenFileMD5 derives from MatchedFile*. */
316       else if ((writtenmd5 = dynamic_cast<JigdoDesc::WrittenFileMD5*>(*i)) != 0) {
317         p = writtenmd5->serialize(buf);
318       } else if ((matchedmd5 = dynamic_cast<JigdoDesc::MatchedFileMD5*>(*i)) != 0) {
319         p = matchedmd5->serialize(buf);
320       }
321     } else /* CHECK_SHA256 */ {
322       if ((infosha = dynamic_cast<JigdoDesc::ImageInfoSHA256*>(*i)) != 0)
323         p = infosha->serialize(buf);
324       /* NB we must first try to cast to WrittenFile*, then to
325          MatchedFile*, because WrittenFileMD5 derives from MatchedFile*. */
326       else if ((writtensha = dynamic_cast<JigdoDesc::WrittenFileSHA256*>(*i)) != 0) {
327         p = writtensha->serialize(buf);
328       }
329       else if ((matchedsha = dynamic_cast<JigdoDesc::MatchedFileSHA256*>(*i)) != 0) {
330         p = matchedsha->serialize(buf);
331       }
332     }
333
334     writeBytes(file, buf, p - buf);
335     if (md != 0 && checksumChoice == MkTemplate::CHECK_MD5)
336       md->update(buf, p - buf);
337     if (sd != 0 && checksumChoice == MkTemplate::CHECK_SHA256)
338       sd->update(buf, p - buf);
339   }
340   p = serialize6(descLen, buf);
341   writeBytes(file, buf, 6);
342   if (md != 0)
343     md->update(buf, 6);
344   if (sd != 0)
345     sd->update(buf, p - buf);
346   if (DEBUG) { Assert(buf[bufLen - 1] == 0xa5); }
347     return file;
348 }
349 //______________________________________________________________________
350
351 namespace {
352   const int J_SIZE_WIDTH = 12;
353   const int J_CSUM_WIDTH = 46;
354   const int J_RSYNC_WIDTH = 12;
355 }
356
357 ostream& JigdoDesc::ImageInfoMD5::put(ostream& s) const {
358   s << "image-info-md5    "
359     << setw(J_SIZE_WIDTH) << size()
360     << " " << setw(J_SIZE_WIDTH) << blockLength()
361     << " " << setw(J_CSUM_WIDTH) << md5()
362     << "\n";
363   return s;
364 }
365 ostream& JigdoDesc::ImageInfoSHA256::put(ostream& s) const {
366   s << "image-info-sha256 "
367     << setw(J_SIZE_WIDTH) << size()
368     << " " << setw(J_SIZE_WIDTH) << blockLength()
369     << " " << setw(J_CSUM_WIDTH) << sha256()
370     << "\n";
371   return s;
372 }
373 ostream& JigdoDesc::UnmatchedData::put(ostream& s) const {
374   s << "in-template       "
375     << setw(J_SIZE_WIDTH) << offset()
376     << " " << setw(J_SIZE_WIDTH) << size()
377     << "\n";
378   return s;
379 }
380 ostream& JigdoDesc::MatchedFileMD5::put(ostream& s) const {
381   s << "need-file-md5     "
382     << setw(J_SIZE_WIDTH) << offset()
383     << " " << setw(J_SIZE_WIDTH) << size()
384     << " " << setw(J_CSUM_WIDTH) << md5()
385     << " " << setw(J_RSYNC_WIDTH) << rsync()
386     << "\n";
387   return s;
388 }
389 ostream& JigdoDesc::MatchedFileSHA256::put(ostream& s) const {
390   s << "need-file-sha256  "
391     << setw(J_SIZE_WIDTH) << offset()
392     << " " << setw(J_SIZE_WIDTH) << size()
393     << " " << setw(J_CSUM_WIDTH) << sha256()
394     << " " << setw(J_RSYNC_WIDTH) << rsync()
395     << "\n";
396   return s;
397 }
398
399 ostream& JigdoDesc::WrittenFileMD5::put(ostream& s) const {
400   s << "have-file        "
401     << setw(J_SIZE_WIDTH) << offset()
402     << " " << setw(J_SIZE_WIDTH) << size()
403     << " " << setw(J_CSUM_WIDTH) << md5()
404     << " " << setw(J_RSYNC_WIDTH) << rsync()
405     << "\n";
406   return s;
407 }
408 ostream& JigdoDesc::WrittenFileSHA256::put(ostream& s) const {
409   s << "have-file-sha256 "
410     << setw(J_SIZE_WIDTH) << offset()
411     << " " << setw(J_SIZE_WIDTH) << size()
412     << " " << setw(J_CSUM_WIDTH) << sha256()
413     << " " << setw(J_RSYNC_WIDTH) << rsync()
414     << "\n";
415   return s;
416 }
417
418 void JigdoDescVec::list(ostream& s) throw() {
419   for (const_iterator i = begin(), e = end(); i != e; ++i) s << (**i);
420   s << flush;
421 }
422 //______________________________________________________________________
423
424 namespace {
425
426   /* Helper functions for makeImage below, declared inline if only
427      used once */
428
429   /// Type of operation when recreating image data
430   enum Task {
431     CREATE_TMP, // Create a new .tmp file and copy some files into it,
432                 // maybe rename at end
433     MERGE_TMP, // .tmp exists, copy over more files, maybe rename at end
434     SINGLE_PASS // single-pass, all or nothing; writing to stdout
435   };
436   //______________________________
437
438   inline void reportBytesWritten(const uint64 n, uint64& off,
439       uint64& nextReport, const uint64 totalBytes,
440       ProgressReporter& reporter) {
441     off += n;
442     if (off >= nextReport) { // Keep user entertained
443       reporter.writingImage(off, totalBytes, off, totalBytes);
444       nextReport += REPORT_INTERVAL;
445     }
446   }
447   //______________________________
448
449   /* Read up to file.size() of bytes from file, write it to image
450      stream. Check MD5/rsync sum if requested. Take care not to write
451      more than specified amount to image, even if file is longer. */
452   int fileToImageMD5(bostream* img, FilePart& file,
453       const JigdoDesc::MatchedFileMD5& matched, bool checkChecksum, size_t rsyncLen,
454       ProgressReporter& reporter, byte* buf, size_t readAmount, uint64& off,
455       uint64& nextReport, const uint64 totalBytes) {
456     uint64 toWrite = file.size();
457     MD5Sum md;
458     RsyncSum64 rs;
459     size_t rl = 0; // Length covered by rs so far
460     string fileName(file.getPath());
461     fileName += file.leafName();
462     bifstream f(fileName.c_str(), ios::binary);
463     string err; // !err.empty() => error occurred
464
465     // Read from file, write to image
466     // First couple of k: Calculate RsyncSum rs and MD5Sum md
467     if (checkChecksum && rsyncLen > 0) {
468       while (*img && f && !f.eof() && toWrite > 0) {
469         size_t n = (toWrite < readAmount ? toWrite : readAmount);
470         readBytes(f, buf, n);
471         n = f.gcount();
472         writeBytes(*img, buf, n);
473         reportBytesWritten(n, off, nextReport, totalBytes, reporter);
474         toWrite -= n;
475         md.update(buf, n);
476         // Update RsyncSum
477         Paranoid(rl < rsyncLen);
478         size_t rsyncToAdd = rsyncLen - rl;
479         if (rsyncToAdd > n) rsyncToAdd = n;
480         rs.addBack(buf, rsyncToAdd);
481         rl += rsyncToAdd;
482         Paranoid(rl <= rsyncLen);
483         if (rl >= rsyncLen) break;
484       }
485     }
486     // Rest of file: Only calculate MD5Sum md
487     while (*img && f && !f.eof() && toWrite > 0) {
488       size_t n = (toWrite < readAmount ? toWrite : readAmount);
489       readBytes(f, buf, n);
490       n = f.gcount();
491       writeBytes(*img, buf, n);
492       reportBytesWritten(n, off, nextReport, totalBytes, reporter);
493       toWrite -= n;
494       if (checkChecksum) md.update(buf, n);
495     }
496
497     if (toWrite > 0 && (!f || f.eof())) {
498       const char* errDetail = "";
499       if (errno != 0) errDetail = strerror(errno);
500       else if (f.eof()) errDetail = _("file is too short");
501       err = subst(_("Error reading from `%1' (%2)"), fileName, errDetail);
502       // Even if there was an error - always try to write right amount
503       memClear(buf, readAmount);
504       while (*img && toWrite > 0) {
505         size_t n = (toWrite < readAmount ? toWrite : readAmount);
506         writeBytes(*img, buf, n);
507         reportBytesWritten(n, off, nextReport, totalBytes, reporter);
508         toWrite -= n;
509       }
510     } else if (checkChecksum
511                && (md.finish() != matched.md5()
512                    || (rsyncLen > 0 && rs != matched.rsync()))) {
513       err = subst(_("Error: `%1' does not match checksum in template data"),
514                   fileName);
515     }
516
517     if (err.empty()) return 0; // Success
518     reporter.error(err);
519     if (toWrite == 0)
520       return 2; // "May have to fix something before you can continue"
521     else
522       return 3; // Yaargh, disaster! Please delete the .tmp file for me
523   }
524   //______________________________
525
526   /* Read up to file.size() of bytes from file, write it to image
527      stream. Check SHA256/rsync sum if requested. Take care not to
528      write more than specified amount to image, even if file is
529      longer. */
530   int fileToImageSHA256(bostream* img, FilePart& file,
531       const JigdoDesc::MatchedFileSHA256& matched, bool checkChecksum, size_t rsyncLen,
532       ProgressReporter& reporter, byte* buf, size_t readAmount, uint64& off,
533       uint64& nextReport, const uint64 totalBytes) {
534     uint64 toWrite = file.size();
535     SHA256Sum md;
536     RsyncSum64 rs;
537     size_t rl = 0; // Length covered by rs so far
538     string fileName(file.getPath());
539     fileName += file.leafName();
540     bifstream f(fileName.c_str(), ios::binary);
541     string err; // !err.empty() => error occurred
542
543     // Read from file, write to image
544     // First couple of k: Calculate RsyncSum rs and SHA256Sum md
545     if (checkChecksum && rsyncLen > 0) {
546       while (*img && f && !f.eof() && toWrite > 0) {
547         size_t n = (toWrite < readAmount ? toWrite : readAmount);
548         readBytes(f, buf, n);
549         n = f.gcount();
550         writeBytes(*img, buf, n);
551         reportBytesWritten(n, off, nextReport, totalBytes, reporter);
552         toWrite -= n;
553         md.update(buf, n);
554         // Update RsyncSum
555         Paranoid(rl < rsyncLen);
556         size_t rsyncToAdd = rsyncLen - rl;
557         if (rsyncToAdd > n) rsyncToAdd = n;
558         rs.addBack(buf, rsyncToAdd);
559         rl += rsyncToAdd;
560         Paranoid(rl <= rsyncLen);
561         if (rl >= rsyncLen) break;
562       }
563     }
564     // Rest of file: Only calculate SHA256Sum md
565     while (*img && f && !f.eof() && toWrite > 0) {
566       size_t n = (toWrite < readAmount ? toWrite : readAmount);
567       readBytes(f, buf, n);
568       n = f.gcount();
569       writeBytes(*img, buf, n);
570       reportBytesWritten(n, off, nextReport, totalBytes, reporter);
571       toWrite -= n;
572       if (checkChecksum) md.update(buf, n);
573     }
574
575     if (toWrite > 0 && (!f || f.eof())) {
576       const char* errDetail = "";
577       if (errno != 0) errDetail = strerror(errno);
578       else if (f.eof()) errDetail = _("file is too short");
579       err = subst(_("Error reading from `%1' (%2)"), fileName, errDetail);
580       // Even if there was an error - always try to write right amount
581       memClear(buf, readAmount);
582       while (*img && toWrite > 0) {
583         size_t n = (toWrite < readAmount ? toWrite : readAmount);
584         writeBytes(*img, buf, n);
585         reportBytesWritten(n, off, nextReport, totalBytes, reporter);
586         toWrite -= n;
587       }
588     } else if (checkChecksum
589                && (md.finish() != matched.sha256()
590                    || (rsyncLen > 0 && rs != matched.rsync()))) {
591       err = subst(_("Error: `%1' does not match checksum in template data"),
592                   fileName);
593     }
594
595     if (err.empty()) return 0; // Success
596     reporter.error(err);
597     if (toWrite == 0)
598       return 2; // "May have to fix something before you can continue"
599     else
600       return 3; // Yaargh, disaster! Please delete the .tmp file for me
601   }
602   //______________________________
603
604   /* Write all bytes of the image data, i.e. both UnmatchedData and
605      MatchedFile*s. If any UnmatchedFiles are present in 'files', write
606      zeroes instead of the file content and also append a DESC section
607      after the actual data.
608
609      Why does this write zeroes, and not simply seek() forward the
610      appropriate amount of bytes? - Because when seek() is used, a
611      sparse file might be generated. This could result in "No room on
612      device" later on - but we'd rather like that error as early as
613      possible.
614
615      @param name Filename corresponding to img
616      @param totalBytes length of image
617
618      if img==0, write to cout. If 0 is returned and not writing to
619      cout, caller should rename file to remove .tmp extension. */
620   inline int writeAll(const Task& task, JigdoDescVec& files,
621       queue<FilePart*>& toCopy, bistream* templ, const size_t readAmount,
622       bostream* img, const char* name, bool checkChecksum,
623       ProgressReporter& reporter, JigdoCache* cache,
624       const uint64 totalBytes) {
625
626     bool isTemplate = JigdoDesc::isTemplate(*templ); // seek to 1st DATA part
627     Assert(isTemplate);
628     int result = 0;
629     uint64 off = 0; // Current offset in image
630     uint64 nextReport = 0; // At what value of off to call reporter
631     uint64 blockLength = 0;
632     uint64 imageSize = 0;
633
634     vector<byte> bufVec(readAmount);
635     byte* buf = &bufVec[0];
636     /* Use an additional 8k of zip buffer. This is good if the
637        unmatched image data is already compressed, which means that
638        when it is compressed again by jigdo, it will get slightly
639        larger. */
640     auto_ptr<Zibstream> data(new Zibstream(*templ, (unsigned int)readAmount + 8*1024));
641 #   if HAVE_WORKING_FSTREAM
642     if (img == 0) img = &cout; // EEEEEK!
643 #   else
644     if (img == 0) img = &bcout;
645 #   endif
646
647     /* First, find basic information about the image */
648     JigdoDesc::ImageInfoMD5* imageInfoMD5 =
649         dynamic_cast<JigdoDesc::ImageInfoMD5*>(files.back());
650     if (imageInfoMD5 != 0) {
651       blockLength = imageInfoMD5->blockLength();
652       imageSize = imageInfoMD5->size();
653     } else {
654       JigdoDesc::ImageInfoSHA256* imageInfoSHA256 =
655         dynamic_cast<JigdoDesc::ImageInfoSHA256*>(files.back());
656       if (imageInfoSHA256) {
657         blockLength = imageInfoSHA256->blockLength();
658         imageSize = imageInfoSHA256->size();
659       }
660     }
661     if (blockLength == 0 || imageSize == 0) {
662       reporter.error(_("Unable to find a valid image info block"));
663       return 3;
664     }
665
666     try {
667       for (JigdoDescVec::iterator i = files.begin(), e = files.end();
668            i != e; ++i) {
669         //____________________
670
671         /* Write all data for this part to img stream. In case of
672            MatchedFile*, write the appropriate number of bytes (of junk
673            data) even if file not present. [Using switch(type()) not
674            nice, but using virtual methods looks even worse.] */
675         switch ((*i)->type()) {
676           case JigdoDesc::IMAGE_INFO_MD5:
677           case JigdoDesc::IMAGE_INFO_SHA256:
678             break;
679           case JigdoDesc::UNMATCHED_DATA: {
680             // Copy data from Zibstream to image.
681             JigdoDesc::UnmatchedData& self =
682                 dynamic_cast<JigdoDesc::UnmatchedData&>(**i);
683             uint64 toWrite = self.size();
684             debug("mkimage writeAll(): %1 of unmatched data", toWrite);
685             memClear(buf, readAmount);
686             while (*img && toWrite > 0) {
687               if (!*data) {
688                 reporter.error(_("Premature end of template data"));
689                 return 3;
690               }
691               data->read(buf, (unsigned int)(toWrite < readAmount ? toWrite : readAmount));
692               size_t n = data->gcount();
693               writeBytes(*img, buf, n);
694               reportBytesWritten(n, off, nextReport, totalBytes, reporter);
695               toWrite -= n;
696             }
697             break;
698           }
699           case JigdoDesc::MATCHED_FILE_MD5: {
700             /* If file present in cache, copy its data to image, if
701                not, copy zeroes. if check==true, verify MD sum match.
702                If successful, turn MatchedFileMD5 into WrittenFileMD5. */
703             JigdoDesc::MatchedFileMD5* self =
704                 dynamic_cast<JigdoDesc::MatchedFileMD5*>(*i);
705             uint64 toWrite = self->size();
706             FilePart* mfile = 0;
707             if (!toCopy.empty()) mfile = toCopy.front();
708             debug("mkimage writeAll(): FilePart@%1, %2 of matched file `%3',"
709                   " toCopy size %4", mfile, toWrite,
710                   (mfile != 0 ? mfile->leafName() : ""), toCopy.size());
711             if (mfile == 0 || self->md5() != *(mfile->getMD5Sum(cache))) {
712               // Write right amount of zeroes
713               memClear(buf, readAmount);
714               while (*img && toWrite > 0) {
715                 size_t n = (toWrite < readAmount ? toWrite : readAmount);
716                 writeBytes(*img, buf, n);
717                 reportBytesWritten(n, off, nextReport, totalBytes, reporter);
718                 toWrite -= n;
719               }
720               if (result == 0) result = 1; // Soft failure
721             } else {
722               /* Copy data from file to image, taking care not to
723                  write beyond toWrite. */
724               int status = fileToImageMD5(img, *mfile, *self, checkChecksum,
725                   blockLength, reporter, buf, readAmount, off,
726                   nextReport, totalBytes);
727               toCopy.pop();
728               if (result < status) result = status;
729               if (status == 0) { // Mark file as written to image
730                 *i = new JigdoDesc::WrittenFileMD5(self->offset(), self->size(),
731                                                 self->rsync(), self->md5());
732                 delete self;
733               } else if (*img && (status > 2 || task == SINGLE_PASS)) {
734                 // If !*img, exit after error msg below
735                 /* If status <= 2 and task == {CREATE_TMP,MERGE_TMP},
736                    we can continue; there has been an error copying
737                    this individual file, but the right *amount* of
738                    data has been written to the .tmp output file, and
739                    the user may retry the failed one later. */
740                 return result;
741               }
742             }
743             break;
744           }
745           case JigdoDesc::MATCHED_FILE_SHA256: {
746             /* If file present in cache, copy its data to image, if
747                not, copy zeroes. if check==true, verify SHA256 sum match.
748                If successful, turn MatchedFileSHA256 into WrittenFileSHA256. */
749             JigdoDesc::MatchedFileSHA256* self =
750                 dynamic_cast<JigdoDesc::MatchedFileSHA256*>(*i);
751             uint64 toWrite = self->size();
752             FilePart* mfile = 0;
753             if (!toCopy.empty()) mfile = toCopy.front();
754             debug("mkimage writeAll(): FilePart@%1, %2 of matched file `%3',"
755                   " toCopy size %4", mfile, toWrite,
756                   (mfile != 0 ? mfile->leafName() : ""), toCopy.size());
757             if (mfile == 0 || self->sha256() != *(mfile->getSHA256Sum(cache))) {
758               // Write right amount of zeroes
759               memClear(buf, readAmount);
760               while (*img && toWrite > 0) {
761                 size_t n = (toWrite < readAmount ? toWrite : readAmount);
762                 writeBytes(*img, buf, n);
763                 reportBytesWritten(n, off, nextReport, totalBytes, reporter);
764                 toWrite -= n;
765               }
766               if (result == 0) result = 1; // Soft failure
767             } else {
768               /* Copy data from file to image, taking care not to
769                  write beyond toWrite. */
770               int status = fileToImageSHA256(img, *mfile, *self, checkChecksum,
771                   blockLength, reporter, buf, readAmount, off,
772                   nextReport, totalBytes);
773               toCopy.pop();
774               if (result < status) result = status;
775               if (status == 0) { // Mark file as written to image
776                 *i = new JigdoDesc::WrittenFileSHA256(self->offset(), self->size(),
777                                                       self->rsync(), self->sha256());
778                 delete self;
779               } else if (*img && (status > 2 || task == SINGLE_PASS)) {
780                 // If !*img, exit after error msg below
781                 /* If status <= 2 and task == {CREATE_TMP,MERGE_TMP},
782                    we can continue; there has been an error copying
783                    this individual file, but the right *amount* of
784                    data has been written to the .tmp output file, and
785                    the user may retry the failed one later. */
786                 return result;
787               }
788             }
789             break;
790           }
791           case JigdoDesc::WRITTEN_FILE_MD5:
792           case JigdoDesc::WRITTEN_FILE_SHA256:
793           // These are never present in memory, cannot occur:
794           case JigdoDesc::OBSOLETE_IMAGE_INFO:
795           case JigdoDesc::OBSOLETE_MATCHED_FILE:
796           case JigdoDesc::OBSOLETE_WRITTEN_FILE:
797             debug("mkimage writeAll(): invalid type %1", (*i)->type());
798             reporter.error(
799                 _("Error - template data's DESC section invalid"));
800             Assert(false); // A WrittenFile* cannot occur here
801             return 3;
802             break;
803         }
804         //____________________
805
806         // Error while writing to image?
807         if (!*img) {
808           string err = subst(_("Error while writing to `%1' (%2)"),
809                              name, strerror(errno));
810           reporter.error(err);
811           return 3;
812         }
813         //____________________
814
815       } // end iterating over 'files'
816
817     } catch (Zerror e) {
818       // Error while unpacking template data
819       reporter.error(e.message); return 3;
820     }
821
822     // If we created a new tmp file, append DESC info
823     if (task == CREATE_TMP && result > 0) {
824       *img << files;
825       if (!*img) return 3;
826     }
827     // Must have "used up" all the parts that we found earlier
828     Assert(toCopy.empty());
829     return result; // 0 or 1
830   }
831   //______________________________
832
833   /* A temporary file already exists. Write the files listed in toCopy
834      to this temporary file. If image is now completed, truncate it to
835      its final length (removing the DESC section at the end),
836      otherwise update the DESC section (turn some need-file/
837      MatchedFile* entries into have-file/WrittenFile* entries). If 0 is
838      returned, caller should rename file to remove .tmp extension. */
839   inline int writeMerge(JigdoDescVec& files, queue<FilePart*>& toCopy,
840       const int missing, const size_t readAmount, bfstream* img,
841       const string& imageTmpFile, bool checkChecksum, ProgressReporter& reporter,
842       JigdoCache* cache, const uint64 totalBytes) {
843     vector<byte> bufVec(readAmount);
844     byte* buf = &bufVec[0];
845     int result = (missing == 0 ? 0 : 1);
846     uint64 bytesWritten = 0; // For 'x% done' calls to reporter
847     uint64 nextReport = 0; // At what value of bytesWritten to call reporter
848     uint64 blockLength = 0;
849     uint64 imageSize = 0;
850
851     /* First, find basic information about the image */
852     JigdoDesc::ImageInfoMD5* imageInfoMD5 =
853         dynamic_cast<JigdoDesc::ImageInfoMD5*>(files.back());
854     if (imageInfoMD5) {
855       blockLength = imageInfoMD5->blockLength();
856       imageSize = imageInfoMD5->size();
857     } else {
858       JigdoDesc::ImageInfoSHA256* imageInfoSHA256 =
859         dynamic_cast<JigdoDesc::ImageInfoSHA256*>(files.back());
860       if (imageInfoSHA256) {
861         blockLength = imageInfoSHA256->blockLength();
862         imageSize = imageInfoSHA256->size();
863       }
864     }
865     if (blockLength == 0 || imageSize == 0) {
866       reporter.error(_("Unable to find a valid image info block"));
867       return 3;
868     }
869
870     if (toCopy.empty() && missing > 0) return 1;
871     for (JigdoDescVec::iterator i = files.begin(), e = files.end();
872          i != e; ++i) {
873
874       JigdoDesc::MatchedFileMD5* matchMD5 =
875         dynamic_cast<JigdoDesc::MatchedFileMD5*>(*i);
876       JigdoDesc::MatchedFileSHA256* matchSHA256 =
877         dynamic_cast<JigdoDesc::MatchedFileSHA256*>(*i);
878
879       // Compare to 'case JigdoDesc::MATCHED_FILE_MD5:' clause in writeAll()
880       if (matchMD5 != 0) {
881         FilePart* mfile = 0;
882         if (!toCopy.empty()) mfile = toCopy.front();
883         debug("mkimage writeMerge(): FilePart@%1, %2 of matched file `%3', "
884               "toCopy size %4", mfile, matchMD5->size(),
885               (mfile != 0 ? mfile->leafName() : ""), toCopy.size());
886         if (mfile == 0 || matchMD5->md5() != *(mfile->getMD5Sum(cache)))
887           continue;
888
889         /* Copy data from file to image, taking care not to write
890            beyond matchMD5->size(). */
891         img->seekp(matchMD5->offset(), ios::beg);
892         if (!*img) {
893           reporter.error(_("Error - could not access temporary file"));
894           result = 2;
895           break;
896         }
897         int status = fileToImageMD5(img, *mfile, *matchMD5, checkChecksum,
898             blockLength, reporter, buf, readAmount, bytesWritten,
899             nextReport, totalBytes);
900         toCopy.pop();
901         if (result < status)
902           result = status;
903         if (status == 0) { // Mark file as written to image
904           *i = new JigdoDesc::WrittenFileMD5(matchMD5->offset(),
905                                              matchMD5->size(),
906                                              matchMD5->rsync(),
907                                              matchMD5->md5());
908           delete matchMD5;
909         } else if (status > 2) {
910           break;
911         }
912       } else if (matchSHA256 != 0) {
913         FilePart* mfile = 0;
914         if (!toCopy.empty()) mfile = toCopy.front();
915         debug("mkimage writeMerge(): FilePart@%1, %2 of matched file `%3', "
916               "toCopy size %4", mfile, matchSHA256->size(),
917               (mfile != 0 ? mfile->leafName() : ""), toCopy.size());
918         if (mfile == 0 || matchSHA256->sha256() != *(mfile->getSHA256Sum(cache)))
919           continue;
920
921         /* Copy data from file to image, taking care not to write
922            beyond matchSHA256->size(). */
923         img->seekp(matchSHA256->offset(), ios::beg);
924         if (!*img) {
925           reporter.error(_("Error - could not access temporary file"));
926           result = 2;
927           break;
928         }
929         int status = fileToImageSHA256(img, *mfile, *matchSHA256, checkChecksum,
930             blockLength, reporter, buf, readAmount, bytesWritten,
931             nextReport, totalBytes);
932         toCopy.pop();
933         if (result < status)
934           result = status;
935         if (status == 0) { // Mark file as written to image
936           *i = new JigdoDesc::WrittenFileSHA256(matchSHA256->offset(),
937                                                 matchSHA256->size(),
938                                                 matchSHA256->rsync(),
939                                                 matchSHA256->sha256());
940           delete matchSHA256;
941         } else if (status > 2) {
942           break;
943         }
944       } else
945         /* no match for either matchMD5 or matchSHA256 */
946         continue;
947     } // end iterating over 'files'
948
949     if (missing == 0 && result == 0) {
950       img->close(); // Necessary on Windows before truncating is possible
951       // Truncate to final image size
952       const char* tmpName = imageTmpFile.c_str();
953       if (compat_truncate(tmpName, imageSize) != 0) {
954         string err = subst(_("Could not truncate `%1' (%2)"),
955                            imageTmpFile, strerror(errno));
956         reporter.error(err);
957         return 3;
958       }
959       return 0;
960     } else {
961       // Update DESC section at end of temporary file
962       img->seekp(imageSize);
963       // No need to truncate here because DESC section never changes size
964       *img << files;
965       if (!*img) return 3;
966       return result;
967     }
968   }
969   //______________________________
970
971   int info_NeedMoreFiles(ProgressReporter& reporter, const string& tmpName) {
972     string info = subst(_(
973           "Copied input files to temporary file `%1' - "
974           "repeat command and supply more files to continue"), tmpName);
975     reporter.info(info);
976     return 1; // Soft failure
977   }
978
979   int error_CouldntRename(ProgressReporter& reporter, const char* name,
980                           const char* finalName) {
981     string err = subst(_(
982         "Could not move finished image from `%1' to `%2' (%3)"),
983         name, finalName, strerror(errno));
984     reporter.error(err);
985     return 3;
986   }
987
988 } // end local namespace
989 //______________________________________________________________________
990
991 namespace {
992
993   /// Read template data from templ (name in templFile) into files
994   void readTemplate(JigdoDescVec& files, const string& templFile,
995                     bistream* templ) {
996     if (JigdoDesc::isTemplate(*templ) == false) { // Check for template hdr
997       string err = subst(_("`%1' is not a template file"), templFile);
998       throw JigdoDescError(err);
999     }
1000     /* Read info at end of template data. NB: Exceptions are not
1001        caught here, but e.g. in ::makeImage() (cf. jigdo-file.cc) */
1002     JigdoDesc::seekFromEnd(*templ);
1003     *templ >> files;
1004   }
1005   //________________________________________
1006
1007   /** Read data from end of temporary file imageTmp, output it to
1008       filesTmp. Next, compare it to template data in "files". If tmp
1009       file is OK for re-using return NULL - this means that the DESC
1010       entries match *exactly* - the only difference allowed is
1011       MatchedFile* turning into WrittenFile*. Otherwise, return a
1012       pointer to an error message describing the reason why the
1013       tmpfile data does not match the template data. */
1014   const char* readTmpFile(bistream& imageTmp, JigdoDescVec& filesTmp,
1015                           const JigdoDescVec& files) {
1016     try {
1017       JigdoDesc::seekFromEnd(imageTmp);
1018       imageTmp >> filesTmp;
1019     } catch (JigdoDescError e) {
1020       return _("it was not created by jigdo-file, or is corrupted.");
1021     }
1022     if (*files.back() != *filesTmp.back())
1023       return _("it corresponds to a different image/template.");
1024     if (files.size() != filesTmp.size())
1025       return _("since its creation, the template was regenerated.");
1026     for (size_t i = 0; i < files.size() - 1; ++i) {
1027       //cerr << "cmp " << i << '/' << (files.size() - 1) << endl;
1028       if (*files[i] != *filesTmp[i])
1029         return _("since its creation, the template was regenerated.");
1030     }
1031     return 0;
1032   }
1033
1034 }
1035 //________________________________________
1036
1037 /* If imageTmpFile.empty(), must either write whole image or nothing
1038    at all. image and temporary file are created as needed, ditto for
1039    renaming of temporary to image. The cache must not throw errors. */
1040 int JigdoDesc::makeImage(JigdoCache* cache, const string& imageFile,
1041     const string& imageTmpFile, const string& templFile,
1042     bistream* templ, const bool optForce, ProgressReporter& reporter,
1043     const size_t readAmount, const bool optMkImageCheck) {
1044
1045   Task task = CREATE_TMP;
1046
1047   if (imageFile == "-" || imageTmpFile.empty()) task = SINGLE_PASS;
1048   //____________________
1049
1050   // Read info from template
1051   JigdoDescVec files;
1052   readTemplate(files, templFile, templ);
1053   //____________________
1054
1055   // Do we need to add new stuff to an existing tmp file?
1056   bfstream* img = 0; // Non-null => tmp file exists
1057   auto_ptr<bfstream> imgDel(img);
1058   struct stat fileInfo;
1059   if (task != SINGLE_PASS && stat(imageTmpFile.c_str(), &fileInfo) == 0) {
1060     /* A tmp file already exists. We'll only reuse it if the DESC
1061        entries match exactly. Otherwise, if --force enabled, overwrite
1062        it, else error. */
1063     const char* wontReuse = 0; // non-NULL means: will not reuse tmp file
1064     JigdoDescVec filesTmp;
1065     imgDel.reset(new bfstream(imageTmpFile.c_str(),
1066                               ios::binary|ios::in|ios::out));
1067     img = imgDel.get();
1068     if (!*img)
1069       wontReuse = strerror(errno);
1070     else
1071       wontReuse = readTmpFile(*img, filesTmp, files);
1072
1073     if (wontReuse != 0) {
1074       // Print out message
1075       string msg = subst(_("Will not reuse existing temporary file `%1' - "
1076                            "%2"), imageTmpFile, wontReuse);
1077       // Return error if not allowed to overwrite tmp file
1078       if (!optForce) {
1079         reporter.error(msg);
1080         throw Error(_("Delete/rename the file or use --force"));
1081       }
1082       // Open a new tmp file later (imgDel will close() this one for us)
1083       reporter.info(msg);
1084       img = 0;
1085       Paranoid(task == CREATE_TMP);
1086     } else {
1087       // Reuse temporary file
1088       task = MERGE_TMP;
1089       files.swap(filesTmp);
1090       Assert(!filesTmp.empty() && img != 0);
1091     }
1092   } // endif (tmp file exists)
1093
1094   Paranoid((task == MERGE_TMP) == (img != 0));
1095   //____________________
1096
1097   /* Variables now in use:
1098      enum task:
1099              Mode of operation (CREATE_TMP/MERGE_TMP/SINGLE_PASS)
1100      JigdoDescVec files:
1101              Contents of image, maybe with some WrittenFile*s if MERGEing
1102      istream* templ: Template data (stream pointer at end of file)
1103      fstream* img: Temporary file if MERGE_TMP, else null
1104   */
1105
1106   /* Create queue of files that need to be copied to the image. Later
1107      on, we will be pop()ing to get to the actual filenames in order.
1108      Referenced FileParts are owned by the JigdoCache - never delete
1109      them. */
1110   queue<FilePart*> toCopy;
1111   int missing = 0; // Nr of files that were not found
1112   JigdoCache::iterator ci, ce = cache->end();
1113   uint64 totalBytes = 0; // Total amount of data to be written, for "x% done"
1114
1115   for (vector<JigdoDesc*>::iterator i = files.begin(), e = files.end();
1116        i != e; ++i) {
1117     // Need this extra test because we do *not* want the WrittenFile*s
1118     switch ((*i)->type()) {
1119
1120       case MATCHED_FILE_MD5:
1121       {
1122         MatchedFileMD5* m = dynamic_cast<MatchedFileMD5*>(*i);
1123         Paranoid(m != 0);
1124         //totalBytes += m->size();
1125
1126         // Search for file with matching MD5 sum
1127         ci = cache->begin();
1128         bool found = false;
1129         while (ci != ce) {
1130           // The call to getMD5Sum() may cause the whole file to be read!
1131           const MD5Sum* md = ci->getMD5Sum(cache);
1132           if (md != 0 && *md == m->md5()) {
1133             toCopy.push(&*ci); // Found matching file
1134             totalBytes += m->size();
1135             debug("%1 found, pushed %2", m->md5().toString(), &*ci);
1136             found = true;
1137             break;
1138           }
1139           ++ci;
1140         }
1141         if (!found)
1142           ++missing;
1143       }
1144       break;
1145
1146       case MATCHED_FILE_SHA256:
1147       {
1148         MatchedFileSHA256* m = dynamic_cast<MatchedFileSHA256*>(*i);
1149         Paranoid(m != 0);
1150         //totalBytes += m->size();
1151
1152         // Search for file with matching SHA256 sum
1153         ci = cache->begin();
1154         bool found = false;
1155         while (ci != ce) {
1156           // The call to getSHA256Sum() may cause the whole file to be read!
1157           const SHA256Sum* md = ci->getSHA256Sum(cache);
1158           if (md != 0 && *md == m->sha256()) {
1159             toCopy.push(&*ci); // Found matching file
1160             totalBytes += m->size();
1161             debug("%1 found, pushed %2", m->sha256().toString(), &*ci);
1162             found = true;
1163             break;
1164           }
1165           ++ci;
1166         }
1167         if (!found)
1168           ++missing;
1169       }
1170       break;
1171
1172       default:
1173         continue;
1174         break;
1175
1176     }
1177   }
1178   //____________________
1179
1180   debug("JigdoDesc::mkImage: %1 missing, %2 found for copying to image, "
1181         "%3 entries in template", missing, toCopy.size(), files.size());
1182
1183   // Files appearing >1 times are counted >1 times for the message
1184   string missingInfo = subst(
1185       _("Found %1 of the %2 files required by the template"),
1186       toCopy.size(), toCopy.size() + missing);
1187   reporter.info(missingInfo);
1188   //____________________
1189
1190   /* There used to be the following here:
1191      | If possible (i.e. all files present, tmp file not yet created),
1192      | avoid creating any tmp file at all.
1193      | if (task == CREATE_TMP && missing == 0) task = SINGLE_PASS;
1194      We do not do this because even though it says "missing==0" *now*,
1195      there could be a read error from one of the files when we
1196      actually access it, in which case we should be able to ignore the
1197      error for the moment, and leave behind a partially complete .tmp
1198      file. */
1199
1200   /* Do nothing at all if a) no tmp file created yet, and b) *none* of
1201      the supplied files matched one of the missing parts, and c) the
1202      template actually contains at least one MatchedFile* (i.e. *do*
1203      write if template consists entirely of UnmatchedData). */
1204 # ifndef MKIMAGE_ALWAYS_CREATE_TMPFILE
1205   if (task == CREATE_TMP && toCopy.size() == 0 && missing != 0) {
1206     const char* m = _("Will not create image or temporary file - try again "
1207                       "with different input files");
1208     reporter.info(m);
1209     return 1; // Return value: "Soft failure - may retry with more files"
1210   }
1211
1212   // Give error if unable to create image in one pass
1213   if (task == SINGLE_PASS && missing > 0) {
1214     reporter.error(_("Cannot create image because of missing files"));
1215     return 3; // Permanent failure
1216   }
1217 # endif
1218   //____________________
1219
1220   if (task == MERGE_TMP) { // If MERGEing, img was already set up above
1221     int result = writeMerge(files, toCopy, missing, readAmount, img,
1222                             imageTmpFile, optMkImageCheck, reporter, cache,
1223                             totalBytes);
1224     if (missing != 0 && result < 3)
1225       info_NeedMoreFiles(reporter, imageTmpFile);
1226     if (result == 0) {
1227       if (compat_rename(imageTmpFile.c_str(), imageFile.c_str()) != 0)
1228         return error_CouldntRename(reporter, imageTmpFile.c_str(),
1229                                    imageFile.c_str());
1230       string info = subst(_("Successfully created `%1'"), imageFile);
1231       reporter.info(info);
1232     }
1233     return result;
1234   }
1235
1236   // task == CREATE_TMP || task == SINGLE_PASS
1237
1238   // Assign a stream to img which we're going to write image data to
1239   // If necessary, create a new temporary/output file
1240   const char* name;
1241   const char* finalName = 0;
1242   if (task == CREATE_TMP) { // CREATE new tmp file
1243     name = imageTmpFile.c_str();
1244     finalName = imageFile.c_str();
1245     imgDel.reset(new bfstream(name, ios::binary|ios::out|ios::trunc));
1246     img = imgDel.get();
1247   } else if (imageFile != "-") { // SINGLE_PASS; create output file
1248     name = imageFile.c_str();
1249     imgDel.reset(new bfstream(name, ios::binary|ios::out|ios::trunc));
1250     img = imgDel.get();
1251   } else { // SINGLE_PASS, outputting to stdout
1252     name = "-";
1253     imgDel.reset(0);
1254     img = 0; // Cannot do "img = &cout", so img==0 is special case: stdout
1255   }
1256   if (img != 0 && !*img) {
1257     string err = subst(_("Could not open `%1' for output: %2"),
1258                        name, strerror(errno));
1259     reporter.error(err);
1260     return 3; // Permanent failure
1261   }
1262
1263   /* Above, totalBytes was calculated for the case of a MERGE_TMP. If
1264      we're not merging, we need to write everything. */
1265   Assert(files.back()->type() == IMAGE_INFO_MD5 ||
1266          files.back()->type() == IMAGE_INFO_SHA256);
1267   uint64 imageSize = files.back()->size();
1268   totalBytes = imageSize;
1269 # if 0 /* # if WINDOWS */
1270   /* The C++ STL of the MinGW 1.1 gcc port for Windows doesn't support
1271      files >2GB. Fail early and with a clear error message... */
1272   if (imageSize >= (1U<<31))
1273     throw Error(_("Sorry, at the moment the Windows port of jigdo cannot "
1274                   "create files bigger than 2 GB. Use the Linux version."));
1275 # endif
1276
1277   int result = writeAll(task, files, toCopy, templ, readAmount, img, name,
1278                         optMkImageCheck, reporter, cache, totalBytes);
1279   if (result >= 3) return result;
1280
1281   if (task == CREATE_TMP && result == 1) {
1282     info_NeedMoreFiles(reporter, imageTmpFile);
1283   } else if (result == 0) {
1284     if (img != 0)
1285       img->close(); // Necessary on Windows before renaming is possible
1286     if (finalName != 0 && compat_rename(name, finalName) != 0)
1287       return error_CouldntRename(reporter, name, finalName);
1288     string info = subst(_("Successfully created `%1'"), imageFile);
1289     reporter.info(info);
1290   }
1291   return result;
1292 }
1293 //______________________________________________________________________
1294
1295 int JigdoDesc::listMissingMD5(set<MD5>& result, const string& imageTmpFile,
1296     const string& templFile, bistream* templ, ProgressReporter& reporter) {
1297   result.clear();
1298
1299   // Read info from template
1300   JigdoDescVec contents;
1301   readTemplate(contents, templFile, templ);
1302
1303   // Read info from temporary file, if any
1304   if (!imageTmpFile.empty()) {
1305     bifstream imageTmp(imageTmpFile.c_str(), ios::binary);
1306     if (imageTmp) {
1307       JigdoDescVec contentsTmp;
1308       const char* wontReuse = readTmpFile(imageTmp, contentsTmp, contents);
1309       if (wontReuse != 0) {
1310         string msg = subst(_("Ignoring existing temporary file `%1' - %2"),
1311                            imageTmpFile, wontReuse);
1312         reporter.info(msg);
1313       } else {
1314         // tmp file present & valid - use *it* below to output missing parts
1315         swap(contents, contentsTmp);
1316       }
1317     }
1318   }
1319
1320   // Output MD5 sums of MatchedFileMD5 (but not WrittenFileMD5) entries
1321   for (size_t i = 0; i < contents.size() - 1; ++i) {
1322     MatchedFileMD5* mf = dynamic_cast<MatchedFileMD5*>(contents[i]);
1323     if (mf != 0 && mf->type() == MATCHED_FILE_MD5)
1324       result.insert(mf->md5());
1325   }
1326   return 0;
1327 }
1328 //______________________________________________________________________
1329
1330 int JigdoDesc::listMissingSHA256(set<SHA256>& result, const string& imageTmpFile,
1331     const string& templFile, bistream* templ, ProgressReporter& reporter) {
1332   result.clear();
1333
1334   // Read info from template
1335   JigdoDescVec contents;
1336   readTemplate(contents, templFile, templ);
1337
1338   // Read info from temporary file, if any
1339   if (!imageTmpFile.empty()) {
1340     bifstream imageTmp(imageTmpFile.c_str(), ios::binary);
1341     if (imageTmp) {
1342       JigdoDescVec contentsTmp;
1343       const char* wontReuse = readTmpFile(imageTmp, contentsTmp, contents);
1344       if (wontReuse != 0) {
1345         string msg = subst(_("Ignoring existing temporary file `%1' - %2"),
1346                            imageTmpFile, wontReuse);
1347         reporter.info(msg);
1348       } else {
1349         // tmp file present & valid - use *it* below to output missing parts
1350         swap(contents, contentsTmp);
1351       }
1352     }
1353   }
1354
1355   // Output SHA256 sums of MatchedFileSHA256 (but not WrittenFileSHA256) entries
1356   for (size_t i = 0; i < contents.size() - 1; ++i) {
1357     MatchedFileSHA256* mf = dynamic_cast<MatchedFileSHA256*>(contents[i]);
1358     if (mf != 0 && mf->type() == MATCHED_FILE_SHA256)
1359       result.insert(mf->sha256());
1360   }
1361   return 0;
1362 }
1363 //______________________________________________________________________
1364
1365 void JigdoDesc::ProgressReporter::error(const string& message) {
1366   cerr << message << endl;
1367 }
1368 void JigdoDesc::ProgressReporter::info(const string& message) {
1369   cerr << message << endl;
1370 }
1371 void JigdoDesc::ProgressReporter::writingImage(uint64, uint64, uint64,
1372                                               uint64) { }
1373
1374 JigdoDesc::ProgressReporter JigdoDesc::noReport;