Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
str++;
result = -EFAULT;
if (put_user(value, vec + i))
goto out_kfree;
copied += sizeof(*vec);
if (!isdigit(*str))
break;
}
}
if (newval && newlen) {
unsigned __user *vec = newval;
size_t length = newlen / sizeof(*vec);
char *str, *end;
int i;
str = buffer;
end = str + BUFSZ;
for (i = 0; i < length; i++) {
unsigned long value;
result = -EFAULT;
if (get_user(value, vec + i))
goto out_kfree;
str += snprintf(str, end - str, "%lu\t", value);
}
result = kernel_write(file, buffer, str - buffer, 0);
if (result < 0)
goto out_kfree;
}
result = copied;
out_kfree:
kfree(buffer);
out:
return result;
}
static ssize_t bin_ulongvec(struct file *file,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
ssize_t copied = 0;
char *buffer;
ssize_t result;
result = -ENOMEM;
buffer = kmalloc(BUFSZ, GFP_KERNEL);
if (!buffer)
goto out;
if (oldval && oldlen) {
unsigned long __user *vec = oldval;
size_t length = oldlen / sizeof(*vec);
char *str, *end;
int i;
result = kernel_read(file, 0, buffer, BUFSZ - 1);
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
if (result < 0)
goto out_kfree;
str = buffer;
end = str + result;
*end++ = '\0';
for (i = 0; i < length; i++) {
unsigned long value;
value = simple_strtoul(str, &str, 10);
while (isspace(*str))
str++;
result = -EFAULT;
if (put_user(value, vec + i))
goto out_kfree;
copied += sizeof(*vec);
if (!isdigit(*str))
break;
}
}
if (newval && newlen) {
unsigned long __user *vec = newval;
size_t length = newlen / sizeof(*vec);
char *str, *end;
int i;
str = buffer;
end = str + BUFSZ;
for (i = 0; i < length; i++) {
unsigned long value;
result = -EFAULT;
if (get_user(value, vec + i))
goto out_kfree;
str += snprintf(str, end - str, "%lu\t", value);
}
result = kernel_write(file, buffer, str - buffer, 0);
if (result < 0)
goto out_kfree;
result = copied;
out_kfree:
kfree(buffer);
out:
return result;
}
static ssize_t bin_uuid(struct file *file,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
ssize_t result, copied = 0;
/* Only supports reads */
if (oldval && oldlen) {
char buf[40], *str = buf;
unsigned char uuid[16];
int i;
result = kernel_read(file, 0, buf, sizeof(buf) - 1);
if (result < 0)
goto out;
buf[result] = '\0';
/* Convert the uuid to from a string to binary */
for (i = 0; i < 16; i++) {
result = -EIO;
if (!isxdigit(str[0]) || !isxdigit(str[1]))
goto out;
uuid[i] = (hex_to_bin(str[0]) << 4) |
hex_to_bin(str[1]);
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
str += 2;
if (*str == '-')
str++;
}
if (oldlen > 16)
oldlen = 16;
result = -EFAULT;
if (copy_to_user(oldval, uuid, oldlen))
goto out;
copied = oldlen;
}
result = copied;
out:
return result;
}
static ssize_t bin_dn_node_address(struct file *file,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
{
ssize_t result, copied = 0;
if (oldval && oldlen) {
char buf[15], *nodep;
unsigned long area, node;
__le16 dnaddr;
result = kernel_read(file, 0, buf, sizeof(buf) - 1);
if (result < 0)
goto out;
buf[result] = '\0';
/* Convert the decnet address to binary */
result = -EIO;
if (!nodep)
goto out;
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
area = simple_strtoul(buf, NULL, 10);
node = simple_strtoul(nodep, NULL, 10);
result = -EIO;
if ((area > 63)||(node > 1023))
goto out;
dnaddr = cpu_to_le16((area << 10) | node);
result = -EFAULT;
if (put_user(dnaddr, (__le16 __user *)oldval))
goto out;
copied = sizeof(dnaddr);
}
if (newval && newlen) {
__le16 dnaddr;
char buf[15];
int len;
result = -EINVAL;
if (newlen != sizeof(dnaddr))
goto out;
result = -EFAULT;
if (get_user(dnaddr, (__le16 __user *)newval))
goto out;
len = snprintf(buf, sizeof(buf), "%hu.%hu",
le16_to_cpu(dnaddr) >> 10,
le16_to_cpu(dnaddr) & 0x3ff);
result = kernel_write(file, buf, len, 0);
if (result < 0)
goto out;
}
result = copied;
out:
return result;
}
static const struct bin_table *get_sysctl(const int *name, int nlen, char *path)
{
const struct bin_table *table = &bin_root_table[0];
int ctl_name;
/* The binary sysctl tables have a small maximum depth so
* there is no danger of overflowing our path as it PATH_MAX
* bytes long.
*/
memcpy(path, "sys/", 4);
path += 4;
repeat:
if (!nlen)
return ERR_PTR(-ENOTDIR);
ctl_name = *name;
name++;
nlen--;
for ( ; table->convert; table++) {
int len = 0;
/*
* For a wild card entry map from ifindex to network
* device name.
*/
if (!table->ctl_name) {
#ifdef CONFIG_NET
struct net *net = current->nsproxy->net_ns;
struct net_device *dev;
dev = dev_get_by_index(net, ctl_name);
if (dev) {
len = strlen(dev->name);
memcpy(path, dev->name, len);
dev_put(dev);
}
#endif
/* Use the well known sysctl number to proc name mapping */
} else if (ctl_name == table->ctl_name) {
len = strlen(table->procname);
memcpy(path, table->procname, len);
}
if (len) {
path += len;
if (table->child) {
*path++ = '/';
table = table->child;
goto repeat;
}
*path = '\0';
return table;
}
}
return ERR_PTR(-ENOTDIR);
}
static char *sysctl_getname(const int *name, int nlen, const struct bin_table **tablep)
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
tmp = __getname();
if (tmp) {
const struct bin_table *table = get_sysctl(name, nlen, tmp);
result = tmp;
*tablep = table;
if (IS_ERR(table)) {
__putname(tmp);
result = ERR_CAST(table);
}
}
return result;
}
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
static ssize_t binary_sysctl(const int *name, int nlen,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
{
const struct bin_table *table = NULL;
struct vfsmount *mnt;
struct file *file;
ssize_t result;
char *pathname;
int flags;
pathname = sysctl_getname(name, nlen, &table);
result = PTR_ERR(pathname);
if (IS_ERR(pathname))
goto out;
/* How should the sysctl be accessed? */
if (oldval && oldlen && newval && newlen) {
flags = O_RDWR;
} else if (newval && newlen) {
flags = O_WRONLY;
} else if (oldval && oldlen) {
flags = O_RDONLY;
} else {
result = 0;
goto out_putname;
}
mnt = task_active_pid_ns(current)->proc_mnt;
file = file_open_root(mnt->mnt_root, mnt, pathname, flags);
result = PTR_ERR(file);
if (IS_ERR(file))
goto out_putname;
result = table->convert(file, oldval, oldlen, newval, newlen);
fput(file);
out_putname:
out:
return result;
}
#else /* CONFIG_SYSCTL_SYSCALL */
static ssize_t binary_sysctl(const int *name, int nlen,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
return -ENOSYS;
}
#endif /* CONFIG_SYSCTL_SYSCALL */
static void deprecated_sysctl_warning(const int *name, int nlen)
{
int i;
/*
* CTL_KERN/KERN_VERSION is used by older glibc and cannot
* ever go away.
*/
if (name[0] == CTL_KERN && name[1] == KERN_VERSION)
return;
if (printk_ratelimit()) {
printk(KERN_INFO
"warning: process `%s' used the deprecated sysctl "
"system call with ", current->comm);
for (i = 0; i < nlen; i++)
printk("%d.", name[i]);
printk("\n");
}
return;
}
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
#define WARN_ONCE_HASH_BITS 8
#define WARN_ONCE_HASH_SIZE (1<<WARN_ONCE_HASH_BITS)
static DECLARE_BITMAP(warn_once_bitmap, WARN_ONCE_HASH_SIZE);
#define FNV32_OFFSET 2166136261U
#define FNV32_PRIME 0x01000193
/*
* Print each legacy sysctl (approximately) only once.
* To avoid making the tables non-const use a external
* hash-table instead.
* Worst case hash collision: 6, but very rarely.
* NOTE! We don't use the SMP-safe bit tests. We simply
* don't care enough.
*/
static void warn_on_bintable(const int *name, int nlen)
{
int i;
u32 hash = FNV32_OFFSET;
for (i = 0; i < nlen; i++)
hash = (hash ^ name[i]) * FNV32_PRIME;
hash %= WARN_ONCE_HASH_SIZE;
if (__test_and_set_bit(hash, warn_once_bitmap))
return;
deprecated_sysctl_warning(name, nlen);
}
static ssize_t do_sysctl(int __user *args_name, int nlen,
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
{
int name[CTL_MAXNAME];
int i;
/* Check args->nlen. */
if (nlen < 0 || nlen > CTL_MAXNAME)
return -ENOTDIR;
/* Read in the sysctl name for simplicity */
for (i = 0; i < nlen; i++)
if (get_user(name[i], args_name + i))
return -EFAULT;
warn_on_bintable(name, nlen);
return binary_sysctl(name, nlen, oldval, oldlen, newval, newlen);
}
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
{
struct __sysctl_args tmp;
size_t oldlen = 0;
ssize_t result;
if (copy_from_user(&tmp, args, sizeof(tmp)))
return -EFAULT;
if (tmp.oldval && !tmp.oldlenp)
return -EFAULT;
if (tmp.oldlenp && get_user(oldlen, tmp.oldlenp))
return -EFAULT;
result = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, oldlen,
tmp.newval, tmp.newlen);
if (result >= 0) {
oldlen = result;
result = 0;
}
if (tmp.oldlenp && put_user(oldlen, tmp.oldlenp))
return -EFAULT;
return result;
#ifdef CONFIG_COMPAT
struct compat_sysctl_args {
compat_uptr_t name;
int nlen;
compat_uptr_t oldval;
compat_uptr_t oldlenp;
compat_uptr_t newval;
compat_size_t newlen;
compat_ulong_t __unused[4];
};
COMPAT_SYSCALL_DEFINE1(sysctl, struct compat_sysctl_args __user *, args)
{
struct compat_sysctl_args tmp;
compat_size_t __user *compat_oldlenp;
size_t oldlen = 0;
ssize_t result;
if (copy_from_user(&tmp, args, sizeof(tmp)))
return -EFAULT;
if (tmp.oldval && !tmp.oldlenp)
return -EFAULT;
compat_oldlenp = compat_ptr(tmp.oldlenp);
if (compat_oldlenp && get_user(oldlen, compat_oldlenp))
return -EFAULT;
result = do_sysctl(compat_ptr(tmp.name), tmp.nlen,
compat_ptr(tmp.oldval), oldlen,
compat_ptr(tmp.newval), tmp.newlen);
if (result >= 0) {
oldlen = result;
result = 0;
if (compat_oldlenp && put_user(oldlen, compat_oldlenp))
return -EFAULT;
return result;
}
#endif /* CONFIG_COMPAT */