📄 af_irda.c
字号:
else ias_obj = irias_find_object(ias_opt->irda_class_name); if(ias_obj == (struct ias_object *) NULL) { kfree(ias_opt); return -EINVAL; } /* Only ROOT can mess with the global IAS database. * Users can only del attributes from the object associated * with the socket they own - Jean II */ if((!capable(CAP_NET_ADMIN)) && ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { kfree(ias_opt); return -EPERM; } /* Find the attribute (in the object) we target */ ias_attr = irias_find_attrib(ias_obj, ias_opt->irda_attrib_name); if(ias_attr == (struct ias_attrib *) NULL) { kfree(ias_opt); return -EINVAL; } /* Check is the user space own the object */ if(ias_attr->value->owner != IAS_USER_ATTR) { IRDA_DEBUG(1, "%s(), attempting to delete a kernel attribute\n", __FUNCTION__); kfree(ias_opt); return -EPERM; } /* Remove the attribute (and maybe the object) */ irias_delete_attrib(ias_obj, ias_attr, 1); kfree(ias_opt); break; case IRLMP_MAX_SDU_SIZE: if (optlen < sizeof(int)) return -EINVAL; if (get_user(opt, (int __user *)optval)) return -EFAULT; /* Only possible for a seqpacket service (TTP with SAR) */ if (sk->sk_type != SOCK_SEQPACKET) { IRDA_DEBUG(2, "%s(), setting max_sdu_size = %d\n", __FUNCTION__, opt); self->max_sdu_size_rx = opt; } else { IRDA_WARNING("%s: not allowed to set MAXSDUSIZE for this socket type!\n", __FUNCTION__); return -ENOPROTOOPT; } break; case IRLMP_HINTS_SET: if (optlen < sizeof(int)) return -EINVAL; /* The input is really a (__u8 hints[2]), easier as an int */ if (get_user(opt, (int __user *)optval)) return -EFAULT; /* Unregister any old registration */ if (self->skey) irlmp_unregister_service(self->skey); self->skey = irlmp_register_service((__u16) opt); break; case IRLMP_HINT_MASK_SET: /* As opposed to the previous case which set the hint bits * that we advertise, this one set the filter we use when * making a discovery (nodes which don't match any hint * bit in the mask are not reported). */ if (optlen < sizeof(int)) return -EINVAL; /* The input is really a (__u8 hints[2]), easier as an int */ if (get_user(opt, (int __user *)optval)) return -EFAULT; /* Set the new hint mask */ self->mask.word = (__u16) opt; /* Mask out extension bits */ self->mask.word &= 0x7f7f; /* Check if no bits */ if(!self->mask.word) self->mask.word = 0xFFFF; break; default: return -ENOPROTOOPT; } return 0;}/* * Function irda_extract_ias_value(ias_opt, ias_value) * * Translate internal IAS value structure to the user space representation * * The external representation of IAS values, as we exchange them with * user space program is quite different from the internal representation, * as stored in the IAS database (because we need a flat structure for * crossing kernel boundary). * This function transform the former in the latter. We also check * that the value type is valid. */static int irda_extract_ias_value(struct irda_ias_set *ias_opt, struct ias_value *ias_value){ /* Look at the type */ switch (ias_value->type) { case IAS_INTEGER: /* Copy the integer */ ias_opt->attribute.irda_attrib_int = ias_value->t.integer; break; case IAS_OCT_SEQ: /* Set length */ ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len; /* Copy over */ memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq, ias_value->t.oct_seq, ias_value->len); break; case IAS_STRING: /* Set length */ ias_opt->attribute.irda_attrib_string.len = ias_value->len; ias_opt->attribute.irda_attrib_string.charset = ias_value->charset; /* Copy over */ memcpy(ias_opt->attribute.irda_attrib_string.string, ias_value->t.string, ias_value->len); /* NULL terminate the string (avoid troubles) */ ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0'; break; case IAS_MISSING: default : return -EINVAL; } /* Copy type over */ ias_opt->irda_attrib_type = ias_value->type; return 0;}/* * Function irda_getsockopt (sock, level, optname, optval, optlen) */static int irda_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); struct irda_device_list list; struct irda_device_info *discoveries; struct irda_ias_set * ias_opt; /* IAS get/query params */ struct ias_object * ias_obj; /* Object in IAS */ struct ias_attrib * ias_attr; /* Attribute in IAS object */ int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */ int val = 0; int len = 0; int err; int offset, total; IRDA_DEBUG(2, "%s(%p)\n", __FUNCTION__, self); if (level != SOL_IRLMP) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; if(len < 0) return -EINVAL; switch (optname) { case IRLMP_ENUMDEVICES: /* Ask lmp for the current discovery log */ discoveries = irlmp_get_discoveries(&list.len, self->mask.word, self->nslots); /* Check if the we got some results */ if (discoveries == NULL) return -EAGAIN; /* Didn't find any devices */ err = 0; /* Write total list length back to client */ if (copy_to_user(optval, &list, sizeof(struct irda_device_list) - sizeof(struct irda_device_info))) err = -EFAULT; /* Offset to first device entry */ offset = sizeof(struct irda_device_list) - sizeof(struct irda_device_info); /* Copy the list itself - watch for overflow */ if(list.len > 2048) { err = -EINVAL; goto bed; } total = offset + (list.len * sizeof(struct irda_device_info)); if (total > len) total = len; if (copy_to_user(optval+offset, discoveries, total - offset)) err = -EFAULT; /* Write total number of bytes used back to client */ if (put_user(total, optlen)) err = -EFAULT;bed: /* Free up our buffer */ kfree(discoveries); if (err) return err; break; case IRLMP_MAX_SDU_SIZE: val = self->max_data_size; len = sizeof(int); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; break; case IRLMP_IAS_GET: /* The user want an object from our local IAS database. * We just need to query the IAS and return the value * that we found */ /* Check that the user has allocated the right space for us */ if (len != sizeof(struct irda_ias_set)) return -EINVAL; ias_opt = kmalloc(sizeof(struct irda_ias_set), GFP_ATOMIC); if (ias_opt == NULL) return -ENOMEM; /* Copy query to the driver. */ if (copy_from_user(ias_opt, optval, len)) { kfree(ias_opt); return -EFAULT; } /* Find the object we target. * If the user gives us an empty string, we use the object * associated with this socket. This will workaround * duplicated class name - Jean II */ if(ias_opt->irda_class_name[0] == '\0') ias_obj = self->ias_obj; else ias_obj = irias_find_object(ias_opt->irda_class_name); if(ias_obj == (struct ias_object *) NULL) { kfree(ias_opt); return -EINVAL; } /* Find the attribute (in the object) we target */ ias_attr = irias_find_attrib(ias_obj, ias_opt->irda_attrib_name); if(ias_attr == (struct ias_attrib *) NULL) { kfree(ias_opt); return -EINVAL; } /* Translate from internal to user structure */ err = irda_extract_ias_value(ias_opt, ias_attr->value); if(err) { kfree(ias_opt); return err; } /* Copy reply to the user */ if (copy_to_user(optval, ias_opt, sizeof(struct irda_ias_set))) { kfree(ias_opt); return -EFAULT; } /* Note : don't need to put optlen, we checked it */ kfree(ias_opt); break; case IRLMP_IAS_QUERY: /* The user want an object from a remote IAS database. * We need to use IAP to query the remote database and * then wait for the answer to come back. */ /* Check that the user has allocated the right space for us */ if (len != sizeof(struct irda_ias_set)) return -EINVAL; ias_opt = kmalloc(sizeof(struct irda_ias_set), GFP_ATOMIC); if (ias_opt == NULL) return -ENOMEM; /* Copy query to the driver. */ if (copy_from_user(ias_opt, optval, len)) { kfree(ias_opt); return -EFAULT; } /* At this point, there are two cases... * 1) the socket is connected - that's the easy case, we * just query the device we are connected to... * 2) the socket is not connected - the user doesn't want * to connect and/or may not have a valid service name * (so can't create a fake connection). In this case, * we assume that the user pass us a valid destination * address in the requesting structure... */ if(self->daddr != DEV_ADDR_ANY) { /* We are connected - reuse known daddr */ daddr = self->daddr; } else { /* We are not connected, we must specify a valid * destination address */ daddr = ias_opt->daddr; if((!daddr) || (daddr == DEV_ADDR_ANY)) { kfree(ias_opt); return -EINVAL; } } /* Check that we can proceed with IAP */ if (self->iriap) { IRDA_WARNING("%s: busy with a previous query\n", __FUNCTION__); kfree(ias_opt); return -EBUSY; } self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, irda_getvalue_confirm); if (self->iriap == NULL) { kfree(ias_opt); return -ENOMEM; } /* Treat unexpected wakeup as disconnect */ self->errno = -EHOSTUNREACH; /* Query remote LM-IAS */ iriap_getvaluebyclass_request(self->iriap, self->saddr, daddr, ias_opt->irda_class_name, ias_opt->irda_attrib_name); /* Wait for answer, if not yet finished (or failed) */ if (wait_event_interruptible(self->query_wait, (self->iriap == NULL))) { /* pending request uses copy of ias_opt-content * we can free it regardless! */ kfree(ias_opt); /* Treat signals as disconnect */ return -EHOSTUNREACH; } /* Check what happened */ if (self->errno) { kfree(ias_opt); /* Requested object/attribute doesn't exist */ if((self->errno == IAS_CLASS_UNKNOWN) || (self->errno == IAS_ATTRIB_UNKNOWN)) return (-EADDRNOTAVAIL); else return (-EHOSTUNREACH); } /* Translate from internal to user structure */ err = irda_extract_ias_value(ias_opt, self->ias_result); if (self->ias_result) irias_delete_value(self->ias_result); if (err) { kfree(ias_opt); return err; } /* Copy reply to the user */ if (copy_to_user(optval, ias_opt, sizeof(struct irda_ias_set))) { kfree(ias_opt); return -EFAULT; } /* Note : don't need to put optlen, we checked it */ kfree(ias_opt); break; case IRLMP_WAITDEVICE: /* This function is just another way of seeing life ;-) * IRLMP_ENUMDEVICES assumes that you have a static network, * and that you just want to pick one of the devices present. * On the other hand, in here we assume that no device is * present and that at some point in the future a device will * come into range. When this device arrive, we just wake * up the caller, so that he has time to connect to it before * the device goes away... * Note : once the node has been discovered for more than a * few second, it won't trigger this function, unless it * goes away and come back changes its hint bits (so we * might call it IRLMP_WAITNEWDEVICE). */ /* Check that the user is passing us an int */ if (len != sizeof(int)) return -EINVAL; /* Get timeout in ms (max time we block the caller) */ if (get_user(val, (int __user *)optval)) return -EFAULT; /* Tell IrLMP we want to be notified */ irlmp_update_client(self->ckey, self->mask.word, irda_selective_discovery_indication, NULL, (void *) self); /* Do some discovery (and also return cached results) */ irlmp_discovery_request(self->nslots); /* Wait until a node is discovered */ if (!self->cachedaddr) { int ret = 0; IRDA_DEBUG(1, "%s(), nothing discovered yet, going to sleep...\n", __FUNCTION__); /* Set watchdog timer to expire in <val> ms. */ self->errno = 0; init_timer(&self->watchdog); self->watchdog.function = irda_discovery_timeout; self->watchdog.data = (unsigned long) self; self->watchdog.expires = jiffies + (val * HZ/1000); add_timer(&(self->watchdog)); /* Wait for IR-LMP to call us back */ __wait_event_interruptible(self->query_wait, (self->cachedaddr != 0 || self->errno == -ETIME), ret); /* If watchdog is still activated, kill it! */ if(timer_pending(&(self->watchdog))) del_timer(&(self->watchdog)); IRDA_DEBUG(1, "%s(), ...waking up !\n", __FUNCTION__); if (ret != 0) return ret; } else IRDA_DEBUG(1, "%s(), found immediately !\n", __FUNCTION__); /* Tell IrLMP that we have been notified */ irlmp_update_client(self->ckey, self->mask.word, NULL, NULL, NULL); /* Check if the we got some results */ if (!self->cachedaddr) return -EAGAIN; /* Didn't find any devices */ daddr = self->cachedaddr; /* Cleanup */ self->cachedaddr = 0; /* We return the daddr of the device that trigger the * wakeup. As irlmp pass us only the new devices, we * are sure that it's not an old device. * If the user want more details, he should query * the whole discovery log and pick one device... */ if (put_user(daddr, (int __user *)optval)) return -EFAULT; break; default: return -ENOPROTOOPT; } return 0;}static struct net_proto_family irda_family_ops = { .family = PF_IRDA, .create = irda_create, .owner = THIS_MODULE,};static const struct proto_ops SOCKOPS_WRAPPED(irda_stream_ops) = { .family = PF_IRDA, .owner = THIS_MODULE, .release = irda_release, .bind = irda_bind, .connect = irda_connect, .socketpair = sock_no_socketpair, .accept = irda_accept, .getname = irda_getname, .poll = irda_poll, .ioctl = irda_ioctl,#ifdef CONFIG_COMPAT .compat_ioctl = irda_c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -