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

Subversion リポジトリの参照

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

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

branches/ccs-patch/fs/realpath.c revision 2719 by kumaneko, Thu Jul 2 06:07:47 2009 UTC trunk/1.7.x/ccs-patch/security/ccsecurity/realpath.c revision 2985 by kumaneko, Thu Sep 3 08:27:32 2009 UTC
# Line 1  Line 1 
1  /*  /*
2   * fs/realpath.c   * security/ccsecurity/realpath.c
  *  
  * Get the canonicalized absolute pathnames. The basis for SAKURA and TOMOYO.  
3   *   *
4   * Copyright (C) 2005-2009  NTT DATA CORPORATION   * Copyright (C) 2005-2009  NTT DATA CORPORATION
5   *   *
6   * Version: 1.7.0-pre   2009/05/28   * Version: 1.7.0   2009/09/03
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/string.h>  #include <linux/string.h>
14  #include <linux/mm.h>  #include <linux/mm.h>
15  #include <linux/utime.h>  #include <linux/utime.h>
# Line 28  static const int ccs_lookup_flags = LOOK Line 27  static const int ccs_lookup_flags = LOOK
27  #else  #else
28  static const int ccs_lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;  static const int ccs_lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;
29  #endif  #endif
 #include <linux/proc_fs.h>  
 #include <linux/ccs_common.h>  
 #include <linux/realpath.h>  
30  #include <net/sock.h>  #include <net/sock.h>
31  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
32  #include <linux/kthread.h>  #include <linux/kthread.h>
33  #endif  #endif
34    #include <linux/proc_fs.h>
35    #include "internal.h"
36    
37    static int ccs_kern_path(const char *pathname, int flags, struct path *path)
38    {
39    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
40            if (!pathname || kern_path(pathname, flags, path))
41                    return -ENOENT;
42    #else
43            struct nameidata nd;
44            if (!pathname || path_lookup(pathname, flags, &nd))
45                    return -ENOENT;
46    #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
47            *path = nd.path;
48    #else
49            path->dentry = nd.dentry;
50            path->mnt = nd.mnt;
51    #endif
52    #endif
53            return 0;
54    }
55    
56  /**  /**
57   * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.   * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
58   *   *
59   * @dentry: Pointer to "struct dentry".   * @path:   Pointer to "struct path".
  * @vfsmnt: Pointer to "struct vfsmount".  
60   * @buffer: Pointer to buffer to return value in.   * @buffer: Pointer to buffer to return value in.
61   * @buflen: Sizeof @buffer.   * @buflen: Sizeof @buffer.
62   *   *
63   * Returns 0 on success, -ENOMEM otherwise.   * Returns the buffer on success, an error code otherwise.
64   *   *
65   * Caller holds the dcache_lock and vfsmount_lock.   * Caller holds the dcache_lock and vfsmount_lock.
66   * Based on __d_path() in fs/dcache.c   * Based on __d_path() in fs/dcache.c
67   *   *
68   * If dentry is a directory, trailing '/' is appended.   * If dentry is a directory, trailing '/' is appended.
69   * Characters out of 0x20 < c < 0x7F range are converted to   * /proc/pid is represented as /proc/self if pid is current.
  * \ooo style octal string.  
  * Character \ is converted to \\ string.  
70   */   */
71  static int ccs_get_absolute_path(struct dentry *dentry, struct vfsmount *vfsmnt,  static char *ccs_get_absolute_path(struct path *path, char * const buffer,
72                                   char *buffer, int buflen)                                     const int buflen)
73  {  {
74          /***** CRITICAL SECTION START *****/          char *pos = buffer + buflen - 1;
75          char *start = buffer;          struct dentry *dentry = path->dentry;
76          char *end = buffer + buflen;          struct vfsmount *vfsmnt = path->mnt;
77          bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));          bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
78            const char *name;
79            int len;
80    
81          if (buflen < 256)          if (buflen < 256)
82                  goto out;                  goto out;
83    
84          *--end = '\0';          *pos = '\0';
         buflen--;  
   
85          for (;;) {          for (;;) {
86                  struct dentry *parent;                  struct dentry *parent;
   
87                  if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {                  if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
                         /* Global root? */  
88                          if (vfsmnt->mnt_parent == vfsmnt)                          if (vfsmnt->mnt_parent == vfsmnt)
89                                  break;                                  break;
90                          dentry = vfsmnt->mnt_mountpoint;                          dentry = vfsmnt->mnt_mountpoint;
# Line 81  static int ccs_get_absolute_path(struct Line 93  static int ccs_get_absolute_path(struct
93                  }                  }
94                  if (is_dir) {                  if (is_dir) {
95                          is_dir = false;                          is_dir = false;
96                          *--end = '/';                          *--pos = '/';
                         buflen--;  
97                  }                  }
98                  parent = dentry->d_parent;                  parent = dentry->d_parent;
99                  {                  name = dentry->d_name.name;
100                          const char *sp = dentry->d_name.name;                  len = dentry->d_name.len;
101                          const char *cp = sp + dentry->d_name.len - 1;                  if (IS_ROOT(parent) && *name > '0' && *name <= '9' &&
102                          unsigned char c;                      parent->d_sb &&
103                        parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
104                          /*                          char *ep;
105                           * Exception: Use /proc/self/ rather than                          const pid_t pid = (pid_t) simple_strtoul(name, &ep,
106                           * /proc/\$/ for current process.                                                                   10);
                          */  
                         if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' &&  
                             parent->d_sb &&  
                             parent->d_sb->s_magic == PROC_SUPER_MAGIC) {  
                                 char *ep;  
                                 const pid_t pid  
                                         = (pid_t) simple_strtoul(sp, &ep, 10);  
107  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
108                                  const pid_t tgid                          const pid_t tgid = task_tgid_nr_ns(current,
109                                          = task_tgid_nr_ns(current,                                                             dentry->d_sb->
110                                                            dentry->d_sb->                                                             s_fs_info);
111                                                            s_fs_info);                          if (!*ep && pid == tgid && tgid) {
112                                  if (!*ep && pid == tgid && tgid) {                                  name = "self";
113                                          sp = "self";                                  len = 4;
                                         cp = sp + 3;  
                                 }  
 #else  
                                 if (!*ep && pid == sys_getpid()) {  
                                         sp = "self";  
                                         cp = sp + 3;  
                                 }  
 #endif  
114                          }                          }
115    #else
116                          while (sp <= cp) {                          if (!*ep && pid == sys_getpid()) {
117                                  c = *(unsigned char *) cp;                                  name = "self";
118                                  if (c == '\\') {                                  len = 4;
                                         buflen -= 2;  
                                         if (buflen < 0)  
                                                 goto out;  
                                         *--end = '\\';  
                                         *--end = '\\';  
                                 } else if (c > ' ' && c < 127) {  
                                         if (--buflen < 0)  
                                                 goto out;  
                                         *--end = (char) c;  
                                 } else {  
                                         buflen -= 4;  
                                         if (buflen < 0)  
                                                 goto out;  
                                         *--end = (c & 7) + '0';  
                                         *--end = ((c >> 3) & 7) + '0';  
                                         *--end = (c >> 6) + '0';  
                                         *--end = '\\';  
                                 }  
                                 cp--;  
119                          }                          }
120                          if (--buflen < 0)  #endif
                                 goto out;  
                         *--end = '/';  
121                  }                  }
122                    pos -= len;
123                    if (pos <= buffer)
124                            goto out;
125                    memmove(pos, name, len);
126                    *--pos = '/';
127                  dentry = parent;                  dentry = parent;
128          }          }
129          if (*end == '/') {          if (*pos == '/')
130                  buflen++;                  pos++;
131                  end++;          len = dentry->d_name.len;
132          }          pos -= len;
133          {          if (pos < buffer)
134                  const char *sp = dentry->d_name.name;                  goto out;
135                  const char *cp = sp + dentry->d_name.len - 1;          memmove(pos, dentry->d_name.name, len);
136                  unsigned char c;          return pos;
                 while (sp <= cp) {  
                         c = *(unsigned char *) cp;  
                         if (c == '\\') {  
                                 buflen -= 2;  
                                 if (buflen < 0)  
                                         goto out;  
                                 *--end = '\\';  
                                 *--end = '\\';  
                         } else if (c > ' ' && c < 127) {  
                                 if (--buflen < 0)  
                                         goto out;  
                                 *--end = (char) c;  
                         } else {  
                                 buflen -= 4;  
                                 if (buflen < 0)  
                                         goto out;  
                                 *--end = (c & 7) + '0';  
                                 *--end = ((c >> 3) & 7) + '0';  
                                 *--end = (c >> 6) + '0';  
                                 *--end = '\\';  
                         }  
                         cp--;  
                 }  
         }  
         /* Move the pathname to the top of the buffer. */  
         memmove(start, end, strlen(end) + 1);  
         return 0;  
137   out:   out:
138          return -ENOMEM;          return ERR_PTR(-ENOMEM);
         /***** CRITICAL SECTION END *****/  
139  }  }
140    
141  #define SOCKFS_MAGIC 0x534F434B  #define SOCKFS_MAGIC 0x534F434B
142    
143  /**  /**
144   * ccs_realpath_from_dentry2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.   * ccs_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
145   *   *
146   * @dentry:      Pointer to "struct dentry".   * @path: Pointer to "struct path".
  * @mnt:         Pointer to "struct vfsmount".  
  * @newname:     Pointer to buffer to return value in.  
  * @newname_len: Size of @newname.  
147   *   *
148   * Returns 0 on success, negative value otherwise.   * Returns the realpath of the given @path on success, NULL otherwise.
149     *
150     * This function uses kzalloc(), so caller must kfree() if this function
151     * didn't return NULL.
152   */   */
153  static int ccs_realpath_from_dentry2(struct dentry *dentry,  char *ccs_realpath_from_path(struct path *path)
                                      struct vfsmount *mnt,  
                                      char *newname, int newname_len)  
154  {  {
155          int error = -EINVAL;          char *buf = NULL;
156          struct dentry *d_dentry;          char *name = NULL;
157          struct vfsmount *d_mnt;          unsigned int buf_len = PAGE_SIZE / 2;
158          if (!dentry || !newname || newname_len <= 2048)          struct dentry *dentry = path->dentry;
159                  goto out;          if (!dentry)
160          /* Get better name for socket. */                  return NULL;
161          if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {          while (1) {
162                  struct inode *inode = dentry->d_inode;                  char *pos;
163                  struct socket *sock = inode ? SOCKET_I(inode) : NULL;                  buf_len <<= 1;
164                  struct sock *sk = sock ? sock->sk : NULL;                  kfree(buf);
165                  if (sk) {                  buf = kmalloc(buf_len, GFP_KERNEL);
166                          snprintf(newname, newname_len - 1,                  if (!buf)
167                                   "socket:[family=%u:type=%u:protocol=%u]",                          break;
168                                   sk->sk_family, sk->sk_type, sk->sk_protocol);                  /* Get better name for socket. */
169                  } else {                  if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) {
170                          snprintf(newname, newname_len - 1, "socket:[unknown]");                          struct inode *inode = dentry->d_inode;
171                  }                          struct socket *sock = inode ? SOCKET_I(inode) : NULL;
172                  return 0;                          struct sock *sk = sock ? sock->sk : NULL;
173          }                          if (sk) {
174  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)                                  snprintf(buf, buf_len - 1, "socket:[family=%u:"
175          if (dentry->d_op && dentry->d_op->d_dname) {                                           "type=%u:protocol=%u]", sk->sk_family,
176                  /* For "socket:[\$]" and "pipe:[\$]". */                                           sk->sk_type, sk->sk_protocol);
                 static const int offset = 1536;  
                 char *dp = newname;  
                 char *sp = dentry->d_op->d_dname(dentry, newname + offset,  
                                                  newname_len - offset);  
                 if (IS_ERR(sp)) {  
                         error = PTR_ERR(sp);  
                         goto out;  
                 }  
                 error = -ENOMEM;  
                 newname += offset;  
                 while (1) {  
                         const unsigned char c = *(unsigned char *) sp++;  
                         if (c == '\\') {  
                                 if (dp + 2 >= newname)  
                                         break;  
                                 *dp++ = '\\';  
                                 *dp++ = '\\';  
                         } else if (c > ' ' && c < 127) {  
                                 if (dp + 1 >= newname)  
                                         break;  
                                 *dp++ = (char) c;  
                         } else if (c) {  
                                 if (dp + 4 >= newname)  
                                         break;  
                                 *dp++ = '\\';  
                                 *dp++ = (c >> 6) + '0';  
                                 *dp++ = ((c >> 3) & 7) + '0';  
                                 *dp++ = (c & 7) + '0';  
177                          } else {                          } else {
178                                  *dp = '\0';                                  snprintf(buf, buf_len - 1, "socket:[unknown]");
                                 return 0;  
179                          }                          }
180                            name = ccs_encode(buf);
181                            break;
182                  }                  }
183                  goto out;  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
184                    /* For "socket:[\$]" and "pipe:[\$]". */
185                    if (dentry->d_op && dentry->d_op->d_dname) {
186                            pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
187                            if (IS_ERR(pos))
188                                    continue;
189                            name = ccs_encode(pos);
190                            break;
191                    }
192    #endif
193                    if (!path->mnt)
194                            break;
195                    path_get(path);
196                    ccs_realpath_lock();
197                    pos = ccs_get_absolute_path(path, buf, buf_len - 1);
198                    ccs_realpath_unlock();
199                    path_put(path);
200                    if (IS_ERR(pos))
201                            continue;
202                    name = ccs_encode(pos);
203                    break;
204          }          }
 #endif  
         if (!mnt)  
                 goto out;  
         d_dentry = dget(dentry);  
         d_mnt = mntget(mnt);  
         /***** CRITICAL SECTION START *****/  
         ccs_realpath_lock();  
         error = ccs_get_absolute_path(d_dentry, d_mnt, newname, newname_len);  
         ccs_realpath_unlock();  
         /***** CRITICAL SECTION END *****/  
         dput(d_dentry);  
         mntput(d_mnt);  
  out:  
         if (error)  
                 printk(KERN_WARNING "ccs_realpath: Pathname too long. (%d)\n",  
                        error);  
         return error;  
 }  
   
 /**  
  * ccs_realpath_from_dentry - Returns realpath(3) of the given pathname but ignores chroot'ed root.  
  *  
  * @dentry: Pointer to "struct dentry".  
  * @mnt:    Pointer to "struct vfsmount".  
  *  
  * Returns the realpath of the given @dentry and @mnt on success,  
  * NULL otherwise.  
  *  
  * These functions use kzalloc(), so caller must kfree()  
  * if these functions didn't return NULL.  
  */  
 char *ccs_realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt)  
 {  
         char *buf = kzalloc(CCS_MAX_PATHNAME_LEN, GFP_KERNEL);  
         if (buf && ccs_realpath_from_dentry2(dentry, mnt, buf,  
                                              CCS_MAX_PATHNAME_LEN - 2) == 0)  
                 return buf;  
205          kfree(buf);          kfree(buf);
206          return NULL;          if (!name)
207  }                  ccs_warn_oom(__func__);
208            return name;
 /**  
  * ccs_realpath - Get realpath of a pathname.  
  *  
  * @pathname: The pathname to solve.  
  *  
  * Returns the realpath of @pathname on success, NULL otherwise.  
  */  
 char *ccs_realpath(const char *pathname)  
 {  
         struct nameidata nd;  
         if (pathname && path_lookup(pathname, ccs_lookup_flags, &nd) == 0) {  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
                 char *buf = ccs_realpath_from_dentry(nd.path.dentry,  
                                                      nd.path.mnt);  
                 path_put(&nd.path);  
 #else  
                 char *buf = ccs_realpath_from_dentry(nd.dentry, nd.mnt);  
                 path_release(&nd);  
 #endif  
                 return buf;  
         }  
         return NULL;  
209  }  }
210    
211  /**  /**
212   * ccs_realpath_both - Get realpath of a pathname and symlink.   * ccs_symlink_path - Get symlink's pathname.
213   *   *
214   * @pathname: The pathname to solve.   * @pathname: The pathname to solve.
215   * @ee:       Pointer to "struct ccs_execve_entry".   * @name:     Pointer to "struct ccs_path_info".
216   *   *
217   * Returns 0 on success, negative value otherwise.   * Returns 0 on success, negative value otherwise.
218     *
219     * This function uses kzalloc(), so caller must kfree() if this function
220     * didn't return NULL.
221   */   */
222  int ccs_realpath_both(const char *pathname, struct ccs_execve_entry *ee)  int ccs_symlink_path(const char *pathname, struct ccs_path_info *name)
223  {  {
224          struct nameidata nd;          char *buf;
225          int ret;          struct path path;
226          bool is_symlink;          if (ccs_kern_path(pathname, ccs_lookup_flags ^ LOOKUP_FOLLOW, &path))
         if (!pathname ||  
             path_lookup(pathname, ccs_lookup_flags ^ LOOKUP_FOLLOW, &nd))  
227                  return -ENOENT;                  return -ENOENT;
228  #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)          buf = ccs_realpath_from_path(&path);
229          is_symlink = nd.path.dentry->d_inode &&          path_put(&path);
230                  S_ISLNK(nd.path.dentry->d_inode->i_mode);          if (buf) {
231          ret = ccs_realpath_from_dentry2(nd.path.dentry, nd.path.mnt,                  name->name = buf;
232                                          ee->tmp, CCS_EXEC_TMPSIZE - 1);                  ccs_fill_path_info(name);
         path_put(&nd.path);  
 #else  
         is_symlink = nd.dentry->d_inode && S_ISLNK(nd.dentry->d_inode->i_mode);  
         ret = ccs_realpath_from_dentry2(nd.dentry, nd.mnt, ee->tmp,  
                                         CCS_EXEC_TMPSIZE - 1);  
         path_release(&nd);  
 #endif  
         if (ret)  
                 return -ENOMEM;  
         if (strlen(ee->tmp) > CCS_MAX_PATHNAME_LEN - 1)  
                 return -ENOMEM;  
         ee->program_path[CCS_MAX_PATHNAME_LEN - 1] = '\0';  
         if (!is_symlink) {  
                 strncpy(ee->program_path, ee->tmp,  
                         CCS_MAX_PATHNAME_LEN - 1);  
233                  return 0;                  return 0;
234          }          }
235          if (path_lookup(pathname, ccs_lookup_flags, &nd))          return -ENOMEM;
                 return -ENOENT;  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)  
         ret = ccs_realpath_from_dentry2(nd.path.dentry, nd.path.mnt,  
                                         ee->program_path,  
                                         CCS_MAX_PATHNAME_LEN - 1);  
         path_put(&nd.path);  
 #else  
         ret = ccs_realpath_from_dentry2(nd.dentry, nd.mnt, ee->program_path,  
                                         CCS_MAX_PATHNAME_LEN - 1);  
         path_release(&nd);  
 #endif  
         return ret ? -ENOMEM : 0;  
236  }  }
237    
238  /**  /**
# Line 405  char *ccs_encode(const char *str) Line 263  char *ccs_encode(const char *str)
263                          len += 4;                          len += 4;
264          }          }
265          len++;          len++;
266          cp = kzalloc(len, GFP_KERNEL);          /* Reserve space for appending "/". */
267            cp = kzalloc(len + 10, GFP_KERNEL);
268          if (!cp)          if (!cp)
269                  return NULL;                  return NULL;
270          cp0 = cp;          cp0 = cp;
# Line 427  char *ccs_encode(const char *str) Line 286  char *ccs_encode(const char *str)
286          return cp0;          return cp0;
287  }  }
288    
 static atomic_t ccs_non_string_memory_size;  
 static unsigned int ccs_quota_for_non_string;  
   
 /**  
  * ccs_memory_ok - Check memory quota.  
  *  
  * @ptr:  Pointer to allocated memory.  
  * @size: Size in byte.  
  *  
  * Returns true if @ptr is not NULL and quota not exceeded, false otehrwise.  
  */  
 bool ccs_memory_ok(const void *ptr, const unsigned int size)  
 {  
         atomic_add(size, &ccs_non_string_memory_size);  
         if (ptr && (!ccs_quota_for_non_string ||  
                     atomic_read(&ccs_non_string_memory_size)  
                     <= ccs_quota_for_non_string))  
                 return true;  
         atomic_sub(size, &ccs_non_string_memory_size);  
         printk(KERN_WARNING "ERROR: Out of memory. (%s)\n", __func__);  
         if (!ccs_policy_loaded)  
                 panic("MAC Initialization failed.\n");  
         return false;  
 }  
   
 /**  
  * ccs_memory_free - Free memory for elements.  
  *  
  * @ptr:  Pointer to allocated memory.  
  * @size: Size in byte.  
  */  
 static void ccs_memory_free(const void *ptr, size_t size)  
 {  
         atomic_sub(size, &ccs_non_string_memory_size);  
         kfree(ptr);  
 }  
   
 /**  
  * ccs_put_path_group - Delete memory for "struct ccs_path_group_entry".  
  *  
  * @group: Pointer to "struct ccs_path_group_entry".  
  */  
 void ccs_put_path_group(struct ccs_path_group_entry *group)  
 {  
         struct ccs_path_group_member *member;  
         struct ccs_path_group_member *next_member;  
         LIST_HEAD(q);  
         bool can_delete_group = false;  
         if (!group)  
                 return;  
         mutex_lock(&ccs_policy_lock);  
         if (atomic_dec_and_test(&group->users)) {  
                 list_for_each_entry_safe(member, next_member,  
                                          &group->path_group_member_list,  
                                          list) {  
                         if (!member->is_deleted)  
                                 break;  
                         list_del(&member->list);  
                         list_add(&member->list, &q);  
                 }  
                 if (list_empty(&group->path_group_member_list)) {  
                         list_del(&group->list);  
                         can_delete_group = true;  
                 }  
         }  
         mutex_unlock(&ccs_policy_lock);  
         list_for_each_entry_safe(member, next_member, &q, list) {  
                 list_del(&member->list);  
                 ccs_put_name(member->member_name);  
                 ccs_memory_free(member, sizeof(*member));  
         }  
         if (can_delete_group) {  
                 ccs_put_name(group->group_name);  
                 ccs_memory_free(group, sizeof(*group));  
         }  
 }  
   
 /**  
  * ccs_put_address_group - Delete memory for "struct ccs_address_group_entry".  
  *  
  * @group: Pointer to "struct ccs_address_group_entry".  
  */  
 void ccs_put_address_group(struct ccs_address_group_entry *group)  
 {  
         struct ccs_address_group_member *member;  
         struct ccs_address_group_member *next_member;  
         LIST_HEAD(q);  
         bool can_delete_group = false;  
         if (!group)  
                 return;  
         mutex_lock(&ccs_policy_lock);  
         if (atomic_dec_and_test(&group->users)) {  
                 list_for_each_entry_safe(member, next_member,  
                                          &group->address_group_member_list,  
                                          list) {  
                         if (!member->is_deleted)  
                                 break;  
                         list_del(&member->list);  
                         list_add(&member->list, &q);  
                 }  
                 if (list_empty(&group->address_group_member_list)) {  
                         list_del(&group->list);  
                         can_delete_group = true;  
                 }  
         }  
         mutex_unlock(&ccs_policy_lock);  
         list_for_each_entry_safe(member, next_member, &q, list) {  
                 list_del(&member->list);  
                 if (member->is_ipv6) {  
                         ccs_put_ipv6_address(member->min.ipv6);  
                         ccs_put_ipv6_address(member->max.ipv6);  
                 }  
                 ccs_memory_free(member, sizeof(*member));  
         }  
         if (can_delete_group) {  
                 ccs_put_name(group->group_name);  
                 ccs_memory_free(group, sizeof(*group));  
         }  
 }  
   
 static LIST_HEAD(ccs_address_list);  
   
 /**  
  * ccs_get_ipv6_address - Keep the given IPv6 address on the RAM.  
  *  
  * @addr: Pointer to "struct in6_addr".  
  *  
  * Returns pointer to "struct in6_addr" on success, NULL otherwise.  
  *  
  * The RAM is shared, so NEVER try to modify or kfree() the returned address.  
  */  
 const struct in6_addr *ccs_get_ipv6_address(const struct in6_addr *addr)  
 {  
         struct ccs_ipv6addr_entry *entry;  
         struct ccs_ipv6addr_entry *ptr;  
         int error = -ENOMEM;  
         if (!addr)  
                 return NULL;  
         entry = kzalloc(sizeof(*entry), GFP_KERNEL);  
         mutex_lock(&ccs_policy_lock);  
         list_for_each_entry(ptr, &ccs_address_list, list) {  
                 if (memcmp(&ptr->addr, addr, sizeof(*addr)))  
                         continue;  
                 atomic_inc(&ptr->users);  
                 error = 0;  
                 break;  
         }  
         if (error && ccs_memory_ok(entry, sizeof(*entry))) {  
                 ptr = entry;  
                 ptr->addr = *addr;  
                 atomic_set(&ptr->users, 1);  
                 list_add_tail(&ptr->list, &ccs_address_list);  
                 entry = NULL;  
         }  
         mutex_unlock(&ccs_policy_lock);  
         kfree(entry);  
         return ptr ? &ptr->addr : NULL;  
 }  
   
 /**  
  * ccs_put_ipv6_address - Delete the given IPv6 address on the RAM.  
  *  
  * @addr: Pointer to "struct in6_addr".  
  */  
 void ccs_put_ipv6_address(const struct in6_addr *addr)  
 {  
         struct ccs_ipv6addr_entry *ptr;  
         bool can_delete = false;  
         if (!addr)  
                 return;  
         ptr = container_of(addr, struct ccs_ipv6addr_entry, addr);  
         mutex_lock(&ccs_policy_lock);  
         if (atomic_dec_and_test(&ptr->users)) {  
                 list_del(&ptr->list);  
                 can_delete = true;  
         }  
         mutex_unlock(&ccs_policy_lock);  
         if (can_delete)  
                 ccs_memory_free(ptr, sizeof(*ptr));  
 }  
   
 /**  
  * ccs_put_condition - Delete memory for "struct ccs_condition".  
  *  
  * @cond: Pointer to "struct ccs_condition".  
  */  
 void ccs_put_condition(struct ccs_condition *cond)  
 {  
         const unsigned long *ptr;  
         const struct ccs_argv_entry *argv;  
         const struct ccs_envp_entry *envp;  
         const struct ccs_symlinkp_entry *symlinkp;  
         u16 condc;  
         u16 argc;  
         u16 envc;  
         u16 symlinkc;  
         u16 i;  
         bool can_delete = false;  
         if (!cond)  
                 return;  
         mutex_lock(&ccs_policy_lock);  
         if (atomic_dec_and_test(&cond->users)) {  
                 list_del(&cond->list);  
                 can_delete = true;  
         }  
         mutex_unlock(&ccs_policy_lock);  
         if (!can_delete)  
                 return;  
         condc = cond->condc;  
         argc = cond->argc;  
         envc = cond->envc;  
         symlinkc = cond->symlinkc;  
         ptr = (const unsigned long *) (cond + 1);  
         argv = (const struct ccs_argv_entry *) (ptr + condc);  
         envp = (const struct ccs_envp_entry *) (argv + argc);  
         symlinkp = (const struct ccs_symlinkp_entry *) (envp + envc);  
         for (i = 0; i < argc; argv++, i++)  
                 ccs_put_name(argv->value);  
         for (i = 0; i < envc; envp++, i++) {  
                 ccs_put_name(envp->name);  
                 ccs_put_name(envp->value);  
         }  
         for (i = 0; i < symlinkc; symlinkp++, i++)  
                 ccs_put_name(symlinkp->value);  
         ccs_memory_free(cond, cond->size);  
 }  
   
 static unsigned int ccs_string_memory_size;  
 static unsigned int ccs_quota_for_string;  
   
 #define MAX_HASH 256  
   
 /* Structure for string data. */  
 struct ccs_name_entry {  
         struct list_head list;  
         atomic_t users;  
         int size;  
         struct ccs_path_info entry;  
 };  
   
 /* The list for "struct ccs_name_entry". */  
 static struct list_head ccs_name_list[MAX_HASH];  
 static DEFINE_MUTEX(ccs_name_list_lock);  
   
 /**  
  * ccs_get_name - Allocate memory for string data.  
  *  
  * @name: The string to store into the permernent memory.  
  *  
  * Returns pointer to "struct ccs_path_info" on success, NULL otherwise.  
  */  
 const struct ccs_path_info *ccs_get_name(const char *name)  
 {  
         struct ccs_name_entry *ptr;  
         unsigned int hash;  
         int len;  
         int allocated_len;  
   
         if (!name)  
                 return NULL;  
         len = strlen(name) + 1;  
         if (len > CCS_MAX_PATHNAME_LEN) {  
                 printk(KERN_WARNING "ERROR: Name too long. (%s)\n", __func__);  
                 return NULL;  
         }  
         hash = full_name_hash((const unsigned char *) name, len - 1);  
         /***** EXCLUSIVE SECTION START *****/  
         mutex_lock(&ccs_name_list_lock);  
         list_for_each_entry(ptr, &ccs_name_list[hash % MAX_HASH], list) {  
                 if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))  
                         continue;  
                 atomic_inc(&ptr->users);  
                 goto out;  
         }  
         ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL);  
         allocated_len = ptr ? sizeof(*ptr) + len : 0;  
         ccs_string_memory_size += allocated_len;  
         if (!allocated_len ||  
             (ccs_quota_for_string &&  
              ccs_string_memory_size > ccs_quota_for_string)) {  
                 ccs_string_memory_size -= allocated_len;  
                 kfree(ptr);  
                 ptr = NULL;  
                 printk(KERN_WARNING "ERROR: Out of memory. (%s)\n", __func__);  
                 if (!ccs_policy_loaded)  
                         panic("MAC Initialization failed.\n");  
                 goto out;  
         }  
         ptr->entry.name = ((char *) ptr) + sizeof(*ptr);  
         memmove((char *) ptr->entry.name, name, len);  
         atomic_set(&ptr->users, 1);  
         ccs_fill_path_info(&ptr->entry);  
         ptr->size = allocated_len;  
         list_add_tail(&ptr->list, &ccs_name_list[hash % MAX_HASH]);  
  out:  
         mutex_unlock(&ccs_name_list_lock);  
         /***** EXCLUSIVE SECTION END *****/  
         return ptr ? &ptr->entry : NULL;  
 }  
   
 /**  
  * ccs_put_name - Delete shared memory for string data.  
  *  
  * @name: Pointer to "struct ccs_path_info".  
  */  
 void ccs_put_name(const struct ccs_path_info *name)  
 {  
         struct ccs_name_entry *ptr;  
         bool can_delete = false;  
         if (!name)  
                 return;  
         ptr = container_of(name, struct ccs_name_entry, entry);  
         /***** EXCLUSIVE SECTION START *****/  
         mutex_lock(&ccs_name_list_lock);  
         if (atomic_dec_and_test(&ptr->users)) {  
                 list_del(&ptr->list);  
                 ccs_string_memory_size -= ptr->size;  
                 can_delete = true;  
         }  
         mutex_unlock(&ccs_name_list_lock);  
         /***** EXCLUSIVE SECTION END *****/  
         if (can_delete)  
                 kfree(ptr);  
 }  
   
 struct srcu_struct ccs_ss;  
   
 /**  
  * ccs_realpath_init - Initialize realpath related code.  
  *  
  * Returns 0.  
  */  
 static int __init ccs_realpath_init(void)  
 {  
         int i;  
         /* Constraint for ccs_get_name(). */  
         if (CCS_MAX_PATHNAME_LEN > PAGE_SIZE)  
                 panic("Bad size.");  
         /* Constraint for "struct ccs_execve_entry"->tmp users. */  
         if (CCS_MAX_PATHNAME_LEN > CCS_EXEC_TMPSIZE)  
                 panic("Bad size.");  
         if (init_srcu_struct(&ccs_ss))  
                 panic("Out of memory.");  
         for (i = 0; i < MAX_HASH; i++)  
                 INIT_LIST_HEAD(&ccs_name_list[i]);  
         INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list);  
         ccs_kernel_domain.domainname = ccs_get_name(ROOT_NAME);  
         list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list);  
         if (ccs_find_domain(ROOT_NAME) != &ccs_kernel_domain)  
                 panic("Can't register ccs_kernel_domain");  
 #ifdef CONFIG_CCSECURITY_BUILTIN_INITIALIZERS  
         {  
                 /* Load built-in policy. */  
                 static char ccs_builtin_initializers[] __initdata  
                         = CONFIG_CCSECURITY_BUILTIN_INITIALIZERS;  
                 char *cp = ccs_builtin_initializers;  
                 ccs_normalize_line(cp);  
                 while (cp && *cp) {  
                         char *cp2 = strchr(cp, ' ');  
                         if (cp2)  
                                 *cp2++ = '\0';  
                         ccs_write_domain_initializer_policy(cp, false, false);  
                         cp = cp2;  
                 }  
         }  
 #endif  
         return 0;  
 }  
   
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)  
 __initcall(ccs_realpath_init);  
 #else  
 core_initcall(ccs_realpath_init);  
 #endif  
   
 unsigned int ccs_audit_log_memory_size;  
 unsigned int ccs_quota_for_audit_log;  
   
 unsigned int ccs_query_memory_size;  
 unsigned int ccs_quota_for_query;  
   
 /**  
  * ccs_read_memory_counter - Check for memory usage.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns memory usage.  
  */  
 int ccs_read_memory_counter(struct ccs_io_buffer *head)  
 {  
         if (!head->read_eof) {  
                 const unsigned int string = ccs_string_memory_size;  
                 const unsigned int nonstring  
                         = atomic_read(&ccs_non_string_memory_size);  
                 const unsigned int audit_log = ccs_audit_log_memory_size;  
                 const unsigned int query = ccs_query_memory_size;  
                 char buffer[64];  
                 memset(buffer, 0, sizeof(buffer));  
                 if (ccs_quota_for_string)  
                         snprintf(buffer, sizeof(buffer) - 1,  
                                  "   (Quota: %10u)", ccs_quota_for_string);  
                 else  
                         buffer[0] = '\0';  
                 ccs_io_printf(head, "Policy (string):         %10u%s\n",  
                               string, buffer);  
                 if (ccs_quota_for_non_string)  
                         snprintf(buffer, sizeof(buffer) - 1,  
                                  "   (Quota: %10u)", ccs_quota_for_non_string);  
                 else  
                         buffer[0] = '\0';  
                 ccs_io_printf(head, "Policy (non-string):     %10u%s\n",  
                               nonstring, buffer);  
                 if (ccs_quota_for_audit_log)  
                         snprintf(buffer, sizeof(buffer) - 1,  
                                  "   (Quota: %10u)", ccs_quota_for_audit_log);  
                 else  
                         buffer[0] = '\0';  
                 ccs_io_printf(head, "Audit logs:              %10u%s\n",  
                               audit_log, buffer);  
                 if (ccs_quota_for_query)  
                         snprintf(buffer, sizeof(buffer) - 1,  
                                  "   (Quota: %10u)", ccs_quota_for_query);  
                 else  
                         buffer[0] = '\0';  
                 ccs_io_printf(head, "Interactive enforcement: %10u%s\n",  
                               query, buffer);  
                 ccs_io_printf(head, "Total:                   %10u\n",  
                               string + nonstring + audit_log + query);  
                 head->read_eof = true;  
         }  
         return 0;  
 }  
   
 /**  
  * ccs_write_memory_quota - Set memory quota.  
  *  
  * @head: Pointer to "struct ccs_io_buffer".  
  *  
  * Returns 0.  
  */  
 int ccs_write_memory_quota(struct ccs_io_buffer *head)  
 {  
         char *data = head->write_buf;  
         unsigned int size;  
         if (sscanf(data, "Policy (string): %u", &size) == 1)  
                 ccs_quota_for_string = size;  
         else if (sscanf(data, "Policy (non-string): %u", &size) == 1)  
                 ccs_quota_for_non_string = size;  
         else if (sscanf(data, "Audit logs: %u", &size) == 1)  
                 ccs_quota_for_audit_log = size;  
         else if (sscanf(data, "Interactive enforcement: %u", &size) == 1)  
                 ccs_quota_for_query = size;  
         return 0;  
 }  
   
 /* Garbage collector functions */  
   
 enum ccs_gc_id {  
         CCS_ID_CONDITION,  
         CCS_ID_RESERVEDPORT,  
         CCS_ID_ADDRESS_GROUP,  
         CCS_ID_ADDRESS_GROUP_MEMBER,  
         CCS_ID_PATH_GROUP,  
         CCS_ID_PATH_GROUP_MEMBER,  
         CCS_ID_GLOBAL_ENV,  
         CCS_ID_AGGREGATOR,  
         CCS_ID_DOMAIN_INITIALIZER,  
         CCS_ID_DOMAIN_KEEPER,  
         CCS_ID_ALIAS,  
         CCS_ID_GLOBALLY_READABLE,  
         CCS_ID_PATTERN,  
         CCS_ID_NO_REWRITE,  
         CCS_ID_MANAGER,  
         CCS_ID_ACL,  
         CCS_ID_DOMAIN  
 };  
   
 struct ccs_gc_entry {  
         struct list_head list;  
         int type;  
         void *element;  
 };  
   
 /* Caller holds ccs_policy_lock mutex. */  
 static bool ccs_add_to_gc(const int type, void *element, struct list_head *head)  
 {  
         struct ccs_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);  
         if (!entry)  
                 return false;  
         entry->type = type;  
         entry->element = element;  
         list_add(&entry->list, head);  
         return true;  
 }  
   
 static size_t ccs_del_allow_read(struct ccs_globally_readable_file_entry *ptr)  
 {  
         ccs_put_name(ptr->filename);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_allow_env(struct ccs_globally_usable_env_entry *ptr)  
 {  
         ccs_put_name(ptr->env);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_file_pattern(struct ccs_pattern_entry *ptr)  
 {  
         ccs_put_name(ptr->pattern);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_no_rewrite(struct ccs_no_rewrite_entry *ptr)  
 {  
         ccs_put_name(ptr->pattern);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_domain_initializer(struct ccs_domain_initializer_entry *  
                                          ptr)  
 {  
         ccs_put_name(ptr->domainname);  
         ccs_put_name(ptr->program);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_domain_keeper(struct ccs_domain_keeper_entry *ptr)  
 {  
         ccs_put_name(ptr->domainname);  
         ccs_put_name(ptr->program);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_alias(struct ccs_alias_entry *ptr)  
 {  
         ccs_put_name(ptr->original_name);  
         ccs_put_name(ptr->aliased_name);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_aggregator(struct ccs_aggregator_entry *ptr)  
 {  
         ccs_put_name(ptr->original_name);  
         ccs_put_name(ptr->aggregated_name);  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_manager(struct ccs_policy_manager_entry *ptr)  
 {  
         ccs_put_name(ptr->manager);  
         return sizeof(*ptr);  
 }  
   
 /* For compatibility with older kernels. */  
 #ifndef for_each_process  
 #define for_each_process for_each_task  
 #endif  
   
289  /**  /**
290   * ccs_used_by_task - Check whether the given pointer is referenced by a task.   * ccs_get_path - Get dentry/vfsmmount of a pathname.
291   *   *
292   * @domain: Pointer to "struct ccs_domain_info".   * @pathname: The pathname to solve.
293     * @path:     Pointer to "struct path".
294   *   *
295   * Returns true if @ptr is in use, false otherwise.   * Returns 0 on success, negative value otherwise.
296   */   */
297  static bool ccs_used_by_task(struct ccs_domain_info *domain)  int ccs_get_path(const char *pathname, struct path *path)
 {  
         bool in_use = false;  
         struct task_struct *p;  
         /***** CRITICAL SECTION START *****/  
         read_lock(&tasklist_lock);  
         for_each_process(p) {  
                 if (p->ccs_domain_info != domain)  
                         continue;  
                 in_use = true;  
                 break;  
         }  
         read_unlock(&tasklist_lock);  
         /***** CRITICAL SECTION END *****/  
         return in_use;  
 }  
   
 static size_t ccs_del_acl(struct ccs_acl_info *acl)  
 {  
         size_t size;  
         ccs_put_condition(acl->cond);  
         switch (ccs_acl_type1(acl)) {  
         case TYPE_SINGLE_PATH_ACL:  
                 {  
                         struct ccs_single_path_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         if (entry->u_is_group)  
                                 ccs_put_path_group(entry->u.group);  
                         else  
                                 ccs_put_name(entry->u.filename);  
                 }  
                 break;  
         case TYPE_DOUBLE_PATH_ACL:  
                 {  
                         struct ccs_double_path_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         if (entry->u1_is_group)  
                                 ccs_put_path_group(entry->u1.group1);  
                         else  
                                 ccs_put_name(entry->u1.filename1);  
                         if (entry->u2_is_group)  
                                 ccs_put_path_group(entry->u2.group2);  
                         else  
                                 ccs_put_name(entry->u2.filename2);  
                 }  
                 break;  
         case TYPE_IP_NETWORK_ACL:  
                 {  
                         struct ccs_ip_network_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         if (entry->record_type == IP_RECORD_TYPE_ADDRESS_GROUP)  
                                 ccs_put_address_group(entry->u.group);  
                         else if (entry->record_type == IP_RECORD_TYPE_IPv6) {  
                                 ccs_put_ipv6_address(entry->u.ipv6.min);  
                                 ccs_put_ipv6_address(entry->u.ipv6.max);  
                         }  
                 }  
                 break;  
         case TYPE_IOCTL_ACL:  
                 {  
                         struct ccs_ioctl_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         if (entry->u_is_group)  
                                 ccs_put_path_group(entry->u.group);  
                         else  
                                 ccs_put_name(entry->u.filename);  
                 }  
                 break;  
         case TYPE_ARGV0_ACL:  
                 {  
                         struct ccs_argv0_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->argv0);  
                 }  
                 break;  
         case TYPE_ENV_ACL:  
                 {  
                         struct ccs_env_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->env);  
                 }  
                 break;  
         case TYPE_CAPABILITY_ACL:  
                 {  
                         struct ccs_capability_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                 }  
                 break;  
         case TYPE_SIGNAL_ACL:  
                 {  
                         struct ccs_signal_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->domainname);  
                 }  
                 break;  
         case TYPE_EXECUTE_HANDLER:  
         case TYPE_DENIED_EXECUTE_HANDLER:  
                 {  
                         struct ccs_execute_handler_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->handler);  
                 }  
                 break;  
         case TYPE_MOUNT_ACL:  
                 {  
                         struct ccs_mount_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->dev_name);  
                         ccs_put_name(entry->dir_name);  
                         ccs_put_name(entry->fs_type);  
                 }  
                 break;  
         case TYPE_UMOUNT_ACL:  
                 {  
                         struct ccs_umount_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->dir);  
                 }  
                 break;  
         case TYPE_CHROOT_ACL:  
                 {  
                         struct ccs_chroot_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->dir);  
                 }  
                 break;  
         case TYPE_PIVOT_ROOT_ACL:  
                 {  
                         struct ccs_pivot_root_acl_record *entry;  
                         size = sizeof(*entry);  
                         entry = container_of(acl, typeof(*entry), head);  
                         ccs_put_name(entry->old_root);  
                         ccs_put_name(entry->new_root);  
                 }  
                 break;  
         default:  
                 size = 0;  
                 printk(KERN_WARNING "Unknown type\n");  
                 break;  
         }  
         return size;  
 }  
   
 static size_t ccs_del_domain(struct ccs_domain_info *domain)  
 {  
         struct ccs_acl_info *acl;  
         struct ccs_acl_info *tmp;  
         if (ccs_used_by_task(domain))  
                 return 0;  
         list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {  
                 size_t size = ccs_del_acl(acl);  
                 ccs_memory_free(acl, size);  
         }  
         ccs_put_name(domain->domainname);  
         return sizeof(*domain);  
 }  
   
 static size_t ccs_del_path_group_member(struct ccs_path_group_member *member)  
 {  
         ccs_put_name(member->member_name);  
         return sizeof(*member);  
 }  
   
 static size_t ccs_del_path_group(struct ccs_path_group_entry *group)  
 {  
         ccs_put_name(group->group_name);  
         return sizeof(*group);  
 }  
   
 static size_t ccs_del_address_group_member(struct ccs_address_group_member *member)  
 {  
         if (member->is_ipv6) {  
                 ccs_put_ipv6_address(member->min.ipv6);  
                 ccs_put_ipv6_address(member->max.ipv6);  
         }  
         return sizeof(*member);  
 }  
   
 static size_t ccs_del_address_group(struct ccs_address_group_entry *group)  
 {  
         ccs_put_name(group->group_name);  
         return sizeof(*group);  
 }  
   
 static size_t ccs_del_reservedport(struct ccs_reserved_entry *ptr)  
 {  
         return sizeof(*ptr);  
 }  
   
 static size_t ccs_del_condition(struct ccs_condition *ptr)  
298  {  {
299          int i;          return ccs_kern_path(pathname, ccs_lookup_flags, path);
         u16 condc = ptr->condc;  
         u16 argc = ptr->argc;  
         u16 envc = ptr->envc;  
         u16 symlinkc = ptr->symlinkc;  
         unsigned long *ptr2 = (unsigned long *) (ptr + 1);  
         struct ccs_argv_entry *argv = (struct ccs_argv_entry *) (ptr2 + condc);  
         struct ccs_envp_entry *envp = (struct ccs_envp_entry *) (argv + argc);  
         struct ccs_symlinkp_entry *symlinkp  
                 = (struct ccs_symlinkp_entry *) (envp + envc);  
         for (i = 0; i < argc; i++)  
                 ccs_put_name(argv[i].value);  
         for (i = 0; i < envc; i++) {  
                 ccs_put_name(envp[i].name);  
                 ccs_put_name(envp[i].value);  
         }  
         for (i = 0; i < symlinkc; i++)  
                 ccs_put_name(symlinkp[i].value);  
         return ptr->size;  
300  }  }
   
 static int ccs_gc_thread(void *unused)  
 {  
         static DEFINE_MUTEX(ccs_gc_mutex);  
         static LIST_HEAD(ccs_gc_queue);  
         if (!mutex_trylock(&ccs_gc_mutex))  
                 goto out;  
         mutex_lock(&ccs_policy_lock);  
         {  
                 struct ccs_globally_readable_file_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_globally_readable_list,  
                                         list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_GLOBALLY_READABLE, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_globally_usable_env_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_globally_usable_env_list,  
                                         list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_GLOBAL_ENV, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_pattern_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_pattern_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_PATTERN, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_no_rewrite_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_no_rewrite_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_NO_REWRITE, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_domain_initializer_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_domain_initializer_list,  
                                         list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_DOMAIN_INITIALIZER,  
                                           ptr, &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_domain_keeper_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_domain_keeper_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_DOMAIN_KEEPER, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_alias_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_alias_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_ALIAS, ptr, &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_policy_manager_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_policy_manager_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_MANAGER, ptr, &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_aggregator_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_aggregator_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_AGGREGATOR, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_domain_info *domain;  
                 list_for_each_entry_rcu(domain, &ccs_domain_list, list) {  
                         struct ccs_acl_info *acl;  
                         list_for_each_entry_rcu(acl, &domain->acl_info_list,  
                                                 list) {  
                                 if (!(acl->type & ACL_DELETED))  
                                         continue;  
                                 if (ccs_add_to_gc(CCS_ID_ACL, acl,  
                                                   &ccs_gc_queue))  
                                         list_del_rcu(&acl->list);  
                                 else  
                                         break;  
                         }  
                         if (!domain->is_deleted ||  
                             ccs_used_by_task(domain))  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_DOMAIN, domain, &ccs_gc_queue))  
                                 list_del_rcu(&domain->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_path_group_entry *group;  
                 list_for_each_entry_rcu(group, &ccs_path_group_list, list) {  
                         struct ccs_path_group_member *member;  
                         list_for_each_entry_rcu(member,  
                                                 &group->path_group_member_list,  
                                                 list) {  
                                 if (!member->is_deleted)  
                                         continue;  
                                 if (ccs_add_to_gc(CCS_ID_PATH_GROUP_MEMBER,  
                                                   member, &ccs_gc_queue))  
                                         list_del_rcu(&member->list);  
                                 else  
                                         break;  
                         }  
                         if (!list_empty(&group->path_group_member_list) ||  
                             atomic_read(&group->users))  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_PATH_GROUP, group,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&group->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_address_group_entry *group;  
                 list_for_each_entry_rcu(group, &ccs_address_group_list, list) {  
                         struct ccs_address_group_member *member;  
                         list_for_each_entry_rcu(member,  
                                         &group->address_group_member_list,  
                                                 list) {  
                                 if (!member->is_deleted)  
                                         break;  
                                 if (ccs_add_to_gc(CCS_ID_ADDRESS_GROUP_MEMBER,  
                                                   member, &ccs_gc_queue))  
                                         list_del_rcu(&member->list);  
                                 else  
                                         break;  
                         }  
                         if (!list_empty(&group->address_group_member_list) ||  
                             atomic_read(&group->users))  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_ADDRESS_GROUP, group,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&group->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_reserved_entry *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_reservedport_list, list) {  
                         if (!ptr->is_deleted)  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_RESERVEDPORT, ptr,  
                                           &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         {  
                 struct ccs_condition *ptr;  
                 list_for_each_entry_rcu(ptr, &ccs_condition_list, list) {  
                         if (atomic_read(&ptr->users))  
                                 continue;  
                         if (ccs_add_to_gc(CCS_ID_CONDITION, ptr, &ccs_gc_queue))  
                                 list_del_rcu(&ptr->list);  
                         else  
                                 break;  
                 }  
         }  
         mutex_unlock(&ccs_policy_lock);  
         if (list_empty(&ccs_gc_queue))  
                 goto done;  
         synchronize_srcu(&ccs_ss);  
         {  
                 struct ccs_gc_entry *p;  
                 struct ccs_gc_entry *tmp;  
                 size_t size = 0;  
                 list_for_each_entry_safe(p, tmp, &ccs_gc_queue, list) {  
                         switch (p->type) {  
                         case CCS_ID_DOMAIN_INITIALIZER:  
                                 size = ccs_del_domain_initializer(p->element);  
                                 break;  
                         case CCS_ID_DOMAIN_KEEPER:  
                                 size = ccs_del_domain_keeper(p->element);  
                                 break;  
                         case CCS_ID_ALIAS:  
                                 size = ccs_del_alias(p->element);  
                                 break;  
                         case CCS_ID_GLOBALLY_READABLE:  
                                 size = ccs_del_allow_read(p->element);  
                                 break;  
                         case CCS_ID_PATTERN:  
                                 size = ccs_del_file_pattern(p->element);  
                                 break;  
                         case CCS_ID_NO_REWRITE:  
                                 size = ccs_del_no_rewrite(p->element);  
                                 break;  
                         case CCS_ID_MANAGER:  
                                 size = ccs_del_manager(p->element);  
                                 break;  
                         case CCS_ID_GLOBAL_ENV:  
                                 size = ccs_del_allow_env(p->element);  
                                 break;  
                         case CCS_ID_AGGREGATOR:  
                                 size = ccs_del_aggregator(p->element);  
                                 break;  
                         case CCS_ID_PATH_GROUP_MEMBER:  
                                 size = ccs_del_path_group_member(p->element);  
                                 break;  
                         case CCS_ID_PATH_GROUP:  
                                 size = ccs_del_path_group(p->element);  
                                 break;  
                         case CCS_ID_ADDRESS_GROUP_MEMBER:  
                                 size = ccs_del_address_group_member(p->element);  
                                 break;  
                         case CCS_ID_ADDRESS_GROUP:  
                                 size = ccs_del_address_group(p->element);  
                                 break;  
                         case CCS_ID_RESERVEDPORT:  
                                 size = ccs_del_reservedport(p->element);  
                                 break;  
                         case CCS_ID_CONDITION:  
                                 size = ccs_del_condition(p->element);  
                                 break;  
                         case CCS_ID_ACL:  
                                 size = ccs_del_acl(p->element);  
                                 break;  
                         case CCS_ID_DOMAIN:  
                                 size = ccs_del_domain(p->element);  
                                 if (!size)  
                                         continue;  
                                 break;  
                         default:  
                                 size = 0;  
                                 printk(KERN_WARNING "Unknown type\n");  
                                 break;  
                         }  
                         ccs_memory_free(p->element, size);  
                         list_del(&p->list);  
                         kfree(p);  
                 }  
         }  
  done:  
         mutex_unlock(&ccs_gc_mutex);  
  out:  
         do_exit(0);  
 }  
   
 void ccs_run_gc(void)  
 {  
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)  
         struct task_struct *task = kthread_create(ccs_gc_thread, NULL,  
                                                   "GC for CCS");  
         if (!IS_ERR(task))  
                 wake_up_process(task);  
 #else  
         kernel_thread(ccs_gc_thread, NULL, 0);  
 #endif  
 }  
   
 #ifndef _LINUX_SRCU_H  
   
 static DEFINE_SPINLOCK(ccs_counter_lock);  
   
 int srcu_read_lock(struct srcu_struct *sp)  
 {  
         int idx;  
         spin_lock(&ccs_counter_lock);  
         idx = sp->counter_idx;  
         sp->counter[idx]++;  
         spin_unlock(&ccs_counter_lock);  
         return idx;  
 }  
   
 void srcu_read_unlock(struct srcu_struct *sp, const int idx)  
 {  
         spin_lock(&ccs_counter_lock);  
         sp->counter[idx]--;  
         spin_unlock(&ccs_counter_lock);  
 }  
   
 void synchronize_srcu(struct srcu_struct *sp)  
 {  
         int idx;  
         int v;  
         spin_lock(&ccs_counter_lock);  
         idx = sp->counter_idx;  
         sp->counter_idx ^= 1;  
         v = sp->counter[idx];  
         spin_unlock(&ccs_counter_lock);  
         while (v) {  
                 ssleep(1);  
                 spin_lock(&ccs_counter_lock);  
                 v = sp->counter[idx];  
                 spin_unlock(&ccs_counter_lock);  
         }  
 }  
   
 #endif  

Legend:
Removed from v.2719  
changed lines
  Added in v.2985

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