openprom.c
来自「linux 内核源代码」· C语言 代码 · 共 753 行 · 第 1/2 页
C
753 行
/* Copy in a whole string from userspace into kernelspace. */static int copyin_string(char __user *user, size_t len, char **ptr){ char *tmp; if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0) return -EINVAL; tmp = kmalloc(len + 1, GFP_KERNEL); if (!tmp) return -ENOMEM; if (copy_from_user(tmp, user, len)) { kfree(tmp); return -EFAULT; } tmp[len] = '\0'; *ptr = tmp; return 0;}/* * NetBSD /dev/openprom ioctl calls. */static int opiocget(void __user *argp, DATA *data){ struct opiocdesc op; struct device_node *dp; char *str; const void *pval; int err, len; if (copy_from_user(&op, argp, sizeof(op))) return -EFAULT; dp = get_node(op.op_nodeid, data); err = copyin_string(op.op_name, op.op_namelen, &str); if (err) return err; pval = of_get_property(dp, str, &len); err = 0; if (!pval || len > op.op_buflen) { err = -EINVAL; } else { op.op_buflen = len; if (copy_to_user(argp, &op, sizeof(op)) || copy_to_user(op.op_buf, pval, len)) err = -EFAULT; } kfree(str); return err;}static int opiocnextprop(void __user *argp, DATA *data){ struct opiocdesc op; struct device_node *dp; struct property *prop; char *str; int err, len; if (copy_from_user(&op, argp, sizeof(op))) return -EFAULT; dp = get_node(op.op_nodeid, data); if (!dp) return -EINVAL; err = copyin_string(op.op_name, op.op_namelen, &str); if (err) return err; if (str[0] == '\0') { prop = dp->properties; } else { prop = of_find_property(dp, str, NULL); if (prop) prop = prop->next; } kfree(str); if (!prop) len = 0; else len = prop->length; if (len > op.op_buflen) len = op.op_buflen; if (copy_to_user(argp, &op, sizeof(op))) return -EFAULT; if (len && copy_to_user(op.op_buf, prop->value, len)) return -EFAULT; return 0;}static int opiocset(void __user *argp, DATA *data){ struct opiocdesc op; struct device_node *dp; char *str, *tmp; int err; if (copy_from_user(&op, argp, sizeof(op))) return -EFAULT; dp = get_node(op.op_nodeid, data); if (!dp) return -EINVAL; err = copyin_string(op.op_name, op.op_namelen, &str); if (err) return err; err = copyin_string(op.op_buf, op.op_buflen, &tmp); if (err) { kfree(str); return err; } err = of_set_property(dp, str, tmp, op.op_buflen); kfree(str); kfree(tmp); return err;}static int opiocgetnext(unsigned int cmd, void __user *argp){ struct device_node *dp; phandle nd; BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); if (copy_from_user(&nd, argp, sizeof(phandle))) return -EFAULT; if (nd == 0) { if (cmd != OPIOCGETNEXT) return -EINVAL; dp = of_find_node_by_path("/"); } else { dp = of_find_node_by_phandle(nd); nd = 0; if (dp) { if (cmd == OPIOCGETNEXT) dp = dp->sibling; else dp = dp->child; } } if (dp) nd = dp->node; if (copy_to_user(argp, &nd, sizeof(phandle))) return -EFAULT; return 0;}static int openprom_bsd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ DATA *data = (DATA *) file->private_data; void __user *argp = (void __user *)arg; int err; switch (cmd) { case OPIOCGET: err = opiocget(argp, data); break; case OPIOCNEXTPROP: err = opiocnextprop(argp, data); break; case OPIOCSET: err = opiocset(argp, data); break; case OPIOCGETOPTNODE: BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); if (copy_to_user(argp, &options_node->node, sizeof(phandle))) return -EFAULT; return 0; case OPIOCGETNEXT: case OPIOCGETCHILD: err = opiocgetnext(cmd, argp); break; default: return -EINVAL; }; return err;}/* * Handoff control to the correct ioctl handler. */static int openprom_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ DATA *data = (DATA *) file->private_data; switch (cmd) { case OPROMGETOPT: case OPROMNXTOPT: if ((file->f_mode & FMODE_READ) == 0) return -EPERM; return openprom_sunos_ioctl(inode, file, cmd, arg, options_node); case OPROMSETOPT: case OPROMSETOPT2: if ((file->f_mode & FMODE_WRITE) == 0) return -EPERM; return openprom_sunos_ioctl(inode, file, cmd, arg, options_node); case OPROMNEXT: case OPROMCHILD: case OPROMGETPROP: case OPROMNXTPROP: if ((file->f_mode & FMODE_READ) == 0) return -EPERM; return openprom_sunos_ioctl(inode, file, cmd, arg, data->current_node); case OPROMU2P: case OPROMGETCONS: case OPROMGETFBNAME: case OPROMGETBOOTARGS: case OPROMSETCUR: case OPROMPCI2NODE: case OPROMPATH2NODE: if ((file->f_mode & FMODE_READ) == 0) return -EPERM; return openprom_sunos_ioctl(inode, file, cmd, arg, NULL); case OPIOCGET: case OPIOCNEXTPROP: case OPIOCGETOPTNODE: case OPIOCGETNEXT: case OPIOCGETCHILD: if ((file->f_mode & FMODE_READ) == 0) return -EBADF; return openprom_bsd_ioctl(inode,file,cmd,arg); case OPIOCSET: if ((file->f_mode & FMODE_WRITE) == 0) return -EBADF; return openprom_bsd_ioctl(inode,file,cmd,arg); default: return -EINVAL; };}static long openprom_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ long rval = -ENOTTY; /* * SunOS/Solaris only, the NetBSD one's have embedded pointers in * the arg which we'd need to clean up... */ switch (cmd) { case OPROMGETOPT: case OPROMSETOPT: case OPROMNXTOPT: case OPROMSETOPT2: case OPROMNEXT: case OPROMCHILD: case OPROMGETPROP: case OPROMNXTPROP: case OPROMU2P: case OPROMGETCONS: case OPROMGETFBNAME: case OPROMGETBOOTARGS: case OPROMSETCUR: case OPROMPCI2NODE: case OPROMPATH2NODE: rval = openprom_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); break; } return rval;}static int openprom_open(struct inode * inode, struct file * file){ DATA *data; data = kmalloc(sizeof(DATA), GFP_KERNEL); if (!data) return -ENOMEM; data->current_node = of_find_node_by_path("/"); data->lastnode = data->current_node; file->private_data = (void *) data; return 0;}static int openprom_release(struct inode * inode, struct file * file){ kfree(file->private_data); return 0;}static const struct file_operations openprom_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = openprom_ioctl, .compat_ioctl = openprom_compat_ioctl, .open = openprom_open, .release = openprom_release,};static struct miscdevice openprom_dev = { .minor = SUN_OPENPROM_MINOR, .name = "openprom", .fops = &openprom_fops,};static int __init openprom_init(void){ struct device_node *dp; int err; err = misc_register(&openprom_dev); if (err) return err; dp = of_find_node_by_path("/"); dp = dp->child; while (dp) { if (!strcmp(dp->name, "options")) break; dp = dp->sibling; } options_node = dp; if (!options_node) { misc_deregister(&openprom_dev); return -EIO; } return 0;}static void __exit openprom_cleanup(void){ misc_deregister(&openprom_dev);}module_init(openprom_init);module_exit(openprom_cleanup);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?