/* Write operation for /sys/kernel/security/tomoyo/ interface. */
 static int tomoyo_write_control(struct file *file, const char __user *buffer,
                                const int buffer_len);
+/* Check whether the domain has too many ACL entries to hold. */
+static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
 
 /**
  * tomoyo_parse_name_union - Parse a tomoyo_name_union.
  *
  * Caller holds tomoyo_read_lock().
  */
-bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
+static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
 {
        unsigned int count = 0;
        struct tomoyo_domain_info *domain = r->domain;
        return 0;
 }
 
+/**
+ * tomoyo_write_domain_policy2 - Write domain policy.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_domain_policy2(char *data,
+                                      struct tomoyo_domain_info *domain,
+                                      const bool is_delete)
+{
+       if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
+                return tomoyo_write_mount_policy(data, domain, is_delete);
+       return tomoyo_write_file_policy(data, domain, is_delete);
+}
+
 /**
  * tomoyo_write_domain_policy - Write domain policy.
  *
                domain->ignore_global_allow_read = !is_delete;
                return 0;
        }
-        if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
-                return tomoyo_write_mount_policy(data, domain, is_delete);
-       return tomoyo_write_file_policy(data, domain, is_delete);
+       return tomoyo_write_domain_policy2(data, domain, is_delete);
 }
 
 /**
        }
 }
 
+/**
+ * tomoyo_print_header - Get header line of audit log.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ *
+ * Returns string representation.
+ *
+ * This function uses kmalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_print_header(struct tomoyo_request_info *r)
+{
+       static const char *tomoyo_mode_4[4] = {
+               "disabled", "learning", "permissive", "enforcing"
+       };
+       struct timeval tv;
+       const pid_t gpid = task_pid_nr(current);
+       static const int tomoyo_buffer_len = 4096;
+       char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
+       if (!buffer)
+               return NULL;
+       do_gettimeofday(&tv);
+       snprintf(buffer, tomoyo_buffer_len - 1,
+                "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
+                " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
+                " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
+                tv.tv_sec, r->profile, tomoyo_mode_4[r->mode], gpid,
+                (pid_t) sys_getpid(), (pid_t) sys_getppid(),
+                current_uid(), current_gid(), current_euid(),
+                current_egid(), current_suid(), current_sgid(),
+                current_fsuid(), current_fsgid());
+       return buffer;
+}
+
+/**
+ * tomoyo_init_audit_log - Allocate buffer for audit logs.
+ *
+ * @len: Required size.
+ * @r:   Pointer to "struct tomoyo_request_info".
+ *
+ * Returns pointer to allocated memory.
+ *
+ * The @len is updated to add the header lines' size on success.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
+{
+       char *buf = NULL;
+       const char *header;
+       const char *domainname;
+       if (!r->domain)
+               r->domain = tomoyo_domain();
+       domainname = r->domain->domainname->name;
+       header = tomoyo_print_header(r);
+       if (!header)
+               return NULL;
+       *len += strlen(domainname) + strlen(header) + 10;
+       buf = kzalloc(*len, GFP_NOFS);
+       if (buf)
+               snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
+       kfree(header);
+       return buf;
+}
+
+/* Wait queue for tomoyo_query_list. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
+
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
+/* Structure for query. */
+struct tomoyo_query_entry {
+       struct list_head list;
+       char *query;
+       int query_len;
+       unsigned int serial;
+       int timer;
+       int answer;
+};
+
+/* The list for "struct tomoyo_query_entry". */
+static LIST_HEAD(tomoyo_query_list);
+
+/*
+ * Number of "struct file" referring /sys/kernel/security/tomoyo/query
+ * interface.
+ */
+static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
+
+/**
+ * tomoyo_supervisor - Ask for the supervisor's decision.
+ *
+ * @r:       Pointer to "struct tomoyo_request_info".
+ * @fmt:     The printf()'s format string, followed by parameters.
+ *
+ * Returns 0 if the supervisor decided to permit the access request which
+ * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
+ * supervisor decided to retry the access request which violated the policy in
+ * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise.
+ */
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+{
+       va_list args;
+       int error = -EPERM;
+       int pos;
+       int len;
+       static unsigned int tomoyo_serial;
+       struct tomoyo_query_entry *tomoyo_query_entry = NULL;
+       bool quota_exceeded = false;
+       char *header;
+       switch (r->mode) {
+               char *buffer;
+       case TOMOYO_CONFIG_LEARNING:
+               if (!tomoyo_domain_quota_is_ok(r))
+                       return 0;
+               va_start(args, fmt);
+               len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
+               va_end(args);
+               buffer = kmalloc(len, GFP_NOFS);
+               if (!buffer)
+                       return 0;
+               va_start(args, fmt);
+               vsnprintf(buffer, len - 1, fmt, args);
+               va_end(args);
+               tomoyo_normalize_line(buffer);
+               tomoyo_write_domain_policy2(buffer, r->domain, false);
+               kfree(buffer);
+               /* fall through */
+       case TOMOYO_CONFIG_PERMISSIVE:
+               return 0;
+       }
+       if (!r->domain)
+               r->domain = tomoyo_domain();
+       if (!atomic_read(&tomoyo_query_observers))
+               return -EPERM;
+       va_start(args, fmt);
+       len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+       va_end(args);
+       header = tomoyo_init_audit_log(&len, r);
+       if (!header)
+               goto out;
+       tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_NOFS);
+       if (!tomoyo_query_entry)
+               goto out;
+       tomoyo_query_entry->query = kzalloc(len, GFP_NOFS);
+       if (!tomoyo_query_entry->query)
+               goto out;
+       len = ksize(tomoyo_query_entry->query);
+       INIT_LIST_HEAD(&tomoyo_query_entry->list);
+       spin_lock(&tomoyo_query_list_lock);
+       if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
+           sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) {
+               quota_exceeded = true;
+       } else {
+               tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry);
+               tomoyo_query_entry->serial = tomoyo_serial++;
+       }
+       spin_unlock(&tomoyo_query_list_lock);
+       if (quota_exceeded)
+               goto out;
+       pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s",
+                      tomoyo_query_entry->serial, r->retry, header);
+       kfree(header);
+       header = NULL;
+       va_start(args, fmt);
+       vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args);
+       tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1;
+       va_end(args);
+       spin_lock(&tomoyo_query_list_lock);
+       list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list);
+       spin_unlock(&tomoyo_query_list_lock);
+       /* Give 10 seconds for supervisor's opinion. */
+       for (tomoyo_query_entry->timer = 0;
+            atomic_read(&tomoyo_query_observers) && tomoyo_query_entry->timer < 100;
+            tomoyo_query_entry->timer++) {
+               wake_up(&tomoyo_query_wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(HZ / 10);
+               if (tomoyo_query_entry->answer)
+                       break;
+       }
+       spin_lock(&tomoyo_query_list_lock);
+       list_del(&tomoyo_query_entry->list);
+       tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry);
+       spin_unlock(&tomoyo_query_list_lock);
+       switch (tomoyo_query_entry->answer) {
+       case 3: /* Asked to retry by administrator. */
+               error = TOMOYO_RETRY_REQUEST;
+               r->retry++;
+               break;
+       case 1:
+               /* Granted by administrator. */
+               error = 0;
+               break;
+       case 0:
+               /* Timed out. */
+               break;
+       default:
+               /* Rejected by administrator. */
+               break;
+       }
+ out:
+       if (tomoyo_query_entry)
+               kfree(tomoyo_query_entry->query);
+       kfree(tomoyo_query_entry);
+       kfree(header);
+       return error;
+}
+
+/**
+ * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise.
+ *
+ * Waits for access requests which violated policy in enforcing mode.
+ */
+static int tomoyo_poll_query(struct file *file, poll_table *wait)
+{
+       struct list_head *tmp;
+       bool found = false;
+       u8 i;
+       for (i = 0; i < 2; i++) {
+               spin_lock(&tomoyo_query_list_lock);
+               list_for_each(tmp, &tomoyo_query_list) {
+                       struct tomoyo_query_entry *ptr
+                               = list_entry(tmp, struct tomoyo_query_entry,
+                                            list);
+                       if (ptr->answer)
+                               continue;
+                       found = true;
+                       break;
+               }
+               spin_unlock(&tomoyo_query_list_lock);
+               if (found)
+                       return POLLIN | POLLRDNORM;
+               if (i)
+                       break;
+               poll_wait(file, &tomoyo_query_wait, wait);
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_read_query - Read access requests which violated policy in enforcing mode.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_read_query(struct tomoyo_io_buffer *head)
+{
+       struct list_head *tmp;
+       int pos = 0;
+       int len = 0;
+       char *buf;
+       if (head->read_avail)
+               return 0;
+       if (head->read_buf) {
+               kfree(head->read_buf);
+               head->read_buf = NULL;
+               head->readbuf_size = 0;
+       }
+       spin_lock(&tomoyo_query_list_lock);
+       list_for_each(tmp, &tomoyo_query_list) {
+               struct tomoyo_query_entry *ptr
+                       = list_entry(tmp, struct tomoyo_query_entry, list);
+               if (ptr->answer)
+                       continue;
+               if (pos++ != head->read_step)
+                       continue;
+               len = ptr->query_len;
+               break;
+       }
+       spin_unlock(&tomoyo_query_list_lock);
+       if (!len) {
+               head->read_step = 0;
+               return 0;
+       }
+       buf = kzalloc(len, GFP_NOFS);
+       if (!buf)
+               return 0;
+       pos = 0;
+       spin_lock(&tomoyo_query_list_lock);
+       list_for_each(tmp, &tomoyo_query_list) {
+               struct tomoyo_query_entry *ptr
+                       = list_entry(tmp, struct tomoyo_query_entry, list);
+               if (ptr->answer)
+                       continue;
+               if (pos++ != head->read_step)
+                       continue;
+               /*
+                * Some query can be skipped because tomoyo_query_list
+                * can change, but I don't care.
+                */
+               if (len == ptr->query_len)
+                       memmove(buf, ptr->query, len);
+               break;
+       }
+       spin_unlock(&tomoyo_query_list_lock);
+       if (buf[0]) {
+               head->read_avail = len;
+               head->readbuf_size = head->read_avail;
+               head->read_buf = buf;
+               head->read_step++;
+       } else {
+               kfree(buf);
+       }
+       return 0;
+}
+
+/**
+ * tomoyo_write_answer - Write the supervisor's decision.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
+{
+       char *data = head->write_buf;
+       struct list_head *tmp;
+       unsigned int serial;
+       unsigned int answer;
+       spin_lock(&tomoyo_query_list_lock);
+       list_for_each(tmp, &tomoyo_query_list) {
+               struct tomoyo_query_entry *ptr
+                       = list_entry(tmp, struct tomoyo_query_entry, list);
+               ptr->timer = 0;
+       }
+       spin_unlock(&tomoyo_query_list_lock);
+       if (sscanf(data, "A%u=%u", &serial, &answer) != 2)
+               return -EINVAL;
+       spin_lock(&tomoyo_query_list_lock);
+       list_for_each(tmp, &tomoyo_query_list) {
+               struct tomoyo_query_entry *ptr
+                       = list_entry(tmp, struct tomoyo_query_entry, list);
+               if (ptr->serial != serial)
+                       continue;
+               if (!ptr->answer)
+                       ptr->answer = answer;
+               break;
+       }
+       spin_unlock(&tomoyo_query_list_lock);
+       return 0;
+}
+
 /**
  * tomoyo_read_version: Get version.
  *
        if (!head)
                return -ENOMEM;
        mutex_init(&head->io_sem);
+       head->type = type;
        switch (type) {
        case TOMOYO_DOMAINPOLICY:
                /* /sys/kernel/security/tomoyo/domain_policy */
                head->write = tomoyo_write_profile;
                head->read = tomoyo_read_profile;
                break;
+       case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */
+               head->poll = tomoyo_poll_query;
+               head->write = tomoyo_write_answer;
+               head->read = tomoyo_read_query;
+               break;
        case TOMOYO_MANAGER:
                /* /sys/kernel/security/tomoyo/manager */
                head->write = tomoyo_write_manager_policy;
                 * for reading.
                 */
                head->read = NULL;
-       } else {
+               head->poll = NULL;
+       } else if (!head->poll) {
+               /* Don't allocate read_buf for poll() access. */
                if (!head->readbuf_size)
                        head->readbuf_size = 4096 * 2;
                head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS);
                        return -ENOMEM;
                }
        }
-       head->reader_idx = tomoyo_read_lock();
+       if (type != TOMOYO_QUERY)
+               head->reader_idx = tomoyo_read_lock();
        file->private_data = head;
        /*
         * Call the handler now if the file is
         */
        if (type == TOMOYO_SELFDOMAIN)
                tomoyo_read_control(file, NULL, 0);
+       /*
+        * If the file is /sys/kernel/security/tomoyo/query , increment the
+        * observer counter.
+        * The obserber counter is used by tomoyo_supervisor() to see if
+        * there is some process monitoring /sys/kernel/security/tomoyo/query.
+        */
+       else if (type == TOMOYO_QUERY)
+               atomic_inc(&tomoyo_query_observers);
        return 0;
 }
 
+/**
+ * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface.
+ *
+ * @file: Pointer to "struct file".
+ * @wait: Pointer to "poll_table".
+ *
+ * Waits for read readiness.
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ */
+int tomoyo_poll_control(struct file *file, poll_table *wait)
+{
+       struct tomoyo_io_buffer *head = file->private_data;
+       if (!head->poll)
+               return -ENOSYS;
+       return head->poll(file, wait);
+}
+
 /**
  * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
  *
        struct tomoyo_io_buffer *head = file->private_data;
        const bool is_write = !!head->write_buf;
 
-       tomoyo_read_unlock(head->reader_idx);
+       /*
+        * If the file is /sys/kernel/security/tomoyo/query , decrement the
+        * observer counter.
+        */
+       if (head->type == TOMOYO_QUERY)
+               atomic_dec(&tomoyo_query_observers);
+       else
+               tomoyo_read_unlock(head->reader_idx);
        /* Release memory used for policy I/O. */
        kfree(head->read_buf);
        head->read_buf = NULL;
                return 0;
 
        tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
+       tomoyo_create_entry("query",            0600, tomoyo_dir,
+                           TOMOYO_QUERY);
        tomoyo_create_entry("domain_policy",    0600, tomoyo_dir,
                            TOMOYO_DOMAINPOLICY);
        tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
 
 #include <linux/mount.h>
 #include <linux/list.h>
 #include <linux/cred.h>
+#include <linux/poll.h>
 struct linux_binprm;
 
 /********** Constants definitions. **********/
        TOMOYO_SELFDOMAIN,
        TOMOYO_VERSION,
        TOMOYO_PROFILE,
+       TOMOYO_QUERY,
        TOMOYO_MANAGER
 };
 
+#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
+
 /********** Structure definitions. **********/
 
 /*
  * tomoyo_request_info is a structure which is used for holding
  *
  * (1) Domain information of current process.
- * (2) Access control mode of the profile.
+ * (2) How many retries are made for this request.
+ * (3) Profile number used for this request.
+ * (4) Access control mode of the profile.
  */
 struct tomoyo_request_info {
        struct tomoyo_domain_info *domain;
+       u8 retry;
+       u8 profile;
        u8 mode; /* One of tomoyo_mode_index . */
 };
 
 struct tomoyo_io_buffer {
        int (*read) (struct tomoyo_io_buffer *);
        int (*write) (struct tomoyo_io_buffer *);
+       int (*poll) (struct file *file, poll_table *wait);
        /* Exclusive lock for this structure.   */
        struct mutex io_sem;
        /* Index returned by tomoyo_read_lock(). */
        int write_avail;
        /* Size of write buffer.                */
        int writebuf_size;
+       /* Type of this interface.              */
+       u8 type;
 };
 
 /*
 
 /********** Function prototypes. **********/
 
+extern asmlinkage long sys_getpid(void);
+extern asmlinkage long sys_getppid(void);
+
 /* Check whether the given name matches the given name_union. */
 bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
                               const struct tomoyo_name_union *ptr);
 /* Check whether the given number matches the given number_union. */
 bool tomoyo_compare_number_union(const unsigned long value,
                                 const struct tomoyo_number_union *ptr);
-/* Check whether the domain has too many ACL entries to hold. */
-bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r);
 /* Transactional sprintf() for policy dump. */
 bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
        __attribute__ ((format(printf, 2, 3)));
 int tomoyo_write_pattern_policy(char *data, const bool is_delete);
 /* Create "path_group" entry in exception policy. */
 int tomoyo_write_path_group_policy(char *data, const bool is_delete);
+int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
+     __attribute__ ((format(printf, 2, 3)));
 /* Create "number_group" entry in exception policy. */
 int tomoyo_write_number_group_policy(char *data, const bool is_delete);
 /* Find a domain by the given name. */
 struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
                                                            domainname,
                                                            const u8 profile);
-/* Get patterned pathname. */
-const struct tomoyo_path_info *
-tomoyo_get_file_pattern(const struct tomoyo_path_info *filename);
 /* Allocate memory for "struct tomoyo_path_group". */
 struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);
 struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name);
 char *tomoyo_realpath_nofollow(const char *pathname);
 /* Same with tomoyo_realpath() except that the pathname is already solved. */
 char *tomoyo_realpath_from_path(struct path *path);
+/* Get patterned pathname. */
+const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename);
 
 /* Check memory quota. */
 bool tomoyo_memory_ok(void *ptr);
 /* The kernel's domain. */
 extern struct tomoyo_domain_info tomoyo_kernel_domain;
 
+extern unsigned int tomoyo_quota_for_query;
+extern unsigned int tomoyo_query_memory_size;
+
 /********** Inlined functions. **********/
 
 static inline int tomoyo_read_lock(void)
 
  */
 int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
+       struct tomoyo_request_info r;
        /*
         * This function assumes that the size of buffer returned by
         * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
        const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
        const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
        int retval = -ENOMEM;
-       struct tomoyo_path_info r; /* real name */
-       struct tomoyo_path_info s; /* symlink name */
-       struct tomoyo_path_info l; /* last name */
+       struct tomoyo_path_info rn; /* real name */
+       struct tomoyo_path_info sn; /* symlink name */
+       struct tomoyo_path_info ln; /* last name */
        static bool initialized;
 
+       tomoyo_init_request_info(&r, NULL);
        if (!tmp)
                goto out;
 
                initialized = true;
        }
 
+ retry:
        /* Get tomoyo_realpath of program. */
        retval = -ENOENT;
        /* I hope tomoyo_realpath() won't fail with -ENOMEM. */
        if (!symlink_program_name)
                goto out;
 
-       r.name = real_program_name;
-       tomoyo_fill_path_info(&r);
-       s.name = symlink_program_name;
-       tomoyo_fill_path_info(&s);
-       l.name = tomoyo_get_last_name(old_domain);
-       tomoyo_fill_path_info(&l);
+       rn.name = real_program_name;
+       tomoyo_fill_path_info(&rn);
+       sn.name = symlink_program_name;
+       tomoyo_fill_path_info(&sn);
+       ln.name = tomoyo_get_last_name(old_domain);
+       tomoyo_fill_path_info(&ln);
 
        /* Check 'alias' directive. */
-       if (tomoyo_pathcmp(&r, &s)) {
+       if (tomoyo_pathcmp(&rn, &sn)) {
                struct tomoyo_alias_entry *ptr;
                /* Is this program allowed to be called via symbolic links? */
                list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                        if (ptr->is_deleted ||
-                           tomoyo_pathcmp(&r, ptr->original_name) ||
-                           tomoyo_pathcmp(&s, ptr->aliased_name))
+                           tomoyo_pathcmp(&rn, ptr->original_name) ||
+                           tomoyo_pathcmp(&sn, ptr->aliased_name))
                                continue;
                        memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN);
                        strncpy(real_program_name, ptr->aliased_name->name,
                                TOMOYO_MAX_PATHNAME_LEN - 1);
-                       tomoyo_fill_path_info(&r);
+                       tomoyo_fill_path_info(&rn);
                        break;
                }
        }
 
        /* Check execute permission. */
-       retval = tomoyo_check_exec_perm(old_domain, &r);
+       retval = tomoyo_check_exec_perm(old_domain, &rn);
+       if (retval == TOMOYO_RETRY_REQUEST)
+               goto retry;
        if (retval < 0)
                goto out;
 
        new_domain_name = tmp->buffer;
-       if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) {
+       if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) {
                /* Transit to the child of tomoyo_kernel_domain domain. */
                snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1,
                         TOMOYO_ROOT_NAME " " "%s", real_program_name);
                 * initializers because they might start before /sbin/init.
                 */
                domain = old_domain;
-       } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) {
+       } else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) {
                /* Keep current domain. */
                domain = old_domain;
        } else {
        domain = tomoyo_find_domain(new_domain_name);
        if (domain)
                goto done;
-       if (is_enforce)
-               goto done;
+       if (is_enforce) {
+               int error = tomoyo_supervisor(&r, "# wants to create domain\n"
+                                             "%s\n", new_domain_name);
+               if (error == TOMOYO_RETRY_REQUEST)
+                       goto retry;
+               if (error < 0)
+                       goto done;
+       }
        domain = tomoyo_find_or_assign_new_domain(new_domain_name,
                                                  old_domain->profile);
  done:
 
 }
 
 /**
- * tomoyo_get_file_pattern - Get patterned pathname.
+ * tomoyo_file_pattern - Get patterned pathname.
  *
  * @filename: The filename to find patterned pathname.
  *
  *
  * Caller holds tomoyo_read_lock().
  */
-const struct tomoyo_path_info *
-tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
+const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename)
 {
        struct tomoyo_pattern_entry *ptr;
        const struct tomoyo_path_info *pattern = NULL;
        }
        if (pattern)
                filename = pattern;
-       return filename;
+       return filename->name;
 }
 
 /**
                perm = 1 << TOMOYO_TYPE_EXECUTE;
        } else
                BUG();
-       error = tomoyo_path_acl(r, filename, perm, mode != 1);
-       if (error && mode == 4 && !r->domain->ignore_global_allow_read
-           && tomoyo_is_globally_readable_file(filename))
+       do {
+               error = tomoyo_path_acl(r, filename, perm, mode != 1);
+               if (error && mode == 4 && !r->domain->ignore_global_allow_read
+                   && tomoyo_is_globally_readable_file(filename))
+                       error = 0;
+               if (!error)
+                       break;
+               tomoyo_warn_log(r, "%s %s", msg, filename->name);
+               error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
+                                         mode == 1 ? filename->name :
+                                         tomoyo_file_pattern(filename));
+               /*
+                 * Do not retry for execute request, for alias may have
+                * changed.
+                 */
+       } while (error == TOMOYO_RETRY_REQUEST && mode != 1);
+       if (r->mode != TOMOYO_CONFIG_ENFORCING)
                error = 0;
-       if (!error)
-               return 0;
-       tomoyo_warn_log(r, "%s %s", msg, filename->name);
-       if (r->mode == TOMOYO_CONFIG_ENFORCING)
-               return error;
-       if (tomoyo_domain_quota_is_ok(r)) {
-               /* Don't use patterns for execute permission. */
-               const struct tomoyo_path_info *patterned_file = (mode != 1) ?
-                       tomoyo_get_file_pattern(filename) : filename;
-               tomoyo_update_file_acl(mode, patterned_file->name, r->domain,
-                                      false);
-       }
-       return 0;
+       return error;
 }
 
 /**
 static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
                                  const struct tomoyo_path_info *filename)
 {
+       const char *msg;
        int error;
 
  next:
-       error = tomoyo_path_acl(r, filename, 1 << operation, 1);
-       if (!error)
-               goto ok;
-       tomoyo_warn_log(r, "%s %s", tomoyo_path2keyword(operation),
-                       filename->name);
-       if (tomoyo_domain_quota_is_ok(r)) {
-               const char *name = tomoyo_get_file_pattern(filename)->name;
-               tomoyo_update_path_acl(operation, name, r->domain, false);
-       }
+       do {
+               error = tomoyo_path_acl(r, filename, 1 << operation, 1);
+               if (!error)
+                       break;
+               msg = tomoyo_path2keyword(operation);
+               tomoyo_warn_log(r, "%s %s", msg, filename->name);
+               error = tomoyo_supervisor(r, "allow_%s %s\n", msg,
+                                         tomoyo_file_pattern(filename));
+       } while (error == TOMOYO_RETRY_REQUEST);
        if (r->mode != TOMOYO_CONFIG_ENFORCING)
                error = 0;
- ok:
        /*
         * Since "allow_truncate" doesn't imply "allow_rewrite" permission,
         * we need to check "allow_rewrite" permission if the filename is
        char buffer[64];
        int error;
        u8 radix;
+       const char *msg;
 
        if (!filename)
                return 0;
                break;
        }
        tomoyo_print_ulong(buffer, sizeof(buffer), number, radix);
-       error = tomoyo_path_number_acl(r, type, filename, number);
-       if (!error)
-               return 0;
-       tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type),
-                       filename->name, buffer);
-       if (tomoyo_domain_quota_is_ok(r))
-               tomoyo_update_path_number_acl(type,
-                                             tomoyo_get_file_pattern(filename)
-                                             ->name, buffer, r->domain, false);
+       do {
+               error = tomoyo_path_number_acl(r, type, filename, number);
+               if (!error)
+                       break;
+               msg = tomoyo_path_number2keyword(type);
+               tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer);
+               error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg,
+                                         tomoyo_file_pattern(filename),
+                                         buffer);
+       } while (error == TOMOYO_RETRY_REQUEST);
        if (r->mode != TOMOYO_CONFIG_ENFORCING)
                error = 0;
        return error;
                                     const unsigned int dev)
 {
        int error;
+       const char *msg;
        const unsigned int major = MAJOR(dev);
        const unsigned int minor = MINOR(dev);
 
-       error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode,
-                                       major, minor);
-       if (!error)
-               return 0;
-       tomoyo_warn_log(r, "%s %s 0%o %u %u",
-                       tomoyo_path_number32keyword(operation),
-                       filename->name, mode, major, minor);
-       if (tomoyo_domain_quota_is_ok(r)) {
-               char mode_buf[64];
-               char major_buf[64];
-               char minor_buf[64];
-               memset(mode_buf, 0, sizeof(mode_buf));
-               memset(major_buf, 0, sizeof(major_buf));
-               memset(minor_buf, 0, sizeof(minor_buf));
-               snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode);
-               snprintf(major_buf, sizeof(major_buf) - 1, "%u", major);
-               snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor);
-               tomoyo_update_path_number3_acl(operation,
-                                              tomoyo_get_file_pattern(filename)
-                                              ->name, mode_buf, major_buf,
-                                              minor_buf, r->domain, false);
-       }
-       if (r->mode != TOMOYO_CONFIG_ENFORCING)
+       do {
+               error = tomoyo_path_number3_acl(r, filename, 1 << operation,
+                                               mode, major, minor);
+               if (!error)
+                       break;
+               msg = tomoyo_path_number32keyword(operation);
+               tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name,
+                               mode, major, minor);
+               error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg,
+                                         tomoyo_file_pattern(filename), mode,
+                                         major, minor);
+       } while (error == TOMOYO_RETRY_REQUEST);
+        if (r->mode != TOMOYO_CONFIG_ENFORCING)
                error = 0;
        return error;
 }
                      struct path *path2)
 {
        int error = -ENOMEM;
+       const char *msg;
        struct tomoyo_path_info *buf1;
        struct tomoyo_path_info *buf2;
        struct tomoyo_request_info r;
                        }
                }
        }
-       error = tomoyo_path2_acl(&r, operation, buf1, buf2);
-       if (!error)
-               goto out;
-       tomoyo_warn_log(&r, "%s %s %s", tomoyo_path22keyword(operation),
-                       buf1->name, buf2->name);
-       if (tomoyo_domain_quota_is_ok(&r)) {
-               const char *name1 = tomoyo_get_file_pattern(buf1)->name;
-               const char *name2 = tomoyo_get_file_pattern(buf2)->name;
-               tomoyo_update_path2_acl(operation, name1, name2, r.domain,
-                                       false);
-       }
+       do {
+               error = tomoyo_path2_acl(&r, operation, buf1, buf2);
+               if (!error)
+                       break;
+               msg = tomoyo_path22keyword(operation);
+               tomoyo_warn_log(&r, "%s %s %s", msg, buf1->name, buf2->name);
+               error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg,
+                                         tomoyo_file_pattern(buf1),
+                                         tomoyo_file_pattern(buf2));
+        } while (error == TOMOYO_RETRY_REQUEST);
  out:
        kfree(buf1);
        kfree(buf2);
 
                error = 0;
                break;
        }
-       if (error) {
-               const char *dev = tomoyo_get_file_pattern(&rdev)->name;
-               const char *dir = tomoyo_get_file_pattern(&rdir)->name;
-               int len = strlen(dev) + strlen(dir) + strlen(requested_type)
-                       + 64;
-               char *buf = kzalloc(len, GFP_NOFS);
-               if (buf) {
-                       snprintf(buf, len - 1, "%s %s %s 0x%lX",
-                                dev, dir, requested_type, flags);
-                       tomoyo_write_mount_policy(buf, r->domain, false);
-                       kfree(buf);
-               }
-       }
+       if (error)
+               error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT
+                                         "%s %s %s 0x%lX\n",
+                                         tomoyo_file_pattern(&rdev),
+                                         tomoyo_file_pattern(&rdir),
+                                         requested_type, flags);
  out:
        kfree(requested_dev_name);
        kfree(requested_dir_name);
                                      TOMOYO_MOUNT_MAKE_SHARED_KEYWORD,
                                      flags & ~MS_SHARED);
        else
-               error = tomoyo_mount_acl2(r, dev_name, dir, type, flags);
+               do {
+                       error = tomoyo_mount_acl2(r, dev_name, dir, type,
+                                                 flags);
+               } while (error == TOMOYO_RETRY_REQUEST);
        if (r->mode != TOMOYO_CONFIG_ENFORCING)
                error = 0;
        return error;
 
                panic("Can't register tomoyo_kernel_domain");
 }
 
+unsigned int tomoyo_quota_for_query;
+unsigned int tomoyo_query_memory_size;
+
 /**
  * tomoyo_read_memory_counter - Check for memory usage in bytes.
  *
        if (!head->read_eof) {
                const unsigned int policy
                        = atomic_read(&tomoyo_policy_memory_size);
+               const unsigned int query = tomoyo_query_memory_size;
                char buffer[64];
 
                memset(buffer, 0, sizeof(buffer));
                                 tomoyo_quota_for_policy);
                else
                        buffer[0] = '\0';
-               tomoyo_io_printf(head, "Policy:  %10u%s\n", policy, buffer);
-               tomoyo_io_printf(head, "Total:   %10u\n", policy);
+               tomoyo_io_printf(head, "Policy:       %10u%s\n", policy,
+                                buffer);
+               if (tomoyo_quota_for_query)
+                       snprintf(buffer, sizeof(buffer) - 1,
+                                "   (Quota: %10u)",
+                                tomoyo_quota_for_query);
+               else
+                       buffer[0] = '\0';
+               tomoyo_io_printf(head, "Query lists:  %10u%s\n", query,
+                                buffer);
+               tomoyo_io_printf(head, "Total:        %10u\n", policy + query);
                head->read_eof = true;
        }
        return 0;
 
        if (sscanf(data, "Policy: %u", &size) == 1)
                tomoyo_quota_for_policy = size;
+       else if (sscanf(data, "Query lists: %u", &size) == 1)
+               tomoyo_quota_for_query = size;
        return 0;
 }