20850a
commit da3a3a14e757ccd517e2eb2a3f0293ff48b3ff7f
20850a
Author: Panu Matilainen <pmatilai@redhat.com>
20850a
Date:   Wed Feb 5 13:36:31 2014 +0200
20850a
20850a
    A boolean flag is not sufficient to cover three different states...
20850a
    
20850a
    - HEADERFLAG_SORTED flag bit has been used to track the header
20850a
      sort state since beginning of times, but the header sort actually
20850a
      has three different states: truly unsorted (such as after additions),
20850a
      sorted by index achieved with headerSort(), or sorted by offset which
20850a
      is creatively called headerUnsort() in the API.
20850a
    
20850a
      There's exactly one place that requires offset sorting, namely
20850a
      headerExport(). It does call headerUnsort() explicitly, but
20850a
      headerUnsort() would not do anything unless the header was in
20850a
      index-sorted state. This is quite often the case, but data addition
20850a
      would unset the sorted-flag, and causing headerUnsort() not to
20850a
      do anything at all when it most certainly should have. That incorrect
20850a
      sorting caused headerExport() calculations to be wildly off.
20850a
    
20850a
      This ancient bug was unearthed by the innocent looking
20850a
      commit eae671556470136c37951f53ca966cd1bd09aac4: prior to that,
20850a
      in half the cases headerSizeof() used to be called first, causing
20850a
      index-sorting to take place, after which the headerUnsort() in
20850a
      headerExport() did the right thing. In the other two cases
20850a
      headerSizeof() was called afterwards, and the headerExport() would
20850a
      be garbage in cases where new data had been just added, such as package
20850a
      installation. With headerExport() eliminating the need to call
20850a
      headerSizeof() all the cases became broken as the header would
20850a
      seem much bigger than it is due to the bad sort order and cause
20850a
      cases like RhBug:953719 where a largish header suddenly appears
20850a
      to be oversized, causing mysterious install failure. Prior to the
20850a
      commit (which in itself IS innocent) the issue would've mainly
20850a
      manifested in cases where the header needs to be rewritten, which
20850a
      only happens when files are replaced and the header is rewritten for that.
20850a
20850a
diff --git a/lib/header.c b/lib/header.c
20850a
index ceffb32..f78ba78 100644
20850a
--- a/lib/header.c
20850a
+++ b/lib/header.c
20850a
@@ -68,8 +68,13 @@ static const int typeSizes[16] =  {
20850a
     0
20850a
 };
20850a
 
20850a
+enum headerSorted_e {
20850a
+    HEADERSORT_NONE	= 0,	/* Not sorted */
20850a
+    HEADERSORT_OFFSET	= 1,	/* Sorted by offset (on-disk format) */
20850a
+    HEADERSORT_INDEX	= 2,	/* Sorted by index  */
20850a
+};
20850a
+
20850a
 enum headerFlags_e {
20850a
-    HEADERFLAG_SORTED    = (1 << 0), /*!< Are header entries sorted? */
20850a
     HEADERFLAG_ALLOCATED = (1 << 1), /*!< Is 1st header region allocated? */
20850a
     HEADERFLAG_LEGACY    = (1 << 2), /*!< Header came from legacy source? */
20850a
     HEADERFLAG_DEBUG     = (1 << 3), /*!< Debug this header? */
20850a
@@ -87,6 +92,7 @@ struct headerToken_s {
20850a
     int indexAlloced;		/*!< Allocated size of tag array. */
20850a
     unsigned int instance;	/*!< Rpmdb instance (offset) */
20850a
     headerFlags flags;
20850a
+    int sorted;			/*!< Current sort method */
20850a
     int nrefs;			/*!< Reference count. */
20850a
 };
20850a
 
20850a
@@ -169,7 +175,7 @@ static Header headerCreate(void *blob, unsigned int pvlen, int32_t indexLen)
20850a
 	h->indexUsed = 0;
20850a
     }
20850a
     h->instance = 0;
20850a
-    h->flags |= HEADERFLAG_SORTED;
20850a
+    h->sorted = HEADERSORT_NONE;
20850a
 
20850a
     h->index = (h->indexAlloced
20850a
 	? xcalloc(h->indexAlloced, sizeof(*h->index))
20850a
@@ -219,9 +225,9 @@ static int indexCmp(const void * avp, const void * bvp)
20850a
 
20850a
 void headerSort(Header h)
20850a
 {
20850a
-    if (!(h->flags & HEADERFLAG_SORTED)) {
20850a
+    if (h->sorted != HEADERSORT_INDEX) {
20850a
 	qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
20850a
-	h->flags |= HEADERFLAG_SORTED;
20850a
+	h->sorted = HEADERSORT_INDEX;
20850a
     }
20850a
 }
20850a
 
20850a
@@ -242,9 +248,9 @@ static int offsetCmp(const void * avp, const void * bvp)
20850a
 
20850a
 void headerUnsort(Header h)
20850a
 {
20850a
-    if (h->flags & HEADERFLAG_SORTED) {
20850a
+    if (h->sorted != HEADERSORT_OFFSET) {
20850a
 	qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
20850a
-	h->flags &= ~HEADERFLAG_SORTED;
20850a
+	h->sorted = HEADERSORT_OFFSET;
20850a
     }
20850a
 }
20850a
 
20850a
@@ -721,7 +727,8 @@ indexEntry findEntry(Header h, rpmTagVal tag, rpm_tagtype_t type)
20850a
     struct indexEntry_s key;
20850a
 
20850a
     if (h == NULL) return NULL;
20850a
-    if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
20850a
+    if (h->sorted != HEADERSORT_INDEX)
20850a
+	headerSort(h);
20850a
 
20850a
     key.info.tag = tag;
20850a
 
20850a
@@ -907,7 +914,8 @@ Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags)
20850a
 	    goto errxit;
20850a
     }
20850a
 
20850a
-    h->flags &= ~HEADERFLAG_SORTED;
20850a
+    /* Force sorting, dribble lookups can cause early sort on partial header */
20850a
+    h->sorted = HEADERSORT_NONE;
20850a
     headerSort(h);
20850a
     h->flags |= HEADERFLAG_ALLOCATED;
20850a
 
20850a
@@ -1439,7 +1447,7 @@ static int intAddEntry(Header h, rpmtd td)
20850a
     entry->length = length;
20850a
 
20850a
     if (h->indexUsed > 0 && td->tag < h->index[h->indexUsed-1].info.tag)
20850a
-	h->flags &= ~HEADERFLAG_SORTED;
20850a
+	h->sorted = HEADERSORT_NONE;
20850a
     h->indexUsed++;
20850a
 
20850a
     return 1;