This file corresponds to security/tomoyo/common.c . There are 4 major
policy files ("domain_policy", "exception_policy", "profile" and
"manager") in TOMOYO while there is only 1 major policy file ("policy")
in CaitSith.

Signed-off-by: Tetsuo Handa <penguin-ker...@i-love.sakura.ne.jp>
---
 security/caitsith/policy_io.c | 1553 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1553 insertions(+)
 create mode 100644 security/caitsith/policy_io.c

diff --git a/security/caitsith/policy_io.c b/security/caitsith/policy_io.c
new file mode 100644
index 0000000..060845f
--- /dev/null
+++ b/security/caitsith/policy_io.c
@@ -0,0 +1,1553 @@
+/*
+ * security/caitsith/policy_io.c
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#include <linux/lsm_hooks.h>
+#include "caitsith.h"
+
+/* Define this to enable debug mode. */
+/* #define DEBUG_CONDITION */
+
+#ifdef DEBUG_CONDITION
+#define dprintk printk
+#else
+#define dprintk(...) do { } while (0)
+#endif
+
+/* String table for operation. */
+static const char * const cs_mac_keywords[CS_MAX_MAC_INDEX] = {
+       [CS_MAC_EXECUTE] = "execute",
+       [CS_MAC_MODIFY_POLICY] = "modify_policy",
+};
+
+/* String table for stat info. */
+static const char * const cs_memory_headers[CS_MAX_MEMORY_STAT] = {
+       [CS_MEMORY_POLICY] = "policy",
+};
+
+#define F(bit) (1U << bit)
+
+enum cs_var_type {
+       CS_TYPE_INVALID,
+       CS_TYPE_STRING,
+} __packed;
+
+/* String table for conditions. */
+static const struct {
+       const char * const keyword;
+       const enum cs_var_type left_type;
+       const enum cs_var_type right_type;
+       const enum cs_conditions_index cmd;
+       const u8 available;
+} cs_conditions[] = {
+       { "exec", CS_TYPE_STRING, CS_TYPE_STRING, CS_COND_SARG1,
+         F(CS_MAC_EXECUTE) },
+       { "path", CS_TYPE_STRING, CS_TYPE_STRING, CS_COND_SARG0,
+         F(CS_MAC_EXECUTE) },
+       { "task.exe", CS_TYPE_STRING, CS_TYPE_STRING, CS_SELF_EXE,
+         F(CS_MAC_EXECUTE) | F(CS_MAC_MODIFY_POLICY) },
+};
+
+/* Structure for holding single condition component. */
+struct cs_cond_tmp {
+       enum cs_conditions_index left;
+       enum cs_conditions_index right;
+       bool is_not;
+       enum cs_var_type type;
+       const struct cs_path_info *path;
+};
+
+static bool cs_correct_word(const char *string);
+static bool cs_flush(struct cs_io_buffer *head);
+static bool cs_print_condition(struct cs_io_buffer *head,
+                              const struct cs_condition *cond);
+static bool cs_memory_ok(const void *ptr);
+static bool cs_read_acl(struct cs_io_buffer *head,
+                       const struct cs_acl_info *acl);
+static bool cs_set_lf(struct cs_io_buffer *head);
+static char *cs_read_token(struct cs_io_buffer *head);
+static const struct cs_path_info *cs_get_dqword(char *start);
+static const struct cs_path_info *cs_get_name(const char *name);
+static int cs_open(struct inode *inode, struct file *file);
+static int cs_parse_policy(struct cs_io_buffer *head, char *line);
+static int cs_release(struct inode *inode, struct file *file);
+static int cs_write_policy(struct cs_io_buffer *head);
+static ssize_t cs_read(struct file *file, char __user *buf, size_t count,
+                      loff_t *ppos);
+static ssize_t cs_write(struct file *file, const char __user *buf,
+                       size_t count, loff_t *ppos);
+static struct cs_condition *cs_get_condition(struct cs_io_buffer *head);
+static void __init cs_create_entry(const char *name, const umode_t mode,
+                                  struct dentry *parent, const u8 key);
+static void __init cs_load_builtin_policy(void);
+static int __init cs_securityfs_init(void);
+static void cs_convert_time(time_t time, struct cs_time *stamp);
+static void cs_io_printf(struct cs_io_buffer *head, const char *fmt, ...)
+       __printf(2, 3);
+static void cs_normalize_line(unsigned char *buffer);
+static void cs_read_policy(struct cs_io_buffer *head);
+static void *cs_commit_ok(void *data, const unsigned int size);
+static void cs_read_stat(struct cs_io_buffer *head);
+static void cs_read_version(struct cs_io_buffer *head);
+static void cs_set_space(struct cs_io_buffer *head);
+static void cs_set_string(struct cs_io_buffer *head, const char *string);
+static void cs_update_stat(const u8 index);
+
+/**
+ * cs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss.
+ *
+ * @time:  Seconds since 1970/01/01 00:00:00.
+ * @stamp: Pointer to "struct cs_time".
+ *
+ * Returns nothing.
+ *
+ * This function does not handle Y2038 problem.
+ */
+static void cs_convert_time(time_t time, struct cs_time *stamp)
+{
+       static const u16 cs_eom[2][12] = {
+               { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+               { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+       };
+       u16 y;
+       u8 m;
+       bool r;
+
+       stamp->sec = time % 60;
+       time /= 60;
+       stamp->min = time % 60;
+       time /= 60;
+       stamp->hour = time % 24;
+       time /= 24;
+       for (y = 1970; ; y++) {
+               const unsigned short days = (y & 3) ? 365 : 366;
+
+               if (time < days)
+                       break;
+               time -= days;
+       }
+       r = (y & 3) == 0;
+       for (m = 0; m < 11 && time >= cs_eom[r][m]; m++)
+               ;
+       if (m)
+               time -= cs_eom[r][m - 1];
+       stamp->year = y;
+       stamp->month = ++m;
+       stamp->day = ++time;
+}
+
+/* Lock for protecting policy. */
+DEFINE_MUTEX(cs_policy_lock);
+
+/* Has /sbin/init started? */
+bool cs_policy_loaded;
+
+/* Policy version. Currently only 20120401 is defined. */
+static unsigned int cs_policy_version;
+
+/* List of "struct cs_condition". */
+LIST_HEAD(cs_condition_list);
+
+/* Memoy currently used by policy. */
+unsigned int cs_memory_used[CS_MAX_MEMORY_STAT];
+
+/* The list for "struct cs_name". */
+struct list_head cs_name_list[CS_MAX_HASH];
+
+/* Timestamp counter for last updated. */
+static unsigned int cs_stat_updated[CS_MAX_POLICY_STAT];
+
+/* Counter for number of updates. */
+static unsigned int cs_stat_modified[CS_MAX_POLICY_STAT];
+
+/* Operations for /sys/kernel/security/caitsith/ interface. */
+static const struct file_operations cs_operations = {
+       .open    = cs_open,
+       .release = cs_release,
+       .read    = cs_read,
+       .write   = cs_write,
+};
+
+/**
+ * list_for_each_cookie - iterate over a list with cookie.
+ *
+ * @pos:  Pointer to "struct list_head".
+ * @head: Pointer to "struct list_head".
+ */
+#define list_for_each_cookie(pos, head)                                        
\
+       for (pos = pos ? pos : srcu_dereference((head)->next, &cs_ss);  \
+            pos != (head); pos = srcu_dereference(pos->next, &cs_ss))
+
+/**
+ * cs_warn_oom - Print out of memory warning message.
+ *
+ * @function: Function's name.
+ *
+ * Returns nothing.
+ */
+void cs_warn_oom(const char *function)
+{
+       /* Reduce error messages. */
+       static pid_t cs_last_pid;
+       const pid_t pid = current->pid;
+
+       if (cs_last_pid != pid) {
+               printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
+                      function);
+               cs_last_pid = pid;
+       }
+       if (!cs_policy_loaded)
+               panic("MAC Initialization failed.\n");
+}
+
+/**
+ * cs_memory_ok - Check memory quota.
+ *
+ * @ptr:  Pointer to allocated memory. Maybe NULL.
+ *
+ * Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
+ *
+ * Caller holds cs_policy_lock mutex.
+ */
+static bool cs_memory_ok(const void *ptr)
+{
+       if (ptr) {
+               cs_memory_used[CS_MEMORY_POLICY] += ksize(ptr);
+               return true;
+       }
+       cs_warn_oom(__func__);
+       return false;
+}
+
+/**
+ * cs_get_name - Allocate memory for string data.
+ *
+ * @name: The string to store into the permernent memory. Maybe NULL.
+ *
+ * Returns pointer to "struct cs_path_info" on success, NULL otherwise.
+ */
+static const struct cs_path_info *cs_get_name(const char *name)
+{
+       struct cs_name *ptr;
+       unsigned int hash;
+       int len;
+       int allocated_len;
+       struct list_head *head;
+
+       if (!name)
+               return NULL;
+       len = strlen(name) + 1;
+       hash = full_name_hash(NULL, name, len - 1);
+       head = &cs_name_list[hash_long(hash, CS_HASH_BITS)];
+       if (mutex_lock_interruptible(&cs_policy_lock))
+               return NULL;
+       list_for_each_entry(ptr, head, head.list) {
+               if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) ||
+                   atomic_read(&ptr->head.users) == CS_GC_IN_PROGRESS)
+                       continue;
+               atomic_inc(&ptr->head.users);
+               goto out;
+       }
+       allocated_len = sizeof(*ptr) + len;
+       ptr = kzalloc(allocated_len, GFP_NOFS);
+       if (cs_memory_ok(ptr)) {
+               ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+               memmove((char *) ptr->entry.name, name, len);
+               atomic_set(&ptr->head.users, 1);
+               cs_fill_path_info(&ptr->entry);
+               ptr->size = allocated_len;
+               list_add_tail(&ptr->head.list, head);
+       } else {
+               kfree(ptr);
+               ptr = NULL;
+       }
+out:
+       mutex_unlock(&cs_policy_lock);
+       return ptr ? &ptr->entry : NULL;
+}
+
+/**
+ * cs_read_token - Read a word from a line.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns a word on success, "" otherwise.
+ *
+ * To allow the caller to skip NULL check, this function returns "" rather than
+ * NULL if there is no more words to read.
+ */
+static char *cs_read_token(struct cs_io_buffer *head)
+{
+       char *pos = head->w.data;
+       char *del = strchr(pos, ' ');
+
+       if (del)
+               *del++ = '\0';
+       else
+               del = pos + strlen(pos);
+       head->w.data = del;
+       return pos;
+}
+
+/**
+ * cs_correct_word - Check whether the given string follows the naming rules.
+ *
+ * @string: The string to check.
+ *
+ * Returns true if @string follows the naming rules, false otherwise.
+ */
+static bool cs_correct_word(const char *string)
+{
+       u8 recursion = 20;
+       const char *const start = string;
+       u8 in_repetition = 0;
+
+       if (!*string)
+               goto out;
+       while (*string) {
+               unsigned char c = *string++;
+
+               if (in_repetition && c == '/')
+                       goto out;
+               if (c <= ' ' || c >= 127)
+                       goto out;
+               if (c != '\\')
+                       continue;
+               c = *string++;
+               if (c >= '0' && c <= '3') {
+                       unsigned char d;
+                       unsigned char e;
+
+                       d = *string++;
+                       if (d < '0' || d > '7')
+                               goto out;
+                       e = *string++;
+                       if (e < '0' || e > '7')
+                               goto out;
+                       c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
+                       if (c <= ' ' || c >= 127 || c == '\\')
+                               continue;
+                       goto out;
+               }
+               switch (c) {
+               case '+':   /* "\+" */
+               case '?':   /* "\?" */
+               case 'x':   /* "\x" */
+               case 'a':   /* "\a" */
+               case '-':   /* "\-" */
+                       continue;
+               }
+               /* Reject too deep wildcard that consumes too much stack. */
+               if (!recursion--)
+                       goto out;
+               switch (c) {
+               case '*':   /* "\*" */
+               case '@':   /* "\@" */
+               case '$':   /* "\$" */
+               case 'X':   /* "\X" */
+               case 'A':   /* "\A" */
+                       continue;
+               case '{':   /* "/\{" */
+                       if (string - 3 < start || *(string - 3) != '/')
+                               goto out;
+                       in_repetition = 1;
+                       continue;
+               case '}':   /* "\}/" */
+                       if (in_repetition != 1 || *string++ != '/')
+                               goto out;
+                       in_repetition = 0;
+                       continue;
+               case '(':   /* "/\(" */
+                       if (string - 3 < start || *(string - 3) != '/')
+                               goto out;
+                       in_repetition = 2;
+                       continue;
+               case ')':   /* "\)/" */
+                       if (in_repetition != 2 || *string++ != '/')
+                               goto out;
+                       in_repetition = 0;
+                       continue;
+               }
+               goto out;
+       }
+       if (in_repetition)
+               goto out;
+       return true;
+out:
+       return false;
+}
+
+/**
+ * cs_commit_ok - Allocate memory and check memory quota.
+ *
+ * @data: Data to copy from.
+ * @size: Size in byte.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
+ * @data is zero-cleared on success.
+ *
+ * Caller holds cs_policy_lock mutex.
+ */
+static void *cs_commit_ok(void *data, const unsigned int size)
+{
+       void *ptr = kmalloc(size, GFP_NOFS);
+
+       if (cs_memory_ok(ptr)) {
+               memmove(ptr, data, size);
+               memset(data, 0, size);
+               return ptr;
+       }
+       kfree(ptr);
+       return NULL;
+}
+
+/**
+ * cs_get_dqword - cs_get_name() for a quoted string.
+ *
+ * @start: String to parse.
+ *
+ * Returns pointer to "struct cs_path_info" on success, NULL otherwise.
+ */
+static const struct cs_path_info *cs_get_dqword(char *start)
+{
+       char *cp = start + strlen(start) - 1;
+
+       if (cp == start || *start++ != '"' || *cp != '"')
+               return NULL;
+       *cp = '\0';
+       if (*start && !cs_correct_word(start))
+               return NULL;
+       return cs_get_name(start);
+}
+
+/**
+ * cs_same_condition - Check for duplicated "struct cs_condition" entry.
+ *
+ * @a: Pointer to "struct cs_condition".
+ * @b: Pointer to "struct cs_condition".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static inline bool cs_same_condition(const struct cs_condition *a,
+                                    const struct cs_condition *b)
+{
+       return a->size == b->size &&
+               !memcmp(a + 1, b + 1, a->size - sizeof(*a));
+}
+
+/**
+ * cs_commit_condition - Commit "struct cs_condition".
+ *
+ * @entry: Pointer to "struct cs_condition".
+ *
+ * Returns pointer to "struct cs_condition" on success, NULL otherwise.
+ *
+ * This function merges duplicated entries. This function returns NULL if
+ * @entry is not duplicated but memory quota for policy has exceeded.
+ */
+static struct cs_condition *cs_commit_condition(struct cs_condition *entry)
+{
+       struct cs_condition *ptr = kmemdup(entry, entry->size, GFP_NOFS);
+       bool found = false;
+
+       if (ptr) {
+               kfree(entry);
+               entry = ptr;
+       }
+       if (mutex_lock_interruptible(&cs_policy_lock)) {
+               dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+               ptr = NULL;
+               found = true;
+               goto out;
+       }
+       list_for_each_entry(ptr, &cs_condition_list, head.list) {
+               if (!cs_same_condition(ptr, entry) ||
+                   atomic_read(&ptr->head.users) == CS_GC_IN_PROGRESS)
+                       continue;
+               /* Same entry found. Share this entry. */
+               atomic_inc(&ptr->head.users);
+               found = true;
+               break;
+       }
+       if (!found) {
+               if (cs_memory_ok(entry)) {
+                       atomic_set(&entry->head.users, 1);
+                       list_add(&entry->head.list, &cs_condition_list);
+               } else {
+                       found = true;
+                       ptr = NULL;
+               }
+       }
+       mutex_unlock(&cs_policy_lock);
+out:
+       if (found) {
+               cs_del_condition(&entry->head.list);
+               kfree(entry);
+               entry = ptr;
+       }
+       return entry;
+}
+
+/**
+ * cs_normalize_line - Format string.
+ *
+ * @buffer: The line to normalize.
+ *
+ * Returns nothing.
+ *
+ * Leading and trailing whitespaces are removed.
+ * Multiple whitespaces are packed into single space.
+ */
+static void cs_normalize_line(unsigned char *buffer)
+{
+       unsigned char *sp = buffer;
+       unsigned char *dp = buffer;
+       bool first = true;
+
+       while (*sp && (*sp <= ' ' || *sp >= 127))
+               sp++;
+       while (*sp) {
+               if (!first)
+                       *dp++ = ' ';
+               first = false;
+               while (*sp > ' ' && *sp < 127)
+                       *dp++ = *sp++;
+               while (*sp && (*sp <= ' ' || *sp >= 127))
+                       sp++;
+       }
+       *dp = '\0';
+}
+
+/**
+ * cs_parse_righthand - Parse special righthand conditions.
+ *
+ * @word: Keyword to search.
+ * @head: Pointer to "struct cs_io_buffer".
+ * @tmp:  Pointer to "struct cs_cond_tmp".
+ *
+ * Returns one of values in "enum cs_conditions_index".
+ */
+static enum cs_conditions_index cs_parse_righthand
+(char *word, struct cs_io_buffer *head, struct cs_cond_tmp *tmp)
+{
+       const enum cs_var_type type = tmp->type;
+
+       dprintk(KERN_WARNING "%u: tmp->left=%u type=%u\n",
+               __LINE__, tmp->left, type);
+       if (type == CS_TYPE_STRING) {
+               dprintk(KERN_WARNING "%u: word='%s'\n", __LINE__, word);
+               if (!strcmp(word, "NULL"))
+                       goto null_word;
+               tmp->path = cs_get_dqword(word);
+               dprintk(KERN_WARNING "%u: tmp->path=%p\n", __LINE__,
+                       tmp->path);
+               if (tmp->path)
+                       return CS_IMM_NAME_ENTRY;
+               goto out;
+       }
+out:
+       dprintk(KERN_WARNING "%u: righthand failed\n", __LINE__);
+       return CS_INVALID_CONDITION;
+null_word:
+       tmp->path = &cs_null_name;
+       return CS_IMM_NAME_ENTRY;
+}
+
+/**
+ * cs_condindex - Get condition's index.
+ *
+ * @word: Name of condition.
+ * @mac:  One of values in "enum cs_mac_index".
+ * @tmp:  Pointer to "struct cs_cond_tmp".
+ * @left: True if lefthand part, false otherwise.
+ *
+ * Returns one of values in "enum cs_condition_index".
+ */
+static enum cs_conditions_index cs_condindex(const char *word,
+                                            const enum cs_mac_index mac,
+                                            struct cs_cond_tmp *tmp,
+                                            const bool lefthand)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs_conditions); i++) {
+               if (!(cs_conditions[i].available & F(mac)) ||
+                   strcmp(cs_conditions[i].keyword, word))
+                       continue;
+               tmp->type = lefthand ? cs_conditions[i].left_type :
+                       cs_conditions[i].right_type;
+               if (tmp->type != CS_TYPE_INVALID)
+                       return cs_conditions[i].cmd;
+               break;
+       }
+       return CS_INVALID_CONDITION;
+}
+
+/**
+ * cs_parse_cond - Parse single condition.
+ *
+ * @tmp:  Pointer to "struct cs_cond_tmp".
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool cs_parse_cond(struct cs_cond_tmp *tmp,
+                         struct cs_io_buffer *head)
+{
+       char *left = head->w.data;
+       char *right;
+       const enum cs_mac_index mac = head->w.acl_index;
+       enum cs_var_type type = CS_TYPE_STRING;
+
+       dprintk(KERN_WARNING "%u: type=%u word='%s'\n",
+               __LINE__, mac, left);
+       right = strchr(left, '=');
+       if (!right || right == left)
+               return false;
+       *right++ = '\0';
+       tmp->is_not = (*(right - 2) == '!');
+       if (tmp->is_not)
+               *(right - 2) = '\0';
+       if (!*left || !*right)
+               return false;
+       tmp->left = cs_condindex(left, mac, tmp, true);
+       dprintk(KERN_WARNING "%u: tmp->left=%u\n", __LINE__, tmp->left);
+       if (tmp->left == CS_INVALID_CONDITION)
+               return false;
+       type = tmp->type;
+       dprintk(KERN_WARNING "%u: tmp->type=%u\n", __LINE__, tmp->type);
+       tmp->right = cs_condindex(right, mac, tmp, false);
+       dprintk(KERN_WARNING "%u: tmp->right=%u tmp->type=%u\n",
+               __LINE__, tmp->right, tmp->type);
+       if (tmp->right != CS_INVALID_CONDITION && type != tmp->type)
+               return false;
+       if (tmp->right == CS_INVALID_CONDITION)
+               tmp->right = cs_parse_righthand(right, head, tmp);
+       dprintk(KERN_WARNING "%u: tmp->right=%u tmp->type=%u\n",
+               __LINE__, tmp->right, tmp->type);
+       return tmp->right != CS_INVALID_CONDITION;
+}
+
+/**
+ * cs_get_condition - Parse condition part.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns pointer to "struct cs_condition" on success, NULL otherwise.
+ */
+struct cs_condition *cs_get_condition(struct cs_io_buffer *head)
+{
+       struct cs_condition *entry = kzalloc(PAGE_SIZE, GFP_NOFS);
+       union cs_condition_element *condp;
+       struct cs_cond_tmp tmp;
+#ifdef DEBUG_CONDITION
+       const enum cs_mac_index type = head->w.acl_index;
+#endif
+       char *pos = head->w.data;
+
+       if (!entry)
+               return NULL;
+       condp = (union cs_condition_element *) (entry + 1);
+       while (1) {
+               memset(&tmp, 0, sizeof(tmp));
+               while (*pos == ' ')
+                       pos++;
+               if (!*pos)
+                       break;
+               if ((u8 *) condp >= ((u8 *) entry) + PAGE_SIZE
+                   - (sizeof(*condp) * 2))
+                       goto out;
+               {
+                       char *next = strchr(pos, ' ');
+
+                       if (next)
+                               *next++ = '\0';
+                       else
+                               next = "";
+                       head->w.data = pos;
+                       pos = next;
+               }
+               if (!cs_parse_cond(&tmp, head))
+                       goto out;
+               condp->is_not = tmp.is_not;
+               condp->left = tmp.left;
+               condp->right = tmp.right;
+               condp++;
+               if (tmp.right == CS_IMM_NAME_ENTRY) {
+                       condp->path = tmp.path;
+                       condp++;
+               }
+       }
+       entry->size = (void *) condp - (void *) entry;
+       return cs_commit_condition(entry);
+out:
+       dprintk(KERN_WARNING "%u: bad condition: type=%u path='%s'\n",
+               __LINE__, type, tmp.path ? tmp.path->name : "");
+       if (tmp.path != &cs_null_name)
+               cs_put_name(tmp.path);
+       entry->size = (void *) condp - (void *) entry;
+       cs_del_condition(&entry->head.list);
+       kfree(entry);
+       return NULL;
+}
+
+/**
+ * cs_flush - Flush queued string to userspace's buffer.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns true if all data was flushed, false otherwise.
+ */
+static bool cs_flush(struct cs_io_buffer *head)
+{
+       while (head->r.w_pos) {
+               const char *w = head->r.w[0];
+               size_t len = strlen(w);
+
+               if (len) {
+                       if (len > head->read_user_buf_avail)
+                               len = head->read_user_buf_avail;
+                       if (!len)
+                               return false;
+                       if (copy_to_user(head->read_user_buf, w, len))
+                               return false;
+                       head->read_user_buf_avail -= len;
+                       head->read_user_buf += len;
+                       w += len;
+               }
+               head->r.w[0] = w;
+               if (*w)
+                       return false;
+               head->r.w_pos--;
+               for (len = 0; len < head->r.w_pos; len++)
+                       head->r.w[len] = head->r.w[len + 1];
+       }
+       head->r.avail = 0;
+       return true;
+}
+
+/**
+ * cs_set_string - Queue string to "struct cs_io_buffer" structure.
+ *
+ * @head:   Pointer to "struct cs_io_buffer".
+ * @string: String to print.
+ *
+ * Returns nothing.
+ *
+ * Note that @string has to be kept valid until @head is kfree()d.
+ * This means that char[] allocated on stack memory cannot be passed to
+ * this function. Use cs_io_printf() for char[] allocated on stack memory.
+ */
+static void cs_set_string(struct cs_io_buffer *head, const char *string)
+{
+       if (head->r.w_pos < CS_MAX_IO_READ_QUEUE) {
+               head->r.w[head->r.w_pos++] = string;
+               cs_flush(head);
+       } else
+               printk(KERN_WARNING "Too many words in a line.\n");
+}
+
+/**
+ * cs_io_printf - printf() to "struct cs_io_buffer" structure.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @fmt:  The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
+static void cs_io_printf(struct cs_io_buffer *head, const char *fmt, ...)
+{
+       va_list args;
+       size_t len;
+       size_t pos = head->r.avail;
+       int size = head->readbuf_size - pos;
+
+       if (size <= 0)
+               return;
+       va_start(args, fmt);
+       len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1;
+       va_end(args);
+       if (pos + len >= head->readbuf_size) {
+               printk(KERN_WARNING "Too many words in a line.\n");
+               return;
+       }
+       head->r.avail += len;
+       cs_set_string(head, head->read_buf + pos);
+}
+
+/**
+ * cs_set_space - Put a space to "struct cs_io_buffer" structure.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void cs_set_space(struct cs_io_buffer *head)
+{
+       cs_set_string(head, " ");
+}
+
+/**
+ * cs_set_lf - Put a line feed to "struct cs_io_buffer" structure.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns true if all data was flushed, false otherwise.
+ */
+static bool cs_set_lf(struct cs_io_buffer *head)
+{
+       cs_set_string(head, "\n");
+       return !head->r.w_pos;
+}
+
+/**
+ * cs_check_profile - Check policy is loaded.
+ *
+ * Returns nothing.
+ */
+void cs_check_profile(void)
+{
+       cs_policy_loaded = true;
+       printk(KERN_INFO "CaitSith 2016/09/25\n");
+       if (cs_policy_version == 20120401) {
+               printk(KERN_INFO "CaitSith module activated.\n");
+               return;
+       }
+       printk(KERN_ERR "Policy version %u is not supported.\n",
+              cs_policy_version);
+       printk(KERN_ERR "Userland tools for CaitSith must be installed and 
policy must be initialized.\n");
+       printk(KERN_ERR "Please see https://caitsith.osdn.jp/ for more 
information.\n");
+       panic("STOP!");
+}
+
+/**
+ * cs_update_acl - Update "struct cs_acl_info" entry.
+ *
+ * @list:   Pointer to "struct list_head".
+ * @head:   Pointer to "struct cs_io_buffer".
+ * @update: True to store matching entry, false otherwise.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static int cs_update_acl(struct list_head * const list,
+                        struct cs_io_buffer *head, const bool update)
+{
+       struct cs_acl_info *ptr;
+       struct cs_acl_info new_entry = { };
+       const bool is_delete = head->w.is_delete;
+       int error = is_delete ? -ENOENT : -ENOMEM;
+
+       new_entry.priority = head->w.priority;
+       new_entry.is_deny = head->w.is_deny;
+       if (head->w.data[0]) {
+               new_entry.cond = cs_get_condition(head);
+               if (!new_entry.cond)
+                       return -EINVAL;
+       }
+       if (mutex_lock_interruptible(&cs_policy_lock))
+               goto out;
+       list_for_each_entry_rcu(ptr, list, list) {
+               if (ptr->priority > new_entry.priority)
+                       break;
+               /*
+                * We cannot reuse deleted "struct cs_acl_info" entry because
+                * somebody might be referencing children of this deleted entry
+                * from srcu section. We cannot delete children of this deleted
+                * entry until all children are no longer referenced. Thus, let
+                * the garbage collector wait and delete rather than trying to
+                * reuse this deleted entry.
+                */
+               if (ptr->is_deleted || ptr->cond != new_entry.cond ||
+                   ptr->priority != new_entry.priority ||
+                   ptr->is_deny != new_entry.is_deny)
+                       continue;
+               ptr->is_deleted = is_delete;
+               if (!is_delete && update)
+                       head->w.acl = ptr;
+               error = 0;
+               break;
+       }
+       if (error && !is_delete) {
+               struct cs_acl_info *entry =
+                       cs_commit_ok(&new_entry, sizeof(new_entry));
+
+               if (entry) {
+                       INIT_LIST_HEAD(&entry->acl_info_list);
+                       list_add_tail_rcu(&entry->list, &ptr->list);
+                       if (update)
+                               head->w.acl = entry;
+               }
+       }
+       mutex_unlock(&cs_policy_lock);
+out:
+       cs_put_condition(new_entry.cond);
+       return error;
+}
+
+/**
+ * cs_parse_entry - Update ACL entry.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static int cs_parse_entry(struct cs_io_buffer *head)
+{
+       enum cs_mac_index type;
+       const char *operation = cs_read_token(head);
+
+       for (type = 0; type < CS_MAX_MAC_INDEX; type++) {
+               if (strcmp(operation, cs_mac_keywords[type]))
+                       continue;
+               head->w.acl_index = type;
+               return cs_update_acl(&cs_acl_list[type], head, true);
+       }
+       return -EINVAL;
+}
+
+/**
+ * cs_condword - Get condition's name.
+ *
+ * @type: One of values in "enum cs_mac_index".
+ * @cond: One of values in "enum cs_condition_index".
+ *
+ * Returns condition's name.
+ */
+static const char *cs_condword(const enum cs_mac_index type,
+                              const enum cs_conditions_index cond)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs_conditions); i++) {
+               if (!(cs_conditions[i].available & F(type)) ||
+                   cs_conditions[i].cmd != cond)
+                       continue;
+               return cs_conditions[i].keyword;
+       }
+       return "unknown"; /* This should not happen. */
+}
+
+/**
+ * cs_print_condition_loop - Print condition part.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @cond: Pointer to "struct cs_condition".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool cs_print_condition_loop(struct cs_io_buffer *head,
+                                   const struct cs_condition *cond)
+{
+       const enum cs_mac_index type = head->r.acl_index;
+       const union cs_condition_element *condp = head->r.cond;
+
+       while ((void *) condp < (void *) ((u8 *) cond) + cond->size) {
+               const bool is_not = condp->is_not;
+               const enum cs_conditions_index left = condp->left;
+               const enum cs_conditions_index right = condp->right;
+
+               if (!cs_flush(head)) {
+                       head->r.cond = condp;
+                       return false;
+               }
+               condp++;
+               cs_set_space(head);
+               cs_set_string(head, cs_condword(type, left));
+               cs_set_string(head, is_not ? "!=" : "=");
+               switch (right) {
+               case CS_IMM_NAME_ENTRY:
+                       if (condp->path != &cs_null_name) {
+                               cs_set_string(head, "\"");
+                               cs_set_string(head, condp->path->name);
+                               cs_set_string(head, "\"");
+                       } else {
+                               cs_set_string(head, "NULL");
+                       }
+                       condp++;
+                       break;
+               default:
+                       cs_set_string(head, cs_condword(type, right));
+               }
+       }
+       head->r.cond = NULL;
+       return true;
+}
+
+/**
+ * cs_print_condition - Print condition part.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @cond: Pointer to "struct cs_condition".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool cs_print_condition(struct cs_io_buffer *head,
+                              const struct cs_condition *cond)
+{
+       switch (head->r.cond_step) {
+       case 0:
+               head->r.cond = (const union cs_condition_element *)
+                       (cond + 1);
+               head->r.cond_step++;
+               /* fall through */
+       case 1:
+               if (!cs_print_condition_loop(head, cond))
+                       return false;
+               head->r.cond_step++;
+               /* fall through */
+       case 2:
+               head->r.cond = NULL;
+               return true;
+       }
+       return false;
+}
+
+/**
+ * cs_read_acl - Print an ACL entry.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @acl:  Pointer to an ACL entry.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool cs_read_acl(struct cs_io_buffer *head,
+                       const struct cs_acl_info *acl)
+{
+       const enum cs_mac_index type = head->r.acl_index;
+
+       if (head->r.cond)
+               goto print_cond_part;
+       if (acl->is_deleted)
+               return true;
+       if (!cs_flush(head))
+               return false;
+       cs_io_printf(head, "%u ", acl->priority);
+       cs_set_string(head, "acl ");
+       cs_set_string(head, cs_mac_keywords[type]);
+       if (acl->cond) {
+               head->r.cond_step = 0;
+print_cond_part:
+               if (!cs_print_condition(head, acl->cond))
+                       return false;
+       }
+       cs_set_lf(head);
+       return true;
+}
+
+/**
+ * cs_write_policy - Write policy.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_write_policy(struct cs_io_buffer *head)
+{
+       unsigned int priority;
+       char *word = cs_read_token(head);
+
+       if (sscanf(word, "%u", &priority) == 1)
+               word = cs_read_token(head);
+       else
+               priority = 1000;
+       if (priority >= 65536 || !*word)
+               return -EINVAL;
+       head->w.priority = priority;
+       if (!head->w.acl)
+               goto no_acl_selected;
+       head->w.is_deny = !strcmp(word, "deny");
+       if (head->w.is_deny || !strcmp(word, "allow"))
+               return cs_update_acl(&head->w.acl->acl_info_list, head,
+                                    false);
+       head->w.acl = NULL;
+no_acl_selected:
+       if (!strcmp(word, "acl"))
+               return cs_parse_entry(head);
+       if (sscanf(word, "POLICY_VERSION=%u", &cs_policy_version) == 1)
+               return 0;
+       return -EINVAL;
+}
+
+/**
+ * cs_audit_log - Audit permission check log.
+ *
+ * @r: Pointer to "struct cs_request_info".
+ *
+ * Returns 0 to grant the request, CS_RETRY_REQUEST to retry the permission
+ * check, -EPERM otherwise.
+ */
+int cs_audit_log(struct cs_request_info *r)
+{
+       /* Do not reject if not yet activated. */
+       if (!cs_policy_loaded)
+               return 0;
+       /* Nothing more to do unless denied. */
+       if (r->result != CS_MATCHING_DENIED)
+               return 0;
+       /* Update policy violation counter if denied. */
+       cs_update_stat(CS_STAT_REQUEST_DENIED);
+       return -EPERM;
+}
+
+/**
+ * cs_read_version - Get version.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void cs_read_version(struct cs_io_buffer *head)
+{
+       if (head->r.eof)
+               return;
+       cs_set_string(head, "2016/09/25");
+       head->r.eof = true;
+}
+
+/**
+ * cs_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+static void cs_update_stat(const u8 index)
+{
+       /*
+        * I don't use atomic operations because race condition is not fatal.
+        */
+       cs_stat_updated[index]++;
+       cs_stat_modified[index] = get_seconds();
+}
+
+/**
+ * cs_read_stat - Read statistic data.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void cs_read_stat(struct cs_io_buffer *head)
+{
+       u8 i;
+
+       for (i = 0; i < CS_MAX_POLICY_STAT; i++) {
+               static const char * const k[CS_MAX_POLICY_STAT] = {
+                       [CS_STAT_POLICY_UPDATES] = "Policy updated:",
+                       [CS_STAT_REQUEST_DENIED] = "Requests denied:",
+               };
+
+               cs_io_printf(head, "stat %s %u", k[i], cs_stat_updated[i]);
+               if (cs_stat_modified[i]) {
+                       struct cs_time stamp;
+
+                       cs_convert_time(cs_stat_modified[i], &stamp);
+                       cs_io_printf(head,
+                                    " (Last: %04u/%02u/%02u %02u:%02u:%02u)",
+                                    stamp.year, stamp.month, stamp.day,
+                                    stamp.hour, stamp.min, stamp.sec);
+               }
+               cs_set_lf(head);
+       }
+       for (i = 0; i < CS_MAX_MEMORY_STAT; i++)
+               cs_io_printf(head, "stat Memory used by %s: %u\n",
+                            cs_memory_headers[i], cs_memory_used[i]);
+}
+
+/**
+ * cs_parse_policy - Parse a policy line.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static int cs_parse_policy(struct cs_io_buffer *head, char *line)
+{
+       /* Set current line's content. */
+       head->w.data = line;
+       head->w.is_deny = false;
+       head->w.priority = 0;
+       /* Delete request? */
+       head->w.is_delete = !strncmp(line, "delete ", 7);
+       if (head->w.is_delete)
+               memmove(line, line + 7, strlen(line + 7) + 1);
+       /* Do the update. */
+       return cs_write_policy(head);
+}
+
+/**
+ * cs_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+static void __init cs_load_builtin_policy(void)
+{
+       /*
+        * This include file is manually created and contains built-in policy.
+        *
+        * static char [] __initdata cs_builtin_policy = { ... };
+        */
+#include "builtin-policy.h"
+       const int idx = cs_read_lock();
+       struct cs_io_buffer head = { };
+       char *start = cs_builtin_policy;
+
+       head.type = CS_POLICY;
+       while (1) {
+               char *end = strchr(start, '\n');
+
+               if (!end)
+                       break;
+               *end = '\0';
+               cs_normalize_line(start);
+               head.write_buf = start;
+               cs_parse_policy(&head, start);
+               start = end + 1;
+       }
+       cs_read_unlock(idx);
+#ifdef CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+       cs_check_profile();
+#endif
+}
+
+/**
+ * cs_read_subacl - Read sub ACL in ACL entry.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static bool cs_read_subacl(struct cs_io_buffer *head,
+                          const struct list_head *list)
+{
+       list_for_each_cookie(head->r.subacl, list) {
+               struct cs_acl_info *acl =
+                       list_entry(head->r.subacl, typeof(*acl), list);
+
+               switch (head->r.step) {
+               case 3:
+                       if (acl->is_deleted)
+                               continue;
+                       if (!cs_flush(head))
+                               return false;
+                       cs_io_printf(head, "    %u ", acl->priority);
+                       if (acl->is_deny)
+                               cs_set_string(head, "deny");
+                       else
+                               cs_set_string(head, "allow");
+                       head->r.cond_step = 0;
+                       head->r.step++;
+                       /* fall through */
+               case 4:
+                       if (!cs_flush(head))
+                               return false;
+                       if (acl->cond &&
+                           !cs_print_condition(head, acl->cond))
+                               return false;
+                       cs_set_lf(head);
+                       head->r.step--;
+               }
+       }
+       head->r.subacl = NULL;
+       return true;
+}
+
+/**
+ * cs_read_policy - Read policy.
+ *
+ * @head: Pointer to "struct cs_io_buffer".
+ *
+ * Caller holds cs_read_lock().
+ */
+static void cs_read_policy(struct cs_io_buffer *head)
+{
+       if (head->r.eof)
+               return;
+       if (!head->r.version_done) {
+               cs_io_printf(head, "POLICY_VERSION=%u\n", cs_policy_version);
+               head->r.version_done = true;
+       }
+       if (!head->r.stat_done) {
+               cs_read_stat(head);
+               head->r.stat_done = true;
+               cs_set_lf(head);
+       }
+       while (head->r.acl_index < CS_MAX_MAC_INDEX) {
+               list_for_each_cookie(head->r.acl,
+                                    &cs_acl_list[head->r.acl_index]) {
+                       struct cs_acl_info *ptr;
+
+                       ptr = list_entry(head->r.acl, typeof(*ptr), list);
+                       switch (head->r.step) {
+                       case 0:
+                               if (ptr->is_deleted)
+                                       continue;
+                               head->r.step++;
+                               /* fall through */
+                       case 1:
+                               if (!cs_read_acl(head, ptr))
+                                       return;
+                               head->r.step++;
+                               /* fall through */
+                       case 2:
+                               if (!cs_flush(head))
+                                       return;
+                               head->r.step++;
+                               /* fall through */
+                       case 3:
+                       case 4:
+                               if (!cs_read_subacl(head, &ptr->acl_info_list))
+                                       return;
+                               head->r.step = 5;
+                               /* fall through */
+                       case 5:
+                               if (!cs_flush(head))
+                                       return;
+                               cs_set_lf(head);
+                               head->r.step = 0;
+                       }
+               }
+               head->r.acl = NULL;
+               head->r.acl_index++;
+       }
+       head->r.eof = true;
+}
+
+/**
+ * cs_open - open() for /sys/kernel/security/caitsith/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_open(struct inode *inode, struct file *file)
+{
+       const u8 type = (unsigned long) inode->i_private;
+       struct cs_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS);
+
+       if (!head)
+               return -ENOMEM;
+       mutex_init(&head->io_sem);
+       head->type = type;
+       if (file->f_mode & FMODE_READ) {
+               head->readbuf_size = 4096;
+               head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS);
+               if (!head->read_buf) {
+                       kfree(head);
+                       return -ENOMEM;
+               }
+       }
+       if (file->f_mode & FMODE_WRITE) {
+               head->writebuf_size = 4096;
+               head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS);
+               if (!head->write_buf) {
+                       kfree(head->read_buf);
+                       kfree(head);
+                       return -ENOMEM;
+               }
+       }
+       file->private_data = head;
+       cs_notify_gc(head, true);
+       return 0;
+}
+
+/**
+ * cs_release - close() for /sys/kernel/security/caitsith/ interface.
+ *
+ * @inode: Pointer to "struct inode".
+ * @file:  Pointer to "struct file".
+ *
+ * Returns 0.
+ */
+static int cs_release(struct inode *inode, struct file *file)
+{
+       struct cs_io_buffer *head = file->private_data;
+
+       cs_notify_gc(head, false);
+       return 0;
+}
+
+/**
+ * cs_read - read() for /sys/kernel/security/caitsith/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns bytes read on success, negative value otherwise.
+ */
+static ssize_t cs_read(struct file *file, char __user *buf, size_t count,
+                      loff_t *ppos)
+{
+       struct cs_io_buffer *head = file->private_data;
+       int len;
+       int idx;
+
+       if (mutex_lock_interruptible(&head->io_sem))
+               return -EINTR;
+       head->read_user_buf = buf;
+       head->read_user_buf_avail = count;
+       idx = cs_read_lock();
+       if (cs_flush(head)) {
+               /* Call the policy handler. */
+               switch (head->type) {
+               case CS_VERSION:
+                       cs_read_version(head);
+                       break;
+               case CS_POLICY:
+                       cs_read_policy(head);
+                       break;
+               }
+               cs_flush(head);
+       }
+       cs_read_unlock(idx);
+       len = head->read_user_buf - buf;
+       mutex_unlock(&head->io_sem);
+       return len;
+}
+
+/**
+ * cs_write - write() for /sys/kernel/security/caitsith/ interface.
+ *
+ * @file:  Pointer to "struct file".
+ * @buf:   Pointer to buffer.
+ * @count: Size of @buf.
+ * @ppos:  Unused.
+ *
+ * Returns @count on success, negative value otherwise.
+ */
+static ssize_t cs_write(struct file *file, const char __user *buf,
+                       size_t count, loff_t *ppos)
+{
+       struct cs_io_buffer *head = file->private_data;
+       int error = count;
+       char *cp0 = head->write_buf;
+       int idx;
+
+       if (head->type != CS_POLICY)
+               return -EIO;
+       if (mutex_lock_interruptible(&head->io_sem))
+               return -EINTR;
+       head->read_user_buf_avail = 0;
+       idx = cs_read_lock();
+       /* Read a line and dispatch it to the policy handler. */
+       while (count) {
+               char c;
+
+               if (head->w.avail >= head->writebuf_size - 1) {
+                       const int len = head->writebuf_size * 2;
+                       char *cp = kzalloc(len, GFP_NOFS);
+
+                       if (!cp) {
+                               error = -ENOMEM;
+                               break;
+                       }
+                       memmove(cp, cp0, head->w.avail);
+                       kfree(cp0);
+                       head->write_buf = cp;
+                       cp0 = cp;
+                       head->writebuf_size = len;
+               }
+               if (get_user(c, buf)) {
+                       error = -EFAULT;
+                       break;
+               }
+               buf++;
+               count--;
+               cp0[head->w.avail++] = c;
+               if (c != '\n')
+                       continue;
+               cp0[head->w.avail - 1] = '\0';
+               head->w.avail = 0;
+               cs_normalize_line(cp0);
+               /* Don't allow updating policies by non manager programs. */
+               if (!cs_manager()) {
+                       error = -EPERM;
+                       goto out;
+               }
+               if (cs_parse_policy(head, cp0) == 0)
+                       /* Update statistics. */
+                       cs_update_stat(CS_STAT_POLICY_UPDATES);
+       }
+out:
+       cs_read_unlock(idx);
+       mutex_unlock(&head->io_sem);
+       return error;
+}
+
+/**
+ * cs_create_entry - Create interface files under 
/sys/kernel/security/caitsith/ directory.
+ *
+ * @name:   The name of the interface file.
+ * @mode:   The permission of the interface file.
+ * @parent: The parent directory.
+ * @key:    Type of interface.
+ *
+ * Returns nothing.
+ */
+static void __init cs_create_entry(const char *name, const umode_t mode,
+                                  struct dentry *parent, const u8 key)
+{
+       securityfs_create_file(name, S_IFREG | mode, parent,
+                              (void *) (unsigned long) key, &cs_operations);
+}
+
+/**
+ * cs_securityfs_init - Initialize /sys/kernel/security/caitsith/ interface.
+ *
+ * Returns 0.
+ */
+static int __init cs_securityfs_init(void)
+{
+       struct dentry *cs_dir;
+
+       if (!security_module_enable("caitsith"))
+               return 0;
+       cs_dir = securityfs_create_dir("caitsith", NULL);
+       cs_create_entry("version", 0400, cs_dir, CS_VERSION);
+       cs_create_entry("policy", 0600, cs_dir, CS_POLICY);
+       cs_load_builtin_policy();
+       return 0;
+}
+
+fs_initcall(cs_securityfs_init);
+
+/**
+ * cs_init_module - Initialize this module.
+ *
+ * Returns nothing.
+ */
+void __init cs_init_module(void)
+{
+       u16 idx;
+
+#ifdef DEBUG_CONDITION
+       for (idx = 0; idx < CS_MAX_MAC_INDEX; idx++) {
+               if (cs_mac_keywords[idx])
+                       continue;
+               panic("cs_mac_keywords[%u]==NULL\n", idx);
+       }
+#endif
+       if (init_srcu_struct(&cs_ss))
+               panic("Out of memory.");
+       for (idx = 0; idx < CS_MAX_MAC_INDEX; idx++)
+               INIT_LIST_HEAD(&cs_acl_list[idx]);
+       for (idx = 0; idx < CS_MAX_HASH; idx++)
+               INIT_LIST_HEAD(&cs_name_list[idx]);
+       cs_null_name.name = "NULL";
+       cs_fill_path_info(&cs_null_name);
+}
-- 
1.8.3.1

Reply via email to