Whamcloud - gitweb
LU-8837 obd: remove linux directory
[fs/lustre-release.git] / lustre / obdclass / class_obd.c
index 59be17a..9bb407c 100644 (file)
@@ -133,6 +133,159 @@ out:
         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;
@@ -348,6 +501,57 @@ out:
        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;