オープンソース・ソフトウェアの開発とダウンロード

Subversion リポジトリの参照

Diff of /trunk/1.7.x/ccs-patch/security/ccsecurity/util.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/1.6.x/ccs-patch/fs/ccs_common.c revision 1719 by kumaneko, Mon Oct 20 05:22:50 2008 UTC trunk/1.7.x/ccs-patch/security/ccsecurity/util.c revision 3046 by kumaneko, Wed Sep 16 06:15:44 2009 UTC
# Line 1  Line 1 
1  /*  /*
2   * fs/ccs_common.c   * security/ccsecurity/util.c
3   *   *
4   * Common functions for SAKURA and TOMOYO.   * Copyright (C) 2005-2009  NTT DATA CORPORATION
5   *   *
6   * Copyright (C) 2005-2008  NTT DATA CORPORATION   * Version: 1.7.0   2009/09/14
  *  
  * Version: 1.6.5-pre   2008/10/20  
7   *   *
8   * This file is applicable to both 2.4.30 and 2.6.11 and later.   * This file is applicable to both 2.4.30 and 2.6.11 and later.
9   * See README.ccs for ChangeLog.   * See README.ccs for ChangeLog.
10   *   *
11   */   */
12    
13  #include <linux/version.h>  #include "internal.h"
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)  
 #define __KERNEL_SYSCALLS__  
 #endif  
 #include <linux/string.h>  
 #include <linux/mm.h>  
 #include <linux/utime.h>  
 #include <linux/file.h>  
 #include <linux/module.h>  
 #include <linux/slab.h>  
 #include <asm/uaccess.h>  
 #include <stdarg.h>  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  
 #include <linux/namei.h>  
 #include <linux/mount.h>  
 static const int lookup_flags = LOOKUP_FOLLOW;  
 #else  
 static const int lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;  
 #endif  
 #include <linux/realpath.h>  
 #include <linux/ccs_common.h>  
 #include <linux/ccs_proc.h>  
 #include <linux/tomoyo.h>  
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)  
 #include <linux/unistd.h>  
 #endif  
   
 /* To support PID namespace. */  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)  
 #define find_task_by_pid find_task_by_vpid  
 #endif  
14    
15  /* Set default specified by the kernel config. */  /* Lock for protecting policy. */
16  #ifdef CONFIG_TOMOYO  DEFINE_MUTEX(ccs_policy_lock);
 #define MAX_ACCEPT_ENTRY (CONFIG_TOMOYO_MAX_ACCEPT_ENTRY)  
 #define MAX_GRANT_LOG    (CONFIG_TOMOYO_MAX_GRANT_LOG)  
 #define MAX_REJECT_LOG   (CONFIG_TOMOYO_MAX_REJECT_LOG)  
 #else  
 #define MAX_ACCEPT_ENTRY 0  
 #define MAX_GRANT_LOG    0  
 #define MAX_REJECT_LOG   0  
 #endif  
17    
18  /* Has /sbin/init started? */  /* Has /sbin/init started? */
19  bool sbin_init_started;  bool ccs_policy_loaded;
   
 /* Log level for SAKURA's printk(). */  
 const char *ccs_log_level = KERN_DEBUG;  
   
 /* String table for functionality that takes 4 modes. */  
 static const char *mode_4[4] = {  
         "disabled", "learning", "permissive", "enforcing"  
 };  
 /* String table for functionality that takes 2 modes. */  
 static const char *mode_2[4] = {  
         "disabled", "enabled", "enabled", "enabled"  
 };  
   
 /* Table for profile. */  
 static struct {  
         const char *keyword;  
         unsigned int current_value;  
         const unsigned int max_value;  
 } ccs_control_array[CCS_MAX_CONTROL_INDEX] = {  
         [CCS_TOMOYO_MAC_FOR_FILE]        = { "MAC_FOR_FILE",        0, 3 },  
         [CCS_TOMOYO_MAC_FOR_ARGV0]       = { "MAC_FOR_ARGV0",       0, 3 },  
         [CCS_TOMOYO_MAC_FOR_ENV]         = { "MAC_FOR_ENV",         0, 3 },  
         [CCS_TOMOYO_MAC_FOR_NETWORK]     = { "MAC_FOR_NETWORK",     0, 3 },  
         [CCS_TOMOYO_MAC_FOR_SIGNAL]      = { "MAC_FOR_SIGNAL",      0, 3 },  
         [CCS_SAKURA_DENY_CONCEAL_MOUNT]  = { "DENY_CONCEAL_MOUNT",  0, 3 },  
         [CCS_SAKURA_RESTRICT_CHROOT]     = { "RESTRICT_CHROOT",     0, 3 },  
         [CCS_SAKURA_RESTRICT_MOUNT]      = { "RESTRICT_MOUNT",      0, 3 },  
         [CCS_SAKURA_RESTRICT_UNMOUNT]    = { "RESTRICT_UNMOUNT",    0, 3 },  
         [CCS_SAKURA_RESTRICT_PIVOT_ROOT] = { "RESTRICT_PIVOT_ROOT", 0, 3 },  
         [CCS_SAKURA_RESTRICT_AUTOBIND]   = { "RESTRICT_AUTOBIND",   0, 1 },  
         [CCS_TOMOYO_MAX_ACCEPT_ENTRY]  
         = { "MAX_ACCEPT_ENTRY",    MAX_ACCEPT_ENTRY, INT_MAX },  
         [CCS_TOMOYO_MAX_GRANT_LOG]  
         = { "MAX_GRANT_LOG",       MAX_GRANT_LOG, INT_MAX },  
         [CCS_TOMOYO_MAX_REJECT_LOG]  
         = { "MAX_REJECT_LOG",      MAX_REJECT_LOG, INT_MAX },  
         [CCS_TOMOYO_VERBOSE]             = { "TOMOYO_VERBOSE",      1, 1 },  
         [CCS_SLEEP_PERIOD]  
         = { "SLEEP_PERIOD",        0, 3000 }, /* in 0.1 second */  
 };  
20    
21  #ifdef CONFIG_TOMOYO  /* Index table for searching parent category. */
22  /* Capability name used by domain policy. */  static const u8 ccs_index2category[CCS_MAX_MAC_INDEX +
23  static const char *capability_control_keyword[TOMOYO_MAX_CAPABILITY_INDEX] = {                                     CCS_MAX_CAPABILITY_INDEX] = {
24          [TOMOYO_INET_STREAM_SOCKET_CREATE]  = "inet_tcp_create",          [CCS_MAC_FILE_EXECUTE]    = CCS_MAC_CATEGORY_FILE,
25          [TOMOYO_INET_STREAM_SOCKET_LISTEN]  = "inet_tcp_listen",          [CCS_MAC_FILE_OPEN]       = CCS_MAC_CATEGORY_FILE,
26          [TOMOYO_INET_STREAM_SOCKET_CONNECT] = "inet_tcp_connect",          [CCS_MAC_FILE_CREATE]     = CCS_MAC_CATEGORY_FILE,
27          [TOMOYO_USE_INET_DGRAM_SOCKET]      = "use_inet_udp",          [CCS_MAC_FILE_UNLINK]     = CCS_MAC_CATEGORY_FILE,
28          [TOMOYO_USE_INET_RAW_SOCKET]        = "use_inet_ip",          [CCS_MAC_FILE_MKDIR]      = CCS_MAC_CATEGORY_FILE,
29          [TOMOYO_USE_ROUTE_SOCKET]           = "use_route",          [CCS_MAC_FILE_RMDIR]      = CCS_MAC_CATEGORY_FILE,
30          [TOMOYO_USE_PACKET_SOCKET]          = "use_packet",          [CCS_MAC_FILE_MKFIFO]     = CCS_MAC_CATEGORY_FILE,
31          [TOMOYO_SYS_MOUNT]                  = "SYS_MOUNT",          [CCS_MAC_FILE_MKSOCK]     = CCS_MAC_CATEGORY_FILE,
32          [TOMOYO_SYS_UMOUNT]                 = "SYS_UMOUNT",          [CCS_MAC_FILE_TRUNCATE]   = CCS_MAC_CATEGORY_FILE,
33          [TOMOYO_SYS_REBOOT]                 = "SYS_REBOOT",          [CCS_MAC_FILE_SYMLINK]    = CCS_MAC_CATEGORY_FILE,
34          [TOMOYO_SYS_CHROOT]                 = "SYS_CHROOT",          [CCS_MAC_FILE_REWRITE]    = CCS_MAC_CATEGORY_FILE,
35          [TOMOYO_SYS_KILL]                   = "SYS_KILL",          [CCS_MAC_FILE_MKBLOCK]    = CCS_MAC_CATEGORY_FILE,
36          [TOMOYO_SYS_VHANGUP]                = "SYS_VHANGUP",          [CCS_MAC_FILE_MKCHAR]     = CCS_MAC_CATEGORY_FILE,
37          [TOMOYO_SYS_SETTIME]                = "SYS_TIME",          [CCS_MAC_FILE_LINK]       = CCS_MAC_CATEGORY_FILE,
38          [TOMOYO_SYS_NICE]                   = "SYS_NICE",          [CCS_MAC_FILE_RENAME]     = CCS_MAC_CATEGORY_FILE,
39          [TOMOYO_SYS_SETHOSTNAME]            = "SYS_SETHOSTNAME",          [CCS_MAC_FILE_CHMOD]      = CCS_MAC_CATEGORY_FILE,
40          [TOMOYO_USE_KERNEL_MODULE]          = "use_kernel_module",          [CCS_MAC_FILE_CHOWN]      = CCS_MAC_CATEGORY_FILE,
41          [TOMOYO_CREATE_FIFO]                = "create_fifo",          [CCS_MAC_FILE_CHGRP]      = CCS_MAC_CATEGORY_FILE,
42          [TOMOYO_CREATE_BLOCK_DEV]           = "create_block_dev",          [CCS_MAC_FILE_IOCTL]      = CCS_MAC_CATEGORY_FILE,
43          [TOMOYO_CREATE_CHAR_DEV]            = "create_char_dev",          [CCS_MAC_FILE_CHROOT]     = CCS_MAC_CATEGORY_FILE,
44          [TOMOYO_CREATE_UNIX_SOCKET]         = "create_unix_socket",          [CCS_MAC_FILE_MOUNT]      = CCS_MAC_CATEGORY_FILE,
45          [TOMOYO_SYS_LINK]                   = "SYS_LINK",          [CCS_MAC_FILE_UMOUNT]     = CCS_MAC_CATEGORY_FILE,
46          [TOMOYO_SYS_SYMLINK]                = "SYS_SYMLINK",          [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE,
47          [TOMOYO_SYS_RENAME]                 = "SYS_RENAME",          [CCS_MAC_ENVIRON]         = CCS_MAC_CATEGORY_MISC,
48          [TOMOYO_SYS_UNLINK]                 = "SYS_UNLINK",          [CCS_MAC_NETWORK_UDP_BIND]    = CCS_MAC_CATEGORY_NETWORK,
49          [TOMOYO_SYS_CHMOD]                  = "SYS_CHMOD",          [CCS_MAC_NETWORK_UDP_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
50          [TOMOYO_SYS_CHOWN]                  = "SYS_CHOWN",          [CCS_MAC_NETWORK_TCP_BIND]    = CCS_MAC_CATEGORY_NETWORK,
51          [TOMOYO_SYS_IOCTL]                  = "SYS_IOCTL",          [CCS_MAC_NETWORK_TCP_LISTEN]  = CCS_MAC_CATEGORY_NETWORK,
52          [TOMOYO_SYS_KEXEC_LOAD]             = "SYS_KEXEC_LOAD",          [CCS_MAC_NETWORK_TCP_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
53          [TOMOYO_SYS_PIVOT_ROOT]             = "SYS_PIVOT_ROOT",          [CCS_MAC_NETWORK_TCP_ACCEPT]  = CCS_MAC_CATEGORY_NETWORK,
54          [TOMOYO_SYS_PTRACE]                 = "SYS_PTRACE",          [CCS_MAC_NETWORK_RAW_BIND]    = CCS_MAC_CATEGORY_NETWORK,
55            [CCS_MAC_NETWORK_RAW_CONNECT] = CCS_MAC_CATEGORY_NETWORK,
56            [CCS_MAC_SIGNAL]          = CCS_MAC_CATEGORY_IPC,
57            [CCS_MAX_MAC_INDEX + CCS_INET_STREAM_SOCKET_CREATE]
58            = CCS_MAC_CATEGORY_CAPABILITY,
59            [CCS_MAX_MAC_INDEX + CCS_INET_STREAM_SOCKET_LISTEN]
60            = CCS_MAC_CATEGORY_CAPABILITY,
61            [CCS_MAX_MAC_INDEX + CCS_INET_STREAM_SOCKET_CONNECT]
62            = CCS_MAC_CATEGORY_CAPABILITY,
63            [CCS_MAX_MAC_INDEX + CCS_USE_INET_DGRAM_SOCKET]
64            = CCS_MAC_CATEGORY_CAPABILITY,
65            [CCS_MAX_MAC_INDEX + CCS_USE_INET_RAW_SOCKET]
66            = CCS_MAC_CATEGORY_CAPABILITY,
67            [CCS_MAX_MAC_INDEX + CCS_USE_ROUTE_SOCKET]
68            = CCS_MAC_CATEGORY_CAPABILITY,
69            [CCS_MAX_MAC_INDEX + CCS_USE_PACKET_SOCKET]
70            = CCS_MAC_CATEGORY_CAPABILITY,
71            [CCS_MAX_MAC_INDEX + CCS_SYS_MOUNT]
72            = CCS_MAC_CATEGORY_CAPABILITY,
73            [CCS_MAX_MAC_INDEX + CCS_SYS_UMOUNT]
74            = CCS_MAC_CATEGORY_CAPABILITY,
75            [CCS_MAX_MAC_INDEX + CCS_SYS_REBOOT]
76            = CCS_MAC_CATEGORY_CAPABILITY,
77            [CCS_MAX_MAC_INDEX + CCS_SYS_CHROOT]
78            = CCS_MAC_CATEGORY_CAPABILITY,
79            [CCS_MAX_MAC_INDEX + CCS_SYS_KILL]
80            = CCS_MAC_CATEGORY_CAPABILITY,
81            [CCS_MAX_MAC_INDEX + CCS_SYS_VHANGUP]
82            = CCS_MAC_CATEGORY_CAPABILITY,
83            [CCS_MAX_MAC_INDEX + CCS_SYS_SETTIME]
84            = CCS_MAC_CATEGORY_CAPABILITY,
85            [CCS_MAX_MAC_INDEX + CCS_SYS_NICE]
86            = CCS_MAC_CATEGORY_CAPABILITY,
87            [CCS_MAX_MAC_INDEX + CCS_SYS_SETHOSTNAME]
88            = CCS_MAC_CATEGORY_CAPABILITY,
89            [CCS_MAX_MAC_INDEX + CCS_USE_KERNEL_MODULE]
90            = CCS_MAC_CATEGORY_CAPABILITY,
91            [CCS_MAX_MAC_INDEX + CCS_CREATE_FIFO]
92            = CCS_MAC_CATEGORY_CAPABILITY,
93            [CCS_MAX_MAC_INDEX + CCS_CREATE_BLOCK_DEV]
94            = CCS_MAC_CATEGORY_CAPABILITY,
95            [CCS_MAX_MAC_INDEX + CCS_CREATE_CHAR_DEV]
96            = CCS_MAC_CATEGORY_CAPABILITY,
97            [CCS_MAX_MAC_INDEX + CCS_CREATE_UNIX_SOCKET]
98            = CCS_MAC_CATEGORY_CAPABILITY,
99            [CCS_MAX_MAC_INDEX + CCS_SYS_LINK]
100            = CCS_MAC_CATEGORY_CAPABILITY,
101            [CCS_MAX_MAC_INDEX + CCS_SYS_SYMLINK]
102            = CCS_MAC_CATEGORY_CAPABILITY,
103            [CCS_MAX_MAC_INDEX + CCS_SYS_RENAME]
104            = CCS_MAC_CATEGORY_CAPABILITY,
105            [CCS_MAX_MAC_INDEX + CCS_SYS_UNLINK]
106            = CCS_MAC_CATEGORY_CAPABILITY,
107            [CCS_MAX_MAC_INDEX + CCS_SYS_CHMOD]
108            = CCS_MAC_CATEGORY_CAPABILITY,
109            [CCS_MAX_MAC_INDEX + CCS_SYS_CHOWN]
110            = CCS_MAC_CATEGORY_CAPABILITY,
111            [CCS_MAX_MAC_INDEX + CCS_SYS_IOCTL]
112            = CCS_MAC_CATEGORY_CAPABILITY,
113            [CCS_MAX_MAC_INDEX + CCS_SYS_KEXEC_LOAD]
114            = CCS_MAC_CATEGORY_CAPABILITY,
115            [CCS_MAX_MAC_INDEX + CCS_SYS_PIVOT_ROOT]
116            = CCS_MAC_CATEGORY_CAPABILITY,
117            [CCS_MAX_MAC_INDEX + CCS_SYS_PTRACE]
118            = CCS_MAC_CATEGORY_CAPABILITY,
119            [CCS_MAX_MAC_INDEX + CCS_CONCEAL_MOUNT]
120            = CCS_MAC_CATEGORY_CAPABILITY,
121  };  };
 #endif  
122    
123  /* Profile table. Memory is allocated as needed. */  /* Utility functions. */
 static struct profile {  
         unsigned int value[CCS_MAX_CONTROL_INDEX];  
         const struct path_info *comment;  
 #ifdef CONFIG_TOMOYO  
         unsigned char capability_value[TOMOYO_MAX_CAPABILITY_INDEX];  
 #endif  
 } *profile_ptr[MAX_PROFILES];  
124    
125  /* Permit policy management by non-root user? */  /**
126  static bool manage_by_non_root;   * ccs_parse_ulong - Parse an "unsigned long" value.
127     *
128     * @result: Pointer to "unsigned long".
129     * @str:    Pointer to string to parse.
130     *
131     * Returns value type on success, 0 otherwise.
132     *
133     * The @src is updated to point the first character after the value
134     * on success.
135     */
136    u8 ccs_parse_ulong(unsigned long *result, char **str)
137    {
138            const char *cp = *str;
139            char *ep;
140            int base = 10;
141            if (*cp == '0') {
142                    char c = *(cp + 1);
143                    if (c == 'x' || c == 'X') {
144                            base = 16;
145                            cp += 2;
146                    } else if (c >= '0' && c <= '7') {
147                            base = 8;
148                            cp++;
149                    }
150            }
151            *result = simple_strtoul(cp, &ep, base);
152            if (cp == ep)
153                    return 0;
154            *str = ep;
155            switch (base) {
156            case 16:
157                    return CCS_VALUE_TYPE_HEXADECIMAL;
158            case 8:
159                    return CCS_VALUE_TYPE_OCTAL;
160            default:
161                    return CCS_VALUE_TYPE_DECIMAL;
162            }
163    }
164    
165  /* Utility functions. */  /**
166     * ccs_print_ulong - Print an "unsigned long" value.
167     *
168     * @buffer:     Pointer to buffer.
169     * @buffer_len: Size of @buffer.
170     * @value:      An "unsigned long" value.
171     * @type:       Type of @value.
172     *
173     * Returns nothing.
174     */
175    void ccs_print_ulong(char *buffer, const int buffer_len,
176                         const unsigned long value, const u8 type)
177    {
178            if (type == CCS_VALUE_TYPE_DECIMAL)
179                    snprintf(buffer, buffer_len, "%lu", value);
180            else if (type == CCS_VALUE_TYPE_OCTAL)
181                    snprintf(buffer, buffer_len, "0%lo", value);
182            else if (type == CCS_VALUE_TYPE_HEXADECIMAL)
183                    snprintf(buffer, buffer_len, "0x%lX", value);
184            else
185                    snprintf(buffer, buffer_len, "type(%u)", type);
186    }
187    
 #ifdef CONFIG_TOMOYO  
188  /**  /**
189   * tomoyo_quiet_setup - Set TOMOYO_VERBOSE=0 by default.   * ccs_parse_name_union - Parse a ccs_name_union.
190   *   *
191   * @str: Unused.   * @filename: Name or name group.
192     * @ptr:      Pointer to "struct ccs_name_union".
193   *   *
194   * Returns 0.   * Returns true on success, false otherwise.
195   */   */
196  static int __init tomoyo_quiet_setup(char *str)  bool ccs_parse_name_union(const char *filename, struct ccs_name_union *ptr)
197  {  {
198          ccs_control_array[CCS_TOMOYO_VERBOSE].current_value = 0;          if (!ccs_is_correct_path(filename, 0, 0, 0))
199          return 0;                  return false;
200            if (filename[0] == '@') {
201                    ptr->group = ccs_get_path_group(filename + 1);
202                    ptr->is_group = true;
203                    return ptr->group != NULL;
204            }
205            ptr->filename = ccs_get_name(filename);
206            ptr->is_group = false;
207            return ptr->filename != NULL;
208  }  }
209    
210  __setup("TOMOYO_QUIET", tomoyo_quiet_setup);  /**
211  #endif   * ccs_parse_number_union - Parse a ccs_number_union.
212     *
213     * @data: Number or number range or number group.
214     * @ptr:  Pointer to "struct ccs_number_union".
215     *
216     * Returns true on success, false otherwise.
217     */
218    bool ccs_parse_number_union(char *data, struct ccs_number_union *num)
219    {
220            u8 type;
221            unsigned long v;
222            memset(num, 0, sizeof(*num));
223            if (data[0] == '@') {
224                    if (!ccs_is_correct_path(data, 0, 0, 0))
225                            return false;
226                    num->group = ccs_get_number_group(data + 1);
227                    num->is_group = true;
228                    return num->group != NULL;
229            }
230            type = ccs_parse_ulong(&v, &data);
231            if (!type)
232                    return false;
233            num->values[0] = v;
234            num->min_type = type;
235            if (!*data) {
236                    num->values[1] = v;
237                    num->max_type = type;
238                    return true;
239            }
240            if (*data++ != '-')
241                    return false;
242            type = ccs_parse_ulong(&v, &data);
243            if (!type || *data)
244                    return false;
245            num->values[1] = v;
246            num->max_type = type;
247            return true;
248    }
249    
250  /**  /**
251   * is_byte_range - Check whether the string isa \ooo style octal value.   * ccs_is_byte_range - Check whether the string isa \ooo style octal value.
252   *   *
253   * @str: Pointer to the string.   * @str: Pointer to the string.
254   *   *
255   * Returns true if @str is a \ooo style octal value, false otherwise.   * Returns true if @str is a \ooo style octal value, false otherwise.
256   */   */
257  static bool is_byte_range(const char *str)  static inline bool ccs_is_byte_range(const char *str)
258  {  {
259          return *str >= '0' && *str++ <= '3' &&          return *str >= '0' && *str++ <= '3' &&
260                  *str >= '0' && *str++ <= '7' &&                  *str >= '0' && *str++ <= '7' &&
# Line 181  static bool is_byte_range(const char *st Line 262  static bool is_byte_range(const char *st
262  }  }
263    
264  /**  /**
265   * is_decimal - Check whether the character is a decimal character.   * ccs_is_decimal - Check whether the character is a decimal character.
266   *   *
267   * @c: The character to check.   * @c: The character to check.
268   *   *
269   * Returns true if @c is a decimal character, false otherwise.   * Returns true if @c is a decimal character, false otherwise.
270   */   */
271  static bool is_decimal(const char c)  static inline bool ccs_is_decimal(const char c)
272  {  {
273          return c >= '0' && c <= '9';          return c >= '0' && c <= '9';
274  }  }
275    
276  /**  /**
277   * is_hexadecimal - Check whether the character is a hexadecimal character.   * ccs_is_hexadecimal - Check whether the character is a hexadecimal character.
278   *   *
279   * @c: The character to check.   * @c: The character to check.
280   *   *
281   * Returns true if @c is a hexadecimal character, false otherwise.   * Returns true if @c is a hexadecimal character, false otherwise.
282   */   */
283  static bool is_hexadecimal(const char c)  static inline bool ccs_is_hexadecimal(const char c)
284  {  {
285          return (c >= '0' && c <= '9') ||          return (c >= '0' && c <= '9') ||
286                  (c >= 'A' && c <= 'F') ||                  (c >= 'A' && c <= 'F') ||
# Line 207  static bool is_hexadecimal(const char c) Line 288  static bool is_hexadecimal(const char c)
288  }  }
289    
290  /**  /**
291   * is_alphabet_char - Check whether the character is an alphabet.   * ccs_is_alphabet_char - Check whether the character is an alphabet.
292   *   *
293   * @c: The character to check.   * @c: The character to check.
294   *   *
295   * Returns true if @c is an alphabet character, false otherwise.   * Returns true if @c is an alphabet character, false otherwise.
296   */   */
297  static bool is_alphabet_char(const char c)  static inline bool ccs_is_alphabet_char(const char c)
298  {  {
299          return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');          return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
300  }  }
301    
302  /**  /**
303   * make_byte - Make byte value from three octal characters.   * ccs_make_byte - Make byte value from three octal characters.
304   *   *
305   * @c1: The first character.   * @c1: The first character.
306   * @c2: The second character.   * @c2: The second character.
# Line 227  static bool is_alphabet_char(const char Line 308  static bool is_alphabet_char(const char
308   *   *
309   * Returns byte value.   * Returns byte value.
310   */   */
311  static u8 make_byte(const u8 c1, const u8 c2, const u8 c3)  static inline u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3)
312  {  {
313          return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');          return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
314  }  }
315    
316  /**  /**
317   * str_starts - Check whether the given string starts with the given keyword.   * ccs_str_starts - Check whether the given string starts with the given keyword.
318   *   *
319   * @src:  Pointer to pointer to the string.   * @src:  Pointer to pointer to the string.
320   * @find: Pointer to the keyword.   * @find: Pointer to the keyword.
# Line 243  static u8 make_byte(const u8 c1, const u Line 324  static u8 make_byte(const u8 c1, const u
324   * The @src is updated to point the first character after the @find   * The @src is updated to point the first character after the @find
325   * if @src starts with @find.   * if @src starts with @find.
326   */   */
327  static bool str_starts(char **src, const char *find)  bool ccs_str_starts(char **src, const char *find)
328  {  {
329          const int len = strlen(find);          const int len = strlen(find);
330          char *tmp = *src;          char *tmp = *src;
# Line 255  static bool str_starts(char **src, const Line 336  static bool str_starts(char **src, const
336  }  }
337    
338  /**  /**
339   * normalize_line - Format string.   * ccs_normalize_line - Format string.
340   *   *
341   * @buffer: The line to normalize.   * @buffer: The line to normalize.
342   *   *
# Line 264  static bool str_starts(char **src, const Line 345  static bool str_starts(char **src, const
345   *   *
346   * Returns nothing.   * Returns nothing.
347   */   */
348  static void normalize_line(unsigned char *buffer)  void ccs_normalize_line(unsigned char *buffer)
349  {  {
350          unsigned char *sp = buffer;          unsigned char *sp = buffer;
351          unsigned char *dp = buffer;          unsigned char *dp = buffer;
# Line 284  static void normalize_line(unsigned char Line 365  static void normalize_line(unsigned char
365  }  }
366    
367  /**  /**
368     * ccs_tokenize - Tokenize string.
369     *
370     * @buffer: The line to tokenize.
371     * @w:      Pointer to "char *".
372     * @size:   Sizeof @w .
373     *
374     * Returns true on success, false otherwise.
375     */
376    bool ccs_tokenize(char *buffer, char *w[], size_t size)
377    {
378            int count = size / sizeof(char *);
379            int i;
380            for (i = 0; i < count; i++)
381                    w[i] = "";
382            for (i = 0; i < count; i++) {
383                    char *cp = strchr(buffer, ' ');
384                    if (cp)
385                            *cp = '\0';
386                    w[i] = buffer;
387                    if (!cp)
388                            break;
389                    buffer = cp + 1;
390            }
391            return i < count || !*buffer;
392    }
393    
394    /**
395   * ccs_is_correct_path - Validate a pathname.   * ccs_is_correct_path - Validate a pathname.
396     *
397   * @filename:     The pathname to check.   * @filename:     The pathname to check.
398   * @start_type:   Should the pathname start with '/'?   * @start_type:   Should the pathname start with '/'?
399   *                1 = must / -1 = must not / 0 = don't care   *                1 = must / -1 = must not / 0 = don't care
# Line 292  static void normalize_line(unsigned char Line 401  static void normalize_line(unsigned char
401   *                1 = must / -1 = must not / 0 = don't care   *                1 = must / -1 = must not / 0 = don't care
402   * @end_type:     Should the pathname end with '/'?   * @end_type:     Should the pathname end with '/'?
403   *                1 = must / -1 = must not / 0 = don't care   *                1 = must / -1 = must not / 0 = don't care
  * @function:     The name of function calling me.  
404   *   *
405   * Check whether the given filename follows the naming rules.   * Check whether the given filename follows the naming rules.
406   * Returns true if @filename follows the naming rules, false otherwise.   * Returns true if @filename follows the naming rules, false otherwise.
407   */   */
408  bool ccs_is_correct_path(const char *filename, const s8 start_type,  bool ccs_is_correct_path(const char *filename, const s8 start_type,
409                           const s8 pattern_type, const s8 end_type,                           const s8 pattern_type, const s8 end_type)
                          const char *function)  
410  {  {
411            const char *const start = filename;
412            bool in_repetition = false;
413          bool contains_pattern = false;          bool contains_pattern = false;
414          unsigned char c;          unsigned char c;
415          unsigned char d;          unsigned char d;
416          unsigned char e;          unsigned char e;
         const char *original_filename = filename;  
417          if (!filename)          if (!filename)
418                  goto out;                  goto out;
419          c = *filename;          c = *filename;
# Line 317  bool ccs_is_correct_path(const char *fil Line 425  bool ccs_is_correct_path(const char *fil
425                          goto out;                          goto out;
426          }          }
427          if (c)          if (c)
428                  c = *(strchr(filename, '\0') - 1);                  c = *(filename + strlen(filename) - 1);
429          if (end_type == 1) { /* Must end with '/' */          if (end_type == 1) { /* Must end with '/' */
430                  if (c != '/')                  if (c != '/')
431                          goto out;                          goto out;
# Line 325  bool ccs_is_correct_path(const char *fil Line 433  bool ccs_is_correct_path(const char *fil
433                  if (c == '/')                  if (c == '/')
434                          goto out;                          goto out;
435          }          }
436          while ((c = *filename++) != '\0') {          while (1) {
437                    c = *filename++;
438                    if (!c)
439                            break;
440                  if (c == '\\') {                  if (c == '\\') {
441                          switch ((c = *filename++)) {                          c = *filename++;
442                            switch (c) {
443                          case '\\':  /* "\\" */                          case '\\':  /* "\\" */
444                                  continue;                                  continue;
445                          case '$':   /* "\$" */                          case '$':   /* "\$" */
# Line 344  bool ccs_is_correct_path(const char *fil Line 456  bool ccs_is_correct_path(const char *fil
456                                          break; /* Must not contain pattern */                                          break; /* Must not contain pattern */
457                                  contains_pattern = true;                                  contains_pattern = true;
458                                  continue;                                  continue;
459                            case '{':   /* "/\{" */
460                                    if (filename - 3 < start ||
461                                        *(filename - 3) != '/')
462                                            break;
463                                    if (pattern_type == -1)
464                                            break; /* Must not contain pattern */
465                                    contains_pattern = true;
466                                    in_repetition = true;
467                                    continue;
468                            case '}':   /* "\}/" */
469                                    if (*filename != '/')
470                                            break;
471                                    if (!in_repetition)
472                                            break;
473                                    in_repetition = false;
474                                    continue;
475                          case '0':   /* "\ooo" */                          case '0':   /* "\ooo" */
476                          case '1':                          case '1':
477                          case '2':                          case '2':
# Line 354  bool ccs_is_correct_path(const char *fil Line 482  bool ccs_is_correct_path(const char *fil
482                                  e = *filename++;                                  e = *filename++;
483                                  if (e < '0' || e > '7')                                  if (e < '0' || e > '7')
484                                          break;                                          break;
485                                  c = make_byte(c, d, e);                                  c = ccs_make_byte(c, d, e);
486                                  if (c && (c <= ' ' || c >= 127))                                  if (c && (c <= ' ' || c >= 127))
487                                          continue; /* pattern is not \000 */                                          continue; /* pattern is not \000 */
488                          }                          }
489                          goto out;                          goto out;
490                    } else if (in_repetition && c == '/') {
491                            goto out;
492                  } else if (c <= ' ' || c >= 127) {                  } else if (c <= ' ' || c >= 127) {
493                          goto out;                          goto out;
494                  }                  }
# Line 367  bool ccs_is_correct_path(const char *fil Line 497  bool ccs_is_correct_path(const char *fil
497                  if (!contains_pattern)                  if (!contains_pattern)
498                          goto out;                          goto out;
499          }          }
500            if (in_repetition)
501                    goto out;
502          return true;          return true;
503   out:   out:
         printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,  
                original_filename);  
504          return false;          return false;
505  }  }
506    
507  /**  /**
508   * ccs_is_correct_domain - Check whether the given domainname follows the naming rules.   * ccs_is_correct_domain - Check whether the given domainname follows the naming rules.
509     *
510   * @domainname:   The domainname to check.   * @domainname:   The domainname to check.
  * @function:     The name of function calling me.  
511   *   *
512   * Returns true if @domainname follows the naming rules, false otherwise.   * Returns true if @domainname follows the naming rules, false otherwise.
513   */   */
514  bool ccs_is_correct_domain(const unsigned char *domainname,  bool ccs_is_correct_domain(const unsigned char *domainname)
                            const char *function)  
515  {  {
516          unsigned char c;          unsigned char c;
517          unsigned char d;          unsigned char d;
518          unsigned char e;          unsigned char e;
         const char *org_domainname = domainname;  
519          if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN))          if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN))
520                  goto out;                  goto out;
521          domainname += ROOT_NAME_LEN;          domainname += ROOT_NAME_LEN;
# Line 398  bool ccs_is_correct_domain(const unsigne Line 526  bool ccs_is_correct_domain(const unsigne
526                          goto out;                          goto out;
527                  if (*domainname++ != '/')                  if (*domainname++ != '/')
528                          goto out;                          goto out;
529                  while ((c = *domainname) != '\0' && c != ' ') {                  while (1) {
530                            c = *domainname;
531                            if (!c || c == ' ')
532                                    break;
533                          domainname++;                          domainname++;
534                          if (c == '\\') {                          if (c == '\\') {
535                                  c = *domainname++;                                  c = *domainname++;
# Line 415  bool ccs_is_correct_domain(const unsigne Line 546  bool ccs_is_correct_domain(const unsigne
546                                          e = *domainname++;                                          e = *domainname++;
547                                          if (e < '0' || e > '7')                                          if (e < '0' || e > '7')
548                                                  break;                                                  break;
549                                          c = make_byte(c, d, e);                                          c = ccs_make_byte(c, d, e);
550                                          if (c && (c <= ' ' || c >= 127))                                          if (c && (c <= ' ' || c >= 127))
551                                                  /* pattern is not \000 */                                                  /* pattern is not \000 */
552                                                  continue;                                                  continue;
# Line 428  bool ccs_is_correct_domain(const unsigne Line 559  bool ccs_is_correct_domain(const unsigne
559          } while (*domainname);          } while (*domainname);
560          return true;          return true;
561   out:   out:
         printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,  
                org_domainname);  
562          return false;          return false;
563  }  }
564    
# Line 450  bool ccs_is_domain_def(const unsigned ch Line 579  bool ccs_is_domain_def(const unsigned ch
579   *   *
580   * @domainname: The domainname to find.   * @domainname: The domainname to find.
581   *   *
582   * Returns pointer to "struct domain_info" if found, NULL otherwise.   * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise.
583     *
584     * Caller holds ccs_read_lock().
585   */   */
586  struct domain_info *ccs_find_domain(const char *domainname)  struct ccs_domain_info *ccs_find_domain(const char *domainname)
587  {  {
588          struct domain_info *domain;          struct ccs_domain_info *domain;
589          struct path_info name;          struct ccs_path_info name;
590          name.name = domainname;          name.name = domainname;
591          ccs_fill_path_info(&name);          ccs_fill_path_info(&name);
592          list1_for_each_entry(domain, &domain_list, list) {          list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
593                  if (!domain->is_deleted &&                  if (!domain->is_deleted &&
594                      !ccs_pathcmp(&name, domain->domainname))                      !ccs_pathcmp(&name, domain->domainname))
595                          return domain;                          return domain;
# Line 467  struct domain_info *ccs_find_domain(cons Line 598  struct domain_info *ccs_find_domain(cons
598  }  }
599    
600  /**  /**
601   * path_depth - Evaluate the number of '/' in a string.   * ccs_const_part_length - Evaluate the initial length without a pattern in a token.
  *  
  * @pathname: The string to evaluate.  
  *  
  * Returns path depth of the string.  
  *  
  * I score 2 for each of the '/' in the @pathname  
  * and score 1 if the @pathname ends with '/'.  
  */  
 static int path_depth(const char *pathname)  
 {  
         int i = 0;  
         if (pathname) {  
                 char *ep = strchr(pathname, '\0');  
                 if (pathname < ep--) {  
                         if (*ep != '/')  
                                 i++;  
                         while (pathname <= ep)  
                                 if (*ep-- == '/')  
                                         i += 2;  
                 }  
         }  
         return i;  
 }  
   
 /**  
  * const_part_length - Evaluate the initial length without a pattern in a token.  
602   *   *
603   * @filename: The string to evaluate.   * @filename: The string to evaluate.
604   *   *
605   * Returns the initial length without a pattern in @filename.   * Returns the initial length without a pattern in @filename.
606   */   */
607  static int const_part_length(const char *filename)  static int ccs_const_part_length(const char *filename)
608  {  {
609          char c;          char c;
610          int len = 0;          int len = 0;
611          if (!filename)          if (!filename)
612                  return 0;                  return 0;
613          while ((c = *filename++) != '\0') {          while (1) {
614                    c = *filename++;
615                    if (!c)
616                            break;
617                  if (c != '\\') {                  if (c != '\\') {
618                          len++;                          len++;
619                          continue;                          continue;
# Line 534  static int const_part_length(const char Line 642  static int const_part_length(const char
642  }  }
643    
644  /**  /**
645   * ccs_fill_path_info - Fill in "struct path_info" members.   * ccs_fill_path_info - Fill in "struct ccs_path_info" members.
646   *   *
647   * @ptr: Pointer to "struct path_info" to fill in.   * @ptr: Pointer to "struct ccs_path_info" to fill in.
648   *   *
649   * The caller sets "struct path_info"->name.   * The caller sets "struct ccs_path_info"->name.
650   */   */
651  void ccs_fill_path_info(struct path_info *ptr)  void ccs_fill_path_info(struct ccs_path_info *ptr)
652  {  {
653          const char *name = ptr->name;          const char *name = ptr->name;
654          const int len = strlen(name);          const int len = strlen(name);
655          ptr->total_len = len;          ptr->total_len = len;
656          ptr->const_len = const_part_length(name);          ptr->const_len = ccs_const_part_length(name);
657          ptr->is_dir = len && (name[len - 1] == '/');          ptr->is_dir = len && (name[len - 1] == '/');
658          ptr->is_patterned = (ptr->const_len < len);          ptr->is_patterned = (ptr->const_len < len);
659          ptr->hash = full_name_hash(name, len);          ptr->hash = full_name_hash(name, len);
         ptr->depth = path_depth(name);  
660  }  }
661    
662  /**  /**
663   * file_matches_to_pattern2 - Pattern matching without '/' character   * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern.
  * and "\-" pattern.  
664   *   *
665   * @filename:     The start of string to check.   * @filename:     The start of string to check.
666   * @filename_end: The end of string to check.   * @filename_end: The end of string to check.
# Line 563  void ccs_fill_path_info(struct path_info Line 669  void ccs_fill_path_info(struct path_info
669   *   *
670   * Returns true if @filename matches @pattern, false otherwise.   * Returns true if @filename matches @pattern, false otherwise.
671   */   */
672  static bool file_matches_to_pattern2(const char *filename,  static bool ccs_file_matches_pattern2(const char *filename,
673                                       const char *filename_end,                                        const char *filename_end,
674                                       const char *pattern,                                        const char *pattern,
675                                       const char *pattern_end)                                        const char *pattern_end)
676  {  {
677          while (filename < filename_end && pattern < pattern_end) {          while (filename < filename_end && pattern < pattern_end) {
678                  char c;                  char c;
# Line 586  static bool file_matches_to_pattern2(con Line 692  static bool file_matches_to_pattern2(con
692                          } else if (c == '\\') {                          } else if (c == '\\') {
693                                  if (filename[1] == '\\')                                  if (filename[1] == '\\')
694                                          filename++;                                          filename++;
695                                  else if (is_byte_range(filename + 1))                                  else if (ccs_is_byte_range(filename + 1))
696                                          filename += 3;                                          filename += 3;
697                                  else                                  else
698                                          return false;                                          return false;
# Line 599  static bool file_matches_to_pattern2(con Line 705  static bool file_matches_to_pattern2(con
705                                  return false;                                  return false;
706                          break;                          break;
707                  case '+':                  case '+':
708                          if (!is_decimal(c))                          if (!ccs_is_decimal(c))
709                                  return false;                                  return false;
710                          break;                          break;
711                  case 'x':                  case 'x':
712                          if (!is_hexadecimal(c))                          if (!ccs_is_hexadecimal(c))
713                                  return false;                                  return false;
714                          break;                          break;
715                  case 'a':                  case 'a':
716                          if (!is_alphabet_char(c))                          if (!ccs_is_alphabet_char(c))
717                                  return false;                                  return false;
718                          break;                          break;
719                  case '0':                  case '0':
720                  case '1':                  case '1':
721                  case '2':                  case '2':
722                  case '3':                  case '3':
723                          if (c == '\\' && is_byte_range(filename + 1)                          if (c == '\\' && ccs_is_byte_range(filename + 1)
724                              && strncmp(filename + 1, pattern, 3) == 0) {                              && !strncmp(filename + 1, pattern, 3)) {
725                                  filename += 3;                                  filename += 3;
726                                  pattern += 2;                                  pattern += 2;
727                                  break;                                  break;
# Line 624  static bool file_matches_to_pattern2(con Line 730  static bool file_matches_to_pattern2(con
730                  case '*':                  case '*':
731                  case '@':                  case '@':
732                          for (i = 0; i <= filename_end - filename; i++) {                          for (i = 0; i <= filename_end - filename; i++) {
733                                  if (file_matches_to_pattern2(filename + i,                                  if (ccs_file_matches_pattern2(filename + i,
734                                                               filename_end,                                                                filename_end,
735                                                               pattern + 1,                                                                pattern + 1,
736                                                               pattern_end))                                                                pattern_end))
737                                          return true;                                          return true;
738                                  c = filename[i];                                  c = filename[i];
739                                  if (c == '.' && *pattern == '@')                                  if (c == '.' && *pattern == '@')
# Line 636  static bool file_matches_to_pattern2(con Line 742  static bool file_matches_to_pattern2(con
742                                          continue;                                          continue;
743                                  if (filename[i + 1] == '\\')                                  if (filename[i + 1] == '\\')
744                                          i++;                                          i++;
745                                  else if (is_byte_range(filename + i + 1))                                  else if (ccs_is_byte_range(filename + i + 1))
746                                          i += 3;                                          i += 3;
747                                  else                                  else
748                                          break; /* Bad pattern. */                                          break; /* Bad pattern. */
# Line 646  static bool file_matches_to_pattern2(con Line 752  static bool file_matches_to_pattern2(con
752                          j = 0;                          j = 0;
753                          c = *pattern;                          c = *pattern;
754                          if (c == '$') {                          if (c == '$') {
755                                  while (is_decimal(filename[j]))                                  while (ccs_is_decimal(filename[j]))
756                                          j++;                                          j++;
757                          } else if (c == 'X') {                          } else if (c == 'X') {
758                                  while (is_hexadecimal(filename[j]))                                  while (ccs_is_hexadecimal(filename[j]))
759                                          j++;                                          j++;
760                          } else if (c == 'A') {                          } else if (c == 'A') {
761                                  while (is_alphabet_char(filename[j]))                                  while (ccs_is_alphabet_char(filename[j]))
762                                          j++;                                          j++;
763                          }                          }
764                          for (i = 1; i <= j; i++) {                          for (i = 1; i <= j; i++) {
765                                  if (file_matches_to_pattern2(filename + i,                                  if (ccs_file_matches_pattern2(filename + i,
766                                                               filename_end,                                                                filename_end,
767                                                               pattern + 1,                                                                pattern + 1,
768                                                               pattern_end))                                                                pattern_end))
769                                          return true;                                          return true;
770                          }                          }
771                          return false; /* Not matched or bad pattern. */                          return false; /* Not matched or bad pattern. */
# Line 674  static bool file_matches_to_pattern2(con Line 780  static bool file_matches_to_pattern2(con
780  }  }
781    
782  /**  /**
783   * file_matches_to_pattern - Pattern matching without without '/' character.   * ccs_file_matches_pattern - Pattern matching without without '/' character.
784   *   *
785   * @filename:     The start of string to check.   * @filename:     The start of string to check.
786   * @filename_end: The end of string to check.   * @filename_end: The end of string to check.
# Line 683  static bool file_matches_to_pattern2(con Line 789  static bool file_matches_to_pattern2(con
789   *   *
790   * Returns true if @filename matches @pattern, false otherwise.   * Returns true if @filename matches @pattern, false otherwise.
791   */   */
792  static bool file_matches_to_pattern(const char *filename,  static bool ccs_file_matches_pattern(const char *filename,
793                                      const char *filename_end,                                       const char *filename_end,
794                                      const char *pattern,                                       const char *pattern,
795                                      const char *pattern_end)                                       const char *pattern_end)
796  {  {
797          const char *pattern_start = pattern;          const char *pattern_start = pattern;
798          bool first = true;          bool first = true;
# Line 695  static bool file_matches_to_pattern(cons Line 801  static bool file_matches_to_pattern(cons
801                  /* Split at "\-" pattern. */                  /* Split at "\-" pattern. */
802                  if (*pattern++ != '\\' || *pattern++ != '-')                  if (*pattern++ != '\\' || *pattern++ != '-')
803                          continue;                          continue;
804                  result = file_matches_to_pattern2(filename, filename_end,                  result = ccs_file_matches_pattern2(filename, filename_end,
805                                                    pattern_start, pattern - 2);                                                     pattern_start, pattern - 2);
806                  if (first)                  if (first)
807                          result = !result;                          result = !result;
808                  if (result)                  if (result)
# Line 704  static bool file_matches_to_pattern(cons Line 810  static bool file_matches_to_pattern(cons
810                  first = false;                  first = false;
811                  pattern_start = pattern;                  pattern_start = pattern;
812          }          }
813          result = file_matches_to_pattern2(filename, filename_end,          result = ccs_file_matches_pattern2(filename, filename_end,
814                                            pattern_start, pattern_end);                                             pattern_start, pattern_end);
815          return first ? result : !result;          return first ? result : !result;
816  }  }
817    
818  /**  /**
819   * ccs_path_matches_pattern - Check whether the given filename matches the given pattern.   * ccs_path_matches_pattern2 - Do pathname pattern matching.
  * @filename: The filename to check.  
  * @pattern:  The pattern to compare.  
820   *   *
821   * Returns true if matches, false otherwise.   * @f: The start of string to check.
822     * @p: The start of pattern to compare.
823   *   *
824   * The following patterns are available.   * Returns true if @f matches @p, false otherwise.
  *   \\     \ itself.  
  *   \ooo   Octal representation of a byte.  
  *   \*     More than or equals to 0 character other than '/'.  
  *   \@     More than or equals to 0 character other than '/' or '.'.  
  *   \?     1 byte character other than '/'.  
  *   \$     More than or equals to 1 decimal digit.  
  *   \+     1 decimal digit.  
  *   \X     More than or equals to 1 hexadecimal digit.  
  *   \x     1 hexadecimal digit.  
  *   \A     More than or equals to 1 alphabet character.  
  *   \a     1 alphabet character.  
  *   \-     Subtraction operator.  
825   */   */
826  bool ccs_path_matches_pattern(const struct path_info *filename,  static bool ccs_path_matches_pattern2(const char *f, const char *p)
                               const struct path_info *pattern)  
827  {  {
828          /*          const char *f_delimiter;
829            if (!filename || !pattern)          const char *p_delimiter;
           return false;  
         */  
         const char *f = filename->name;  
         const char *p = pattern->name;  
         const int len = pattern->const_len;  
         /* If @pattern doesn't contain pattern, I can use strcmp(). */  
         if (!pattern->is_patterned)  
                 return !ccs_pathcmp(filename, pattern);  
         /* Dont compare if the number of '/' differs. */  
         if (filename->depth != pattern->depth)  
                 return false;  
         /* Compare the initial length without patterns. */  
         if (strncmp(f, p, len))  
                 return false;  
         f += len;  
         p += len;  
         /* Main loop. Compare each directory component. */  
830          while (*f && *p) {          while (*f && *p) {
831                  const char *f_delimiter = strchr(f, '/');                  f_delimiter = strchr(f, '/');
                 const char *p_delimiter = strchr(p, '/');  
832                  if (!f_delimiter)                  if (!f_delimiter)
833                          f_delimiter = strchr(f, '\0');                          f_delimiter = f + strlen(f);
834                    p_delimiter = strchr(p, '/');
835                  if (!p_delimiter)                  if (!p_delimiter)
836                          p_delimiter = strchr(p, '\0');                          p_delimiter = p + strlen(p);
837                  if (!file_matches_to_pattern(f, f_delimiter, p, p_delimiter))                  if (*p == '\\' && *(p + 1) == '{')
838                            goto recursive;
839                    if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter))
840                          return false;                          return false;
841                  f = f_delimiter;                  f = f_delimiter;
842                  if (*f)                  if (*f)
# Line 773  bool ccs_path_matches_pattern(const stru Line 850  bool ccs_path_matches_pattern(const stru
850                 (*(p + 1) == '*' || *(p + 1) == '@'))                 (*(p + 1) == '*' || *(p + 1) == '@'))
851                  p += 2;                  p += 2;
852          return !*f && !*p;          return !*f && !*p;
853     recursive:
854            /*
855             * The "\{" pattern is permitted only after '/' character.
856             * This guarantees that below "*(p - 1)" is safe.
857             * Also, the "\}" pattern is permitted only before '/' character
858             * so that "\{" + "\}" pair will not break the "\-" operator.
859             */
860            if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
861                *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
862                    return false; /* Bad pattern. */
863            do {
864                    /* Compare current component with pattern. */
865                    if (!ccs_file_matches_pattern(f, f_delimiter, p + 2,
866                                                  p_delimiter - 2))
867                            break;
868                    /* Proceed to next component. */
869                    f = f_delimiter;
870                    if (!*f)
871                            break;
872                    f++;
873                    /* Continue comparison. */
874                    if (ccs_path_matches_pattern2(f, p_delimiter + 1))
875                            return true;
876                    f_delimiter = strchr(f, '/');
877            } while (f_delimiter);
878            return false; /* Not matched. */
879  }  }
880    
881  /**  /**
882   * ccs_io_printf - Transactional printf() to "struct ccs_io_buffer" structure.   * ccs_path_matches_pattern - Check whether the given filename matches the given pattern.
883   *   *
884   * @head: Pointer to "struct ccs_io_buffer".   * @filename: The filename to check.
885   * @fmt:  The printf()'s format string, followed by parameters.   * @pattern:  The pattern to compare.
886   *   *
887   * Returns true on success, false otherwise.   * Returns true if matches, false otherwise.
888     *
889     * The following patterns are available.
890     *   \\     \ itself.
891     *   \ooo   Octal representation of a byte.
892     *   \*     Zero or more repetitions of characters other than '/'.
893     *   \@     Zero or more repetitions of characters other than '/' or '.'.
894     *   \?     1 byte character other than '/'.
895     *   \$     One or more repetitions of decimal digits.
896     *   \+     1 decimal digit.
897     *   \X     One or more repetitions of hexadecimal digits.
898     *   \x     1 hexadecimal digit.
899     *   \A     One or more repetitions of alphabet characters.
900     *   \a     1 alphabet character.
901   *   *
902   * The snprintf() will truncate, but ccs_io_printf() won't.   *   \-     Subtraction operator.
903     *
904     *   /\{dir\}/   '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
905     *               /dir/dir/dir/ ).
906   */   */
907  bool ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...)  bool ccs_path_matches_pattern(const struct ccs_path_info *filename,
908                                  const struct ccs_path_info *pattern)
909  {  {
910          va_list args;          const char *f = filename->name;
911          int len;          const char *p = pattern->name;
912          int pos = head->read_avail;          const int len = pattern->const_len;
913          int size = head->readbuf_size - pos;          /* If @pattern doesn't contain pattern, I can use strcmp(). */
914          if (size <= 0)          if (!pattern->is_patterned)
915                    return !ccs_pathcmp(filename, pattern);
916            /* Don't compare directory and non-directory. */
917            if (filename->is_dir != pattern->is_dir)
918                  return false;                  return false;
919          va_start(args, fmt);          /* Compare the initial length without patterns. */
920          len = vsnprintf(head->read_buf + pos, size, fmt, args);          if (strncmp(f, p, len))
         va_end(args);  
         if (pos + len >= head->readbuf_size)  
921                  return false;                  return false;
922          head->read_avail += len;          f += len;
923          return true;          p += len;
924            return ccs_path_matches_pattern2(f, p);
925  }  }
926    
927  /**  /**
# Line 807  bool ccs_io_printf(struct ccs_io_buffer Line 929  bool ccs_io_printf(struct ccs_io_buffer
929   *   *
930   * Returns the ccs_realpath() of current process on success, NULL otherwise.   * Returns the ccs_realpath() of current process on success, NULL otherwise.
931   *   *
932   * This function uses ccs_alloc(), so the caller must ccs_free()   * This function uses kzalloc(), so the caller must kfree()
933   * if this function didn't return NULL.   * if this function didn't return NULL.
934   */   */
935  const char *ccs_get_exe(void)  const char *ccs_get_exe(void)
# Line 820  const char *ccs_get_exe(void) Line 942  const char *ccs_get_exe(void)
942          down_read(&mm->mmap_sem);          down_read(&mm->mmap_sem);
943          for (vma = mm->mmap; vma; vma = vma->vm_next) {          for (vma = mm->mmap; vma; vma = vma->vm_next) {
944                  if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {                  if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) {
945                          cp = ccs_realpath_from_dentry(vma->vm_file->f_dentry,  #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
946                                                        vma->vm_file->f_vfsmnt);                          struct path path = { vma->vm_file->f_vfsmnt,
947                                                 vma->vm_file->f_dentry };
948                            cp = ccs_realpath_from_path(&path);
949    #else
950                            cp = ccs_realpath_from_path(&vma->vm_file->f_path);
951    #endif
952                          break;                          break;
953                  }                  }
954          }          }
# Line 830  const char *ccs_get_exe(void) Line 957  const char *ccs_get_exe(void)
957  }  }
958    
959  /**  /**
960   * ccs_get_msg - Get warning message.   * ccs_get_audit - Get audit mode.
961   *   *
962   * @is_enforce: Is it enforcing mode?   * @profile:    Profile number.
963     * @index:      Index number of functionality.
964     * @is_granted: True if granted log, false otehrwise.
965   *   *
966   * Returns "ERROR" or "WARNING".   * Returns mode.
967   */   */
968  const char *ccs_get_msg(const bool is_enforce)  bool ccs_get_audit(const u8 profile, const u8 index, const bool is_granted)
969  {  {
970          if (is_enforce)          u8 mode;
971                  return "ERROR";          const u8 category = ccs_index2category[index] + CCS_MAX_MAC_INDEX
972          else                  + CCS_MAX_CAPABILITY_INDEX;
973                  return "WARNING";          if (!ccs_policy_loaded)
974                    return false;
975            mode = ccs_profile(profile)->config[index];
976            if (mode == CCS_CONFIG_USE_DEFAULT)
977                    mode = ccs_profile(profile)->config[category];
978            if (mode == CCS_CONFIG_USE_DEFAULT)
979                    mode = ccs_profile(profile)->default_config;
980            if (is_granted)
981                    return mode & CCS_CONFIG_WANT_GRANT_LOG;
982            return mode & CCS_CONFIG_WANT_REJECT_LOG;
983  }  }
984    
985  /**  /**
986   * ccs_can_sleep - Check whether it is permitted to do operations that may sleep.   * ccs_get_mode - Get MAC mode.
987   *   *
988   * Returns true if it is permitted to do operations that may sleep,   * @profile: Profile number.
989   * false otherwise.   * @index:   Index number of functionality.
990   *   *
991   * TOMOYO Linux supports interactive enforcement that lets processes   * Returns mode.
992   * wait for the administrator's decision.   */
993   * All hooks but the one for ccs_may_autobind() are inserted where  int ccs_get_mode(const u8 profile, const u8 index)
994   * it is permitted to do operations that may sleep.  {
995   * Thus, this warning should not happen.          u8 mode;
996   */          const u8 category = ccs_index2category[index] + CCS_MAX_MAC_INDEX
997  bool ccs_can_sleep(void)                  + CCS_MAX_CAPABILITY_INDEX;
998  {          if (!ccs_policy_loaded)
999          static u8 count = 20;                  return CCS_CONFIG_DISABLED;
1000          if (likely(!in_interrupt()))          mode = ccs_profile(profile)->config[index];
1001                  return true;          if (mode == CCS_CONFIG_USE_DEFAULT)
1002          if (count) {                  mode = ccs_profile(profile)->config[category];
1003                  count--;          if (mode == CCS_CONFIG_USE_DEFAULT)
1004                  printk(KERN_ERR "BUG: sleeping function called "                  mode = ccs_profile(profile)->default_config;
1005                         "from invalid context.\n");          return mode & 3;
                 dump_stack();  
         }  
         return false;  
1006  }  }
1007    
1008  /**  /**
1009   * ccs_check_flags - Check mode for specified functionality.   * ccs_init_request_info - Initialize "struct ccs_request_info" members.
1010   *   *
1011   * @domain: Pointer to "struct domain_info". NULL for current->domain_info.   * @r:      Pointer to "struct ccs_request_info" to initialize.
1012   * @index:  The functionality to check mode.   * @domain: Pointer to "struct ccs_domain_info". NULL for ccs_current_domain().
1013     * @index:  Index number of functionality.
1014   *   *
1015   * Returns the mode of specified functionality.   * Returns mode.
1016   */   */
1017  unsigned int ccs_check_flags(const struct domain_info *domain, const u8 index)  int ccs_init_request_info(struct ccs_request_info *r,
1018                              struct ccs_domain_info *domain, const u8 index)
1019  {  {
1020          u8 profile;          u8 profile;
1021            memset(r, 0, sizeof(*r));
1022          if (!domain)          if (!domain)
1023                  domain = current->domain_info;                  domain = ccs_current_domain();
1024            r->domain = domain;
1025          profile = domain->profile;          profile = domain->profile;
1026          return sbin_init_started && index < CCS_MAX_CONTROL_INDEX          r->profile = profile;
1027  #if MAX_PROFILES != 256          r->type = index;
1028                  && profile < MAX_PROFILES          r->mode = ccs_get_mode(profile, index);
1029  #endif          return r->mode;
                 && profile_ptr[profile] ?  
                 profile_ptr[profile]->value[index] : 0;  
1030  }  }
1031    
 #ifdef CONFIG_TOMOYO  
1032  /**  /**
1033   * ccs_check_capability_flags - Check mode for specified capability.   * ccs_last_word - Get last component of a line.
  *  
  * @domain: Pointer to "struct domain_info". NULL for current->domain_info.  
  * @index:  The capability to check mode.  
1034   *   *
1035   * Returns the mode of specified capability.   * @line: A line.
  */  
 static u8 ccs_check_capability_flags(const struct domain_info *domain,  
                                      const u8 index)  
 {  
         const u8 profile = domain ? domain->profile :  
                 current->domain_info->profile;  
         return sbin_init_started && index < TOMOYO_MAX_CAPABILITY_INDEX  
 #if MAX_PROFILES != 256  
                 && profile < MAX_PROFILES  
 #endif  
                 && profile_ptr[profile] ?  
                 profile_ptr[profile]->capability_value[index] : 0;  
 }  
   
 /**  
  * ccs_cap2keyword - Convert capability operation to capability name.  
  *  
  * @operation: The capability index.  
  *  
  * Returns the name of the specified capability's name.  
  */  
 const char *ccs_cap2keyword(const u8 operation)  
 {  
         return operation < TOMOYO_MAX_CAPABILITY_INDEX  
                 ? capability_control_keyword[operation] : NULL;  
 }  
   
 #endif  
   
 /**  
  * ccs_init_request_info - Initialize "struct ccs_request_info" members.  
1036   *   *
1037   * @r:      Pointer to "struct ccs_request_info" to initialize.   * Returns the last word of a line.
  * @domain: Pointer to "struct domain_info". NULL for current->domain_info.  
  * @index:  Index number of functionality.  
1038   */   */
1039  void ccs_init_request_info(struct ccs_request_info *r,  const char *ccs_last_word(const char *name)
                            struct domain_info *domain, const u8 index)  
1040  {  {
1041          memset(r, 0, sizeof(*r));          const char *cp = strrchr(name, ' ');
1042          if (!domain)          if (cp)
1043                  domain = current->domain_info;                  return cp + 1;
1044          r->domain = domain;          return name;
         r->profile = domain->profile;  
         if (index < CCS_MAX_CONTROL_INDEX)  
                 r->mode = ccs_check_flags(domain, index);  
 #ifdef CONFIG_TOMOYO  
         else  
                 r->mode = ccs_check_capability_flags(domain, index  
                                                      - CCS_MAX_CONTROL_INDEX);  
 #endif  
         r->tomoyo_flags = current->tomoyo_flags;  
1045  }  }
1046    
1047  /**  /**
1048   * ccs_verbose_mode - Check whether TOMOYO is verbose mode.   * ccs_warn_log - Print warning or error message on console.
  *  
  * @domain: Pointer to "struct domain_info". NULL for current->domain_info.  
1049   *   *
1050   * Returns true if domain policy violation warning should be printed to   * @r:   Pointer to "struct ccs_request_info".
1051   * console.   * @fmt: The printf()'s format string, followed by parameters.
1052   */   */
1053  bool ccs_verbose_mode(const struct domain_info *domain)  void ccs_warn_log(struct ccs_request_info *r, const char *fmt, ...)
1054  {  {
1055          return ccs_check_flags(domain, CCS_TOMOYO_VERBOSE) != 0;          int len = PAGE_SIZE;
1056            va_list args;
1057            char *buffer;
1058            const struct ccs_profile *profile =
1059                    ccs_profile(r->domain->profile);
1060            switch (r->mode) {
1061            case CCS_CONFIG_ENFORCING:
1062                    if (!profile->enforcing->enforcing_verbose)
1063                            return;
1064                    break;
1065            case CCS_CONFIG_PERMISSIVE:
1066                    if (!profile->permissive->permissive_verbose)
1067                            return;
1068                    break;
1069            case CCS_CONFIG_LEARNING:
1070                    if (!profile->learning->learning_verbose)
1071                            return;
1072                    break;
1073            }
1074            while (1) {
1075                    int len2;
1076                    buffer = kmalloc(len, GFP_KERNEL);
1077                    if (!buffer)
1078                            return;
1079                    va_start(args, fmt);
1080                    len2 = vsnprintf(buffer, len - 1, fmt, args);
1081                    va_end(args);
1082                    if (len2 <= len - 1) {
1083                            buffer[len2] = '\0';
1084                            break;
1085                    }
1086                    len = len2 + 1;
1087                    kfree(buffer);
1088            }
1089            printk(KERN_WARNING "%s: Access %s denied for %s\n",
1090                   r->mode == CCS_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
1091                   ccs_last_word(r->domain->domainname->name));
1092            kfree(buffer);
1093  }  }
1094    
1095  /**  /**
1096   * ccs_check_domain_quota - Check for domain's quota.   * ccs_domain_quota_ok - Check for domain's quota.
1097   *   *
1098   * @domain: Pointer to "struct domain_info".   * @r: Pointer to "struct ccs_request_info".
1099   *   *
1100   * Returns true if the domain is not exceeded quota, false otherwise.   * Returns true if the domain is not exceeded quota, false otherwise.
1101     *
1102     * Caller holds ccs_read_lock().
1103   */   */
1104  bool ccs_check_domain_quota(struct domain_info * const domain)  bool ccs_domain_quota_ok(struct ccs_request_info *r)
1105  {  {
1106          unsigned int count = 0;          unsigned int count = 0;
1107          struct acl_info *ptr;          struct ccs_domain_info *domain = r->domain;
1108            struct ccs_acl_info *ptr;
1109            if (r->mode != 1)
1110                    return false;
1111          if (!domain)          if (!domain)
1112                  return true;                  return true;
1113          list1_for_each_entry(ptr, &domain->acl_info_list, list) {          list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
1114                  if (ptr->type & ACL_DELETED)                  if (ptr->is_deleted)
1115                          continue;                          continue;
1116                  switch (ccs_acl_type2(ptr)) {                  switch (ptr->type) {
                         struct single_path_acl_record *acl1;  
                         struct double_path_acl_record *acl2;  
1117                          u16 perm;                          u16 perm;
1118                  case TYPE_SINGLE_PATH_ACL:                          u8 i;
1119                          acl1 = container_of(ptr, struct single_path_acl_record,                  case CCS_TYPE_PATH_ACL:
1120                                              head);                          perm = container_of(ptr, struct ccs_path_acl, head)->
1121                          perm = acl1->perm;                                  perm;
1122                          if (perm & (1 << TYPE_EXECUTE_ACL))                          for (i = 0; i < CCS_MAX_PATH_OPERATION; i++)
1123                                  count++;                                  if (perm & (1 << i))
1124                          if (perm &                                          count++;
1125                              ((1 << TYPE_READ_ACL) | (1 << TYPE_WRITE_ACL)))                          if (perm & (1 << CCS_TYPE_READ_WRITE))
1126                                  count++;                                  count -= 2;
1127                          if (perm & (1 << TYPE_CREATE_ACL))                          break;
1128                                  count++;                  case CCS_TYPE_PATH2_ACL:
1129                          if (perm & (1 << TYPE_UNLINK_ACL))                          perm = container_of(ptr, struct ccs_path2_acl,
1130                                  count++;                                              head)->perm;
1131                          if (perm & (1 << TYPE_MKDIR_ACL))                          for (i = 0; i < CCS_MAX_PATH2_OPERATION; i++)
1132                                  count++;                                  if (perm & (1 << i))
1133                          if (perm & (1 << TYPE_RMDIR_ACL))                                          count++;
                                 count++;  
                         if (perm & (1 << TYPE_MKFIFO_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_MKSOCK_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_MKBLOCK_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_MKCHAR_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_TRUNCATE_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_SYMLINK_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_REWRITE_ACL))  
                                 count++;  
1134                          break;                          break;
1135                  case TYPE_DOUBLE_PATH_ACL:                  case CCS_TYPE_EXECUTE_HANDLER:
1136                          acl2 = container_of(ptr, struct double_path_acl_record,                  case CCS_TYPE_DENIED_EXECUTE_HANDLER:
                                             head);  
                         perm = acl2->perm;  
                         if (perm & (1 << TYPE_LINK_ACL))  
                                 count++;  
                         if (perm & (1 << TYPE_RENAME_ACL))  
                                 count++;  
1137                          break;                          break;
1138                  case TYPE_EXECUTE_HANDLER:                  case CCS_TYPE_PATH_NUMBER_ACL:
1139                  case TYPE_DENIED_EXECUTE_HANDLER:                          perm = container_of(ptr, struct ccs_path_number_acl,
1140                                                head)->perm;
1141                            for (i = 0; i < CCS_MAX_PATH_NUMBER_OPERATION; i++)
1142                                    if (perm & (1 << i))
1143                                            count++;
1144                            break;
1145                    case CCS_TYPE_PATH_NUMBER3_ACL:
1146                            perm = container_of(ptr,
1147                                                struct ccs_path_number3_acl,
1148                                                head)->perm;
1149                            for (i = 0; i < CCS_MAX_PATH_NUMBER3_OPERATION;
1150                                 i++)
1151                                    if (perm & (1 << i))
1152                                            count++;
1153                            break;
1154                    case CCS_TYPE_IP_NETWORK_ACL:
1155                            perm = container_of(ptr, struct ccs_ip_network_acl,
1156                                                head)->perm;
1157                            for (i = 0; i < CCS_MAX_NETWORK_OPERATION; i++)
1158                                    if (perm & (1 << i))
1159                                            count++;
1160                          break;                          break;
1161                  default:                  default:
1162                          count++;                          count++;
1163                  }                  }
1164          }          }
1165          if (count < ccs_check_flags(domain, CCS_TOMOYO_MAX_ACCEPT_ENTRY))          if (count < ccs_profile(domain->profile)->learning->learning_max_entry)
1166                  return true;                  return true;
1167          if (!domain->quota_warned) {          if (!domain->quota_warned) {
1168                  domain->quota_warned = true;                  domain->quota_warned = true;
1169                  printk(KERN_WARNING "TOMOYO-WARNING: "                  printk(KERN_WARNING "WARNING: "
1170                         "Domain '%s' has so many ACLs to hold. "                         "Domain '%s' has so many ACLs to hold. "
1171                         "Stopped learning mode.\n", domain->domainname->name);                         "Stopped learning mode.\n", domain->domainname->name);
1172          }          }
1173          return false;          return false;
1174  }  }
   
 /**  
  * ccs_find_or_assign_new_profile - Create a new profile.  
  *  
  * @profile: Profile number to create.  
  *  
  * Returns pointer to "struct profile" on success, NULL otherwise.  
  */  
 static struct profile *ccs_find_or_assign_new_profile(const unsigned int  
                                                       profile)  
 {  
         static DEFINE_MUTEX(lock);  
         struct profile *ptr = NULL;  
         mutex_lock(&lock);  
         if (profile < MAX_PROFILES) {  
                 ptr = profile_ptr[profile];  
                 if (ptr)  
                         goto ok;  
                 ptr = ccs_alloc_element(sizeof(*ptr));  
                 if (ptr) {  
                         int i;  
                         for (i = 0; i < CCS_MAX_CONTROL_INDEX; i++)  
                                 ptr->value[i]  
                                         = ccs_control_array[i].current_value;  
                         /*  
                          * Needn't to initialize "ptr->capability_value"  
                          * because they are always 0.  
                          */  
                         mb(); /* Avoid out-of-order execution. */  
                         profile_ptr[profile] = ptr;  
                 }  
         }  
  ok:  
         mutex_unlock(&lock);  
         return ptr;  
 }  
   
 /**  
  * write_profile - Write profile table.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int write_profile(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         unsigned int i;  
         unsigned int value;  
         char *cp;  
         struct profile *profile;  
         i = simple_strtoul(data, &cp, 10);  
         if (data != cp) {  
                 if (*cp != '-')  
                         return -EINVAL;  
                 data = cp + 1;  
         }  
         profile = ccs_find_or_assign_new_profile(i);  
         if (!profile)  
                 return -EINVAL;  
         cp = strchr(data, '=');  
         if (!cp)  
                 return -EINVAL;  
         *cp = '\0';  
         ccs_update_counter(CCS_UPDATES_COUNTER_PROFILE);  
         if (!strcmp(data, "COMMENT")) {  
                 profile->comment = ccs_save_name(cp + 1);  
                 return 0;  
         }  
 #ifdef CONFIG_TOMOYO  
         if (str_starts(&data, KEYWORD_MAC_FOR_CAPABILITY)) {  
                 if (sscanf(cp + 1, "%u", &value) != 1) {  
                         for (i = 0; i < 4; i++) {  
                                 if (strcmp(cp + 1, mode_4[i]))  
                                         continue;  
                                 value = i;  
                                 break;  
                         }  
                         if (i == 4)  
                                 return -EINVAL;  
                 }  
                 if (value > 3)  
                         value = 3;  
                 for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) {  
                         if (strcmp(data, capability_control_keyword[i]))  
                                 continue;  
                         profile->capability_value[i] = value;  
                         return 0;  
                 }  
                 return -EINVAL;  
         }  
 #endif  
         for (i = 0; i < CCS_MAX_CONTROL_INDEX; i++) {  
                 if (strcmp(data, ccs_control_array[i].keyword))  
                         continue;  
                 if (sscanf(cp + 1, "%u", &value) != 1) {  
                         int j;  
                         const char **modes;  
                         switch (i) {  
                         case CCS_SAKURA_RESTRICT_AUTOBIND:  
                         case CCS_TOMOYO_VERBOSE:  
                                 modes = mode_2;  
                                 break;  
                         default:  
                                 modes = mode_4;  
                                 break;  
                         }  
                         for (j = 0; j < 4; j++) {  
                                 if (strcmp(cp + 1, modes[j]))  
                                         continue;  
                                 value = j;  
                                 break;  
                         }  
                         if (j == 4)  
                                 return -EINVAL;  
                 } else if (value > ccs_control_array[i].max_value) {  
                         value = ccs_control_array[i].max_value;  
                 }  
                 switch (i) {  
                 case CCS_SAKURA_DENY_CONCEAL_MOUNT:  
                 case CCS_SAKURA_RESTRICT_UNMOUNT:  
                         if (value == 1)  
                                 value = 2; /* learning mode is not supported. */  
                 }  
                 profile->value[i] = value;  
                 return 0;  
         }  
         return -EINVAL;  
 }  
   
 /**  
  * read_profile - Read profile table.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 static int read_profile(struct ccs_io_buffer *head)  
 {  
         static const int total  
                 = CCS_MAX_CONTROL_INDEX + TOMOYO_MAX_CAPABILITY_INDEX + 1;  
         int step;  
         if (head->read_eof)  
                 return 0;  
         for (step = head->read_step; step < MAX_PROFILES * total; step++) {  
                 const u8 index = step / total;  
                 u8 type = step % total;  
                 const struct profile *profile = profile_ptr[index];  
                 head->read_step = step;  
                 if (!profile)  
                         continue;  
 #if !defined(CONFIG_SAKURA) || !defined(CONFIG_TOMOYO)  
                 switch (type) {  
 #ifndef CONFIG_SAKURA  
                 case CCS_SAKURA_DENY_CONCEAL_MOUNT:  
                 case CCS_SAKURA_RESTRICT_CHROOT:  
                 case CCS_SAKURA_RESTRICT_MOUNT:  
                 case CCS_SAKURA_RESTRICT_UNMOUNT:  
                 case CCS_SAKURA_RESTRICT_PIVOT_ROOT:  
                 case CCS_SAKURA_RESTRICT_AUTOBIND:  
 #endif  
 #ifndef CONFIG_TOMOYO  
                 case CCS_TOMOYO_MAC_FOR_FILE:  
                 case CCS_TOMOYO_MAC_FOR_ARGV0:  
                 case CCS_TOMOYO_MAC_FOR_ENV:  
                 case CCS_TOMOYO_MAC_FOR_NETWORK:  
                 case CCS_TOMOYO_MAC_FOR_SIGNAL:  
                 case CCS_TOMOYO_MAX_ACCEPT_ENTRY:  
                 case CCS_TOMOYO_MAX_GRANT_LOG:  
                 case CCS_TOMOYO_MAX_REJECT_LOG:  
                 case CCS_TOMOYO_VERBOSE:  
 #endif  
                         continue;  
                 }  
 #endif  
                 if (!type) { /* Print profile' comment tag. */  
                         if (!ccs_io_printf(head, "%u-COMMENT=%s\n",  
                                            index, profile->comment ?  
                                            profile->comment->name : ""))  
                                 break;  
                         continue;  
                 }  
                 type--;  
                 if (type >= CCS_MAX_CONTROL_INDEX) {  
 #ifdef CONFIG_TOMOYO  
                         const int i = type - CCS_MAX_CONTROL_INDEX;  
                         const u8 value = profile->capability_value[i];  
                         if (!ccs_io_printf(head,  
                                            "%u-" KEYWORD_MAC_FOR_CAPABILITY  
                                            "%s=%s\n", index,  
                                            capability_control_keyword[i],  
                                            mode_4[value]))  
                                 break;  
 #endif  
                 } else {  
                         const unsigned int value = profile->value[type];  
                         const char **modes = NULL;  
                         const char *keyword = ccs_control_array[type].keyword;  
                         switch (ccs_control_array[type].max_value) {  
                         case 3:  
                                 modes = mode_4;  
                                 break;  
                         case 1:  
                                 modes = mode_2;  
                                 break;  
                         }  
                         if (modes) {  
                                 if (!ccs_io_printf(head, "%u-%s=%s\n", index,  
                                                    keyword, modes[value]))  
                                         break;  
                         } else {  
                                 if (!ccs_io_printf(head, "%u-%s=%u\n", index,  
                                                    keyword, value))  
                                         break;  
                         }  
                 }  
         }  
         if (step == MAX_PROFILES * total)  
                 head->read_eof = true;  
         return 0;  
 }  
   
 /* Structure for policy manager. */  
 struct policy_manager_entry {  
         struct list1_head list;  
         /* A path to program or a domainname. */  
         const struct path_info *manager;  
         bool is_domain;  /* True if manager is a domainname. */  
         bool is_deleted; /* True if this entry is deleted. */  
 };  
   
 /* The list for "struct policy_manager_entry". */  
 static LIST1_HEAD(policy_manager_list);  
   
 /**  
  * update_manager_entry - Add a manager entry.  
  *  
  * @manager:   The path to manager or the domainnamme.  
  * @is_delete: True if it is a delete request.  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int update_manager_entry(const char *manager, const bool is_delete)  
 {  
         struct policy_manager_entry *new_entry;  
         struct policy_manager_entry *ptr;  
         static DEFINE_MUTEX(lock);  
         const struct path_info *saved_manager;  
         int error = -ENOMEM;  
         bool is_domain = false;  
         if (ccs_is_domain_def(manager)) {  
                 if (!ccs_is_correct_domain(manager, __func__))  
                         return -EINVAL;  
                 is_domain = true;  
         } else {  
                 if (!ccs_is_correct_path(manager, 1, -1, -1, __func__))  
                         return -EINVAL;  
         }  
         saved_manager = ccs_save_name(manager);  
         if (!saved_manager)  
                 return -ENOMEM;  
         mutex_lock(&lock);  
         list1_for_each_entry(ptr, &policy_manager_list, list) {  
                 if (ptr->manager != saved_manager)  
                         continue;  
                 ptr->is_deleted = is_delete;  
                 error = 0;  
                 goto out;  
         }  
         if (is_delete) {  
                 error = -ENOENT;  
                 goto out;  
         }  
         new_entry = ccs_alloc_element(sizeof(*new_entry));  
         if (!new_entry)  
                 goto out;  
         new_entry->manager = saved_manager;  
         new_entry->is_domain = is_domain;  
         list1_add_tail_mb(&new_entry->list, &policy_manager_list);  
         error = 0;  
  out:  
         mutex_unlock(&lock);  
         if (!error)  
                 ccs_update_counter(CCS_UPDATES_COUNTER_MANAGER);  
         return error;  
 }  
   
 /**  
  * write_manager_policy - Write manager policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int write_manager_policy(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         bool is_delete = str_starts(&data, KEYWORD_DELETE);  
         if (!strcmp(data, "manage_by_non_root")) {  
                 manage_by_non_root = !is_delete;  
                 return 0;  
         }  
         return update_manager_entry(data, is_delete);  
 }  
   
 /**  
  * read_manager_policy - Read manager policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 static int read_manager_policy(struct ccs_io_buffer *head)  
 {  
         struct list1_head *pos;  
         if (head->read_eof)  
                 return 0;  
         list1_for_each_cookie(pos, head->read_var2, &policy_manager_list) {  
                 struct policy_manager_entry *ptr;  
                 ptr = list1_entry(pos, struct policy_manager_entry, list);  
                 if (ptr->is_deleted)  
                         continue;  
                 if (!ccs_io_printf(head, "%s\n", ptr->manager->name))  
                         return 0;  
         }  
         head->read_eof = true;  
         return 0;  
 }  
   
 /**  
  * is_policy_manager - Check whether the current process is a policy manager.  
  *  
  * Returns true if the current process is permitted to modify policy  
  * via /proc/ccs/ interface.  
  */  
 static bool is_policy_manager(void)  
 {  
         struct policy_manager_entry *ptr;  
         const char *exe;  
         struct task_struct *task = current;  
         const struct path_info *domainname = task->domain_info->domainname;  
         bool found = false;  
         if (!sbin_init_started)  
                 return true;  
         if (task->tomoyo_flags & CCS_TASK_IS_POLICY_MANAGER)  
                 return true;  
         if (!manage_by_non_root && (task->uid || task->euid))  
                 return false;  
         list1_for_each_entry(ptr, &policy_manager_list, list) {  
                 if (!ptr->is_deleted && ptr->is_domain  
                     && !ccs_pathcmp(domainname, ptr->manager)) {  
                         /* Set manager flag. */  
                         task->tomoyo_flags |= CCS_TASK_IS_POLICY_MANAGER;  
                         return true;  
                 }  
         }  
         exe = ccs_get_exe();  
         if (!exe)  
                 return false;  
         list1_for_each_entry(ptr, &policy_manager_list, list) {  
                 if (!ptr->is_deleted && !ptr->is_domain  
                     && !strcmp(exe, ptr->manager->name)) {  
                         found = true;  
                         /* Set manager flag. */  
                         task->tomoyo_flags |= CCS_TASK_IS_POLICY_MANAGER;  
                         break;  
                 }  
         }  
         if (!found) { /* Reduce error messages. */  
                 static pid_t last_pid;  
                 const pid_t pid = current->pid;  
                 if (last_pid != pid) {  
                         printk(KERN_WARNING "%s ( %s ) is not permitted to "  
                                "update policies.\n", domainname->name, exe);  
                         last_pid = pid;  
                 }  
         }  
         ccs_free(exe);  
         return found;  
 }  
   
 #ifdef CONFIG_TOMOYO  
   
 /**  
  * ccs_find_condition_part - Find condition part from the statement.  
  *  
  * @data: String to parse.  
  *  
  * Returns pointer to the condition part if it was found in the statement,  
  * NULL otherwise.  
  */  
 static char *ccs_find_condition_part(char *data)  
 {  
         char *cp = strstr(data, " if ");  
         if (cp) {  
                 char *cp2;  
                 while ((cp2 = strstr(cp + 3, " if ")) != NULL)  
                         cp = cp2;  
                 *cp++ = '\0';  
         } else {  
                 cp = strstr(data, " ; set ");  
                 if (cp)  
                         *cp++ = '\0';  
         }  
         return cp;  
 }  
   
 /**  
  * is_select_one - Parse select command.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @data: String to parse.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool is_select_one(struct ccs_io_buffer *head, const char *data)  
 {  
         unsigned int pid;  
         struct domain_info *domain = NULL;  
         if (sscanf(data, "pid=%u", &pid) == 1) {  
                 struct task_struct *p;  
                 /***** CRITICAL SECTION START *****/  
                 read_lock(&tasklist_lock);  
                 p = find_task_by_pid(pid);  
                 if (p)  
                         domain = p->domain_info;  
                 read_unlock(&tasklist_lock);  
                 /***** CRITICAL SECTION END *****/  
         } else if (!strncmp(data, "domain=", 7)) {  
                 if (ccs_is_domain_def(data + 7))  
                         domain = ccs_find_domain(data + 7);  
         } else  
                 return false;  
         head->read_avail = 0;  
         ccs_io_printf(head, "# select %s\n", data);  
         head->read_single_domain = true;  
         head->read_eof = !domain;  
         if (domain) {  
                 struct domain_info *d;  
                 head->read_var1 = NULL;  
                 list1_for_each_entry(d, &domain_list, list) {  
                         if (d == domain)  
                                 break;  
                         head->read_var1 = &d->list;  
                 }  
                 head->read_var2 = NULL;  
                 head->read_bit = 0;  
                 head->read_step = 0;  
                 if (domain->is_deleted)  
                         ccs_io_printf(head, "# This is a deleted domain.\n");  
         }  
         head->write_var1 = domain;  
         return true;  
 }  
   
 /**  
  * write_domain_policy - Write domain policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int write_domain_policy(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         struct domain_info *domain = head->write_var1;  
         bool is_delete = false;  
         bool is_select = false;  
         bool is_undelete = false;  
         unsigned int profile;  
         const struct condition_list *cond = NULL;  
         char *cp;  
         if (str_starts(&data, KEYWORD_DELETE))  
                 is_delete = true;  
         else if (str_starts(&data, KEYWORD_SELECT))  
                 is_select = true;  
         else if (str_starts(&data, KEYWORD_UNDELETE))  
                 is_undelete = true;  
         if (is_select && is_select_one(head, data))  
                 return 0;  
         /* Don't allow updating policies by non manager programs. */  
         if (!is_policy_manager())  
                 return -EPERM;  
         if (ccs_is_domain_def(data)) {  
                 domain = NULL;  
                 if (is_delete)  
                         ccs_delete_domain(data);  
                 else if (is_select)  
                         domain = ccs_find_domain(data);  
                 else if (is_undelete)  
                         domain = ccs_undelete_domain(data);  
                 else  
                         domain = ccs_find_or_assign_new_domain(data, 0);  
                 head->write_var1 = domain;  
                 ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);  
                 return 0;  
         }  
         if (!domain)  
                 return -EINVAL;  
   
         if (sscanf(data, KEYWORD_USE_PROFILE "%u", &profile) == 1  
             && profile < MAX_PROFILES) {  
                 if (profile_ptr[profile] || !sbin_init_started)  
                         domain->profile = (u8) profile;  
                 return 0;  
         }  
         if (!strcmp(data, KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {  
                 ccs_set_domain_flag(domain, is_delete,  
                                     DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);  
                 return 0;  
         }  
         if (!strcmp(data, KEYWORD_IGNORE_GLOBAL_ALLOW_ENV)) {  
                 ccs_set_domain_flag(domain, is_delete,  
                                     DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_ENV);  
                 return 0;  
         }  
         cp = ccs_find_condition_part(data);  
         if (cp) {  
                 cond = ccs_find_or_assign_new_condition(cp);  
                 if (!cond)  
                         return -EINVAL;  
         }  
         if (str_starts(&data, KEYWORD_ALLOW_CAPABILITY))  
                 return ccs_write_capability_policy(data, domain, cond,  
                                                    is_delete);  
         else if (str_starts(&data, KEYWORD_ALLOW_NETWORK))  
                 return ccs_write_network_policy(data, domain, cond, is_delete);  
         else if (str_starts(&data, KEYWORD_ALLOW_SIGNAL))  
                 return ccs_write_signal_policy(data, domain, cond, is_delete);  
         else if (str_starts(&data, KEYWORD_ALLOW_ARGV0))  
                 return ccs_write_argv0_policy(data, domain, cond, is_delete);  
         else if (str_starts(&data, KEYWORD_ALLOW_ENV))  
                 return ccs_write_env_policy(data, domain, cond, is_delete);  
         else  
                 return ccs_write_file_policy(data, domain, cond, is_delete);  
 }  
   
 /**  
  * print_single_path_acl - Print a single path ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct single_path_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_single_path_acl(struct ccs_io_buffer *head,  
                                   struct single_path_acl_record *ptr,  
                                   const struct condition_list *cond)  
 {  
         int pos;  
         u8 bit;  
         const char *atmark = "";  
         const char *filename;  
         const u16 perm = ptr->perm;  
         if (ptr->u_is_group) {  
                 atmark = "@";  
                 filename = ptr->u.group->group_name->name;  
         } else {  
                 filename = ptr->u.filename->name;  
         }  
         for (bit = head->read_bit; bit < MAX_SINGLE_PATH_OPERATION; bit++) {  
                 const char *msg;  
                 if (!(perm & (1 << bit)))  
                         continue;  
                 /* Print "read/write" instead of "read" and "write". */  
                 if ((bit == TYPE_READ_ACL || bit == TYPE_WRITE_ACL)  
                     && (perm & (1 << TYPE_READ_WRITE_ACL)))  
                         continue;  
                 msg = ccs_sp2keyword(bit);  
                 pos = head->read_avail;  
                 if (!ccs_io_printf(head, "allow_%s %s%s", msg,  
                                    atmark, filename) ||  
                     !ccs_print_condition(head, cond))  
                         goto out;  
         }  
         head->read_bit = 0;  
         return true;  
  out:  
         head->read_bit = bit;  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_double_path_acl - Print a double path ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct double_path_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_double_path_acl(struct ccs_io_buffer *head,  
                                   struct double_path_acl_record *ptr,  
                                   const struct condition_list *cond)  
 {  
         int pos;  
         const char *atmark1 = "";  
         const char *atmark2 = "";  
         const char *filename1;  
         const char *filename2;  
         const u8 perm = ptr->perm;  
         u8 bit;  
         if (ptr->u1_is_group) {  
                 atmark1 = "@";  
                 filename1 = ptr->u1.group1->group_name->name;  
         } else {  
                 filename1 = ptr->u1.filename1->name;  
         }  
         if (ptr->u2_is_group) {  
                 atmark2 = "@";  
                 filename2 = ptr->u2.group2->group_name->name;  
         } else {  
                 filename2 = ptr->u2.filename2->name;  
         }  
         for (bit = head->read_bit; bit < MAX_DOUBLE_PATH_OPERATION; bit++) {  
                 const char *msg;  
                 if (!(perm & (1 << bit)))  
                         continue;  
                 msg = ccs_dp2keyword(bit);  
                 pos = head->read_avail;  
                 if (!ccs_io_printf(head, "allow_%s %s%s %s%s", msg,  
                                    atmark1, filename1, atmark2, filename2) ||  
                     !ccs_print_condition(head, cond))  
                         goto out;  
         }  
         head->read_bit = 0;  
         return true;  
  out:  
         head->read_bit = bit;  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_argv0_acl - Print an argv[0] ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct argv0_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_argv0_acl(struct ccs_io_buffer *head,  
                             struct argv0_acl_record *ptr,  
                             const struct condition_list *cond)  
 {  
         int pos = head->read_avail;  
         if (!ccs_io_printf(head, KEYWORD_ALLOW_ARGV0 "%s %s",  
                            ptr->filename->name, ptr->argv0->name))  
                 goto out;  
         if (!ccs_print_condition(head, cond))  
                 goto out;  
         return true;  
  out:  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_env_acl - Print an evironment variable name's ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct env_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_env_acl(struct ccs_io_buffer *head,  
                           struct env_acl_record *ptr,  
                           const struct condition_list *cond)  
 {  
         int pos = head->read_avail;  
         if (!ccs_io_printf(head, KEYWORD_ALLOW_ENV "%s", ptr->env->name))  
                 goto out;  
         if (!ccs_print_condition(head, cond))  
                 goto out;  
         return true;  
  out:  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_capability_acl - Print a capability ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct capability_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_capability_acl(struct ccs_io_buffer *head,  
                                  struct capability_acl_record *ptr,  
                                  const struct condition_list *cond)  
 {  
         int pos = head->read_avail;  
         if (!ccs_io_printf(head, KEYWORD_ALLOW_CAPABILITY "%s",  
                            ccs_cap2keyword(ptr->operation)))  
                 goto out;  
         if (!ccs_print_condition(head, cond))  
                 goto out;  
         return true;  
  out:  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_ipv4_entry - Print IPv4 address of a network ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct ip_network_acl_record".  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_ipv4_entry(struct ccs_io_buffer *head,  
                              struct ip_network_acl_record *ptr)  
 {  
         const u32 min_address = ptr->u.ipv4.min;  
         const u32 max_address = ptr->u.ipv4.max;  
         if (!ccs_io_printf(head, "%u.%u.%u.%u", HIPQUAD(min_address)))  
                 return false;  
         if (min_address != max_address  
             && !ccs_io_printf(head, "-%u.%u.%u.%u", HIPQUAD(max_address)))  
                 return false;  
         return true;  
 }  
   
 /**  
  * print_ipv6_entry - Print IPv6 address of a network ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct ip_network_acl_record".  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_ipv6_entry(struct ccs_io_buffer *head,  
                              struct ip_network_acl_record *ptr)  
 {  
         char buf[64];  
         const struct in6_addr *min_address = ptr->u.ipv6.min;  
         const struct in6_addr *max_address = ptr->u.ipv6.max;  
         ccs_print_ipv6(buf, sizeof(buf), min_address);  
         if (!ccs_io_printf(head, "%s", buf))  
                 return false;  
         if (min_address != max_address) {  
                 ccs_print_ipv6(buf, sizeof(buf), max_address);  
                 if (!ccs_io_printf(head, "-%s", buf))  
                         return false;  
         }  
         return true;  
 }  
   
 /**  
  * print_port_entry - Print port number of a network ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct ip_network_acl_record".  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_port_entry(struct ccs_io_buffer *head,  
                              struct ip_network_acl_record *ptr)  
 {  
         const u16 min_port = ptr->min_port, max_port = ptr->max_port;  
         if (!ccs_io_printf(head, " %u", min_port))  
                 return false;  
         if (min_port != max_port && !ccs_io_printf(head, "-%u", max_port))  
                 return false;  
         return true;  
 }  
   
 /**  
  * print_network_acl - Print a network ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct ip_network_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_network_acl(struct ccs_io_buffer *head,  
                               struct ip_network_acl_record *ptr,  
                               const struct condition_list *cond)  
 {  
         int pos = head->read_avail;  
         if (!ccs_io_printf(head, KEYWORD_ALLOW_NETWORK "%s ",  
                            ccs_net2keyword(ptr->operation_type)))  
                 goto out;  
         switch (ptr->record_type) {  
         case IP_RECORD_TYPE_ADDRESS_GROUP:  
                 if (!ccs_io_printf(head, "@%s", ptr->u.group->group_name->name))  
                         goto out;  
                 break;  
         case IP_RECORD_TYPE_IPv4:  
                 if (!print_ipv4_entry(head, ptr))  
                         goto out;  
                 break;  
         case IP_RECORD_TYPE_IPv6:  
                 if (!print_ipv6_entry(head, ptr))  
                         goto out;  
                 break;  
         }  
         if (!print_port_entry(head, ptr))  
                 goto out;  
         if (!ccs_print_condition(head, cond))  
                 goto out;  
         return true;  
  out:  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_signal_acl - Print a signal ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to "struct signale_acl_record".  
  * @cond: Pointer to "struct condition_list". May be NULL.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_signal_acl(struct ccs_io_buffer *head,  
                              struct signal_acl_record *ptr,  
                              const struct condition_list *cond)  
 {  
         int pos = head->read_avail;  
         if (!ccs_io_printf(head, KEYWORD_ALLOW_SIGNAL "%u %s",  
                            ptr->sig, ptr->domainname->name))  
                 goto out;  
         if (!ccs_print_condition(head, cond))  
                 goto out;  
         return true;  
  out:  
         head->read_avail = pos;  
         return false;  
 }  
   
 /**  
  * print_execute_handler_record - Print an execute handler ACL entry.  
  *  
  * @head:    Pointer to "struct ccs_io_buffer".  
  * @keyword: Name of the keyword.  
  * @ptr:     Pointer to "struct execute_handler_record".  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_execute_handler_record(struct ccs_io_buffer *head,  
                                          const char *keyword,  
                                          struct execute_handler_record *ptr)  
 {  
         return ccs_io_printf(head, "%s %s\n", keyword, ptr->handler->name);  
 }  
   
 /**  
  * print_entry - Print an ACL entry.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  * @ptr:  Pointer to an ACL entry.  
  *  
  * Returns true on success, false otherwise.  
  */  
 static bool print_entry(struct ccs_io_buffer *head, struct acl_info *ptr)  
 {  
         const struct condition_list *cond = ccs_get_condition_part(ptr);  
         const u8 acl_type = ccs_acl_type2(ptr);  
         if (acl_type & ACL_DELETED)  
                 return true;  
         if (acl_type == TYPE_SINGLE_PATH_ACL) {  
                 struct single_path_acl_record *acl  
                         = container_of(ptr, struct single_path_acl_record,  
                                        head);  
                 return print_single_path_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_DOUBLE_PATH_ACL) {  
                 struct double_path_acl_record *acl  
                         = container_of(ptr, struct double_path_acl_record,  
                                        head);  
                 return print_double_path_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_ARGV0_ACL) {  
                 struct argv0_acl_record *acl  
                         = container_of(ptr, struct argv0_acl_record, head);  
                 return print_argv0_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_ENV_ACL) {  
                 struct env_acl_record *acl  
                         = container_of(ptr, struct env_acl_record, head);  
                 return print_env_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_CAPABILITY_ACL) {  
                 struct capability_acl_record *acl  
                         = container_of(ptr, struct capability_acl_record, head);  
                 return print_capability_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_IP_NETWORK_ACL) {  
                 struct ip_network_acl_record *acl  
                         = container_of(ptr, struct ip_network_acl_record, head);  
                 return print_network_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_SIGNAL_ACL) {  
                 struct signal_acl_record *acl  
                         = container_of(ptr, struct signal_acl_record, head);  
                 return print_signal_acl(head, acl, cond);  
         }  
         if (acl_type == TYPE_EXECUTE_HANDLER) {  
                 struct execute_handler_record *acl  
                         = container_of(ptr, struct execute_handler_record,  
                                        head);  
                 const char *keyword = KEYWORD_EXECUTE_HANDLER;  
                 return print_execute_handler_record(head, keyword, acl);  
         }  
         if (acl_type == TYPE_DENIED_EXECUTE_HANDLER) {  
                 struct execute_handler_record *acl  
                         = container_of(ptr, struct execute_handler_record,  
                                        head);  
                 const char *keyword = KEYWORD_DENIED_EXECUTE_HANDLER;  
                 return print_execute_handler_record(head, keyword, acl);  
         }  
         /* Workaround for gcc 3.2.2's inline bug. */  
         if (acl_type & ACL_DELETED)  
                 return true;  
         BUG(); /* This must not happen. */  
         return false;  
 }  
   
 /**  
  * read_domain_policy - Read domain policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 static int read_domain_policy(struct ccs_io_buffer *head)  
 {  
         struct list1_head *dpos;  
         struct list1_head *apos;  
         if (head->read_eof)  
                 return 0;  
         if (head->read_step == 0)  
                 head->read_step = 1;  
         list1_for_each_cookie(dpos, head->read_var1, &domain_list) {  
                 struct domain_info *domain;  
                 const char *quota_exceeded = "";  
                 const char *transition_failed = "";  
                 const char *ignore_global_allow_read = "";  
                 const char *ignore_global_allow_env = "";  
                 domain = list1_entry(dpos, struct domain_info, list);  
                 if (head->read_step != 1)  
                         goto acl_loop;  
                 if (domain->is_deleted && !head->read_single_domain)  
                         continue;  
                 /* Print domainname and flags. */  
                 if (domain->quota_warned)  
                         quota_exceeded = "quota_exceeded\n";  
                 if (domain->flags & DOMAIN_FLAGS_TRANSITION_FAILED)  
                         transition_failed = "transition_failed\n";  
                 if (domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)  
                         ignore_global_allow_read  
                                 = KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";  
                 if (domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_ENV)  
                         ignore_global_allow_env  
                                 = KEYWORD_IGNORE_GLOBAL_ALLOW_ENV "\n";  
                 if (!ccs_io_printf(head, "%s\n" KEYWORD_USE_PROFILE "%u\n"  
                                    "%s%s%s%s\n", domain->domainname->name,  
                                    domain->profile, quota_exceeded,  
                                    transition_failed,  
                                    ignore_global_allow_read,  
                                    ignore_global_allow_env))  
                         return 0;  
                 head->read_step = 2;  
  acl_loop:  
                 if (head->read_step == 3)  
                         goto tail_mark;  
                 /* Print ACL entries in the domain. */  
                 list1_for_each_cookie(apos, head->read_var2,  
                                       &domain->acl_info_list) {  
                         struct acl_info *ptr  
                                 = list1_entry(apos, struct acl_info, list);  
                         if (!print_entry(head, ptr))  
                                 return 0;  
                 }  
                 head->read_step = 3;  
  tail_mark:  
                 if (!ccs_io_printf(head, "\n"))  
                         return 0;  
                 head->read_step = 1;  
                 if (head->read_single_domain)  
                         break;  
         }  
         head->read_eof = true;  
         return 0;  
 }  
   
 #endif  
   
 /**  
  * write_domain_profile - Assign profile for specified domain.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, -EINVAL otherwise.  
  *  
  * This is equivalent to doing  
  *  
  *     ( echo "select " $domainname; echo "use_profile " $profile ) |  
  *     /usr/lib/ccs/loadpolicy -d  
  */  
 static int write_domain_profile(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         char *cp = strchr(data, ' ');  
         struct domain_info *domain;  
         unsigned int profile;  
         if (!cp)  
                 return -EINVAL;  
         *cp = '\0';  
         domain = ccs_find_domain(cp + 1);  
         profile = simple_strtoul(data, NULL, 10);  
         if (domain && profile < MAX_PROFILES  
             && (profile_ptr[profile] || !sbin_init_started))  
                 domain->profile = (u8) profile;  
         ccs_update_counter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);  
         return 0;  
 }  
   
 /**  
  * read_domain_profile - Read only domainname and profile.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns list of profile number and domainname pairs.  
  *  
  * This is equivalent to doing  
  *  
  *     grep -A 1 '^<kernel>' /proc/ccs/domain_policy |  
  *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )  
  *     domainname = $0; } else if ( $1 == "use_profile" ) {  
  *     print $2 " " domainname; domainname = ""; } } ; '  
  */  
 static int read_domain_profile(struct ccs_io_buffer *head)  
 {  
         struct list1_head *pos;  
         if (head->read_eof)  
                 return 0;  
         list1_for_each_cookie(pos, head->read_var1, &domain_list) {  
                 struct domain_info *domain;  
                 domain = list1_entry(pos, struct domain_info, list);  
                 if (domain->is_deleted)  
                         continue;  
                 if (!ccs_io_printf(head, "%u %s\n", domain->profile,  
                                    domain->domainname->name))  
                         return 0;  
         }  
         head->read_eof = true;  
         return 0;  
 }  
   
 /**  
  * write_pid: Specify PID to obtain domainname.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 static int write_pid(struct ccs_io_buffer *head)  
 {  
         head->read_eof = false;  
         return 0;  
 }  
   
 /**  
  * read_pid - Read information of a process.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns the domainname which the specified PID is in or  
  * process information of the specified PID on success,  
  * empty string otherwise.  
  */  
 static int read_pid(struct ccs_io_buffer *head)  
 {  
         /* Accessing write_buf is safe because head->io_sem is held. */  
         char *buf = head->write_buf;  
         bool task_info = false;  
         unsigned int pid;  
         struct task_struct *p;  
         struct domain_info *domain = NULL;  
         u32 tomoyo_flags = 0;  
         if (head->read_avail || head->read_eof)  
                 goto done;  
         head->read_eof = true;  
         if (str_starts(&buf, "info "))  
                 task_info = true;  
         pid = (unsigned int) simple_strtoul(buf, NULL, 10);  
         /***** CRITICAL SECTION START *****/  
         read_lock(&tasklist_lock);  
         p = find_task_by_pid(pid);  
         if (p) {  
                 domain = p->domain_info;  
                 tomoyo_flags = p->tomoyo_flags;  
         }  
         read_unlock(&tasklist_lock);  
         /***** CRITICAL SECTION END *****/  
         if (!domain)  
                 goto done;  
         if (!task_info)  
                 ccs_io_printf(head, "%u %u %s", pid, domain->profile,  
                               domain->domainname->name);  
         else  
                 ccs_io_printf(head, "%u manager=%s execute_handler=%s "  
                               "state[0]=%u state[1]=%u state[2]=%u", pid,  
                               tomoyo_flags & CCS_TASK_IS_POLICY_MANAGER ?  
                               "yes" : "no",  
                               tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER ?  
                               "yes" : "no",  
                               (u8) (tomoyo_flags >> 24),  
                               (u8) (tomoyo_flags >> 16),  
                               (u8) (tomoyo_flags >> 8));  
  done:  
         return 0;  
 }  
   
 #ifdef CONFIG_TOMOYO  
   
 /**  
  * write_exception_policy - Write exception policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int write_exception_policy(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         bool is_delete = str_starts(&data, KEYWORD_DELETE);  
         if (str_starts(&data, KEYWORD_KEEP_DOMAIN))  
                 return ccs_write_domain_keeper_policy(data, false, is_delete);  
         if (str_starts(&data, KEYWORD_NO_KEEP_DOMAIN))  
                 return ccs_write_domain_keeper_policy(data, true, is_delete);  
         if (str_starts(&data, KEYWORD_INITIALIZE_DOMAIN))  
                 return ccs_write_domain_initializer_policy(data, false,  
                                                            is_delete);  
         if (str_starts(&data, KEYWORD_NO_INITIALIZE_DOMAIN))  
                 return ccs_write_domain_initializer_policy(data, true,  
                                                            is_delete);  
         if (str_starts(&data, KEYWORD_ALIAS))  
                 return ccs_write_alias_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_AGGREGATOR))  
                 return ccs_write_aggregator_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_ALLOW_READ))  
                 return ccs_write_globally_readable_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_ALLOW_ENV))  
                 return ccs_write_globally_usable_env_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_FILE_PATTERN))  
                 return ccs_write_pattern_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_PATH_GROUP))  
                 return ccs_write_path_group_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_DENY_REWRITE))  
                 return ccs_write_no_rewrite_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_ADDRESS_GROUP))  
                 return ccs_write_address_group_policy(data, is_delete);  
         return -EINVAL;  
 }  
   
 /**  
  * read_exception_policy - Read exception policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, -EINVAL otherwise.  
  */  
 static int read_exception_policy(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 switch (head->read_step) {  
                 case 0:  
                         head->read_var2 = NULL;  
                         head->read_step = 1;  
                 case 1:  
                         if (!ccs_read_domain_keeper_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 2;  
                 case 2:  
                         if (!ccs_read_globally_readable_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 3;  
                 case 3:  
                         if (!ccs_read_globally_usable_env_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 4;  
                 case 4:  
                         if (!ccs_read_domain_initializer_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 5;  
                 case 5:  
                         if (!ccs_read_alias_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 6;  
                 case 6:  
                         if (!ccs_read_aggregator_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 7;  
                 case 7:  
                         if (!ccs_read_file_pattern(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 8;  
                 case 8:  
                         if (!ccs_read_no_rewrite_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 9;  
                 case 9:  
                         if (!ccs_read_path_group_policy(head))  
                                 break;  
                         head->read_var1 = NULL;  
                         head->read_var2 = NULL;  
                         head->read_step = 10;  
                 case 10:  
                         if (!ccs_read_address_group_policy(head))  
                                 break;  
                         head->read_eof = true;  
                         break;  
                 default:  
                         return -EINVAL;  
                 }  
         }  
         return 0;  
 }  
   
 #endif  
   
 #ifdef CONFIG_SAKURA  
   
 /**  
  * write_system_policy - Write system policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, negative value otherwise.  
  */  
 static int write_system_policy(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         bool is_delete = false;  
         if (str_starts(&data, KEYWORD_DELETE))  
                 is_delete = true;  
         if (str_starts(&data, KEYWORD_ALLOW_MOUNT))  
                 return ccs_write_mount_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_DENY_UNMOUNT))  
                 return ccs_write_no_umount_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_ALLOW_CHROOT))  
                 return ccs_write_chroot_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_ALLOW_PIVOT_ROOT))  
                 return ccs_write_pivot_root_policy(data, is_delete);  
         if (str_starts(&data, KEYWORD_DENY_AUTOBIND))  
                 return ccs_write_reserved_port_policy(data, is_delete);  
         return -EINVAL;  
 }  
   
 /**  
  * read_system_policy - Read system policy.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, -EINVAL otherwise.  
  */  
 static int read_system_policy(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 switch (head->read_step) {  
                 case 0:  
                         head->read_var2 = NULL;  
                         head->read_step = 1;  
                 case 1:  
                         if (!ccs_read_mount_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 2;  
                 case 2:  
                         if (!ccs_read_no_umount_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 3;  
                 case 3:  
                         if (!ccs_read_chroot_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 4;  
                 case 4:  
                         if (!ccs_read_pivot_root_policy(head))  
                                 break;  
                         head->read_var2 = NULL;  
                         head->read_step = 5;  
                 case 5:  
                         if (!ccs_read_reserved_port_policy(head))  
                                 break;  
                         head->read_eof = true;  
                         break;  
                 default:  
                         return -EINVAL;  
                 }  
         }  
         return 0;  
 }  
   
 #endif  
   
 /* Path to the policy loader. The default is /sbin/ccs-init. */  
 static const char *ccs_loader;  
   
 /**  
  * loader_setup - Specify the policy loader to use.  
  *  
  * @str: Path to the policy loader.  
  *  
  * Returns 0.  
  */  
 static int __init loader_setup(char *str)  
 {  
         ccs_loader = str;  
         return 0;  
 }  
   
 __setup("CCS_loader=", loader_setup);  
   
 /**  
  * policy_loader_exists - Check whether /sbin/ccs-init exists.  
  *  
  * Returns true if /sbin/ccs-init exists, false otherwise.  
  */  
 static bool policy_loader_exists(void)  
 {  
         /*  
          * Don't activate MAC if the path given by 'CCS_loader=' option doesn't  
          * exist. If the initrd includes /sbin/init but real-root-dev has not  
          * mounted on / yet, activating MAC will block the system since  
          * policies are not loaded yet.  
          * Thus, let do_execve() call this function everytime.  
          */  
         struct nameidata nd;  
         if (!ccs_loader)  
                 ccs_loader = "/sbin/ccs-init";  
         if (path_lookup(ccs_loader, lookup_flags, &nd)) {  
                 printk(KERN_INFO "Not activating Mandatory Access Control now "  
                        "since %s doesn't exist.\n", ccs_loader);  
                 return false;  
         }  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
         path_put(&nd.path);  
 #else  
         path_release(&nd);  
 #endif  
         return true;  
 }  
   
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)  
 /**  
  * run_ccs_loader - Start /sbin/ccs-init .  
  *  
  * @unused: Not used.  
  *  
  * Returns PID of /sbin/ccs-init on success, negative value otherwise.  
  */  
 static int run_ccs_loader(void *unused)  
 {  
         char *argv[2];  
         char *envp[3];  
         printk(KERN_INFO "Calling %s to load policy. Please wait.\n",  
                ccs_loader);  
         argv[0] = (char *) ccs_loader;  
         argv[1] = NULL;  
         envp[0] = "HOME=/";  
         envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";  
         envp[2] = NULL;  
         return exec_usermodehelper(argv[0], argv, envp);  
 }  
 #endif  
   
 /**  
  * ccs_load_policy - Run external policy loader to load policy.  
  *  
  * @filename: The program about to start.  
  *  
  * This function checks whether @filename is /sbin/init , and if so  
  * invoke /sbin/ccs-init and wait for the termination of /sbin/ccs-init  
  * and then continues invocation of /sbin/init.  
  * /sbin/ccs-init reads policy files in /etc/ccs/ directory and  
  * writes to /proc/ccs/ interfaces.  
  *  
  * Returns nothing.  
  */  
 void ccs_load_policy(const char *filename)  
 {  
         if (sbin_init_started)  
                 return;  
         /*  
          * Check filename is /sbin/init or /sbin/ccs-start.  
          * /sbin/ccs-start is a dummy filename in case where /sbin/init can't  
          * be passed.  
          * You can create /sbin/ccs-start by "ln -s /bin/true /sbin/ccs-start".  
          */  
         if (strcmp(filename, "/sbin/init") &&  
             strcmp(filename, "/sbin/ccs-start"))  
                 return;  
         if (!policy_loader_exists())  
                 return;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  
         {  
                 char *argv[2];  
                 char *envp[3];  
                 printk(KERN_INFO "Calling %s to load policy. Please wait.\n",  
                        ccs_loader);  
                 argv[0] = (char *) ccs_loader;  
                 argv[1] = NULL;  
                 envp[0] = "HOME=/";  
                 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";  
                 envp[2] = NULL;  
                 call_usermodehelper(argv[0], argv, envp, 1);  
         }  
 #elif defined(TASK_DEAD)  
         {  
                 /* Copied from kernel/kmod.c */  
                 struct task_struct *task = current;  
                 pid_t pid = kernel_thread(run_ccs_loader, NULL, 0);  
                 sigset_t tmpsig;  
                 spin_lock_irq(&task->sighand->siglock);  
                 tmpsig = task->blocked;  
                 siginitsetinv(&task->blocked,  
                               sigmask(SIGKILL) | sigmask(SIGSTOP));  
                 recalc_sigpending();  
                 spin_unlock_irq(&current->sighand->siglock);  
                 if (pid >= 0)  
                         waitpid(pid, NULL, __WCLONE);  
                 spin_lock_irq(&task->sighand->siglock);  
                 task->blocked = tmpsig;  
                 recalc_sigpending();  
                 spin_unlock_irq(&task->sighand->siglock);  
         }  
 #else  
         {  
                 /* Copied from kernel/kmod.c */  
                 struct task_struct *task = current;  
                 pid_t pid = kernel_thread(run_ccs_loader, NULL, 0);  
                 sigset_t tmpsig;  
                 spin_lock_irq(&task->sigmask_lock);  
                 tmpsig = task->blocked;  
                 siginitsetinv(&task->blocked,  
                               sigmask(SIGKILL) | sigmask(SIGSTOP));  
                 recalc_sigpending(task);  
                 spin_unlock_irq(&task->sigmask_lock);  
                 if (pid >= 0)  
                         waitpid(pid, NULL, __WCLONE);  
                 spin_lock_irq(&task->sigmask_lock);  
                 task->blocked = tmpsig;  
                 recalc_sigpending(task);  
                 spin_unlock_irq(&task->sigmask_lock);  
         }  
 #endif  
 #ifdef CONFIG_SAKURA  
         printk(KERN_INFO "SAKURA: 1.6.5-pre   2008/10/20\n");  
 #endif  
 #ifdef CONFIG_TOMOYO  
         printk(KERN_INFO "TOMOYO: 1.6.5-pre   2008/10/20\n");  
 #endif  
         printk(KERN_INFO "Mandatory Access Control activated.\n");  
         sbin_init_started = true;  
         ccs_log_level = KERN_WARNING;  
         { /* Check all profiles currently assigned to domains are defined. */  
                 struct domain_info *domain;  
                 list1_for_each_entry(domain, &domain_list, list) {  
                         const u8 profile = domain->profile;  
                         if (profile_ptr[profile])  
                                 continue;  
                         panic("Profile %u (used by '%s') not defined.\n",  
                               profile, domain->domainname->name);  
                 }  
         }  
 }  
   
 /* Wait queue for query_list. */  
 static DECLARE_WAIT_QUEUE_HEAD(query_wait);  
   
 /* Lock for manipulating query_list. */  
 static DEFINE_SPINLOCK(query_list_lock);  
   
 /* Structure for query. */  
 struct query_entry {  
         struct list_head list;  
         char *query;  
         int query_len;  
         unsigned int serial;  
         int timer;  
         int answer;  
 };  
   
 /* The list for "struct query_entry". */  
 static LIST_HEAD(query_list);  
   
 /* Number of "struct file" referring /proc/ccs/query interface. */  
 static atomic_t queryd_watcher = ATOMIC_INIT(0);  
   
 /**  
  * ccs_check_supervisor - Ask for the supervisor's decision.  
  *  
  * @r:       Pointer to "struct ccs_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, 1 if the supervisor decided to  
  * retry the access request which violated the policy in enforcing mode,  
  * -EPERM otherwise.  
  */  
 int ccs_check_supervisor(struct ccs_request_info *r, const char *fmt, ...)  
 {  
         va_list args;  
         int error = -EPERM;  
         int pos;  
         int len;  
         static unsigned int serial;  
         struct query_entry *query_entry = NULL;  
         char *header;  
         if (!r->domain)  
                 r->domain = current->domain_info;  
         if (!atomic_read(&queryd_watcher)) {  
                 int i;  
                 if (current->tomoyo_flags & CCS_DONT_SLEEP_ON_ENFORCE_ERROR)  
                         return -EPERM;  
                 for (i = 0; i < ccs_check_flags(r->domain, CCS_SLEEP_PERIOD);  
                      i++) {  
                         set_current_state(TASK_INTERRUPTIBLE);  
                         schedule_timeout(HZ / 10);  
                 }  
                 return -EPERM;  
         }  
         va_start(args, fmt);  
         len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;  
         va_end(args);  
 #ifdef CONFIG_TOMOYO  
         header = ccs_init_audit_log(&len, r);  
 #else  
         header = ccs_alloc(1);  
 #endif  
         if (!header)  
                 goto out;  
         query_entry = ccs_alloc(sizeof(*query_entry));  
         if (!query_entry)  
                 goto out;  
         query_entry->query = ccs_alloc(len);  
         if (!query_entry->query)  
                 goto out;  
         INIT_LIST_HEAD(&query_entry->list);  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         query_entry->serial = serial++;  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         pos = snprintf(query_entry->query, len - 1, "Q%u-%hu\n%s",  
                        query_entry->serial, r->retry, header);  
         ccs_free(header);  
         header = NULL;  
         va_start(args, fmt);  
         vsnprintf(query_entry->query + pos, len - 1 - pos, fmt, args);  
         query_entry->query_len = strlen(query_entry->query) + 1;  
         va_end(args);  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_add_tail(&query_entry->list, &query_list);  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         ccs_update_counter(CCS_UPDATES_COUNTER_QUERY);  
         /* Give 10 seconds for supervisor's opinion. */  
         for (query_entry->timer = 0; atomic_read(&queryd_watcher)  
                      && query_entry->timer < 100; query_entry->timer++) {  
                 wake_up(&query_wait);  
                 set_current_state(TASK_INTERRUPTIBLE);  
                 schedule_timeout(HZ / 10);  
                 if (query_entry->answer)  
                         break;  
         }  
         ccs_update_counter(CCS_UPDATES_COUNTER_QUERY);  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_del(&query_entry->list);  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         switch (query_entry->answer) {  
         case 3: /* Asked to retry by administrator. */  
                 error = 1;  
                 break;  
         case 1:  
                 /* Granted by administrator. */  
                 error = 0;  
                 break;  
         case 0:  
                 /* Timed out. */  
                 break;  
         default:  
                 /* Rejected by administrator. */  
                 break;  
         }  
  out:  
         if (query_entry)  
                 ccs_free(query_entry->query);  
         ccs_free(query_entry);  
         ccs_free(header);  
         return error;  
 }  
   
 /**  
  * poll_query - poll() for /proc/ccs/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 poll_query(struct file *file, poll_table *wait)  
 {  
         struct list_head *tmp;  
         bool found = false;  
         u8 i;  
         for (i = 0; i < 2; i++) {  
                 /***** CRITICAL SECTION START *****/  
                 spin_lock(&query_list_lock);  
                 list_for_each(tmp, &query_list) {  
                         struct query_entry *ptr  
                                 = list_entry(tmp, struct query_entry, list);  
                         if (ptr->answer)  
                                 continue;  
                         found = true;  
                         break;  
                 }  
                 spin_unlock(&query_list_lock);  
                 /***** CRITICAL SECTION END *****/  
                 if (found)  
                         return POLLIN | POLLRDNORM;  
                 if (i)  
                         break;  
                 poll_wait(file, &query_wait, wait);  
         }  
         return 0;  
 }  
   
 /**  
  * read_query - Read access requests which violated policy in enforcing mode.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 static int read_query(struct ccs_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) {  
                 ccs_free(head->read_buf);  
                 head->read_buf = NULL;  
                 head->readbuf_size = 0;  
         }  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_for_each(tmp, &query_list) {  
                 struct query_entry *ptr  
                         = list_entry(tmp, struct query_entry, list);  
                 if (ptr->answer)  
                         continue;  
                 if (pos++ != head->read_step)  
                         continue;  
                 len = ptr->query_len;  
                 break;  
         }  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         if (!len) {  
                 head->read_step = 0;  
                 return 0;  
         }  
         buf = ccs_alloc(len);  
         if (!buf)  
                 return 0;  
         pos = 0;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_for_each(tmp, &query_list) {  
                 struct query_entry *ptr  
                         = list_entry(tmp, struct query_entry, list);  
                 if (ptr->answer)  
                         continue;  
                 if (pos++ != head->read_step)  
                         continue;  
                 /*  
                  * Some query can be skipped because query_list  
                  * can change, but I don't care.  
                  */  
                 if (len == ptr->query_len)  
                         memmove(buf, ptr->query, len);  
                 break;  
         }  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         if (buf[0]) {  
                 head->read_avail = len;  
                 head->readbuf_size = head->read_avail;  
                 head->read_buf = buf;  
                 head->read_step++;  
         } else {  
                 ccs_free(buf);  
         }  
         return 0;  
 }  
   
 /**  
  * write_answer - Write the supervisor's decision.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0 on success, -EINVAL otherwise.  
  */  
 static int write_answer(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         struct list_head *tmp;  
         unsigned int serial;  
         unsigned int answer;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_for_each(tmp, &query_list) {  
                 struct query_entry *ptr  
                         = list_entry(tmp, struct query_entry, list);  
                 ptr->timer = 0;  
         }  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         if (sscanf(data, "A%u=%u", &serial, &answer) != 2)  
                 return -EINVAL;  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&query_list_lock);  
         list_for_each(tmp, &query_list) {  
                 struct query_entry *ptr  
                         = list_entry(tmp, struct query_entry, list);  
                 if (ptr->serial != serial)  
                         continue;  
                 if (!ptr->answer)  
                         ptr->answer = answer;  
                 break;  
         }  
         spin_unlock(&query_list_lock);  
         /***** CRITICAL SECTION END *****/  
         return 0;  
 }  
   
 /* Policy updates counter. */  
 static unsigned int updates_counter[MAX_CCS_UPDATES_COUNTER];  
   
 /* Policy updates counter lock. */  
 static DEFINE_SPINLOCK(updates_counter_lock);  
   
 /**  
  * ccs_update_counter - Increment policy change counter.  
  *  
  * @index: Type of policy.  
  *  
  * Returns nothing.  
  */  
 void ccs_update_counter(const unsigned char index)  
 {  
         /***** CRITICAL SECTION START *****/  
         spin_lock(&updates_counter_lock);  
         if (index < MAX_CCS_UPDATES_COUNTER)  
                 updates_counter[index]++;  
         spin_unlock(&updates_counter_lock);  
         /***** CRITICAL SECTION END *****/  
 }  
   
 /**  
  * read_updates_counter - Check for policy change counter.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns how many times policy has changed since the previous check.  
  */  
 static int read_updates_counter(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 unsigned int counter[MAX_CCS_UPDATES_COUNTER];  
                 /***** CRITICAL SECTION START *****/  
                 spin_lock(&updates_counter_lock);  
                 memmove(counter, updates_counter, sizeof(updates_counter));  
                 memset(updates_counter, 0, sizeof(updates_counter));  
                 spin_unlock(&updates_counter_lock);  
                 /***** CRITICAL SECTION END *****/  
                 ccs_io_printf(head,  
                               "/proc/ccs/system_policy:    %10u\n"  
                               "/proc/ccs/domain_policy:    %10u\n"  
                               "/proc/ccs/exception_policy: %10u\n"  
                               "/proc/ccs/profile:          %10u\n"  
                               "/proc/ccs/query:            %10u\n"  
                               "/proc/ccs/manager:          %10u\n"  
                               "/proc/ccs/grant_log:        %10u\n"  
                               "/proc/ccs/reject_log:       %10u\n",  
                               counter[CCS_UPDATES_COUNTER_SYSTEM_POLICY],  
                               counter[CCS_UPDATES_COUNTER_DOMAIN_POLICY],  
                               counter[CCS_UPDATES_COUNTER_EXCEPTION_POLICY],  
                               counter[CCS_UPDATES_COUNTER_PROFILE],  
                               counter[CCS_UPDATES_COUNTER_QUERY],  
                               counter[CCS_UPDATES_COUNTER_MANAGER],  
                               counter[CCS_UPDATES_COUNTER_GRANT_LOG],  
                               counter[CCS_UPDATES_COUNTER_REJECT_LOG]);  
                 head->read_eof = true;  
         }  
         return 0;  
 }  
   
 /**  
  * read_version: Get version.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns version information.  
  */  
 static int read_version(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 ccs_io_printf(head, "1.6.5-pre");  
                 head->read_eof = true;  
         }  
         return 0;  
 }  
   
 /**  
  * read_self_domain - Get the current process's domainname.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns the current process's domainname.  
  */  
 static int read_self_domain(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 /*  
                  * current->domain_info->domainname != NULL  
                  * because every process belongs to a domain and  
                  * the domain's name cannot be NULL.  
                  */  
                 ccs_io_printf(head, "%s",  
                               current->domain_info->domainname->name);  
                 head->read_eof = true;  
         }  
         return 0;  
 }  
   
 /**  
  * ccs_open_control - open() for /proc/ccs/ interface.  
  *  
  * @type: Type of interface.  
  * @file: Pointer to "struct file".  
  *  
  * Associates policy handler and returns 0 on success, -ENOMEM otherwise.  
  */  
 int ccs_open_control(const u8 type, struct file *file)  
 {  
         struct ccs_io_buffer *head = ccs_alloc(sizeof(*head));  
         if (!head)  
                 return -ENOMEM;  
         mutex_init(&head->io_sem);  
         switch (type) {  
 #ifdef CONFIG_SAKURA  
         case CCS_SYSTEMPOLICY: /* /proc/ccs/system_policy */  
                 head->write = write_system_policy;  
                 head->read = read_system_policy;  
                 break;  
 #endif  
 #ifdef CONFIG_TOMOYO  
         case CCS_DOMAINPOLICY: /* /proc/ccs/domain_policy */  
                 head->write = write_domain_policy;  
                 head->read = read_domain_policy;  
                 break;  
         case CCS_EXCEPTIONPOLICY: /* /proc/ccs/exception_policy */  
                 head->write = write_exception_policy;  
                 head->read = read_exception_policy;  
                 break;  
         case CCS_GRANTLOG: /* /proc/ccs/grant_log */  
                 head->poll = ccs_poll_grant_log;  
                 head->read = ccs_read_grant_log;  
                 break;  
         case CCS_REJECTLOG: /* /proc/ccs/reject_log */  
                 head->poll = ccs_poll_reject_log;  
                 head->read = ccs_read_reject_log;  
                 break;  
 #endif  
         case CCS_SELFDOMAIN: /* /proc/ccs/self_domain */  
                 head->read = read_self_domain;  
                 break;  
         case CCS_DOMAIN_STATUS: /* /proc/ccs/.domain_status */  
                 head->write = write_domain_profile;  
                 head->read = read_domain_profile;  
                 break;  
         case CCS_PROCESS_STATUS: /* /proc/ccs/.process_status */  
                 head->write = write_pid;  
                 head->read = read_pid;  
                 break;  
         case CCS_VERSION: /* /proc/ccs/version */  
                 head->read = read_version;  
                 head->readbuf_size = 128;  
                 break;  
         case CCS_MEMINFO: /* /proc/ccs/meminfo */  
                 head->write = ccs_write_memory_quota;  
                 head->read = ccs_read_memory_counter;  
                 head->readbuf_size = 512;  
                 break;  
         case CCS_PROFILE: /* /proc/ccs/profile */  
                 head->write = write_profile;  
                 head->read = read_profile;  
                 break;  
         case CCS_QUERY: /* /proc/ccs/query */  
                 head->poll = poll_query;  
                 head->write = write_answer;  
                 head->read = read_query;  
                 break;  
         case CCS_MANAGER: /* /proc/ccs/manager */  
                 head->write = write_manager_policy;  
                 head->read = read_manager_policy;  
                 break;  
         case CCS_UPDATESCOUNTER: /* /proc/ccs/.updates_counter */  
                 head->read = read_updates_counter;  
                 break;  
         }  
         if (!(file->f_mode & FMODE_READ)) {  
                 /*  
                  * No need to allocate read_buf since it is not opened  
                  * for reading.  
                  */  
                 head->read = NULL;  
                 head->poll = NULL;  
         } else if (type != CCS_GRANTLOG && type != CCS_REJECTLOG  
                    && type != CCS_QUERY) {  
                 /*  
                  * Don't allocate buffer for reading if the file is one of  
                  * /proc/ccs/grant_log , /proc/ccs/reject_log , /proc/ccs/query.  
                  */  
                 if (!head->readbuf_size)  
                         head->readbuf_size = 4096 * 2;  
                 head->read_buf = ccs_alloc(head->readbuf_size);  
                 if (!head->read_buf) {  
                         ccs_free(head);  
                         return -ENOMEM;  
                 }  
         }  
         if (!(file->f_mode & FMODE_WRITE)) {  
                 /*  
                  * No need to allocate write_buf since it is not opened  
                  * for writing.  
                  */  
                 head->write = NULL;  
         } else if (head->write) {  
                 head->writebuf_size = 4096 * 2;  
                 head->write_buf = ccs_alloc(head->writebuf_size);  
                 if (!head->write_buf) {  
                         ccs_free(head->read_buf);  
                         ccs_free(head);  
                         return -ENOMEM;  
                 }  
         }  
         file->private_data = head;  
         /*  
          * Call the handler now if the file is /proc/ccs/self_domain  
          * so that the user can use "cat < /proc/ccs/self_domain" to  
          * know the current process's domainname.  
          */  
         if (type == CCS_SELFDOMAIN)  
                 ccs_read_control(file, NULL, 0);  
         /*  
          * If the file is /proc/ccs/query , increment the monitor count.  
          * The monitor count is used by ccs_check_supervisor() to see if  
          * there is some process monitoring /proc/ccs/query.  
          */  
         else if (head->write == write_answer || head->read == read_query)  
                 atomic_inc(&queryd_watcher);  
         return 0;  
 }  
   
 /**  
  * ccs_poll_control - poll() for /proc/ccs/ interface.  
  *  
  * @file: Pointer to "struct file".  
  * @wait: Pointer to "poll_table".  
  *  
  * Waits for read readiness.  
  * /proc/ccs/query is handled by /usr/lib/ccs/ccs-queryd and  
  * /proc/ccs/grant_log and /proc/ccs/reject_log are handled by  
  * /usr/lib/ccs/ccs-auditd.  
  */  
 int ccs_poll_control(struct file *file, poll_table *wait)  
 {  
         struct ccs_io_buffer *head = file->private_data;  
         if (!head->poll)  
                 return -ENOSYS;  
         return head->poll(file, wait);  
 }  
   
 /**  
  * ccs_read_control - read() for /proc/ccs/ interface.  
  *  
  * @file:       Pointer to "struct file".  
  * @buffer:     Poiner to buffer to write to.  
  * @buffer_len: Size of @buffer.  
  *  
  * Returns bytes read on success, negative value otherwise.  
  */  
 int ccs_read_control(struct file *file, char __user *buffer,  
                      const int buffer_len)  
 {  
         int len = 0;  
         struct ccs_io_buffer *head = file->private_data;  
         char *cp;  
         if (!head->read)  
                 return -ENOSYS;  
         if (!access_ok(VERIFY_WRITE, buffer, buffer_len))  
                 return -EFAULT;  
         if (mutex_lock_interruptible(&head->io_sem))  
                 return -EINTR;  
         /* Call the policy handler. */  
         len = head->read(head);  
         if (len < 0)  
                 goto out;  
         /* Write to buffer. */  
         len = head->read_avail;  
         if (len > buffer_len)  
                 len = buffer_len;  
         if (!len)  
                 goto out;  
         /* head->read_buf changes by some functions. */  
         cp = head->read_buf;  
         if (copy_to_user(buffer, cp, len)) {  
                 len = -EFAULT;  
                 goto out;  
         }  
         head->read_avail -= len;  
         memmove(cp, cp + len, head->read_avail);  
  out:  
         mutex_unlock(&head->io_sem);  
         return len;  
 }  
   
 /**  
  * ccs_write_control - write() for /proc/ccs/ interface.  
  *  
  * @file:       Pointer to "struct file".  
  * @buffer:     Pointer to buffer to read from.  
  * @buffer_len: Size of @buffer.  
  *  
  * Returns @buffer_len on success, negative value otherwise.  
  */  
 int ccs_write_control(struct file *file, const char __user *buffer,  
                       const int buffer_len)  
 {  
         struct ccs_io_buffer *head = file->private_data;  
         int error = buffer_len;  
         int avail_len = buffer_len;  
         char *cp0 = head->write_buf;  
         if (!head->write)  
                 return -ENOSYS;  
         if (!access_ok(VERIFY_READ, buffer, buffer_len))  
                 return -EFAULT;  
         /* Don't allow updating policies by non manager programs. */  
         if (head->write != write_pid &&  
 #ifdef CONFIG_TOMOYO  
             head->write != write_domain_policy &&  
 #endif  
             !is_policy_manager())  
                 return -EPERM;  
         if (mutex_lock_interruptible(&head->io_sem))  
                 return -EINTR;  
         /* Read a line and dispatch it to the policy handler. */  
         while (avail_len > 0) {  
                 char c;  
                 if (head->write_avail >= head->writebuf_size - 1) {  
                         error = -ENOMEM;  
                         break;  
                 } else if (get_user(c, buffer)) {  
                         error = -EFAULT;  
                         break;  
                 }  
                 buffer++;  
                 avail_len--;  
                 cp0[head->write_avail++] = c;  
                 if (c != '\n')  
                         continue;  
                 cp0[head->write_avail - 1] = '\0';  
                 head->write_avail = 0;  
                 normalize_line(cp0);  
                 head->write(head);  
         }  
         mutex_unlock(&head->io_sem);  
         return error;  
 }  
   
 /**  
  * ccs_close_control - close() for /proc/ccs/ interface.  
  *  
  * @file: Pointer to "struct file".  
  *  
  * Releases memory and returns 0.  
  */  
 int ccs_close_control(struct file *file)  
 {  
         struct ccs_io_buffer *head = file->private_data;  
         /*  
          * If the file is /proc/ccs/query , decrement the monitor count.  
          */  
         if (head->write == write_answer || head->read == read_query)  
                 atomic_dec(&queryd_watcher);  
         /* Release memory used for policy I/O. */  
         ccs_free(head->read_buf);  
         head->read_buf = NULL;  
         ccs_free(head->write_buf);  
         head->write_buf = NULL;  
         ccs_free(head);  
         head = NULL;  
         file->private_data = NULL;  
         return 0;  
 }  
   
 /**  
  * ccs_alloc_acl_element - Allocate permanent memory for ACL entry.  
  *  
  * @acl_type:  Type of ACL entry.  
  * @condition: Pointer to condition part of the ACL entry. May be NULL.  
  *  
  * Returns pointer to the ACL entry on success, NULL otherwise.  
  */  
 void *ccs_alloc_acl_element(const u8 acl_type,  
                             const struct condition_list *condition)  
 {  
         int len;  
         struct acl_info *ptr;  
         switch (acl_type) {  
         case TYPE_SINGLE_PATH_ACL:  
                 len = sizeof(struct single_path_acl_record);  
                 break;  
         case TYPE_DOUBLE_PATH_ACL:  
                 len = sizeof(struct double_path_acl_record);  
                 break;  
         case TYPE_ARGV0_ACL:  
                 len = sizeof(struct argv0_acl_record);  
                 break;  
         case TYPE_ENV_ACL:  
                 len = sizeof(struct env_acl_record);  
                 break;  
         case TYPE_CAPABILITY_ACL:  
                 len = sizeof(struct capability_acl_record);  
                 break;  
         case TYPE_IP_NETWORK_ACL:  
                 len = sizeof(struct ip_network_acl_record);  
                 break;  
         case TYPE_SIGNAL_ACL:  
                 len = sizeof(struct signal_acl_record);  
                 break;  
         case TYPE_EXECUTE_HANDLER:  
         case TYPE_DENIED_EXECUTE_HANDLER:  
                 len = sizeof(struct execute_handler_record);  
                 break;  
         default:  
                 return NULL;  
         }  
         /*  
          * If the ACL doesn't have condition part, reduce memory usage  
          * by eliminating sizeof(struct condition_list *).  
          */  
         if (!condition)  
                 len -= sizeof(ptr->access_me_via_ccs_get_condition_part);  
         ptr = ccs_alloc_element(len);  
         if (!ptr)  
                 return NULL;  
         if (condition) {  
                 ptr->access_me_via_ccs_get_condition_part = condition;  
                 ptr->type = acl_type | ACL_WITH_CONDITION;  
                 return ptr;  
         }  
         /*  
          * Substract sizeof(struct condition_list *) because I eliminated  
          * sizeof(struct condition_list *) from "struct acl_info"  
          * but I must return the start address of "struct acl_info".  
          */  
         ptr = (void *) (((u8 *) ptr)  
                         - sizeof(ptr->access_me_via_ccs_get_condition_part));  
         ptr->type = acl_type;  
         return ptr;  
 }  
   
 /**  
  * ccs_get_condition_part - Get condition part of the given ACL entry.  
  *  
  * @acl: Pointer to "struct acl_info". Pointer to an ACL entry.  
  *  
  * Returns pointer to the condition part if the ACL has it, NULL otherwise.  
  */  
 const struct condition_list *ccs_get_condition_part(const struct acl_info *acl)  
 {  
         return (acl->type & ACL_WITH_CONDITION) ?  
                 acl->access_me_via_ccs_get_condition_part : NULL;  
 }  

Legend:
Removed from v.1719  
changed lines
  Added in v.3046

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26