selinux: fix error return code in policydb_read()
[sfrench/cifs-2.6.git] / security / selinux / ss / policydb.c
index 2aa7f2e1a8e7c3ebb725b377e8f60b728a4ee6b9..a42369dd96a96b7a0ac2830eccaac6627a803839 100644 (file)
@@ -56,17 +56,6 @@ static const char *symtab_name[SYM_NUM] = {
 };
 #endif
 
-static unsigned int symtab_sizes[SYM_NUM] = {
-       2,
-       32,
-       16,
-       512,
-       128,
-       16,
-       16,
-       16,
-};
-
 struct policydb_compat_info {
        int version;
        int sym_num;
@@ -165,6 +154,11 @@ static struct policydb_compat_info policydb_compat[] = {
                .sym_num        = SYM_NUM,
                .ocon_num       = OCON_NUM,
        },
+       {
+               .version        = POLICYDB_VERSION_COMP_FTRANS,
+               .sym_num        = SYM_NUM,
+               .ocon_num       = OCON_NUM,
+       },
 };
 
 static struct policydb_compat_info *policydb_lookup_compat(int version)
@@ -336,11 +330,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
 
 static int filenametr_destroy(void *key, void *datum, void *p)
 {
-       struct filename_trans *ft = key;
+       struct filename_trans_key *ft = key;
+       struct filename_trans_datum *next, *d = datum;
 
        kfree(ft->name);
        kfree(key);
-       kfree(datum);
+       do {
+               ebitmap_destroy(&d->stypes);
+               next = d->next;
+               kfree(d);
+               d = next;
+       } while (unlikely(d));
        cond_resched();
        return 0;
 }
@@ -357,6 +357,13 @@ static int range_tr_destroy(void *key, void *datum, void *p)
        return 0;
 }
 
+static int role_tr_destroy(void *key, void *datum, void *p)
+{
+       kfree(key);
+       kfree(datum);
+       return 0;
+}
+
 static void ocontext_destroy(struct ocontext *c, int i)
 {
        if (!c)
@@ -406,12 +413,12 @@ out:
 
 static u32 filenametr_hash(struct hashtab *h, const void *k)
 {
-       const struct filename_trans *ft = k;
+       const struct filename_trans_key *ft = k;
        unsigned long hash;
        unsigned int byte_num;
        unsigned char focus;
 
-       hash = ft->stype ^ ft->ttype ^ ft->tclass;
+       hash = ft->ttype ^ ft->tclass;
 
        byte_num = 0;
        while ((focus = ft->name[byte_num++]))
@@ -421,14 +428,10 @@ static u32 filenametr_hash(struct hashtab *h, const void *k)
 
 static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
 {
-       const struct filename_trans *ft1 = k1;
-       const struct filename_trans *ft2 = k2;
+       const struct filename_trans_key *ft1 = k1;
+       const struct filename_trans_key *ft2 = k2;
        int v;
 
-       v = ft1->stype - ft2->stype;
-       if (v)
-               return v;
-
        v = ft1->ttype - ft2->ttype;
        if (v)
                return v;
@@ -467,59 +470,43 @@ static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
        return v;
 }
 
-/*
- * Initialize a policy database structure.
- */
-static int policydb_init(struct policydb *p)
+static u32 role_trans_hash(struct hashtab *h, const void *k)
 {
-       int i, rc;
+       const struct role_trans_key *key = k;
 
-       memset(p, 0, sizeof(*p));
+       return (key->role + (key->type << 3) + (key->tclass << 5)) &
+               (h->size - 1);
+}
 
-       for (i = 0; i < SYM_NUM; i++) {
-               rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
-               if (rc)
-                       goto out;
-       }
+static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2)
+{
+       const struct role_trans_key *key1 = k1, *key2 = k2;
+       int v;
 
-       rc = avtab_init(&p->te_avtab);
-       if (rc)
-               goto out;
+       v = key1->role - key2->role;
+       if (v)
+               return v;
 
-       rc = roles_init(p);
-       if (rc)
-               goto out;
+       v = key1->type - key2->type;
+       if (v)
+               return v;
 
-       rc = cond_policydb_init(p);
-       if (rc)
-               goto out;
+       return key1->tclass - key2->tclass;
+}
 
-       p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
-                                          (1 << 10));
-       if (!p->filename_trans) {
-               rc = -ENOMEM;
-               goto out;
-       }
+/*
+ * Initialize a policy database structure.
+ */
+static void policydb_init(struct policydb *p)
+{
+       memset(p, 0, sizeof(*p));
 
-       p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
-       if (!p->range_tr) {
-               rc = -ENOMEM;
-               goto out;
-       }
+       avtab_init(&p->te_avtab);
+       cond_policydb_init(p);
 
        ebitmap_init(&p->filename_trans_ttypes);
        ebitmap_init(&p->policycaps);
        ebitmap_init(&p->permissive_map);
-
-       return 0;
-out:
-       hashtab_destroy(p->filename_trans);
-       hashtab_destroy(p->range_tr);
-       for (i = 0; i < SYM_NUM; i++) {
-               hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
-               hashtab_destroy(p->symtab[i].table);
-       }
-       return rc;
 }
 
 /*
@@ -770,7 +757,6 @@ void policydb_destroy(struct policydb *p)
        struct genfs *g, *gtmp;
        int i;
        struct role_allow *ra, *lra = NULL;
-       struct role_trans *tr, *ltr = NULL;
 
        for (i = 0; i < SYM_NUM; i++) {
                cond_resched();
@@ -817,12 +803,8 @@ void policydb_destroy(struct policydb *p)
 
        cond_policydb_destroy(p);
 
-       for (tr = p->role_tr; tr; tr = tr->next) {
-               cond_resched();
-               kfree(ltr);
-               ltr = tr;
-       }
-       kfree(ltr);
+       hashtab_map(p->role_tr, role_tr_destroy, NULL);
+       hashtab_destroy(p->role_tr);
 
        for (ra = p->role_allow; ra; ra = ra->next) {
                cond_resched();
@@ -865,29 +847,23 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
 
        head = p->ocontexts[OCON_ISID];
        for (c = head; c; c = c->next) {
-               rc = -EINVAL;
-               if (!c->context[0].user) {
-                       pr_err("SELinux:  SID %s was never defined.\n",
-                               c->u.name);
-                       sidtab_destroy(s);
-                       goto out;
-               }
-               if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) {
-                       pr_err("SELinux:  Initial SID %s out of range.\n",
-                               c->u.name);
-                       sidtab_destroy(s);
-                       goto out;
-               }
-               rc = context_add_hash(p, &c->context[0]);
-               if (rc) {
+               u32 sid = c->sid[0];
+               const char *name = security_get_initial_sid_context(sid);
+
+               if (sid == SECSID_NULL) {
+                       pr_err("SELinux:  SID 0 was assigned a context.\n");
                        sidtab_destroy(s);
                        goto out;
                }
 
-               rc = sidtab_set_initial(s, c->sid[0], &c->context[0]);
+               /* Ignore initial SIDs unused by this kernel. */
+               if (!name)
+                       continue;
+
+               rc = sidtab_set_initial(s, sid, &c->context[0]);
                if (rc) {
                        pr_err("SELinux:  unable to load initial SID %s.\n",
-                               c->u.name);
+                              name);
                        sidtab_destroy(s);
                        goto out;
                }
@@ -1140,12 +1116,12 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp)
 
        len = le32_to_cpu(buf[0]);
        comdatum->value = le32_to_cpu(buf[1]);
+       nel = le32_to_cpu(buf[3]);
 
-       rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
+       rc = symtab_init(&comdatum->permissions, nel);
        if (rc)
                goto bad;
        comdatum->permissions.nprim = le32_to_cpu(buf[2]);
-       nel = le32_to_cpu(buf[3]);
 
        rc = str_read(&key, GFP_KERNEL, fp, len);
        if (rc)
@@ -1262,10 +1238,9 @@ static int read_cons_helper(struct policydb *p,
                                if (rc)
                                        return rc;
                                if (p->policyvers >=
-                                       POLICYDB_VERSION_CONSTRAINT_NAMES) {
-                                               e->type_names = kzalloc(sizeof
-                                               (*e->type_names),
-                                               GFP_KERNEL);
+                                   POLICYDB_VERSION_CONSTRAINT_NAMES) {
+                                       e->type_names = kzalloc(sizeof
+                                               (*e->type_names), GFP_KERNEL);
                                        if (!e->type_names)
                                                return -ENOMEM;
                                        type_set_init(e->type_names);
@@ -1306,12 +1281,12 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp)
        len = le32_to_cpu(buf[0]);
        len2 = le32_to_cpu(buf[1]);
        cladatum->value = le32_to_cpu(buf[2]);
+       nel = le32_to_cpu(buf[4]);
 
-       rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
+       rc = symtab_init(&cladatum->permissions, nel);
        if (rc)
                goto bad;
        cladatum->permissions.nprim = le32_to_cpu(buf[3]);
-       nel = le32_to_cpu(buf[4]);
 
        ncons = le32_to_cpu(buf[5]);
 
@@ -1824,6 +1799,11 @@ static int range_read(struct policydb *p, void *fp)
                return rc;
 
        nel = le32_to_cpu(buf[0]);
+
+       p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, nel);
+       if (!p->range_tr)
+               return -ENOMEM;
+
        for (i = 0; i < nel; i++) {
                rc = -ENOMEM;
                rt = kzalloc(sizeof(*rt), GFP_KERNEL);
@@ -1880,88 +1860,219 @@ out:
        return rc;
 }
 
-static int filename_trans_read(struct policydb *p, void *fp)
+static int filename_trans_read_helper_compat(struct policydb *p, void *fp)
 {
-       struct filename_trans *ft;
-       struct filename_trans_datum *otype;
-       char *name;
-       u32 nel, len;
+       struct filename_trans_key key, *ft = NULL;
+       struct filename_trans_datum *last, *datum = NULL;
+       char *name = NULL;
+       u32 len, stype, otype;
        __le32 buf[4];
-       int rc, i;
-
-       if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
-               return 0;
+       int rc;
 
+       /* length of the path component string */
        rc = next_entry(buf, fp, sizeof(u32));
        if (rc)
                return rc;
-       nel = le32_to_cpu(buf[0]);
+       len = le32_to_cpu(buf[0]);
 
-       for (i = 0; i < nel; i++) {
-               otype = NULL;
-               name = NULL;
+       /* path component string */
+       rc = str_read(&name, GFP_KERNEL, fp, len);
+       if (rc)
+               return rc;
 
+       rc = next_entry(buf, fp, sizeof(u32) * 4);
+       if (rc)
+               goto out;
+
+       stype = le32_to_cpu(buf[0]);
+       key.ttype = le32_to_cpu(buf[1]);
+       key.tclass = le32_to_cpu(buf[2]);
+       key.name = name;
+
+       otype = le32_to_cpu(buf[3]);
+
+       last = NULL;
+       datum = hashtab_search(p->filename_trans, &key);
+       while (datum) {
+               if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) {
+                       /* conflicting/duplicate rules are ignored */
+                       datum = NULL;
+                       goto out;
+               }
+               if (likely(datum->otype == otype))
+                       break;
+               last = datum;
+               datum = datum->next;
+       }
+       if (!datum) {
                rc = -ENOMEM;
-               ft = kzalloc(sizeof(*ft), GFP_KERNEL);
-               if (!ft)
+               datum = kmalloc(sizeof(*datum), GFP_KERNEL);
+               if (!datum)
                        goto out;
 
+               ebitmap_init(&datum->stypes);
+               datum->otype = otype;
+               datum->next = NULL;
+
+               if (unlikely(last)) {
+                       last->next = datum;
+               } else {
+                       rc = -ENOMEM;
+                       ft = kmemdup(&key, sizeof(key), GFP_KERNEL);
+                       if (!ft)
+                               goto out;
+
+                       rc = hashtab_insert(p->filename_trans, ft, datum);
+                       if (rc)
+                               goto out;
+                       name = NULL;
+
+                       rc = ebitmap_set_bit(&p->filename_trans_ttypes,
+                                            key.ttype, 1);
+                       if (rc)
+                               return rc;
+               }
+       }
+       kfree(name);
+       return ebitmap_set_bit(&datum->stypes, stype - 1, 1);
+
+out:
+       kfree(ft);
+       kfree(name);
+       kfree(datum);
+       return rc;
+}
+
+static int filename_trans_read_helper(struct policydb *p, void *fp)
+{
+       struct filename_trans_key *ft = NULL;
+       struct filename_trans_datum **dst, *datum, *first = NULL;
+       char *name = NULL;
+       u32 len, ttype, tclass, ndatum, i;
+       __le32 buf[3];
+       int rc;
+
+       /* length of the path component string */
+       rc = next_entry(buf, fp, sizeof(u32));
+       if (rc)
+               return rc;
+       len = le32_to_cpu(buf[0]);
+
+       /* path component string */
+       rc = str_read(&name, GFP_KERNEL, fp, len);
+       if (rc)
+               return rc;
+
+       rc = next_entry(buf, fp, sizeof(u32) * 3);
+       if (rc)
+               goto out;
+
+       ttype = le32_to_cpu(buf[0]);
+       tclass = le32_to_cpu(buf[1]);
+
+       ndatum = le32_to_cpu(buf[2]);
+       if (ndatum == 0) {
+               pr_err("SELinux:  Filename transition key with no datum\n");
+               rc = -ENOENT;
+               goto out;
+       }
+
+       dst = &first;
+       for (i = 0; i < ndatum; i++) {
                rc = -ENOMEM;
-               otype = kmalloc(sizeof(*otype), GFP_KERNEL);
-               if (!otype)
+               datum = kmalloc(sizeof(*datum), GFP_KERNEL);
+               if (!datum)
                        goto out;
 
-               /* length of the path component string */
-               rc = next_entry(buf, fp, sizeof(u32));
+               *dst = datum;
+
+               /* ebitmap_read() will at least init the bitmap */
+               rc = ebitmap_read(&datum->stypes, fp);
                if (rc)
                        goto out;
-               len = le32_to_cpu(buf[0]);
 
-               /* path component string */
-               rc = str_read(&name, GFP_KERNEL, fp, len);
+               rc = next_entry(buf, fp, sizeof(u32));
                if (rc)
                        goto out;
 
-               ft->name = name;
+               datum->otype = le32_to_cpu(buf[0]);
+               datum->next = NULL;
 
-               rc = next_entry(buf, fp, sizeof(u32) * 4);
-               if (rc)
-                       goto out;
+               dst = &datum->next;
+       }
 
-               ft->stype = le32_to_cpu(buf[0]);
-               ft->ttype = le32_to_cpu(buf[1]);
-               ft->tclass = le32_to_cpu(buf[2]);
+       rc = -ENOMEM;
+       ft = kmalloc(sizeof(*ft), GFP_KERNEL);
+       if (!ft)
+               goto out;
 
-               otype->otype = le32_to_cpu(buf[3]);
+       ft->ttype = ttype;
+       ft->tclass = tclass;
+       ft->name = name;
 
-               rc = ebitmap_set_bit(&p->filename_trans_ttypes, ft->ttype, 1);
-               if (rc)
-                       goto out;
+       rc = hashtab_insert(p->filename_trans, ft, first);
+       if (rc == -EEXIST)
+               pr_err("SELinux:  Duplicate filename transition key\n");
+       if (rc)
+               goto out;
+
+       return ebitmap_set_bit(&p->filename_trans_ttypes, ttype, 1);
 
-               rc = hashtab_insert(p->filename_trans, ft, otype);
-               if (rc) {
-                       /*
-                        * Do not return -EEXIST to the caller, or the system
-                        * will not boot.
-                        */
-                       if (rc != -EEXIST)
-                               goto out;
-                       /* But free memory to avoid memory leak. */
-                       kfree(ft);
-                       kfree(name);
-                       kfree(otype);
-               }
-       }
-       hash_eval(p->filename_trans, "filenametr");
-       return 0;
 out:
        kfree(ft);
        kfree(name);
-       kfree(otype);
+       while (first) {
+               datum = first;
+               first = first->next;
 
+               ebitmap_destroy(&datum->stypes);
+               kfree(datum);
+       }
        return rc;
 }
 
+static int filename_trans_read(struct policydb *p, void *fp)
+{
+       u32 nel;
+       __le32 buf[1];
+       int rc, i;
+
+       if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
+               return 0;
+
+       rc = next_entry(buf, fp, sizeof(u32));
+       if (rc)
+               return rc;
+       nel = le32_to_cpu(buf[0]);
+
+       if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+               p->compat_filename_trans_count = nel;
+               p->filename_trans = hashtab_create(filenametr_hash,
+                                                  filenametr_cmp, (1 << 11));
+               if (!p->filename_trans)
+                       return -ENOMEM;
+
+               for (i = 0; i < nel; i++) {
+                       rc = filename_trans_read_helper_compat(p, fp);
+                       if (rc)
+                               return rc;
+               }
+       } else {
+               p->filename_trans = hashtab_create(filenametr_hash,
+                                                  filenametr_cmp, nel);
+               if (!p->filename_trans)
+                       return -ENOMEM;
+
+               for (i = 0; i < nel; i++) {
+                       rc = filename_trans_read_helper(p, fp);
+                       if (rc)
+                               return rc;
+               }
+       }
+       hash_eval(p->filename_trans, "filenametr");
+       return 0;
+}
+
 static int genfs_read(struct policydb *p, void *fp)
 {
        int i, j, rc;
@@ -2264,7 +2375,8 @@ out:
 int policydb_read(struct policydb *p, void *fp)
 {
        struct role_allow *ra, *lra;
-       struct role_trans *tr, *ltr;
+       struct role_trans_key *rtk = NULL;
+       struct role_trans_datum *rtd = NULL;
        int i, j, rc;
        __le32 buf[4];
        u32 len, nprim, nel;
@@ -2272,9 +2384,7 @@ int policydb_read(struct policydb *p, void *fp)
        char *policydb_str;
        struct policydb_compat_info *info;
 
-       rc = policydb_init(p);
-       if (rc)
-               return rc;
+       policydb_init(p);
 
        /* Read the magic number and string length. */
        rc = next_entry(buf, fp, sizeof(u32) * 2);
@@ -2390,6 +2500,17 @@ int policydb_read(struct policydb *p, void *fp)
                        goto bad;
                nprim = le32_to_cpu(buf[0]);
                nel = le32_to_cpu(buf[1]);
+
+               rc = symtab_init(&p->symtab[i], nel);
+               if (rc)
+                       goto out;
+
+               if (i == SYM_ROLES) {
+                       rc = roles_init(p);
+                       if (rc)
+                               goto out;
+               }
+
                for (j = 0; j < nel; j++) {
                        rc = read_f[i](p, p->symtab[i].table, fp);
                        if (rc)
@@ -2418,39 +2539,50 @@ int policydb_read(struct policydb *p, void *fp)
        if (rc)
                goto bad;
        nel = le32_to_cpu(buf[0]);
-       ltr = NULL;
+
+       p->role_tr = hashtab_create(role_trans_hash, role_trans_cmp, nel);
+       if (!p->role_tr)
+               goto bad;
        for (i = 0; i < nel; i++) {
                rc = -ENOMEM;
-               tr = kzalloc(sizeof(*tr), GFP_KERNEL);
-               if (!tr)
+               rtk = kmalloc(sizeof(*rtk), GFP_KERNEL);
+               if (!rtk)
                        goto bad;
-               if (ltr)
-                       ltr->next = tr;
-               else
-                       p->role_tr = tr;
+
+               rc = -ENOMEM;
+               rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+               if (!rtd)
+                       goto bad;
+
                rc = next_entry(buf, fp, sizeof(u32)*3);
                if (rc)
                        goto bad;
 
                rc = -EINVAL;
-               tr->role = le32_to_cpu(buf[0]);
-               tr->type = le32_to_cpu(buf[1]);
-               tr->new_role = le32_to_cpu(buf[2]);
+               rtk->role = le32_to_cpu(buf[0]);
+               rtk->type = le32_to_cpu(buf[1]);
+               rtd->new_role = le32_to_cpu(buf[2]);
                if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
                        rc = next_entry(buf, fp, sizeof(u32));
                        if (rc)
                                goto bad;
-                       tr->tclass = le32_to_cpu(buf[0]);
+                       rtk->tclass = le32_to_cpu(buf[0]);
                } else
-                       tr->tclass = p->process_class;
+                       rtk->tclass = p->process_class;
 
                rc = -EINVAL;
-               if (!policydb_role_isvalid(p, tr->role) ||
-                   !policydb_type_isvalid(p, tr->type) ||
-                   !policydb_class_isvalid(p, tr->tclass) ||
-                   !policydb_role_isvalid(p, tr->new_role))
+               if (!policydb_role_isvalid(p, rtk->role) ||
+                   !policydb_type_isvalid(p, rtk->type) ||
+                   !policydb_class_isvalid(p, rtk->tclass) ||
+                   !policydb_role_isvalid(p, rtd->new_role))
                        goto bad;
-               ltr = tr;
+
+               rc = hashtab_insert(p->role_tr, rtk, rtd);
+               if (rc)
+                       goto bad;
+
+               rtk = NULL;
+               rtd = NULL;
        }
 
        rc = next_entry(buf, fp, sizeof(u32));
@@ -2506,6 +2638,7 @@ int policydb_read(struct policydb *p, void *fp)
        if (rc)
                goto bad;
 
+       rc = -ENOMEM;
        p->type_attr_map_array = kvcalloc(p->p_types.nprim,
                                          sizeof(*p->type_attr_map_array),
                                          GFP_KERNEL);
@@ -2538,6 +2671,8 @@ int policydb_read(struct policydb *p, void *fp)
 out:
        return rc;
 bad:
+       kfree(rtk);
+       kfree(rtd);
        policydb_destroy(p);
        goto out;
 }
@@ -2655,39 +2790,45 @@ static int cat_write(void *vkey, void *datum, void *ptr)
        return 0;
 }
 
-static int role_trans_write(struct policydb *p, void *fp)
+static int role_trans_write_one(void *key, void *datum, void *ptr)
 {
-       struct role_trans *r = p->role_tr;
-       struct role_trans *tr;
+       struct role_trans_key *rtk = key;
+       struct role_trans_datum *rtd = datum;
+       struct policy_data *pd = ptr;
+       void *fp = pd->fp;
+       struct policydb *p = pd->p;
        __le32 buf[3];
-       size_t nel;
        int rc;
 
-       nel = 0;
-       for (tr = r; tr; tr = tr->next)
-               nel++;
-       buf[0] = cpu_to_le32(nel);
-       rc = put_entry(buf, sizeof(u32), 1, fp);
+       buf[0] = cpu_to_le32(rtk->role);
+       buf[1] = cpu_to_le32(rtk->type);
+       buf[2] = cpu_to_le32(rtd->new_role);
+       rc = put_entry(buf, sizeof(u32), 3, fp);
        if (rc)
                return rc;
-       for (tr = r; tr; tr = tr->next) {
-               buf[0] = cpu_to_le32(tr->role);
-               buf[1] = cpu_to_le32(tr->type);
-               buf[2] = cpu_to_le32(tr->new_role);
-               rc = put_entry(buf, sizeof(u32), 3, fp);
+       if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
+               buf[0] = cpu_to_le32(rtk->tclass);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
                if (rc)
                        return rc;
-               if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) {
-                       buf[0] = cpu_to_le32(tr->tclass);
-                       rc = put_entry(buf, sizeof(u32), 1, fp);
-                       if (rc)
-                               return rc;
-               }
        }
-
        return 0;
 }
 
+static int role_trans_write(struct policydb *p, void *fp)
+{
+       struct policy_data pd = { .p = p, .fp = fp };
+       __le32 buf[1];
+       int rc;
+
+       buf[0] = cpu_to_le32(p->role_tr->nel);
+       rc = put_entry(buf, sizeof(u32), 1, fp);
+       if (rc)
+               return rc;
+
+       return hashtab_map(p->role_tr, role_trans_write_one, &pd);
+}
+
 static int role_allow_write(struct role_allow *r, void *fp)
 {
        struct role_allow *ra;
@@ -3328,16 +3469,52 @@ static int range_write(struct policydb *p, void *fp)
        return 0;
 }
 
-static int filename_write_helper(void *key, void *data, void *ptr)
+static int filename_write_helper_compat(void *key, void *data, void *ptr)
 {
+       struct filename_trans_key *ft = key;
+       struct filename_trans_datum *datum = data;
+       struct ebitmap_node *node;
+       void *fp = ptr;
        __le32 buf[4];
-       struct filename_trans *ft = key;
-       struct filename_trans_datum *otype = data;
+       int rc;
+       u32 bit, len = strlen(ft->name);
+
+       do {
+               ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
+                       buf[0] = cpu_to_le32(len);
+                       rc = put_entry(buf, sizeof(u32), 1, fp);
+                       if (rc)
+                               return rc;
+
+                       rc = put_entry(ft->name, sizeof(char), len, fp);
+                       if (rc)
+                               return rc;
+
+                       buf[0] = cpu_to_le32(bit + 1);
+                       buf[1] = cpu_to_le32(ft->ttype);
+                       buf[2] = cpu_to_le32(ft->tclass);
+                       buf[3] = cpu_to_le32(datum->otype);
+
+                       rc = put_entry(buf, sizeof(u32), 4, fp);
+                       if (rc)
+                               return rc;
+               }
+
+               datum = datum->next;
+       } while (unlikely(datum));
+
+       return 0;
+}
+
+static int filename_write_helper(void *key, void *data, void *ptr)
+{
+       struct filename_trans_key *ft = key;
+       struct filename_trans_datum *datum;
        void *fp = ptr;
+       __le32 buf[3];
        int rc;
-       u32 len;
+       u32 ndatum, len = strlen(ft->name);
 
-       len = strlen(ft->name);
        buf[0] = cpu_to_le32(len);
        rc = put_entry(buf, sizeof(u32), 1, fp);
        if (rc)
@@ -3347,42 +3524,62 @@ static int filename_write_helper(void *key, void *data, void *ptr)
        if (rc)
                return rc;
 
-       buf[0] = cpu_to_le32(ft->stype);
-       buf[1] = cpu_to_le32(ft->ttype);
-       buf[2] = cpu_to_le32(ft->tclass);
-       buf[3] = cpu_to_le32(otype->otype);
+       ndatum = 0;
+       datum = data;
+       do {
+               ndatum++;
+               datum = datum->next;
+       } while (unlikely(datum));
 
-       rc = put_entry(buf, sizeof(u32), 4, fp);
+       buf[0] = cpu_to_le32(ft->ttype);
+       buf[1] = cpu_to_le32(ft->tclass);
+       buf[2] = cpu_to_le32(ndatum);
+       rc = put_entry(buf, sizeof(u32), 3, fp);
        if (rc)
                return rc;
 
+       datum = data;
+       do {
+               rc = ebitmap_write(&datum->stypes, fp);
+               if (rc)
+                       return rc;
+
+               buf[0] = cpu_to_le32(datum->otype);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
+
+               datum = datum->next;
+       } while (unlikely(datum));
+
        return 0;
 }
 
 static int filename_trans_write(struct policydb *p, void *fp)
 {
-       u32 nel;
        __le32 buf[1];
        int rc;
 
        if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
                return 0;
 
-       nel = 0;
-       rc = hashtab_map(p->filename_trans, hashtab_cnt, &nel);
-       if (rc)
-               return rc;
-
-       buf[0] = cpu_to_le32(nel);
-       rc = put_entry(buf, sizeof(u32), 1, fp);
-       if (rc)
-               return rc;
+       if (p->policyvers < POLICYDB_VERSION_COMP_FTRANS) {
+               buf[0] = cpu_to_le32(p->compat_filename_trans_count);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
 
-       rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
-       if (rc)
-               return rc;
+               rc = hashtab_map(p->filename_trans,
+                                filename_write_helper_compat, fp);
+       } else {
+               buf[0] = cpu_to_le32(p->filename_trans->nel);
+               rc = put_entry(buf, sizeof(u32), 1, fp);
+               if (rc)
+                       return rc;
 
-       return 0;
+               rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
+       }
+       return rc;
 }
 
 /*
@@ -3483,7 +3680,7 @@ int policydb_write(struct policydb *p, void *fp)
        if (rc)
                return rc;
 
-       rc = cond_write_list(p, p->cond_list, fp);
+       rc = cond_write_list(p, fp);
        if (rc)
                return rc;