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 + -
显示快捷键?