#include "security.h"
 
-int security_get_bools(struct selinux_state *state,
+int security_get_bools(struct selinux_policy *policy,
                       u32 *len, char ***names, int **values);
 
 int security_set_bools(struct selinux_state *state, u32 len, int *values);
 
 
 struct selinux_avc;
 struct selinux_ss;
+struct selinux_policy;
 
 struct selinux_state {
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 
 int security_mls_enabled(struct selinux_state *state);
 int security_load_policy(struct selinux_state *state,
-                        void *data, size_t len);
+                       void *data, size_t len,
+                       struct selinux_policy **newpolicyp);
+void selinux_policy_commit(struct selinux_state *state,
+                       struct selinux_policy *newpolicy);
+void selinux_policy_cancel(struct selinux_state *state,
+                       struct selinux_policy *policy);
 int security_read_policy(struct selinux_state *state,
                         void **data, size_t *len);
 size_t security_policydb_len(struct selinux_state *state);
                                 u32 xfrm_sid,
                                 u32 *peer_sid);
 
-int security_get_classes(struct selinux_state *state,
+int security_get_classes(struct selinux_policy *policy,
                         char ***classes, int *nclasses);
-int security_get_permissions(struct selinux_state *state,
+int security_get_permissions(struct selinux_policy *policy,
                             char *class, char ***perms, int *nperms);
 int security_get_reject_unknown(struct selinux_state *state);
 int security_get_allow_unknown(struct selinux_state *state);
                       const char *fstype, char *name, u16 sclass,
                       u32 *sid);
 
+int selinux_policy_genfs_sid(struct selinux_policy *policy,
+                      const char *fstype, char *name, u16 sclass,
+                      u32 *sid);
+
 #ifdef CONFIG_NETLABEL
 int security_netlbl_secattr_to_sid(struct selinux_state *state,
                                   struct netlbl_lsm_secattr *secattr,
 
 };
 
 /* declaration for sel_write_load */
-static int sel_make_bools(struct selinux_fs_info *fsi);
-static int sel_make_classes(struct selinux_fs_info *fsi);
-static int sel_make_policycap(struct selinux_fs_info *fsi);
+static int sel_make_bools(struct selinux_fs_info *fsi,
+                       struct selinux_policy *newpolicy);
+static int sel_make_classes(struct selinux_fs_info *fsi,
+                       struct selinux_policy *newpolicy);
 
 /* declaration for sel_make_class_dirs */
 static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
        .llseek         = generic_file_llseek,
 };
 
-static int sel_make_policy_nodes(struct selinux_fs_info *fsi)
+static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
+                               struct selinux_policy *newpolicy)
 {
        int ret;
 
-       ret = sel_make_bools(fsi);
+       ret = sel_make_bools(fsi, newpolicy);
        if (ret) {
                pr_err("SELinux: failed to load policy booleans\n");
                return ret;
        }
 
-       ret = sel_make_classes(fsi);
+       ret = sel_make_classes(fsi, newpolicy);
        if (ret) {
                pr_err("SELinux: failed to load policy classes\n");
                return ret;
        }
 
-       ret = sel_make_policycap(fsi);
-       if (ret) {
-               pr_err("SELinux: failed to load policy capabilities\n");
-               return ret;
-       }
-
        return 0;
 }
 
 
 {
        struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+       struct selinux_policy *newpolicy;
        ssize_t length;
        void *data = NULL;
 
        if (copy_from_user(data, buf, count) != 0)
                goto out;
 
-       length = security_load_policy(fsi->state, data, count);
+       length = security_load_policy(fsi->state, data, count, &newpolicy);
        if (length) {
                pr_warn_ratelimited("SELinux: failed to load policy\n");
                goto out;
        }
 
-       length = sel_make_policy_nodes(fsi);
-       if (length)
+       length = sel_make_policy_nodes(fsi, newpolicy);
+       if (length) {
+               selinux_policy_cancel(fsi->state, newpolicy);
                goto out1;
+       }
+
+       selinux_policy_commit(fsi->state, newpolicy);
 
        length = count;
 
 
 #define BOOL_DIR_NAME "booleans"
 
-static int sel_make_bools(struct selinux_fs_info *fsi)
+static int sel_make_bools(struct selinux_fs_info *fsi,
+                       struct selinux_policy *newpolicy)
 {
        int ret;
        ssize_t len;
        if (!page)
                goto out;
 
-       ret = security_get_bools(fsi->state, &num, &names, &values);
+       ret = security_get_bools(newpolicy, &num, &names, &values);
        if (ret)
                goto out;
 
                }
 
                isec = selinux_inode(inode);
-               ret = security_genfs_sid(fsi->state, "selinuxfs", page,
+               ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page,
                                         SECCLASS_FILE, &sid);
                if (ret) {
                        pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
        .llseek         = generic_file_llseek,
 };
 
-static int sel_make_perm_files(char *objclass, int classvalue,
-                               struct dentry *dir)
+static int sel_make_perm_files(struct selinux_policy *newpolicy,
+                       char *objclass, int classvalue,
+                       struct dentry *dir)
 {
-       struct selinux_fs_info *fsi = dir->d_sb->s_fs_info;
        int i, rc, nperms;
        char **perms;
 
-       rc = security_get_permissions(fsi->state, objclass, &perms, &nperms);
+       rc = security_get_permissions(newpolicy, objclass, &perms, &nperms);
        if (rc)
                return rc;
 
        return rc;
 }
 
-static int sel_make_class_dir_entries(char *classname, int index,
-                                       struct dentry *dir)
+static int sel_make_class_dir_entries(struct selinux_policy *newpolicy,
+                               char *classname, int index,
+                               struct dentry *dir)
 {
        struct super_block *sb = dir->d_sb;
        struct selinux_fs_info *fsi = sb->s_fs_info;
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
 
-       rc = sel_make_perm_files(classname, index, dentry);
+       rc = sel_make_perm_files(newpolicy, classname, index, dentry);
 
        return rc;
 }
 
-static int sel_make_classes(struct selinux_fs_info *fsi)
+static int sel_make_classes(struct selinux_fs_info *fsi,
+                       struct selinux_policy *newpolicy)
 {
 
        int rc, nclasses, i;
        /* delete any existing entries */
        sel_remove_entries(fsi->class_dir);
 
-       rc = security_get_classes(fsi->state, &classes, &nclasses);
+       rc = security_get_classes(newpolicy, &classes, &nclasses);
        if (rc)
                return rc;
 
                }
 
                /* i+1 since class values are 1-indexed */
-               rc = sel_make_class_dir_entries(classes[i], i + 1,
+               rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1,
                                class_name_dir);
                if (rc)
                        goto out;
        struct dentry *dentry = NULL;
        struct inode *inode = NULL;
 
-       sel_remove_entries(fsi->policycap_dir);
-
        for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
                if (iter < ARRAY_SIZE(selinux_policycap_names))
                        dentry = d_alloc_name(fsi->policycap_dir,
                goto err;
        }
 
-       ret = sel_make_policy_nodes(fsi);
-       if (ret)
+       ret = sel_make_policycap(fsi);
+       if (ret) {
+               pr_err("SELinux: failed to load policy capabilities\n");
                goto err;
+       }
+
        return 0;
 err:
        pr_err("SELinux: %s:  failed while creating inodes\n",
 
        kfree(policy);
 }
 
-static void selinux_policy_commit(struct selinux_state *state,
-                               struct selinux_policy *newpolicy)
+void selinux_policy_cancel(struct selinux_state *state,
+                       struct selinux_policy *policy)
+{
+
+       sidtab_cancel_convert(&state->ss->policy->sidtab);
+       selinux_policy_free(policy);
+}
+
+void selinux_policy_commit(struct selinux_state *state,
+                       struct selinux_policy *newpolicy)
 {
        struct selinux_policy *oldpolicy;
        u32 seqno;
  * This function will flush the access vector cache after
  * loading the new policy.
  */
-int security_load_policy(struct selinux_state *state, void *data, size_t len)
+int security_load_policy(struct selinux_state *state, void *data, size_t len,
+                       struct selinux_policy **newpolicyp)
 {
        struct selinux_policy *newpolicy;
        struct sidtab_convert_params convert_params;
 
        if (!selinux_initialized(state)) {
                /* First policy load, so no need to preserve state from old policy */
-               selinux_policy_commit(state, newpolicy);
+               *newpolicyp = newpolicy;
                return 0;
        }
 
                goto err;
        }
 
-       selinux_policy_commit(state, newpolicy);
+       *newpolicyp = newpolicy;
        return 0;
 err:
        selinux_policy_free(newpolicy);
  * Obtain a SID to use for a file in a filesystem that
  * cannot support xattr or use a fixed labeling behavior like
  * transition SIDs or task SIDs.
- *
- * The caller must acquire the policy_rwlock before calling this function.
  */
-static inline int __security_genfs_sid(struct selinux_state *state,
+static inline int __security_genfs_sid(struct selinux_policy *policy,
                                       const char *fstype,
                                       char *path,
                                       u16 orig_sclass,
                                       u32 *sid)
 {
-       struct policydb *policydb = &state->ss->policy->policydb;
-       struct sidtab *sidtab = &state->ss->policy->sidtab;
+       struct policydb *policydb = &policy->policydb;
+       struct sidtab *sidtab = &policy->sidtab;
        int len;
        u16 sclass;
        struct genfs *genfs;
        while (path[0] == '/' && path[1] == '/')
                path++;
 
-       sclass = unmap_class(&state->ss->policy->map, orig_sclass);
+       sclass = unmap_class(&policy->map, orig_sclass);
        *sid = SECINITSID_UNLABELED;
 
        for (genfs = policydb->genfs; genfs; genfs = genfs->next) {
        int retval;
 
        read_lock(&state->ss->policy_rwlock);
-       retval = __security_genfs_sid(state, fstype, path, orig_sclass, sid);
+       retval = __security_genfs_sid(state->ss->policy,
+                               fstype, path, orig_sclass, sid);
        read_unlock(&state->ss->policy_rwlock);
        return retval;
 }
 
+int selinux_policy_genfs_sid(struct selinux_policy *policy,
+                       const char *fstype,
+                       char *path,
+                       u16 orig_sclass,
+                       u32 *sid)
+{
+       /* no lock required, policy is not yet accessible by other threads */
+       return __security_genfs_sid(policy, fstype, path, orig_sclass, sid);
+}
+
 /**
  * security_fs_use - Determine how to handle labeling for a filesystem.
  * @sb: superblock in question
                }
                sbsec->sid = c->sid[0];
        } else {
-               rc = __security_genfs_sid(state, fstype, "/", SECCLASS_DIR,
-                                         &sbsec->sid);
+               rc = __security_genfs_sid(state->ss->policy, fstype, "/",
+                                       SECCLASS_DIR, &sbsec->sid);
                if (rc) {
                        sbsec->behavior = SECURITY_FS_USE_NONE;
                        rc = 0;
        return rc;
 }
 
-int security_get_bools(struct selinux_state *state,
+int security_get_bools(struct selinux_policy *policy,
                       u32 *len, char ***names, int **values)
 {
        struct policydb *policydb;
        u32 i;
        int rc;
 
-       if (!selinux_initialized(state)) {
-               *len = 0;
-               *names = NULL;
-               *values = NULL;
-               return 0;
-       }
-
-       read_lock(&state->ss->policy_rwlock);
-
-       policydb = &state->ss->policy->policydb;
+       policydb = &policy->policydb;
 
        *names = NULL;
        *values = NULL;
        }
        rc = 0;
 out:
-       read_unlock(&state->ss->policy_rwlock);
        return rc;
 err:
        if (*names) {
        struct cond_bool_datum *booldatum;
        u32 i, nbools = 0;
 
-       rc = security_get_bools(state, &nbools, &bnames, &bvalues);
+       read_lock(&state->ss->policy_rwlock);
+       rc = security_get_bools(state->ss->policy, &nbools, &bnames, &bvalues);
+       read_unlock(&state->ss->policy_rwlock);
        if (rc)
                goto out;
        for (i = 0; i < nbools; i++) {
        return 0;
 }
 
-int security_get_classes(struct selinux_state *state,
+int security_get_classes(struct selinux_policy *policy,
                         char ***classes, int *nclasses)
 {
        struct policydb *policydb;
        int rc;
 
-       if (!selinux_initialized(state)) {
-               *nclasses = 0;
-               *classes = NULL;
-               return 0;
-       }
-
-       read_lock(&state->ss->policy_rwlock);
-
-       policydb = &state->ss->policy->policydb;
+       policydb = &policy->policydb;
 
        rc = -ENOMEM;
        *nclasses = policydb->p_classes.nprim;
        }
 
 out:
-       read_unlock(&state->ss->policy_rwlock);
        return rc;
 }
 
        return 0;
 }
 
-int security_get_permissions(struct selinux_state *state,
+int security_get_permissions(struct selinux_policy *policy,
                             char *class, char ***perms, int *nperms)
 {
        struct policydb *policydb;
        int rc, i;
        struct class_datum *match;
 
-       read_lock(&state->ss->policy_rwlock);
-
-       policydb = &state->ss->policy->policydb;
+       policydb = &policy->policydb;
 
        rc = -EINVAL;
        match = symtab_search(&policydb->p_classes, class);
                goto err;
 
 out:
-       read_unlock(&state->ss->policy_rwlock);
        return rc;
 
 err:
-       read_unlock(&state->ss->policy_rwlock);
        for (i = 0; i < *nperms; i++)
                kfree((*perms)[i]);
        kfree(*perms);
 
        return 0;
 }
 
+void sidtab_cancel_convert(struct sidtab *s)
+{
+       unsigned long flags;
+
+       /* cancelling policy load - disable live convert of sidtab */
+       spin_lock_irqsave(&s->lock, flags);
+       s->convert = NULL;
+       spin_unlock_irqrestore(&s->lock, flags);
+}
+
 static void sidtab_destroy_entry(struct sidtab_entry *entry)
 {
        context_destroy(&entry->context);
 
 
 int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
 
+void sidtab_cancel_convert(struct sidtab *s);
+
 int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
 
 void sidtab_destroy(struct sidtab *s);