[Secure-testing-commits] r1994 - / bin lib/python

Florian Weimer fw at costa.debian.org
Thu Sep 15 10:11:45 UTC 2005


Author: fw
Date: 2005-09-15 10:11:44 +0000 (Thu, 15 Sep 2005)
New Revision: 1994

Added:
   bin/update-db
Removed:
   bin/update-bug-list-db
   bin/update-packages
   bin/update-vulnerabilities
Modified:
   Makefile
   bin/check-syntax
   lib/python/bugs.py
   lib/python/security_db.py
Log:
Implement bin/update-db, to update the database with a single command.
Most processing is skipped if no input files have been modified.

lib/python/security_db.py (SchemaMismatch):
  New exception.
(DB):
  Handle schema versioning.
(DB.initSchema):
  Add subrelease column to source_packages and binary_packages.
  Set user_version.
  Remove stray commit.
(DB._parseFile):
  Return information to the caller if the file is unchanged.
(DB.readPackages):
  Move deletion code to callees.
(DB._readSourcePackages, DB._readBinaryPackages):
  Implement incremental updates.  Add subrelease.
  Need to invoke _clearVersions if any changes are made.
(DB.deleteBugs, DB.finishBugs):
  Moved into readBugs.
(DB.insertBugs):
  Rename ...
(DB.readBugs):
  ... to this one.  Implement incremental updates.
  Invoke _clearVersions if necessary.
(DB._clearVersions):
  Add.
(DB._updateVersions):
  Skip processing if _clearVersions has not been invoked.
(DB.getVersion, DB.releaseContainsPackage, DB._synthesizeReleases):
  Obsolete, remove.
(test):
  Update.

lib/python/bugs.py (CANFile, CVEFile):
  Split into two classes, which handle the differences between the two
  files.

bin/check-syntax:
  Update accordingly.

bin/update-db:
  New database update script.  Implements incremental updates.

Makefile:
  Remove references to bin/update-packages.  Simplify drastically.


Modified: Makefile
===================================================================
--- Makefile	2005-09-15 09:14:17 UTC (rev 1993)
+++ Makefile	2005-09-15 10:11:44 UTC (rev 1994)
@@ -14,29 +14,9 @@
 PACKAGE_FILES = $(wildcard data/packages/*_Sources) \
 	$(wildcard data/packages/*_Packages)
 
-all: stamps/bug-lists-imported stamps/packages-imported \
-	stamps/calc-vulns
+all:
+	$(PYTHON) bin/update-db
 
-stamps/bug-lists-imported: bin/update-bug-list-db \
-	$(BUG_LISTS) $(PYTHON_MODULES)
-	$(PYTHON) bin/update-bug-list-db
-	touch $@
-
-# No dependencies on the Python files.  This part of the code should
-# be quite stable.  We only run the packages import if "make
-# update-packages" has been invoked before.
-stamps/packages-imported: $(PACKAGE_FILES)
-	if test -e stamps/packages-downloaded ; then \
-		$(PYTHON) bin/update-packages import ; \
-	fi
-	touch $@
-
-stamps/calc-vulns: stamps/bug-lists-imported stamps/packages-imported
-	if test -e stamps/packages-downloaded ; then \
-		$(PYTHON) bin/update-vulnerabilities ; \
-	fi
-	touch $@
-
 clean:
 	-rm data/security.db
 	-rm stamps/*-*
@@ -64,9 +44,43 @@
 	$(PYTHON) bin/check-syntax DTSA data/DTSA/list
 	touch $@
 
-.PHONY: update-packages
+.PHONY: update-packages update-etch-security
 update-packages:
-	$(PYTHON) bin/update-packages download $(MIRROR) $(RELEASES)
-	$(PYTHON) bin/update-packages download \
-		http://security.debian.org/ $(SECURITY_RELEASES)
+	set -e ; for rel in woody sarge etch sid ; do \
+		for archive in main contrib non-free ; do \
+		$(PYTHON) bin/apt-update-file \
+			$(MIRROR)/dists/$$rel/$$archive/source/Sources \
+			data/packages/$${rel}__$${archive}_Sources ; \
+		done ; \
+	        for arch in i386 ia64 ; do \
+		  for archive in main contrib non-free ; do \
+		  $(PYTHON) bin/apt-update-file \
+		    $(MIRROR)/dists/$$rel/$$archive/binary-$$arch/Packages \
+		    data/packages/$${rel}__$${archive}_$${arch}_Packages ; \
+		  done ; \
+		done ; \
+	done
 	touch stamps/packages-downloaded
+
+ST_MIRROR = http://secure-testing.debian.net/debian-secure-testing/dists/etch/security-updates
+ST_FILE = data/packages/etch_security_
+update-testing-security:
+	$(PYTHON) bin/apt-update-file \
+	  $(ST_MIRROR)/main/source/Sources $(ST_FILE)main_Sources
+	$(PYTHON) bin/apt-update-file \
+	  $(ST_MIRROR)/main/binary-i386/Packages $(ST_FILE)main_i386_Packages
+	$(PYTHON) bin/apt-update-file \
+	  $(ST_MIRROR)/main/binary-ia64/Packages $(ST_FILE)main_ia64_Packages
+
+SEC_MIRROR = http://security.debian.org/dists
+update-security:
+	for archive in woody sarge ; do \
+	  $(PYTHON) bin/apt-update-file \
+	    $(SEC_MIRROR)/$$archive/updates/main/source/Sources \
+	    data/packages/$${archive}_security_main_Sources ; \
+	  for arch in i386 ia64 ; do \
+	    $(PYTHON) bin/apt-update-file \
+	      $(SEC_MIRROR)/$$archive/updates/main/binary-$$arch/Packages \
+	      data/packages/$${archive}_security_main_$${arch}_Packages ; \
+	  done ; \
+	done

Modified: bin/check-syntax
===================================================================
--- bin/check-syntax	2005-09-15 09:14:17 UTC (rev 1993)
+++ bin/check-syntax	2005-09-15 10:11:44 UTC (rev 1994)
@@ -46,7 +46,7 @@
     
 
 def parse_CAN(name):
-    do_parse(bugs.CVEFile(name))
+    do_parse(bugs.CANFile(name))
 
 def parse_CVE(name):
     f = bugs.CVEFile(name)

Deleted: bin/update-bug-list-db
===================================================================
--- bin/update-bug-list-db	2005-09-15 09:14:17 UTC (rev 1993)
+++ bin/update-bug-list-db	2005-09-15 10:11:44 UTC (rev 1994)
@@ -1,55 +0,0 @@
-#!/usr/bin/python
-
-import os
-import os.path
-import string
-import sys
-
-def setup_paths():
-    check_file = 'lib/python/debian_support.py'
-    path = os.getcwd()
-    while 1:
-        if os.path.exists("%s/%s" % (path, check_file)):
-            sys.path = [path + '/lib/python'] + sys.path
-            return path
-        idx = string.rfind(path, '/')
-        if idx == -1:
-            raise ImportError, "could not setup paths"
-        path = path[0:idx]
-root_path = setup_paths()
-
-import bugs
-import debian_support
-import security_db
-
-db_file = root_path + '/data/security.db'
-new_file = not os.path.exists(db_file)
-db = security_db.DB(db_file)
-if new_file:
-    db.initSchema()
-cursor = db.writeTxn()
-db.deleteBugs(cursor)
-try:
-    db.insertBugs(cursor, bugs.CVEFile(root_path + '/data/CAN/list'))
-    db.insertBugs(cursor, bugs.CVEFile(root_path + '/data/CVE/list',
-                                       no_version_needs_note=False))
-    db.insertBugs(cursor, bugs.DSAFile(root_path + '/data/DSA/list'))
-    db.insertBugs(cursor, bugs.DTSAFile(root_path + '/data/DTSA/list'))
-except debian_support.ParseError, e:
-    db.rollback(cursor)
-    e.printOut(sys.stderr)
-    sys.exit(1)
-except security_db.InsertError, e:
-    db.rollback(cursor)
-    for err in e.errors:
-        print err
-    sys.exit(1)
-
-warnings = db.finishBugs(cursor)
-if warnings:
-    db.rollback(cursor)
-    for x in warnings:
-        print x
-    sys.exit(1)
-else:
-    db.commit(cursor)

Added: bin/update-db
===================================================================
--- bin/update-db	2005-09-15 09:14:17 UTC (rev 1993)
+++ bin/update-db	2005-09-15 10:11:44 UTC (rev 1994)
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+import os
+import os.path
+import string
+import sys
+
+def setup_paths():
+    check_file = 'lib/python/debian_support.py'
+    path = os.getcwd()
+    while 1:
+        if os.path.exists("%s/%s" % (path, check_file)):
+            sys.path = [path + '/lib/python'] + sys.path
+            return path
+        idx = string.rfind(path, '/')
+        if idx == -1:
+            raise ImportError, "could not setup paths"
+        path = path[0:idx]
+os.chdir(setup_paths())
+
+import bugs
+import debian_support
+import security_db
+
+db_file = 'data/security.db'
+try:
+    db = security_db.DB(db_file, verbose=True)
+except security_db.SchemaMismatch:
+    os.unlink(db_file)
+    db = security_db.DB(db_file, verbose=True)
+    
+cursor = db.writeTxn()
+
+# Bug lists (CAN/CVE/DSA/DTSA)
+
+try:
+    warnings = db.readBugs(cursor, 'data')
+except debian_support.ParseError, e:
+    e.printOut(sys.stderr)
+    sys.exit(1)
+except security_db.InsertError, e:
+    for err in e.errors:
+        print err
+    sys.exit(1)
+if warnings:
+    for x in warnings:
+        print x
+    sys.exit(1)
+
+# Packages
+
+db.readPackages(cursor, 'data/packages')
+
+# Calculate vulnerability information.
+
+warnings = db.calculateVulnerabilities(cursor)
+if warnings:
+    for x in warnings:
+        print x
+    sys.exit(1)
+
+# Everything worked well.
+    
+db.commit(cursor)


Property changes on: bin/update-db
___________________________________________________________________
Name: svn:executable
   + *

Deleted: bin/update-packages
===================================================================
--- bin/update-packages	2005-09-15 09:14:17 UTC (rev 1993)
+++ bin/update-packages	2005-09-15 10:11:44 UTC (rev 1994)
@@ -1,95 +0,0 @@
-#!/usr/bin/python
-
-# This script downloads and imports Debian package files.
-
-import errno
-import os
-import os.path
-import string
-import sys
-
-def setup_paths():
-    check_file = 'lib/python/debian_support.py'
-    path = os.getcwd()
-    while 1:
-        if os.path.exists("%s/%s" % (path, check_file)):
-            sys.path = [path + '/lib/python'] + sys.path
-            return path
-        idx = string.rfind(path, '/')
-        if idx == -1:
-            raise ImportError, "could not setup paths"
-        path = path[0:idx]
-root_path = setup_paths()
-
-import debian_support
-import security_db
-
-def explodeReleases(args):
-    for arg in args:
-        (release, archs) = arg.split('=')
-        # FIXME: What shall we do with these?
-        # if debian_support.internRelease(release) is None:
-        #     sys.stderr.write("error: unknown release: %s\n" % release)
-        #     sys.exit(1)
-        yield release, archs.split(',')
-
-archives = ('main', 'contrib', 'non-free')
-
-def nameSources(release, archive):
-    return '%s/data/packages/%s_%s_Sources' % (root_path, release, archive)
-
-def namePackages(release, archive, arch):
-    return '%s/data/packages/%s_%s_%s_Packages' % (root_path, release,
-                                                   archive, arch)
-
-def cmd_download(args):
-    url_base = args[0]
-    if url_base[-1] != '/':
-        url_base += '/'
-        
-    for release, archs in explodeReleases(args[1:]):
-        # Security updates are stored in a different directory.
-        if release[-9:] == '-security':
-            rrel = release[:-9] + '/updates'
-        else:
-            rrel = release
-
-        for archive in archives:
-            print "Updating source package %s/%s" % (release, archive)
-            debian_support.updateFile("%sdists/%s/%s/source/Sources"
-                                      % (url_base, rrel, archive),
-                                      nameSources(release, archive),
-                                      verbose=True)
-            for arch in archs:
-                print "Updating binary package %s/%s/%s" \
-                                      % (release, archive, arch)
-                debian_support.updateFile("%sdists/%s/%s/binary-%s/Packages"
-                                          % (url_base, rrel, archive, arch),
-                                          namePackages(release, archive, arch),
-                                          verbose=True)
-
-def cmd_import(args):
-    db_file = root_path + '/data/security.db'
-    new_file = not os.path.exists(db_file)
-    db = security_db.DB(db_file, verbose=True)
-    if new_file:
-        db.initSchema()
-    c = db.writeTxn()
-    db.readPackages(c, root_path + '/data/packages')
-    db.commit(c)
-
-cmds = {"download" : cmd_download,
-        "import" : cmd_import}
-
-if len(sys.argv) < 2 or not cmds.has_key(sys.argv[1]):
-    sys.stderr.write(\
-"""usage: update-packages download URL-BASE RELEASE=ARCH...
-       update-packages import
-""")
-    sys.exit(1)
-try:
-    cmds[sys.argv[1]](sys.argv[2:])
-except debian_support.ParseError, e:
-    e.printOut(sys.stderr)
-    sys.exit(1)
-

Deleted: bin/update-vulnerabilities
===================================================================
--- bin/update-vulnerabilities	2005-09-15 09:14:17 UTC (rev 1993)
+++ bin/update-vulnerabilities	2005-09-15 10:11:44 UTC (rev 1994)
@@ -1,37 +0,0 @@
-#!/usr/bin/python
-
-# This script recalculates the vulnerability information in the
-# security database.
-
-import errno
-import os
-import os.path
-import string
-import sys
-
-def setup_paths():
-    check_file = 'lib/python/debian_support.py'
-    path = os.getcwd()
-    while 1:
-        if os.path.exists("%s/%s" % (path, check_file)):
-            sys.path = [path + '/lib/python'] + sys.path
-            return path
-        idx = string.rfind(path, '/')
-        if idx == -1:
-            raise ImportError, "could not setup paths"
-        path = path[0:idx]
-root_path = setup_paths()
-
-import security_db
-
-db_file = root_path + '/data/security.db'
-assert os.path.exists(db_file)
-db = security_db.DB(db_file, verbose=True)
-c = db.writeTxn()
-warnings = db.calculateVulnerabilities(c)
-if warnings:
-    db.rollback(c)
-    for x in warnings:
-        print x
-    sys.exit(1)
-db.commit(c)

Modified: lib/python/bugs.py
===================================================================
--- lib/python/bugs.py	2005-09-15 09:14:17 UTC (rev 1993)
+++ lib/python/bugs.py	2005-09-15 10:11:44 UTC (rev 1994)
@@ -616,15 +616,15 @@
                 yield Bug(self.file.name, first_lineno, date,
                           record_name, description,
                           comments, notes=pkg_notes, xref=xref)
-            
-class CVEFile(FileBase):
-    """A CVE file, as used by the Debian testing security team."""
+
+class CANFile(FileBase):
+    """A CAN file, as used by the Debian testing security team."""
     
-    re_cve = re.compile(r'^((?:CAN|CVE)-\d{4}-(?:\d{4}|XXXX))\s+(.*?)\s*$')
+    re_cve = re.compile(r'^(CAN-\d{4}-(?:\d{4}|XXXX))\s+(.*?)\s*$')
 
-    def __init__(self, name, fileObj=None, no_version_needs_note=True):
+    def __init__(self, name, fileObj=None):
         FileBase.__init__(self, name, fileObj)
-        self.no_version_needs_note = no_version_needs_note
+        self.no_version_needs_note = True
 
     def isUniqueName(self, name):
         return BugBase.re_cve_name.match(name) is not None
@@ -632,6 +632,34 @@
     def matchHeader(self, line):
         match = self.re_cve.match(line)
         if not match:
+            self.raiseSyntaxError("expected CAN record, got: %s" % `line`)
+            (record_name, description) = match.groups()
+        (cve, desc) = match.groups()
+        if desc:
+            if desc[0] == '(':
+                if desc[-1] <> ')':
+                    self.raiseSyntaxError("missing closing parenthesis")
+                else:
+                    desc = desc[1:-1]
+            elif desc[0] == '[':
+                if desc[-1] <> ']':
+                    self.raiseSyntaxError("missing closing bracket")
+                else:
+                    desc = desc[1:-1]
+        return (None, cve, desc)
+
+class CVEFile(FileBase):
+    """A CVE file, as used by the Debian testing security team."""
+
+    re_cve = re.compile(r'^(CVE-\d{4}-\d{4})\s+(.*?)\s*$')
+
+    def __init__(self, name, fileObj=None):
+        FileBase.__init__(self, name, fileObj)
+        self.no_version_needs_note = False
+        
+    def matchHeader(self, line):
+        match = self.re_cve.match(line)
+        if not match:
             self.raiseSyntaxError("expected CVE record, got: %s" % `line`)
             (record_name, description) = match.groups()
         (cve, desc) = match.groups()

Modified: lib/python/security_db.py
===================================================================
--- lib/python/security_db.py	2005-09-15 09:14:17 UTC (rev 1993)
+++ lib/python/security_db.py	2005-09-15 10:11:44 UTC (rev 1994)
@@ -73,6 +73,11 @@
     result.sort()
     return result
 
+class SchemaMismatch(Exception):
+    """Raised to indicate a schema mismatch.
+
+    The caller is expected to remove and regenerate the database."""
+
 class DB:
     """Access to the security database.
 
@@ -91,6 +96,15 @@
                           'sarge' : 'stable',
                           'woody': 'oldstable'}
 
+        c = self.cursor()
+        for (v,) in c.execute("PRAGMA user_version"):
+            if v == 0:
+                self.initSchema()
+            if v <> 1:
+                raise SchemaMismatch, `v`
+            return
+        assert False
+
     def cursor(self):
         """Creates a new database cursor.
 
@@ -140,22 +154,24 @@
             """CREATE TABLE source_packages
             (name TEXT NOT NULL,
             release TEXT NOT NULL,
+            subrelease TEXT NOT NULL,
             archive TEXT NOT NULL,
             version TEXT NOT NULL,
             version_id INTEGER NOT NULL DEFAULT 0,
-            PRIMARY KEY (name, release, archive))""")
+            PRIMARY KEY (name, release, subrelease, archive))""")
 
         cursor.execute(
             """CREATE TABLE binary_packages
             (name TEXT NOT NULL,
             release TEXT NOT NULL,
+            subrelease TEXT NOT NULL,
             archive TEXT NOT NULL,
             version TEXT NOT NULL,
             source TEXT NOT NULL,
             source_version TEXT NOT NULL,
             archs TEXT NOT NULL,
             version_id INTEGER NOT NULL DEFAULT 0,
-            PRIMARY KEY (name, release, archive, version, source,
+            PRIMARY KEY (name, release, subrelease, archive, version, source,
             source_version))""")
         cursor.execute(
             """CREATE INDEX binary_packages_source
@@ -225,8 +241,13 @@
             """CREATE INDEX binary_package_status_package
             ON binary_package_status(package)""")
 
-        self.commit(cursor)
+        # Put this at the end.  Any exception will leave the schema
+        # version at 0, so we automatically recreate the schema once
+        # the application is started after the underlying error has
+        # been fixed.
 
+        cursor.execute("PRAGMA user_version = 1")
+
     def filePrint(self, filename):
         """Returns a fingerprint string for filename."""
 
@@ -284,26 +305,23 @@
             "SELECT inodeprint, parsed FROM inodeprints WHERE file = ?",
             (filename,)):
             if old_print == current_print:
-                return cPickle.load(cStringIO.StringIO(contents))
+                return (True, cPickle.load(cStringIO.StringIO(contents)))
             result = do_parse(debian_support.PackageFile(filename))
             cursor.execute("""UPDATE inodeprints SET inodeprint = ?, parsed = ?
             WHERE file = ?""", (current_print, toString(result), filename))
-            return result
+            return (False, result)
 
         # No inodeprints entry, load file and add one.
         result = do_parse(debian_support.PackageFile(filename))
         cursor.execute("""INSERT INTO inodeprints (file, inodeprint, parsed)
         VALUES (?, ?, ?)""", (filename, current_print, toString(result)))
-        return result
+        return (False, result)
 
     def readPackages(self, cursor, directory):
         """Reads a directory of package files."""
 
         if self.verbose:
             print "readPackages:"
-            print "  deleting old data"
-        cursor.execute("DELETE FROM source_packages")
-        cursor.execute("DELETE FROM binary_packages")
 
         self._readSourcePackages(cursor, directory)
         self._readBinaryPackages(cursor, directory)
@@ -314,7 +332,7 @@
     def _readSourcePackages(self, cursor, directory):
         """Reads from directory with source package files."""
 
-        re_sources = re.compile(r'.*/([a-z-]+)_([a-z-]+)_Sources$')
+        re_sources = re.compile(r'.*/([a-z-]+)_([a-z-]*)_([a-z-]+)_Sources$')
 
 
         if self.verbose:
@@ -325,48 +343,71 @@
             if match is None:
                 raise ValueError, "invalid file name: " + `filename`
 
-            (release, archive) = match.groups()
-            parsed = self._parseFile(cursor, filename)
+            (release, subrelease, archive) = match.groups()
+            (unchanged, parsed) = self._parseFile(cursor, filename)
+            if unchanged:
+                continue
 
+            cursor.execute(
+                """DELETE FROM source_packages
+                WHERE release = ? AND subrelease = ? AND archive = ?""",
+                (release, subrelease, archive))
+            self._clearVersions(cursor)
+
             def gen():
                 for (name, version, source, source_version) in parsed:
                     assert source is None
                     assert source_version is None
-                    yield name, release, archive, version
+                    yield name, release, subrelease, archive, version
             cursor.executemany(
                 """INSERT INTO source_packages
-               (name, release, archive, version) VALUES (?, ?, ?, ?)""",
+               (name, release, subrelease, archive, version)
+               VALUES (?, ?, ?, ?, ?)""",
                 gen())
         
     def _readBinaryPackages(self, cursor, directory):
         """Reads from a directory with binary package files."""
 
         re_packages \
-            = re.compile(r'.*/([a-z-]+)_([a-z-]+)_([a-z0-9]+)_Packages$')
+            = re.compile(
+            r'.*/([a-z-]+)_([a-z-]*)_([a-z-]+)_([a-z0-9]+)_Packages$')
         
         if self.verbose:
             print "  reading binary packages"
 
         packages = {}
+        unchanged = True
         for filename in glob.glob(directory + '/*_Packages'):
             match = re_packages.match(filename)
             if match is None:
                 raise ValueError, "invalid file name: " + `filename`
 
-            (release, archive, architecture) = match.groups()
-            parsed = self._parseFile(cursor, filename)
+            (release, subrelease, archive, architecture) = match.groups()
+            (unch, parsed) = self._parseFile(cursor, filename)
+            unchanged = unchanged and unch
             for (name, version, source, source_version) in parsed:
                 if source is None:
                     source = name
                 if source_version is None:
                     source_version = version
         
-                key = (name, release, archive, version, source, source_version)
+                key = (name, release, subrelease, archive, version,
+                       source, source_version)
                 if packages.has_key(key):
                     packages[key][architecture] = 1
                 else:
                     packages[key] = {architecture : 1}
 
+        if unchanged:
+            if self.verbose:
+                print "    finished (no changes)"
+            return
+
+        if self.verbose:
+            print "    deleting old data"
+        cursor.execute("DELETE FROM binary_packages")
+        self._clearVersions(cursor)
+
         l = packages.keys()
 
         if len(l) == 0:
@@ -385,41 +426,110 @@
 
         cursor.executemany(
             """INSERT INTO binary_packages
-            (name, release, archive, version,
+            (name, release, subrelease, archive, version,
             source, source_version, archs)
-            VALUES (?, ?, ?, ?, ?, ?, ?)""",
+            VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
             gen())
             
-    def deleteBugs(self, cursor):
-        """Deletes all record bug reports from the database."""
-        cursor.execute("DELETE FROM package_notes")
-        cursor.execute("DELETE FROM debian_bugs")
-        cursor.execute("DELETE FROM bugs")
-        cursor.execute("DELETE FROM bugs_notes")
-        cursor.execute("DELETE FROM bugs_xref")
+    def readBugs(self, cursor, path):
+        if self.verbose:
+            print "readBugs:"
 
-    def insertBugs(self, cursor, source):
-        """Reads the CAN/CVE/DSA/DTSA file and writes them to the database."""
+        def clear_db(filename):
+            cursor.execute(
+                """CREATE TEMPORARY TABLE bugs_to_delete
+                (tbd TEXT NOT NULL PRIMARY KEY)""")
+            cursor.execute(
+                """INSERT INTO bugs_to_delete
+                SELECT name FROM bugs WHERE source_file = ?""",
+                (filename,))
 
-        errors = []
-        for bug in source:
-            try:
-                bug.writeDB(cursor)
-            except ValueError, e:
-                errors.append("%s: %d: error: %s"
-                              % (bug.source_file, bug.source_line, e))
-        if errors:
-            raise InsertError(errors)
+            cursor.execute(
+                """DELETE FROM debian_bugs
+                WHERE EXISTS (SELECT 1
+                              FROM package_notes AS p, bugs_to_delete AS b
+                              WHERE p.id = debian_bugs.note
+                              AND p.bug_name = b.tbd)""")
 
-    def finishBugs(self, cursor):
-        """After inserting new bugs, update cross-references.
+            cursor.execute("""DELETE FROM bugs
+                WHERE EXISTS (SELECT * FROM bugs_to_delete
+                              WHERE tbd = name)""")
+            cursor.execute("""DELETE FROM package_notes
+                WHERE EXISTS (SELECT * FROM bugs_to_delete
+                              WHERE tbd = bug_name)""")
+            cursor.execute("""DELETE FROM bugs_notes
+                WHERE EXISTS (SELECT * FROM bugs_to_delete
+                              WHERE tbd = bug_name)""")
+            cursor.execute("""DELETE FROM bugs_xref
+                WHERE EXISTS (SELECT * FROM bugs_to_delete
+                              WHERE tbd = source)""")
 
-        Returns a list of warning messages."""
+            # The *_status tables are regenerated anyway, no need to
+            # delete them here.
+            
+            cursor.execute("""DROP TABLE bugs_to_delete""")
+            
+            self._clearVersions(cursor)
+            
+        def do_parse(source):
+            errors = []
+          
+            if self.verbose:
+                print "  reading " + `source.name`
 
-        warnings = []
+            clear_db(source.name)
+            
+            for bug in source:
+                try:
+                    bug.writeDB(cursor)
+                except ValueError, e:
+                    errors.append("%s: %d: error: %s"
+                                  % (bug.source_file, bug.source_line, e))
+            if errors:
+                raise InsertError(errors)
 
-        # Check that there are no CAN/CVE collisions.
+        def read_one(source):
+            filename = source.name
+            current_print = self.filePrint(filename)
 
+            for (old_print,) in cursor.execute(
+                "SELECT inodeprint FROM inodeprints WHERE file = ?",
+                (filename,)):
+                if old_print == current_print:
+                    return False
+                do_parse(source)
+                cursor.execute(
+                    "UPDATE inodeprints SET inodeprint = ? WHERE file = ?",
+                    (current_print, filename))
+                return True
+
+            # No inodeprints entry, load file and add one.
+            do_parse(source)
+            cursor.execute(
+                "INSERT INTO inodeprints (file, inodeprint) VALUES (?, ?)",
+                (filename, current_print))
+            return True
+
+        unchanged = True
+        if read_one(bugs.CANFile(path + '/CAN/list')):
+            unchanged = False
+        if read_one(bugs.CVEFile(path + '/CVE/list')):
+            unchanged = False
+        if read_one(bugs.DSAFile(path + '/DSA/list')):
+            unchanged = False
+        if read_one(bugs.DTSAFile(path + '/DTSA/list')):
+            unchanged = False
+
+        if unchanged:
+            if self.verbose:
+                print "  finished (no changes)"
+            return
+
+        errors = []
+        
+        if self.verbose:
+            print "  checking CAN/CVE collisions"
+        
         for b1, b2 in list(cursor.execute\
             ("""SELECT b1.name, b2.name FROM bugs AS b1, bugs AS b2
              WHERE b1.name LIKE 'CVE-%'
@@ -427,19 +537,22 @@
             b1 = bugs.BugFromDB(cursor, b1)
             b2 = bugs.BugFromDB(cursor, b2)
 
-            warnings.append("%s:%d: duplicate CVE entries %s and %s"
-                           % (b1.source_file, b1.source_line,
-                              b1.name, b2.name))
-            warnings.append("%s:%d: location of %s"
-                            % (b1.source_file, b1.source_line, b1.name))
-            warnings.append("%s:%d: location of %s"
-                            % (b2.source_file, b2.source_line, b2.name))
+            errors.append("%s:%d: duplicate CVE entries %s and %s"
+                          % (b1.source_file, b1.source_line,
+                             b1.name, b2.name))
+            errors.append("%s:%d: location of %s"
+                          % (b1.source_file, b1.source_line, b1.name))
+            errors.append("%s:%d: location of %s"
+                          % (b2.source_file, b2.source_line, b2.name))
 
         # Normalize the CAN/CVE references to the entry which is
         # actually in the database.  After the CAN -> CVE transition,
         # this can go away (but we should check that the
         # cross-references are valid).
 
+        if self.verbose:
+            print "  normalize CAN/CVE references"
+        
         for source, target in list(cursor.execute\
             ("""SELECT source, target FROM bugs_xref
             WHERE normalized_target = ''""")):
@@ -458,11 +571,12 @@
                     break
                 if not found:
                     b = bugs.BugFromDB(cursor, source)
-                    warnings.append\
+                    errors.append\
                         ("%s: %d: reference to unknwown CVE entry %s"
                          % (b.source_file, b.source_line, target))
 
-        # Check that the DSA/DTSA references are valid.
+        if self.verbose:
+            print "  check DSA/DTSA references"
 
         for source, target in list(cursor.execute
             ("""SELECT source, target FROM bugs_xref
@@ -473,12 +587,16 @@
                 found = True
             if not found:
                 b = bugs.BugFromDB(cursor, source)
-                warnings.append\
+                errors.append\
                     ("%s: %d: reference to unknwown advisory %s"
                      % (b.source_file, b.source_line, target))
 
-        return warnings
+        if errors:
+            raise InsertErrors(errors)
 
+        if self.verbose:
+            print "  finished"
+
     def availableReleases(self, cursor=None):
         """Returns a list of tuples (RELEASE, ARCHIVE,
         SOURCES-PRESENT, ARCHITECTURE-LIST)."""
@@ -487,20 +605,21 @@
 
         releases = {}
         for r in cursor.execute(
-            "SELECT DISTINCT release, archive FROM source_packages"):
+            """SELECT DISTINCT release, subrelease, archive
+            FROM source_packages"""):
             releases[r] = (True, [])
 
-        for (rel, archive, archs) in cursor.execute(
-            """SELECT DISTINCT release, archive, archs
+        for (rel, subrel, archive, archs) in cursor.execute(
+            """SELECT DISTINCT release, subrelease, archive, archs
             FROM binary_packages"""):
-            key = (rel, archive)
+            key = (rel, subrel, archive)
             if not releases.has_key(key):
                 releases[key] = (False, [])
             releases[key][1][:] = mergeLists(releases[key][1], archs)
 
         result = []
-        for ((rel, archive), (sources, archs)) in releases.items():
-            result.append((rel, archive, sources, archs))
+        for ((rel, subrel, archive), (sources, archs)) in releases.items():
+            result.append((rel, subrel, archive, sources, archs))
         result.sort()
 
         return result
@@ -517,48 +636,21 @@
             WHERE name = source AND version <> source_version
             ORDER BY name, release, archive"""))
 
-    def getVersion(self, cursor, release, package):
-        """Returns the version number for package in release.
+    def _clearVersions(self, cursor):
+        cursor.execute("DELETE FROM version_linear_order")
 
-        Package can be a source or binary package.  Binary package
-        versions take precedence.
-
-        Security updates etc. are not considered."""
-
-        versions = list(cursor.execute(
-            """SELECT version FROM binary_packages
-            WHERE package = ? AND release = ?""", (package, release)))
-        if versions:
-            return min(map(lambda (v,): debian_support.Version(v), versions))
-
-        versions = list(cursor.execute(
-            """SELECT version FROM source_packages
-            WHERE package = ? AND release = ?""", (package, release)))
-        if versions:
-            assert len(versions) == 1
-            return debian_support.Version(versions[0][0])
-        
-        return None
-
-    def releaseContainsPackage(self, cursor, release, package):
-        """Returns True if the source or binary package exists in release."""
-        for (c,) in cursor.execute(
-            """SELECT version FROM binary_packages
-            WHERE package = ? AND release = ?""", (package, release)):
-            return True
-        for (c,) in cursor.execute(
-            """SELECT version FROM source_packages
-            WHERE package = ? AND release = ?""", (package, release)):
-            return True
-        return False
-
     def _updateVersions(self, cursor):
         """Updates the linear version table."""
 
-        cursor.execute("DELETE FROM version_linear_order");
+        if self.verbose:
+            print "updateVersions:"
 
+        for x in cursor.execute("SELECT * FROM version_linear_order LIMIT 1"):
+            if self.verbose:
+                print "  finished (no changes)"
+            return
+
         if self.verbose:
-            print "updateVersions:"
             print "  reading"
 
         versions = []
@@ -606,66 +698,6 @@
         if self.verbose:
             print "  finished"
         
-    def _synthesizeReleases(self, cursor):
-        """Creates the package lists for testing, stable and oldstable.
-
-        These package lists include security updates.
-        """
-
-        if self.verbose:
-            print "synthesizeReleases:"
-            print "  clear old data"
-            print "    source packages"
-        cursor.execute(
-            """DELETE FROM source_packages
-            WHERE release IN ('stable', 'oldstable', 'testing')""")
-        if self.verbose:
-            print "    binary packages"
-        cursor.execute(
-            """DELETE FROM binary_packages
-            WHERE release IN ('stable', 'oldstable', 'testing')""")
-
-        for (realname, nickname) in self.nicknames.items():
-            if self.verbose:
-                print "  synthesize %s to %s" % (realname, nickname)
-                print "    source packages"
-            cursor.execute(
-                """INSERT INTO source_packages 
-                SELECT name, ?, archive, '', MAX(version_id) AS vid
-                FROM source_packages WHERE release IN (?, ?)
-                GROUP BY name, archive""",
-                (nickname, realname, realname + '-security'))
-            
-            if self.verbose:
-                print "    binary packages"
-            cursor.execute(
-                """INSERT INTO binary_packages
-                SELECT DISTINCT name, ?, archive,
-                MAX (version_id) AS vid, source, source_version,
-                ''
-                FROM binary_packages WHERE release IN (?, ?)
-                GROUP BY name, archive, archs""",
-                (nickname, realname, realname + '-security'))
-
-        if self.verbose:
-            print "  patch version strings"
-            print "    source packages"
-        cursor.execute(
-            """UPDATE source_packages
-            SET version = (SELECT version FROM version_linear_order
-            WHERE id = version_id)
-            WHERE version = ''""")
-        if self.verbose:
-            print "    binary packages"
-        cursor.execute(
-            """UPDATE binary_packages
-            SET version = (SELECT version FROM version_linear_order
-            WHERE id = version_id)
-            WHERE version = ''""")
-
-        if self.verbose:
-            print "  finished"
-
     def calculateVulnerabilities(self, cursor):
         """Calculate vulnerable packages.
 
@@ -949,19 +981,15 @@
     assert mergeLists('a,c', ['b', 'de']) == ['a', 'b', 'c', 'de']
 
     import os
-    if os.path.exists('test_security.db'):
-        os.unlink('test_security.db')
-    db = DB('test_security.db')
-    db.initSchema()
+    db_file = 'test_security.db'
+    try:
+        db = DB(db_file)
+    except SchemaMismatch:
+        os.unlink(db_file)
+        db = DB(db_file)
 
     cursor = db.writeTxn()
-    db.deleteBugs(cursor)
-    db.insertBugs(cursor, bugs.CVEFile('../../data/CAN/list'))
-    db.insertBugs(cursor, bugs.CVEFile('../../data/CVE/list',
-                                              no_version_needs_note=False))
-    db.insertBugs(cursor, bugs.DSAFile('../../data/DSA/list'))
-    db.insertBugs(cursor, bugs.DTSAFile('../../data/DTSA/list'))
-    db.finishBugs(cursor)
+    db.readBugs(cursor, '../../data')
     db.commit(cursor)
 
     b = bugs.BugFromDB(cursor, 'CAN-2005-2491')




More information about the Secure-testing-commits mailing list