Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 1 | /* |
| 2 | * GPL HEADER START |
| 3 | * |
| 4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2 only, |
| 8 | * as published by the Free Software Foundation. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, but |
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | * General Public License version 2 for more details (a copy is included |
| 14 | * in the LICENSE file that accompanied this code). |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * version 2 along with this program; If not, see |
Oleg Drokin | 6a5b99a | 2016-06-14 23:33:40 -0400 | [diff] [blame] | 18 | * http://www.gnu.org/licenses/gpl-2.0.html |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 19 | * |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 20 | * GPL HEADER END |
| 21 | */ |
| 22 | /* |
| 23 | * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. |
| 24 | * Use is subject to license terms. |
| 25 | * |
| 26 | * Copyright (c) 2012, Intel Corporation. |
| 27 | */ |
| 28 | /* |
| 29 | * This file is part of Lustre, http://www.lustre.org/ |
| 30 | * Lustre is a trademark of Sun Microsystems, Inc. |
| 31 | */ |
| 32 | |
| 33 | #define DEBUG_SUBSYSTEM S_LNET |
| 34 | |
Greg Kroah-Hartman | 9fdaf8c | 2014-07-11 20:51:16 -0700 | [diff] [blame] | 35 | #include "../../../include/linux/libcfs/libcfs.h" |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 36 | |
| 37 | #define LNET_MINOR 240 |
| 38 | |
James Simmons | c9493bd | 2016-03-22 19:04:16 -0400 | [diff] [blame] | 39 | static inline size_t libcfs_ioctl_packlen(struct libcfs_ioctl_data *data) |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 40 | { |
James Simmons | c9493bd | 2016-03-22 19:04:16 -0400 | [diff] [blame] | 41 | size_t len = sizeof(*data); |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 42 | |
| 43 | len += cfs_size_round(data->ioc_inllen1); |
| 44 | len += cfs_size_round(data->ioc_inllen2); |
| 45 | return len; |
| 46 | } |
| 47 | |
| 48 | static inline bool libcfs_ioctl_is_invalid(struct libcfs_ioctl_data *data) |
| 49 | { |
James Simmons | 243fdde | 2016-03-22 19:04:15 -0400 | [diff] [blame] | 50 | if (data->ioc_hdr.ioc_len > BIT(30)) { |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 51 | CERROR("LIBCFS ioctl: ioc_len larger than 1<<30\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 52 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 53 | } |
James Simmons | 243fdde | 2016-03-22 19:04:15 -0400 | [diff] [blame] | 54 | if (data->ioc_inllen1 > BIT(30)) { |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 55 | CERROR("LIBCFS ioctl: ioc_inllen1 larger than 1<<30\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 56 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 57 | } |
James Simmons | 243fdde | 2016-03-22 19:04:15 -0400 | [diff] [blame] | 58 | if (data->ioc_inllen2 > BIT(30)) { |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 59 | CERROR("LIBCFS ioctl: ioc_inllen2 larger than 1<<30\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 60 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 61 | } |
| 62 | if (data->ioc_inlbuf1 && !data->ioc_inllen1) { |
| 63 | CERROR("LIBCFS ioctl: inlbuf1 pointer but 0 length\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 64 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 65 | } |
| 66 | if (data->ioc_inlbuf2 && !data->ioc_inllen2) { |
| 67 | CERROR("LIBCFS ioctl: inlbuf2 pointer but 0 length\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 68 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 69 | } |
| 70 | if (data->ioc_pbuf1 && !data->ioc_plen1) { |
| 71 | CERROR("LIBCFS ioctl: pbuf1 pointer but 0 length\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 72 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 73 | } |
| 74 | if (data->ioc_pbuf2 && !data->ioc_plen2) { |
| 75 | CERROR("LIBCFS ioctl: pbuf2 pointer but 0 length\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 76 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 77 | } |
| 78 | if (data->ioc_plen1 && !data->ioc_pbuf1) { |
| 79 | CERROR("LIBCFS ioctl: plen1 nonzero but no pbuf1 pointer\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 80 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 81 | } |
| 82 | if (data->ioc_plen2 && !data->ioc_pbuf2) { |
| 83 | CERROR("LIBCFS ioctl: plen2 nonzero but no pbuf2 pointer\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 84 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 85 | } |
| 86 | if ((__u32)libcfs_ioctl_packlen(data) != data->ioc_hdr.ioc_len) { |
| 87 | CERROR("LIBCFS ioctl: packlen != ioc_len\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 88 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 89 | } |
| 90 | if (data->ioc_inllen1 && |
| 91 | data->ioc_bulk[data->ioc_inllen1 - 1] != '\0') { |
| 92 | CERROR("LIBCFS ioctl: inlbuf1 not 0 terminated\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 93 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 94 | } |
| 95 | if (data->ioc_inllen2 && |
| 96 | data->ioc_bulk[cfs_size_round(data->ioc_inllen1) + |
| 97 | data->ioc_inllen2 - 1] != '\0') { |
| 98 | CERROR("LIBCFS ioctl: inlbuf2 not 0 terminated\n"); |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 99 | return true; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 100 | } |
James Simmons | 460a222 | 2016-03-22 19:04:14 -0400 | [diff] [blame] | 101 | return false; |
James Simmons | cfec4b5 | 2016-03-22 19:04:10 -0400 | [diff] [blame] | 102 | } |
| 103 | |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 104 | int libcfs_ioctl_data_adjust(struct libcfs_ioctl_data *data) |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 105 | { |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 106 | if (libcfs_ioctl_is_invalid(data)) { |
Liang Zhen | 6d0aeaa3 | 2016-03-22 19:03:56 -0400 | [diff] [blame] | 107 | CERROR("libcfs ioctl: parameter not correctly formatted\n"); |
Greg Kroah-Hartman | 0a3bdb0 | 2013-08-03 10:35:28 +0800 | [diff] [blame] | 108 | return -EINVAL; |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | if (data->ioc_inllen1) |
| 112 | data->ioc_inlbuf1 = &data->ioc_bulk[0]; |
| 113 | |
| 114 | if (data->ioc_inllen2) |
| 115 | data->ioc_inlbuf2 = &data->ioc_bulk[0] + |
| 116 | cfs_size_round(data->ioc_inllen1); |
| 117 | |
Greg Kroah-Hartman | 0a3bdb0 | 2013-08-03 10:35:28 +0800 | [diff] [blame] | 118 | return 0; |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 119 | } |
| 120 | |
Liang Zhen | e4efb34 | 2016-03-22 19:03:50 -0400 | [diff] [blame] | 121 | int libcfs_ioctl_getdata(struct libcfs_ioctl_hdr **hdr_pp, |
| 122 | const struct libcfs_ioctl_hdr __user *uhdr) |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 123 | { |
| 124 | struct libcfs_ioctl_hdr hdr; |
Liang Zhen | e4efb34 | 2016-03-22 19:03:50 -0400 | [diff] [blame] | 125 | int err = 0; |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 126 | |
Oleg Drokin | 78ab125 | 2016-03-27 12:05:03 -0400 | [diff] [blame] | 127 | if (copy_from_user(&hdr, uhdr, sizeof(hdr))) |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 128 | return -EFAULT; |
| 129 | |
Amir Shehata | 2e9a51b | 2016-02-22 17:29:03 -0500 | [diff] [blame] | 130 | if (hdr.ioc_version != LIBCFS_IOCTL_VERSION && |
| 131 | hdr.ioc_version != LIBCFS_IOCTL_VERSION2) { |
Liang Zhen | 6d0aeaa3 | 2016-03-22 19:03:56 -0400 | [diff] [blame] | 132 | CERROR("libcfs ioctl: version mismatch expected %#x, got %#x\n", |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 133 | LIBCFS_IOCTL_VERSION, hdr.ioc_version); |
| 134 | return -EINVAL; |
| 135 | } |
| 136 | |
Liang Zhen | ed2f549 | 2016-03-22 19:03:57 -0400 | [diff] [blame] | 137 | if (hdr.ioc_len < sizeof(struct libcfs_ioctl_data)) { |
| 138 | CERROR("libcfs ioctl: user buffer too small for ioctl\n"); |
| 139 | return -EINVAL; |
| 140 | } |
| 141 | |
Liang Zhen | e4efb34 | 2016-03-22 19:03:50 -0400 | [diff] [blame] | 142 | if (hdr.ioc_len > LIBCFS_IOC_DATA_MAX) { |
| 143 | CERROR("libcfs ioctl: user buffer is too large %d/%d\n", |
| 144 | hdr.ioc_len, LIBCFS_IOC_DATA_MAX); |
| 145 | return -EINVAL; |
| 146 | } |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 147 | |
Liang Zhen | e4efb34 | 2016-03-22 19:03:50 -0400 | [diff] [blame] | 148 | LIBCFS_ALLOC(*hdr_pp, hdr.ioc_len); |
| 149 | if (!*hdr_pp) |
| 150 | return -ENOMEM; |
| 151 | |
| 152 | if (copy_from_user(*hdr_pp, uhdr, hdr.ioc_len)) { |
| 153 | LIBCFS_FREE(*hdr_pp, hdr.ioc_len); |
| 154 | err = -EFAULT; |
| 155 | } |
| 156 | return err; |
Amir Shehata | 1290932 | 2016-02-22 17:29:02 -0500 | [diff] [blame] | 157 | } |
| 158 | |
Parinay Kondekar | ae272a4 | 2016-03-22 19:04:07 -0400 | [diff] [blame] | 159 | static long |
| 160 | libcfs_psdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 161 | { |
Peng Tao | 4b1a25f | 2013-07-15 22:27:14 +0800 | [diff] [blame] | 162 | if (!capable(CAP_SYS_ADMIN)) |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 163 | return -EACCES; |
| 164 | |
Jessica Yu | 91a002c | 2014-07-28 06:33:13 -0700 | [diff] [blame] | 165 | if (_IOC_TYPE(cmd) != IOC_LIBCFS_TYPE || |
Oleg Drokin | ae0b483 | 2016-02-16 00:47:00 -0500 | [diff] [blame] | 166 | _IOC_NR(cmd) < IOC_LIBCFS_MIN_NR || |
| 167 | _IOC_NR(cmd) > IOC_LIBCFS_MAX_NR) { |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 168 | CDEBUG(D_IOCTL, "invalid ioctl ( type %d, nr %d, size %d )\n", |
| 169 | _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); |
Julia Lawall | fbe7c6c | 2014-08-26 22:00:33 +0200 | [diff] [blame] | 170 | return -EINVAL; |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 171 | } |
| 172 | |
Parinay Kondekar | 89010fb | 2016-03-22 19:04:08 -0400 | [diff] [blame] | 173 | return libcfs_ioctl(cmd, (void __user *)arg); |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 174 | } |
| 175 | |
Jessica Yu | d81f9f5 | 2014-07-28 06:33:15 -0700 | [diff] [blame] | 176 | static const struct file_operations libcfs_fops = { |
Parinay Kondekar | 4678d18 | 2016-03-22 19:04:06 -0400 | [diff] [blame] | 177 | .owner = THIS_MODULE, |
Parinay Kondekar | ae272a4 | 2016-03-22 19:04:07 -0400 | [diff] [blame] | 178 | .unlocked_ioctl = libcfs_psdev_ioctl, |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 179 | }; |
| 180 | |
Greg Kroah-Hartman | c0426cf | 2013-07-24 10:21:26 -0700 | [diff] [blame] | 181 | struct miscdevice libcfs_dev = { |
| 182 | .minor = LNET_MINOR, |
| 183 | .name = "lnet", |
| 184 | .fops = &libcfs_fops, |
Peng Tao | d7e09d0 | 2013-05-02 16:46:55 +0800 | [diff] [blame] | 185 | }; |