+#define NULL_FILE_NAME "null"
+
+struct dentry *selinux_null = NULL;
+
+static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_avc_cache_threshold(struct file * file,
+ const char __user * buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t ret;
+ int new_value;
+
+ if (count >= PAGE_SIZE) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ page = (char*)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(page, buf, count)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ if (sscanf(page, "%u", &new_value) != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (new_value != avc_cache_threshold) {
+ ret = task_has_security(current, SECURITY__SETSECPARAM);
+ if (ret)
+ goto out_free;
+ avc_cache_threshold = new_value;
+ }
+ ret = count;
+out_free:
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t ret = 0;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = avc_get_hash_stats(page);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(buf, count, ppos, page, ret);
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static struct file_operations sel_avc_cache_threshold_ops = {
+ .read = sel_read_avc_cache_threshold,
+ .write = sel_write_avc_cache_threshold,
+};
+
+static struct file_operations sel_avc_hash_stats_ops = {
+ .read = sel_read_avc_hash_stats,
+};
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
+{
+ int cpu;
+
+ for (cpu = *idx; cpu < NR_CPUS; ++cpu) {
+ if (!cpu_possible(cpu))
+ continue;
+ *idx = cpu + 1;
+ return &per_cpu(avc_cache_stats, cpu);
+ }
+ return NULL;
+}
+
+static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ loff_t n = *pos - 1;
+
+ if (*pos == 0)
+ return SEQ_START_TOKEN;
+
+ return sel_avc_get_stat_idx(&n);
+}
+
+static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ return sel_avc_get_stat_idx(pos);
+}
+
+static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct avc_cache_stats *st = v;
+
+ if (v == SEQ_START_TOKEN)
+ seq_printf(seq, "lookups hits misses allocations reclaims "
+ "frees\n");
+ else
+ seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups,
+ st->hits, st->misses, st->allocations,
+ st->reclaims, st->frees);
+ return 0;
+}
+
+static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v)
+{ }
+
+static struct seq_operations sel_avc_cache_stats_seq_ops = {
+ .start = sel_avc_stats_seq_start,
+ .next = sel_avc_stats_seq_next,
+ .show = sel_avc_stats_seq_show,
+ .stop = sel_avc_stats_seq_stop,
+};
+
+static int sel_open_avc_cache_stats(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &sel_avc_cache_stats_seq_ops);
+}
+
+static struct file_operations sel_avc_cache_stats_ops = {
+ .open = sel_open_avc_cache_stats,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif
+
+static int sel_make_avc_files(struct dentry *dir)
+{
+ int i, ret = 0;
+ static struct tree_descr files[] = {
+ { "cache_threshold",
+ &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
+ { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO },
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+ { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO },
+#endif
+ };
+
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, files[i].name);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_fop = files[i].ops;
+ d_add(dentry, inode);
+ }
+out:
+ return ret;
+}
+
+static int sel_make_dir(struct inode *dir, struct dentry *dentry)
+{
+ int ret = 0;
+ struct inode *inode;
+
+ inode = sel_make_inode(dir->i_sb, S_IFDIR | S_IRUGO | S_IXUGO);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inode->i_nlink++;
+ d_add(dentry, inode);
+ /* bump link count on parent directory, too */
+ dir->i_nlink++;
+out:
+ return ret;
+}
+