RETURN(rc);
}
+#define OBD_MAX_IOCTL_BUFFER 8192
+
+static int obd_ioctl_is_invalid(struct obd_ioctl_data *data)
+{
+ if (data->ioc_len > BIT(30)) {
+ CERROR("OBD ioctl: ioc_len larger than 1<<30\n");
+ return 1;
+ }
+
+ if (data->ioc_inllen1 > BIT(30)) {
+ CERROR("OBD ioctl: ioc_inllen1 larger than 1<<30\n");
+ return 1;
+ }
+
+ if (data->ioc_inllen2 > BIT(30)) {
+ CERROR("OBD ioctl: ioc_inllen2 larger than 1<<30\n");
+ return 1;
+ }
+
+ if (data->ioc_inllen3 > BIT(30)) {
+ CERROR("OBD ioctl: ioc_inllen3 larger than 1<<30\n");
+ return 1;
+ }
+
+ if (data->ioc_inllen4 > BIT(30)) {
+ CERROR("OBD ioctl: ioc_inllen4 larger than 1<<30\n");
+ return 1;
+ }
+
+ if (data->ioc_inlbuf1 && data->ioc_inllen1 == 0) {
+ CERROR("OBD ioctl: inlbuf1 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (data->ioc_inlbuf2 && data->ioc_inllen2 == 0) {
+ CERROR("OBD ioctl: inlbuf2 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (data->ioc_inlbuf3 && data->ioc_inllen3 == 0) {
+ CERROR("OBD ioctl: inlbuf3 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (data->ioc_inlbuf4 && data->ioc_inllen4 == 0) {
+ CERROR("OBD ioctl: inlbuf4 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (data->ioc_pbuf1 && data->ioc_plen1 == 0) {
+ CERROR("OBD ioctl: pbuf1 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (data->ioc_pbuf2 && data->ioc_plen2 == 0) {
+ CERROR("OBD ioctl: pbuf2 pointer but 0 length\n");
+ return 1;
+ }
+
+ if (!data->ioc_pbuf1 && data->ioc_plen1 != 0) {
+ CERROR("OBD ioctl: plen1 set but NULL pointer\n");
+ return 1;
+ }
+
+ if (!data->ioc_pbuf2 && data->ioc_plen2 != 0) {
+ CERROR("OBD ioctl: plen2 set but NULL pointer\n");
+ return 1;
+ }
+
+ if (obd_ioctl_packlen(data) > data->ioc_len) {
+ CERROR("OBD ioctl: packlen exceeds ioc_len (%d > %d)\n",
+ obd_ioctl_packlen(data), data->ioc_len);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* buffer MUST be at least the size of obd_ioctl_hdr */
+int obd_ioctl_getdata(char **buf, int *len, void __user *arg)
+{
+ struct obd_ioctl_hdr hdr;
+ struct obd_ioctl_data *data;
+ int offset = 0;
+
+ ENTRY;
+ if (copy_from_user(&hdr, arg, sizeof(hdr)))
+ RETURN(-EFAULT);
+
+ if (hdr.ioc_version != OBD_IOCTL_VERSION) {
+ CERROR("Version mismatch kernel (%x) vs application (%x)\n",
+ OBD_IOCTL_VERSION, hdr.ioc_version);
+ RETURN(-EINVAL);
+ }
+
+ if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("User buffer len %d exceeds %d max buffer\n",
+ hdr.ioc_len, OBD_MAX_IOCTL_BUFFER);
+ RETURN(-EINVAL);
+ }
+
+ if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) {
+ CERROR("User buffer too small for ioctl (%d)\n", hdr.ioc_len);
+ RETURN(-EINVAL);
+ }
+
+ /* When there are lots of processes calling vmalloc on multi-core
+ * system, the high lock contention will hurt performance badly,
+ * obdfilter-survey is an example, which relies on ioctl. So we'd
+ * better avoid vmalloc on ioctl path. LU-66
+ */
+ OBD_ALLOC_LARGE(*buf, hdr.ioc_len);
+ if (!*buf) {
+ CERROR("Cannot allocate control buffer of len %d\n",
+ hdr.ioc_len);
+ RETURN(-EINVAL);
+ }
+ *len = hdr.ioc_len;
+ data = (struct obd_ioctl_data *)*buf;
+
+ if (copy_from_user(*buf, arg, hdr.ioc_len)) {
+ OBD_FREE_LARGE(*buf, hdr.ioc_len);
+ RETURN(-EFAULT);
+ }
+
+ if (obd_ioctl_is_invalid(data)) {
+ CERROR("ioctl not correctly formatted\n");
+ OBD_FREE_LARGE(*buf, hdr.ioc_len);
+ RETURN(-EINVAL);
+ }
+
+ if (data->ioc_inllen1) {
+ data->ioc_inlbuf1 = &data->ioc_bulk[0];
+ offset += cfs_size_round(data->ioc_inllen1);
+ }
+
+ if (data->ioc_inllen2) {
+ data->ioc_inlbuf2 = &data->ioc_bulk[0] + offset;
+ offset += cfs_size_round(data->ioc_inllen2);
+ }
+
+ if (data->ioc_inllen3) {
+ data->ioc_inlbuf3 = &data->ioc_bulk[0] + offset;
+ offset += cfs_size_round(data->ioc_inllen3);
+ }
+
+ if (data->ioc_inllen4)
+ data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
+
+ RETURN(0);
+}
+EXPORT_SYMBOL(obd_ioctl_getdata);
+
int class_handle_ioctl(unsigned int cmd, unsigned long arg)
{
char *buf = NULL;
RETURN(err);
} /* class_handle_ioctl */
+/* opening /dev/obd */
+static int obd_class_open(struct inode * inode, struct file * file)
+{
+ ENTRY;
+ try_module_get(THIS_MODULE);
+ RETURN(0);
+}
+
+/* closing /dev/obd */
+static int obd_class_release(struct inode * inode, struct file * file)
+{
+ ENTRY;
+
+ module_put(THIS_MODULE);
+ RETURN(0);
+}
+
+/* to control /dev/obd */
+static long obd_class_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+
+ ENTRY;
+ /* Allow non-root access for OBD_IOC_PING_TARGET - used by lfs check */
+ if (!cfs_capable(CFS_CAP_SYS_ADMIN) && (cmd != OBD_IOC_PING_TARGET))
+ RETURN(err = -EACCES);
+
+ if ((cmd & 0xffffff00) == ((int)'T') << 8) /* ignore all tty ioctls */
+ RETURN(err = -ENOTTY);
+
+ err = class_handle_ioctl(cmd, (unsigned long)arg);
+
+ RETURN(err);
+}
+
+/* declare character device */
+static struct file_operations obd_psdev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = obd_class_ioctl, /* unlocked_ioctl */
+ .open = obd_class_open, /* open */
+ .release = obd_class_release, /* release */
+};
+
+/* modules setup */
+struct miscdevice obd_psdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = OBD_DEV_NAME,
+ .fops = &obd_psdev_fops,
+};
+
static int obd_init_checks(void)
{
__u64 u64val, div64val;