9a32144e9d
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
225 lines
4.8 KiB
C
225 lines
4.8 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
|
|
*/
|
|
|
|
#include <linux/capability.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/types.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/in.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sockios.h>
|
|
#include <linux/net.h>
|
|
#include <linux/spinlock.h>
|
|
#include <net/ax25.h>
|
|
#include <linux/inet.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/sock.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/system.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/list.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/sysctl.h>
|
|
#include <net/ip.h>
|
|
#include <net/arp.h>
|
|
|
|
/*
|
|
* Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
|
|
*/
|
|
|
|
HLIST_HEAD(ax25_uid_list);
|
|
static DEFINE_RWLOCK(ax25_uid_lock);
|
|
|
|
int ax25_uid_policy = 0;
|
|
|
|
EXPORT_SYMBOL(ax25_uid_policy);
|
|
|
|
ax25_uid_assoc *ax25_findbyuid(uid_t uid)
|
|
{
|
|
ax25_uid_assoc *ax25_uid, *res = NULL;
|
|
struct hlist_node *node;
|
|
|
|
read_lock(&ax25_uid_lock);
|
|
ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) {
|
|
if (ax25_uid->uid == uid) {
|
|
ax25_uid_hold(ax25_uid);
|
|
res = ax25_uid;
|
|
break;
|
|
}
|
|
}
|
|
read_unlock(&ax25_uid_lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
EXPORT_SYMBOL(ax25_findbyuid);
|
|
|
|
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
|
|
{
|
|
ax25_uid_assoc *ax25_uid;
|
|
struct hlist_node *node;
|
|
ax25_uid_assoc *user;
|
|
unsigned long res;
|
|
|
|
switch (cmd) {
|
|
case SIOCAX25GETUID:
|
|
res = -ENOENT;
|
|
read_lock(&ax25_uid_lock);
|
|
ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) {
|
|
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) {
|
|
res = ax25_uid->uid;
|
|
break;
|
|
}
|
|
}
|
|
read_unlock(&ax25_uid_lock);
|
|
|
|
return res;
|
|
|
|
case SIOCAX25ADDUID:
|
|
if (!capable(CAP_NET_ADMIN))
|
|
return -EPERM;
|
|
user = ax25_findbyuid(sax->sax25_uid);
|
|
if (user) {
|
|
ax25_uid_put(user);
|
|
return -EEXIST;
|
|
}
|
|
if (sax->sax25_uid == 0)
|
|
return -EINVAL;
|
|
if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
|
|
return -ENOMEM;
|
|
|
|
atomic_set(&ax25_uid->refcount, 1);
|
|
ax25_uid->uid = sax->sax25_uid;
|
|
ax25_uid->call = sax->sax25_call;
|
|
|
|
write_lock(&ax25_uid_lock);
|
|
hlist_add_head(&ax25_uid->uid_node, &ax25_uid_list);
|
|
write_unlock(&ax25_uid_lock);
|
|
|
|
return 0;
|
|
|
|
case SIOCAX25DELUID:
|
|
if (!capable(CAP_NET_ADMIN))
|
|
return -EPERM;
|
|
|
|
ax25_uid = NULL;
|
|
write_lock(&ax25_uid_lock);
|
|
ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) {
|
|
if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
|
|
break;
|
|
}
|
|
if (ax25_uid == NULL) {
|
|
write_unlock(&ax25_uid_lock);
|
|
return -ENOENT;
|
|
}
|
|
hlist_del_init(&ax25_uid->uid_node);
|
|
ax25_uid_put(ax25_uid);
|
|
write_unlock(&ax25_uid_lock);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return -EINVAL; /*NOTREACHED */
|
|
}
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
|
static void *ax25_uid_seq_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
struct ax25_uid_assoc *pt;
|
|
struct hlist_node *node;
|
|
int i = 0;
|
|
|
|
read_lock(&ax25_uid_lock);
|
|
ax25_uid_for_each(pt, node, &ax25_uid_list) {
|
|
if (i == *pos)
|
|
return pt;
|
|
++i;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *ax25_uid_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
++*pos;
|
|
|
|
return hlist_entry(((ax25_uid_assoc *)v)->uid_node.next,
|
|
ax25_uid_assoc, uid_node);
|
|
}
|
|
|
|
static void ax25_uid_seq_stop(struct seq_file *seq, void *v)
|
|
{
|
|
read_unlock(&ax25_uid_lock);
|
|
}
|
|
|
|
static int ax25_uid_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
char buf[11];
|
|
|
|
if (v == SEQ_START_TOKEN)
|
|
seq_printf(seq, "Policy: %d\n", ax25_uid_policy);
|
|
else {
|
|
struct ax25_uid_assoc *pt = v;
|
|
|
|
seq_printf(seq, "%6d %s\n", pt->uid, ax2asc(buf, &pt->call));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct seq_operations ax25_uid_seqops = {
|
|
.start = ax25_uid_seq_start,
|
|
.next = ax25_uid_seq_next,
|
|
.stop = ax25_uid_seq_stop,
|
|
.show = ax25_uid_seq_show,
|
|
};
|
|
|
|
static int ax25_uid_info_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ax25_uid_seqops);
|
|
}
|
|
|
|
const struct file_operations ax25_uid_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ax25_uid_info_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Free all memory associated with UID/Callsign structures.
|
|
*/
|
|
void __exit ax25_uid_free(void)
|
|
{
|
|
ax25_uid_assoc *ax25_uid;
|
|
struct hlist_node *node;
|
|
|
|
write_lock(&ax25_uid_lock);
|
|
ax25_uid_for_each(ax25_uid, node, &ax25_uid_list) {
|
|
hlist_del_init(&ax25_uid->uid_node);
|
|
ax25_uid_put(ax25_uid);
|
|
}
|
|
write_unlock(&ax25_uid_lock);
|
|
}
|