This is the mail archive of the cygwin-apps-cvs mailing list for the cygwin-apps project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[setup - the official Cygwin setup program] branch master, updated. release_2.884-1-gff2cd3f




https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=ff2cd3f4d71cefc1a68d6454cb27115e88acdaea

commit ff2cd3f4d71cefc1a68d6454cb27115e88acdaea
Author: Ken Brown <kbrown@cornell.edu>
Date:   Tue Jan 9 12:04:20 2018 -0500

    Query the user if a corrupt local file is found
    
    Also reorganize package validation.
    
    Move the size-validation code in download.cc and the hash-validation
    code in install.cc into new member functions of the packagesource
    class.  Add a bool member 'validated' to the class to make sure that
    the checking is done only once.
    
    Change download.cc:check_for_cached() so that it offers to delete a
    corrupt package file instead of throwing an exception.  The latter
    previously caused a fatal error when check_for_cached() was called
    from do_download_thread and download_one.  Now we get a fatal error
    only if the user chooses not to delete the file.
    
    Also make check_for_cached() check the hash of the file in addition to
    the size.  Similarly, check the hash in addition to the size after
    downloading a file.


Diff:
---
 download.cc       |  122 +++++++++++++++++++++++++---------------
 download.h        |    5 +-
 install.cc        |  141 ++---------------------------------------------
 package_meta.cc   |    2 +-
 package_source.cc |  159 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 package_source.h  |   17 +++++-
 res.rc            |    3 +-
 resource.h        |    1 +
 8 files changed, 264 insertions(+), 186 deletions(-)

diff --git a/download.cc b/download.cc
index 322a4c1..129b226 100644
--- a/download.cc
+++ b/download.cc
@@ -54,29 +54,41 @@ extern ThreeBarProgressPage Progress;
 
 BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed");
 
+// Return true if selected checks pass, false if they don't and the
+// user chooses to delete the file; otherwise throw an exception.
 static bool
-validateCachedPackage (const std::string& fullname, packagesource & pkgsource)
+validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
+		       HWND owner, bool check_hash, bool check_size)
 {
-  DWORD size = get_file_size(fullname);
-  if (size != pkgsource.size)
-  {
-    Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
-      << " - Size mismatch: Ini-file: " << pkgsource.size
-      << " != On-disk: " << size << endLog;
-    return false;
-  }
-  return true;
+  try
+    {
+      if (check_size)
+	pkgsource.check_size_and_cache (fullname);
+      if (check_hash)
+	pkgsource.check_hash ();
+      return true;
+    }
+  catch (Exception *e)
+    {
+      pkgsource.set_cached ("");
+      const char *filename = fullname.c_str ();
+      if (strncmp (filename, "file://", 7) == 0)
+	filename += 7;
+      if (e->errNo() == APPERR_CORRUPT_PACKAGE
+	  && yesno (owner, IDS_QUERY_CORRUPT, filename) == IDYES)
+	remove (filename);
+      else
+	throw e;
+    }
+  return false;
 }
 
-/* 0 on failure
+/* 0 if not cached; may throw exception if validation fails.
  */
 int
-check_for_cached (packagesource & pkgsource, bool mirror_mode)
+check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
+		  bool check_hash)
 {
-  // Already found one.
-  if (pkgsource.Cached())
-    return 1;
-
   /* Note that the cache dir is represented by a mirror site of file://local_dir */
   std::string prefix = "file://" + local_dir + "/";
   std::string fullname = prefix + (pkgsource.Canonical() ? pkgsource.Canonical() : "");
@@ -84,22 +96,31 @@ check_for_cached (packagesource & pkgsource, bool mirror_mode)
   if (mirror_mode)
     {
       /* Just assume correctness of mirror. */
-      pkgsource.set_cached (fullname);
+      if (!pkgsource.Cached())
+	pkgsource.set_cached (fullname);
       return 1;
     }
 
+  // Already found one, which we can assume to have the right size.
+  if (pkgsource.Cached())
+    {
+      if (validateCachedPackage (pkgsource.Cached(), pkgsource, owner,
+				 check_hash, false))
+	return 1;
+      // If we get here, pkgsource.Cached() was corrupt and deleted.
+      pkgsource.set_cached ("");
+    }
+
   /*
      1) is there a legacy version in the cache dir available.
   */
   if (io_stream::exists (fullname))
     {
-      if (validateCachedPackage (fullname, pkgsource))
-        pkgsource.set_cached (fullname);
-      else
-        throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-            "Package validation failure for " + fullname,
-            APPERR_CORRUPT_PACKAGE);
-      return 1;
+      if (validateCachedPackage (fullname, pkgsource, owner, check_hash, true))
+	return 1;
+      // If we get here, fullname was corrupt and deleted, but it
+      // might have been cached.
+      pkgsource.set_cached ("");
     }
 
   /*
@@ -111,15 +132,14 @@ check_for_cached (packagesource & pkgsource, bool mirror_mode)
     std::string fullname = prefix + rfc1738_escape_part (n->key) + "/" +
       pkgsource.Canonical ();
     if (io_stream::exists(fullname))
-    {
-      if (validateCachedPackage (fullname, pkgsource))
-        pkgsource.set_cached (fullname);
-      else
-        throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-            "Package validation failure for " + fullname,
-            APPERR_CORRUPT_PACKAGE);
-      return 1;
-    }
+	{
+	  if (validateCachedPackage (fullname, pkgsource, owner, check_hash,
+				     true))
+	    return 1;
+	  // If we get here, fullname was corrupt and deleted, but it
+	  // might have been cached.
+	  pkgsource.set_cached ("");
+	}
   }
   return 0;
 }
@@ -130,7 +150,7 @@ download_one (packagesource & pkgsource, HWND owner)
 {
   try
     {
-      if (check_for_cached (pkgsource))
+      if (check_for_cached (pkgsource, owner))
         return 0;
     }
   catch (Exception * e)
@@ -163,26 +183,36 @@ download_one (packagesource & pkgsource, HWND owner)
 	}
       else
 	{
-	  size_t size = get_file_size ("file://" + local + ".tmp");
-	  if (size == pkgsource.size)
+	  try
 	    {
-	      Log (LOG_PLAIN) << "Downloaded " << local << endLog;
 	      if (_access (local.c_str(), 0) == 0)
 		remove (local.c_str());
 	      rename ((local + ".tmp").c_str(), local.c_str());
+	      pkgsource.check_size_and_cache ("file://" + local);
+	      pkgsource.check_hash ();
+	      Log (LOG_PLAIN) << "Downloaded " << local << endLog;
 	      success = 1;
-	      pkgsource.set_cached ("file://" + local);
 	      // FIXME: move the downloaded file to the 
 	      //  original locations - without the mirror site dir in the way
 	      continue;
 	    }
-	  else
+	  catch (Exception *e)
 	    {
-	      Log (LOG_PLAIN) << "Download " << local << " wrong size (" <<
-		size << " actual vs " << pkgsource.size << " expected)" << 
-		endLog;
-	      remove ((local + ".tmp").c_str());
-	      continue;
+	      remove (local.c_str());
+	      pkgsource.set_cached ("");
+	      if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+		{
+		  Log (LOG_PLAIN) << "Downloaded file " << local
+				  << " is corrupt; deleting." << endLog;
+		  continue;
+		}
+	      else
+		{
+		  Log (LOG_PLAIN) << "Unexpected exception while validating "
+				  << "downloaded file " << local
+				  << "; deleting." << endLog;
+		  throw e;
+		}
 	    }
 	}
     }
@@ -266,12 +296,12 @@ do_download_thread (HINSTANCE h, HWND owner)
 	    {
 	      if (pkg.picked())
 		{
-		    if (!check_for_cached (*version.source()))
+		    if (!check_for_cached (*version.source(), owner))
 		      total_download_bytes += version.source()->size;
 		}
 	      if (pkg.srcpicked () || IncludeSource)
 		{
-		    if (!check_for_cached (*sourceversion.source()))
+		    if (!check_for_cached (*sourceversion.source(), owner))
 		      total_download_bytes += sourceversion.source()->size;
 		}
 	    }
diff --git a/download.h b/download.h
index 117a44d..9f4cb8e 100644
--- a/download.h
+++ b/download.h
@@ -16,7 +16,10 @@
 #ifndef SETUP_DOWNLOAD_H
 #define SETUP_DOWNLOAD_H
 
+#include "win32.h"
+
 class packagesource;
-int check_for_cached (packagesource & pkgsource, bool = false);
+int check_for_cached (packagesource & pkgsource, HWND owner,
+		      bool mirror_mode = false, bool check_hash = true);
 
 #endif /* SETUP_DOWNLOAD_H */
diff --git a/install.cc b/install.cc
index d6af331..366aeb2 100644
--- a/install.cc
+++ b/install.cc
@@ -21,7 +21,6 @@
    files in /etc/setup/\* and create the mount points. */
 
 #include "getopt++/BoolOption.h"
-#include "csu_util/MD5Sum.h"
 #include "LogFile.h"
 
 #include "win32.h"
@@ -148,7 +147,6 @@ Installer::StandardDirs[] = {
 };
 
 static int num_installs, num_uninstalls;
-static void chksum_one (const packagemeta &pkg, const packagesource& pkgsource);
 
 void
 Installer::preremoveOne (packagemeta & pkg)
@@ -841,7 +839,10 @@ do_install_thread (HINSTANCE h, HWND owner)
   }
 
   /* md5sum the packages, build lists of packages to install and uninstall
-     and calculate the total amount of data to install */
+     and calculate the total amount of data to install.
+     The hash checking is relevant only for local installs.  For a
+     net install, the hashes will have already been verified at download
+     time, and all calls to check_hash() below should instantly return.  */
   long long int md5sum_total_bytes_sofar = 0;
   for (packagedb::packagecollection::iterator i = db.packages.begin ();
        i != db.packages.end (); ++i)
@@ -852,7 +853,7 @@ do_install_thread (HINSTANCE h, HWND owner)
     {
       try
       {
-        chksum_one (pkg, *pkg.desired.source ());
+        (*pkg.desired.source ()).check_hash ();
       }
       catch (Exception *e)
       {
@@ -872,7 +873,7 @@ do_install_thread (HINSTANCE h, HWND owner)
       bool skiprequested = false ;
       try
       {
-        chksum_one (pkg, *pkg.desired.sourcePackage ().source ());
+        (*pkg.desired.sourcePackage ().source ()).check_hash ();
       }
       catch (Exception *e)
       {
@@ -1013,133 +1014,3 @@ do_install (HINSTANCE h, HWND owner)
   DWORD threadID;
   CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);
 }
-
-static char *
-sha512_str (const unsigned char *in, char *buf)
-{
-  char *bp = buf;
-  for (int i = 0; i < SHA512_DIGEST_LENGTH; ++i)
-    bp += sprintf (bp, "%02x", in[i]);
-  *bp = '\0';
-  return buf;
-}
-
-static void
-sha512_one (const packagemeta &pkg, const packagesource& pkgsource)
-{
-  std::string fullname (pkgsource.Cached ());
-
-  io_stream *thefile = io_stream::open (fullname, "rb", 0);
-  if (!thefile)
-    throw new Exception (TOSTRING (__LINE__) " " __FILE__,
-			 std::string ("IO Error opening ") + fullname,
-			 APPERR_IO_ERROR);
-  SHA2_CTX ctx;
-  unsigned char sha512result[SHA512_DIGEST_LENGTH];
-  char ini_sum[SHA512_DIGEST_STRING_LENGTH],
-       disk_sum[SHA512_DIGEST_STRING_LENGTH];
-
-  SHA512Init (&ctx);
-
-  Log (LOG_BABBLE) << "Checking SHA512 for " << fullname << endLog;
-
-  Progress.SetText1 ((std::string ("Checking SHA512 for ")
-		      + pkg.name).c_str ());
-  Progress.SetText4 ("Progress:");
-  Progress.SetBar1 (0);
-
-  unsigned char buffer[64 * 1024];
-  ssize_t count;
-  while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
-  {
-    SHA512Update (&ctx, buffer, count);
-    Progress.SetBar1 (thefile->tell (), thefile->get_size ());
-  }
-  delete thefile;
-  if (count < 0)
-    throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-			 "IO Error reading " + fullname,
-			 APPERR_IO_ERROR);
-
-  SHA512Final (sha512result, &ctx);
-
-  if (memcmp (pkgsource.sha512sum, sha512result, sizeof sha512result))
-    {
-      Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
-		       << " - SHA512 mismatch: Ini-file: "
-		       << sha512_str (pkgsource.sha512sum, ini_sum)
-		       << " != On-disk: "
-		       << sha512_str (sha512result, disk_sum)
-		       << endLog;
-      throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-			   "SHA512 failure for " + fullname,
-			   APPERR_CORRUPT_PACKAGE);
-    }
-
-  Log (LOG_BABBLE) << "SHA512 verified OK: " << fullname << " "
-    <<  sha512_str (pkgsource.sha512sum, ini_sum) << endLog;
-}
-
-static void
-md5_one (const packagemeta &pkg, const packagesource& pkgsource)
-{
-  std::string fullname (pkgsource.Cached ());
-
-  io_stream *thefile = io_stream::open (fullname, "rb", 0);
-  if (!thefile)
-    throw new Exception (TOSTRING (__LINE__) " " __FILE__,
-			 std::string ("IO Error opening ") + fullname,
-			 APPERR_IO_ERROR);
-  MD5Sum tempMD5;
-  tempMD5.begin ();
-
-  Log (LOG_BABBLE) << "Checking MD5 for " << fullname << endLog;
-
-  Progress.SetText1 ((std::string ("Checking MD5 for ")
-		      + pkg.name).c_str ());
-  Progress.SetText4 ("Progress:");
-  Progress.SetBar1 (0);
-
-  unsigned char buffer[64 * 1024];
-  ssize_t count;
-  while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
-    {
-      tempMD5.append (buffer, count);
-      Progress.SetBar1 (thefile->tell (), thefile->get_size ());
-    }
-  delete thefile;
-  if (count < 0)
-    throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-			 "IO Error reading " + fullname,
-			 APPERR_IO_ERROR);
-
-  tempMD5.finish ();
-
-  if (pkgsource.md5 != tempMD5)
-    {
-      Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
-	<< " - MD5 mismatch: Ini-file: " << pkgsource.md5.str()
-	<< " != On-disk: " << tempMD5.str() << endLog;
-      throw new Exception (TOSTRING(__LINE__) " " __FILE__,
-			   "MD5 failure for " + fullname,
-			   APPERR_CORRUPT_PACKAGE);
-    }
-
-  Log (LOG_BABBLE) << "MD5 verified OK: " << fullname << " "
-    << pkgsource.md5.str() << endLog;
-}
-
-static void
-chksum_one (const packagemeta &pkg, const packagesource& pkgsource)
-{
-  if (!pkgsource.Cached ())
-    return;
-  if (pkgsource.sha512_isSet)
-    sha512_one (pkg, pkgsource);
-  else if (pkgsource.md5.isSet())
-    md5_one (pkg, pkgsource);
-  else
-    Log (LOG_BABBLE) << "No checksum recorded for " << pkg.name
-		     << ", cannot determine integrity of package!"
-		     << endLog;
-}
diff --git a/package_meta.cc b/package_meta.cc
index 7b79738..af494f4 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -606,7 +606,7 @@ packagemeta::scan (const packageversion &pkg, bool mirror_mode)
    */
   try
     {
-      if (!check_for_cached (*(pkg.source ()), mirror_mode)
+      if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false)
 	  && ::source == IDC_SOURCE_LOCALDIR)
 	pkg.source ()->sites.clear ();
     }
diff --git a/package_source.cc b/package_source.cc
index 15540f6..dca1945 100644
--- a/package_source.cc
+++ b/package_source.cc
@@ -18,6 +18,15 @@
  */
 
 #include "package_source.h"
+#include "sha2.h"
+#include "csu_util/MD5Sum.h"
+#include "LogFile.h"
+#include "threebar.h"
+#include "Exception.h"
+#include "filemanip.h"
+#include "io_stream.h"
+
+extern ThreeBarProgressPage Progress;
 
 site::site (const std::string& newkey) : key(newkey)
 {
@@ -27,6 +36,8 @@ void
 packagesource::set_canonical (char const *fn)
 {
   canonical = fn;
+  size_t found = canonical.find_last_of ("/");
+  shortname = canonical.substr (found + 1);
 }
 
 void
@@ -34,3 +45,151 @@ packagesource::set_cached (const std::string& fp)
 {
   cached = fp;
 }
+
+void
+packagesource::check_size_and_cache (const std::string fullname)
+{
+  DWORD fnsize = get_file_size (fullname);
+  if (fnsize != size)
+    {
+      Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
+		       << " - Size mismatch: Ini-file: " << size
+		       << " != On-disk: " << fnsize << endLog;
+      throw new Exception (TOSTRING(__LINE__) " " __FILE__,
+			   "Size mismatch for " + fullname,
+			   APPERR_CORRUPT_PACKAGE);
+    }
+  cached = fullname;
+  validated = false;
+}
+
+void
+packagesource::check_hash ()
+{
+  if (validated || cached.empty ())
+    return;
+
+  if (sha512_isSet)
+    {
+      check_sha512 (cached);
+      validated = true;
+    }
+  else if (md5.isSet())
+    {
+      check_md5 (cached);
+      validated = true;
+    }
+  else
+    Log (LOG_BABBLE) << "No checksum recorded for " << cached
+		     << ", cannot determine integrity of package!"
+		     << endLog;
+}
+
+static char *
+sha512_str (const unsigned char *in, char *buf)
+{
+  char *bp = buf;
+  for (int i = 0; i < SHA512_DIGEST_LENGTH; ++i)
+    bp += sprintf (bp, "%02x", in[i]);
+  *bp = '\0';
+  return buf;
+}
+
+void
+packagesource::check_sha512 (const std::string fullname) const
+{
+  io_stream *thefile = io_stream::open (fullname, "rb", 0);
+  if (!thefile)
+    throw new Exception (TOSTRING (__LINE__) " " __FILE__,
+			 std::string ("IO Error opening ") + fullname,
+			 APPERR_IO_ERROR);
+  SHA2_CTX ctx;
+  unsigned char sha512result[SHA512_DIGEST_LENGTH];
+  char ini_sum[SHA512_DIGEST_STRING_LENGTH],
+       disk_sum[SHA512_DIGEST_STRING_LENGTH];
+
+  SHA512Init (&ctx);
+
+  Log (LOG_BABBLE) << "Checking SHA512 for " << fullname << endLog;
+
+  Progress.SetText1 (("Checking SHA512 for " + shortname).c_str ());
+  Progress.SetText4 ("Progress:");
+  Progress.SetBar1 (0);
+
+  unsigned char buffer[64 * 1024];
+  ssize_t count;
+  while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
+  {
+    SHA512Update (&ctx, buffer, count);
+    Progress.SetBar1 (thefile->tell (), thefile->get_size ());
+  }
+  delete thefile;
+  if (count < 0)
+    throw new Exception (TOSTRING(__LINE__) " " __FILE__,
+			 "IO Error reading " + fullname,
+			 APPERR_IO_ERROR);
+
+  SHA512Final (sha512result, &ctx);
+
+  if (memcmp (sha512sum, sha512result, sizeof sha512result))
+    {
+      Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
+		       << " - SHA512 mismatch: Ini-file: "
+		       << sha512_str (sha512sum, ini_sum)
+		       << " != On-disk: "
+		       << sha512_str (sha512result, disk_sum)
+		       << endLog;
+      throw new Exception (TOSTRING(__LINE__) " " __FILE__,
+			   "SHA512 failure for " + fullname,
+			   APPERR_CORRUPT_PACKAGE);
+    }
+
+  Log (LOG_BABBLE) << "SHA512 verified OK: " << fullname << " "
+    <<  sha512_str (sha512sum, ini_sum) << endLog;
+}
+
+void
+packagesource::check_md5 (const std::string fullname) const
+{
+  io_stream *thefile = io_stream::open (fullname, "rb", 0);
+  if (!thefile)
+    throw new Exception (TOSTRING (__LINE__) " " __FILE__,
+			 std::string ("IO Error opening ") + fullname,
+			 APPERR_IO_ERROR);
+  MD5Sum tempMD5;
+  tempMD5.begin ();
+
+  Log (LOG_BABBLE) << "Checking MD5 for " << fullname << endLog;
+
+  Progress.SetText1 (("Checking MD5 for " + shortname).c_str ());
+  Progress.SetText4 ("Progress:");
+  Progress.SetBar1 (0);
+
+  unsigned char buffer[64 * 1024];
+  ssize_t count;
+  while ((count = thefile->read (buffer, sizeof (buffer))) > 0)
+    {
+      tempMD5.append (buffer, count);
+      Progress.SetBar1 (thefile->tell (), thefile->get_size ());
+    }
+  delete thefile;
+  if (count < 0)
+    throw new Exception (TOSTRING(__LINE__) " " __FILE__,
+			 "IO Error reading " + fullname,
+			 APPERR_IO_ERROR);
+
+  tempMD5.finish ();
+
+  if (md5 != tempMD5)
+    {
+      Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
+	<< " - MD5 mismatch: Ini-file: " << md5.str()
+	<< " != On-disk: " << tempMD5.str() << endLog;
+      throw new Exception (TOSTRING(__LINE__) " " __FILE__,
+			   "MD5 failure for " + fullname,
+			   APPERR_CORRUPT_PACKAGE);
+    }
+
+  Log (LOG_BABBLE) << "MD5 verified OK: " << fullname << " "
+    << md5.str() << endLog;
+}
diff --git a/package_source.h b/package_source.h
index 8675c51..fae46f7 100644
--- a/package_source.h
+++ b/package_source.h
@@ -41,7 +41,7 @@ public:
 class packagesource
 {
 public:
-  packagesource ():size (0), canonical (), cached ()
+  packagesource ():size (0), canonical (), shortname (), cached (), validated (false)
   {
     memset (sha512sum, 0, sizeof sha512sum);
     sha512_isSet = false;
@@ -58,7 +58,12 @@ public:
 
     return NULL;
   };
-  /* what is the cached filename, to prevent directory scanning during install */
+  /* What is the cached filename, to prevent directory scanning during
+     install?  Except in mirror mode, we never set this without
+     checking the size.  The more expensive hash checking is reserved
+     for verifying the integrity of downloaded files and sources of
+     packages about to be installed.  Set the 'validated' flag to
+     avoid checking the hash twice.  */
   char const *Cached () const
   {
     /* Pointer-coerce-to-boolean is used by many callers. */
@@ -72,12 +77,20 @@ public:
   unsigned char sha512sum[SHA512_DIGEST_LENGTH];
   bool sha512_isSet;
   MD5Sum md5;
+  /* The next two functions throw exceptions on failure.  */
+  void check_size_and_cache (const std::string fullname);
+  void check_hash ();
   typedef std::vector <site> sitestype;
   sitestype sites;
 
 private:
   std::string canonical;
+  /* For progress reporting.  */
+  std::string shortname;
   std::string cached;
+  bool validated;
+  void check_sha512 (const std::string fullname) const;
+  void check_md5 (const std::string fullname) const;
 };
 
 #endif /* SETUP_PACKAGE_SOURCE_H */
diff --git a/res.rc b/res.rc
index 82a9757..5b7d239 100644
--- a/res.rc
+++ b/res.rc
@@ -550,7 +550,8 @@ BEGIN
     IDS_DOWNLOAD_INCOMPLETE_EXIT  "Download incomplete.  Check %s for details"
     IDS_INSTALL_ERROR	    "Installation error (%s), Continue with other packages?"
     IDS_INSTALL_INCOMPLETE  "Installation incomplete.  Check %s for details"
-    IDS_CORRUPT_PACKAGE     "Package file %s has a corrupt local copy, please remove and retry."
+    IDS_CORRUPT_PACKAGE     "Package %s has a corrupt local copy, please remove and retry."
+    IDS_QUERY_CORRUPT       "The file %s is corrupt.  Delete it and download again?"
     IDS_SKIP_PACKAGE	    "%s\nDo you want to skip this package ?"
     IDS_UNCAUGHT_EXCEPTION  "Fatal Error: Uncaught Exception\nThread: %s\nType: %s\nMessage: %s"
     IDS_UNCAUGHT_EXCEPTION_WITH_ERRNO  "Fatal Error: Uncaught Exception\nThread: %s\nType: %s\nMessage: %s\nAppErrNo: %d"
diff --git a/resource.h b/resource.h
index 79b876d..70d90ca 100644
--- a/resource.h
+++ b/resource.h
@@ -39,6 +39,7 @@
 #define IDS_ELEVATED			  139
 #define IDS_INSTALLEDB_VERSION            140
 #define IDS_DOWNLOAD_INCOMPLETE_EXIT      141
+#define IDS_QUERY_CORRUPT                 142
 
 // Dialogs
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]