Print this page
6842 Fix empty xattr dir causing lockup
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>

@@ -365,10 +365,13 @@
 mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
 {
         zap_t *winner;
         zap_t *zap;
         int i;
+        uint64_t *zap_hdr = (uint64_t *)db->db_data;
+        uint64_t zap_block_type = zap_hdr[0];
+        uint64_t zap_magic = zap_hdr[1];
 
         ASSERT3U(MZAP_ENT_LEN, ==, sizeof (mzap_ent_phys_t));
 
         zap = kmem_zalloc(sizeof (zap_t), KM_SLEEP);
         rw_init(&zap->zap_rwlock, 0, 0, 0);

@@ -375,13 +378,17 @@
         rw_enter(&zap->zap_rwlock, RW_WRITER);
         zap->zap_objset = os;
         zap->zap_object = obj;
         zap->zap_dbuf = db;
 
-        if (*(uint64_t *)db->db_data != ZBT_MICRO) {
+        if (zap_block_type != ZBT_MICRO) {
                 mutex_init(&zap->zap_f.zap_num_entries_mtx, 0, 0, 0);
                 zap->zap_f.zap_block_shift = highbit64(db->db_size) - 1;
+                if (zap_block_type != ZBT_HEADER || zap_magic != ZAP_MAGIC) {
+                        winner = NULL;  /* No actual winner here... */
+                        goto handle_winner;
+                }
         } else {
                 zap->zap_ismicro = TRUE;
         }
 
         /*

@@ -390,18 +397,12 @@
          * held.
          */
         dmu_buf_init_user(&zap->zap_dbu, zap_evict, &zap->zap_dbuf);
         winner = dmu_buf_set_user(db, &zap->zap_dbu);
 
-        if (winner != NULL) {
-                rw_exit(&zap->zap_rwlock);
-                rw_destroy(&zap->zap_rwlock);
-                if (!zap->zap_ismicro)
-                        mutex_destroy(&zap->zap_f.zap_num_entries_mtx);
-                kmem_free(zap, sizeof (zap_t));
-                return (winner);
-        }
+        if (winner != NULL)
+                goto handle_winner;
 
         if (zap->zap_ismicro) {
                 zap->zap_salt = zap_m_phys(zap)->mz_salt;
                 zap->zap_normflags = zap_m_phys(zap)->mz_normflags;
                 zap->zap_m.zap_num_chunks = db->db_size / MZAP_ENT_LEN - 1;

@@ -444,10 +445,18 @@
                     (uintptr_t)zap_f_phys(zap), ==,
                     zap->zap_dbuf->db_size);
         }
         rw_exit(&zap->zap_rwlock);
         return (zap);
+
+handle_winner:
+        rw_exit(&zap->zap_rwlock);
+        rw_destroy(&zap->zap_rwlock);
+        if (!zap->zap_ismicro)
+                mutex_destroy(&zap->zap_f.zap_num_entries_mtx);
+        kmem_free(zap, sizeof (zap_t));
+        return (winner);
 }
 
 int
 zap_lockdir(objset_t *os, uint64_t obj, dmu_tx_t *tx,
     krw_t lti, boolean_t fatreader, boolean_t adding, zap_t **zapp)

@@ -470,12 +479,21 @@
                 ASSERT3U(DMU_OT_BYTESWAP(doi.doi_type), ==, DMU_BSWAP_ZAP);
         }
 #endif
 
         zap = dmu_buf_get_user(db);
-        if (zap == NULL)
+        if (zap == NULL) {
                 zap = mzap_open(os, obj, db);
+                if (zap == NULL) {
+                        /*
+                         * mzap_open() didn't like what it saw on-disk.
+                         * Check for corruption!
+                         */
+                        dmu_buf_rele(db, NULL);
+                        return (SET_ERROR(EIO));
+                }
+        }
 
         /*
          * We're checking zap_ismicro without the lock held, in order to
          * tell what type of lock we want.  Once we have some sort of
          * lock, see if it really is the right type.  In practice this