Bluetooth: notify userspace of security level change
[linux-flexiantxendom0-3.2.10.git] / net / bluetooth / af_bluetooth.c
index c4cf3f5..6fb68a9 100644 (file)
@@ -40,7 +40,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 
-#define VERSION "2.15"
+#define VERSION "2.16"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
@@ -71,19 +71,16 @@ static const char *const bt_slock_key_strings[BT_MAX_PROTO] = {
        "slock-AF_BLUETOOTH-BTPROTO_AVDTP",
 };
 
-static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+void bt_sock_reclassify_lock(struct sock *sk, int proto)
 {
-       struct sock *sk = sock->sk;
-
-       if (!sk)
-               return;
-
+       BUG_ON(!sk);
        BUG_ON(sock_owned_by_user(sk));
 
        sock_lock_init_class_and_name(sk,
                        bt_slock_key_strings[proto], &bt_slock_key[proto],
                                bt_key_strings[proto], &bt_lock_key[proto]);
 }
+EXPORT_SYMBOL(bt_sock_reclassify_lock);
 
 int bt_sock_register(int proto, const struct net_proto_family *ops)
 {
@@ -145,7 +142,8 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto,
 
        if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) {
                err = bt_proto[proto]->create(net, sock, proto, kern);
-               bt_sock_reclassify_lock(sock, proto);
+               if (!err)
+                       bt_sock_reclassify_lock(sock->sk, proto);
                module_put(bt_proto[proto]->owner);
        }
 
@@ -156,17 +154,17 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto,
 
 void bt_sock_link(struct bt_sock_list *l, struct sock *sk)
 {
-       write_lock_bh(&l->lock);
+       write_lock(&l->lock);
        sk_add_node(sk, &l->head);
-       write_unlock_bh(&l->lock);
+       write_unlock(&l->lock);
 }
 EXPORT_SYMBOL(bt_sock_link);
 
 void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk)
 {
-       write_lock_bh(&l->lock);
+       write_lock(&l->lock);
        sk_del_node_init(sk);
-       write_unlock_bh(&l->lock);
+       write_unlock(&l->lock);
 }
 EXPORT_SYMBOL(bt_sock_unlink);
 
@@ -216,12 +214,14 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
                        bt_accept_unlink(sk);
                        if (newsock)
                                sock_graft(sk, newsock);
+
                        release_sock(sk);
                        return sk;
                }
 
                release_sock(sk);
        }
+
        return NULL;
 }
 EXPORT_SYMBOL(bt_accept_dequeue);
@@ -240,7 +240,8 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (flags & (MSG_OOB))
                return -EOPNOTSUPP;
 
-       if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
+       skb = skb_recv_datagram(sk, flags, noblock, &err);
+       if (!skb) {
                if (sk->sk_shutdown & RCV_SHUTDOWN)
                        return 0;
                return err;
@@ -323,7 +324,8 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                        if (copied >= target)
                                break;
 
-                       if ((err = sock_error(sk)) != 0)
+                       err = sock_error(sk);
+                       if (err)
                                break;
                        if (sk->sk_shutdown & RCV_SHUTDOWN)
                                break;
@@ -342,7 +344,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                }
 
                chunk = min_t(unsigned int, skb->len, size);
-               if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+               if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
                        skb_queue_head(&sk->sk_receive_queue, skb);
                        if (!copied)
                                copied = -EFAULT;
@@ -354,7 +356,33 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                sock_recv_ts_and_drops(msg, sk, skb);
 
                if (!(flags & MSG_PEEK)) {
-                       skb_pull(skb, chunk);
+                       int skb_len = skb_headlen(skb);
+
+                       if (chunk <= skb_len) {
+                               __skb_pull(skb, chunk);
+                       } else {
+                               struct sk_buff *frag;
+
+                               __skb_pull(skb, skb_len);
+                               chunk -= skb_len;
+
+                               skb_walk_frags(skb, frag) {
+                                       if (chunk <= frag->len) {
+                                               /* Pulling partial data */
+                                               skb->len -= chunk;
+                                               skb->data_len -= chunk;
+                                               __skb_pull(frag, chunk);
+                                               break;
+                                       } else if (frag->len) {
+                                               /* Pulling all frag data */
+                                               chunk -= frag->len;
+                                               skb->len -= frag->len;
+                                               skb->data_len -= frag->len;
+                                               __skb_pull(frag, frag->len);
+                                       }
+                               }
+                       }
+
                        if (skb->len) {
                                skb_queue_head(&sk->sk_receive_queue, skb);
                                break;
@@ -390,7 +418,7 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
        return 0;
 }
 
-unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait)
 {
        struct sock *sk = sock->sk;
        unsigned int mask = 0;
@@ -422,7 +450,7 @@ unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *w
                        sk->sk_state == BT_CONFIG)
                return mask;
 
-       if (sock_writeable(sk))
+       if (!bt_sk(sk)->suspended && sock_writeable(sk))
                mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
        else
                set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
@@ -487,9 +515,8 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
        BT_DBG("sk %p", sk);
 
        add_wait_queue(sk_sleep(sk), &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
        while (sk->sk_state != state) {
-               set_current_state(TASK_INTERRUPTIBLE);
-
                if (!timeo) {
                        err = -EINPROGRESS;
                        break;
@@ -503,12 +530,13 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
                release_sock(sk);
                timeo = schedule_timeout(timeo);
                lock_sock(sk);
+               set_current_state(TASK_INTERRUPTIBLE);
 
                err = sock_error(sk);
                if (err)
                        break;
        }
-       set_current_state(TASK_RUNNING);
+       __set_current_state(TASK_RUNNING);
        remove_wait_queue(sk_sleep(sk), &wait);
        return err;
 }
@@ -538,13 +566,39 @@ static int __init bt_init(void)
 
        BT_INFO("HCI device and connection manager initialized");
 
-       hci_sock_init();
+       err = hci_sock_init();
+       if (err < 0)
+               goto error;
+
+       err = l2cap_init();
+       if (err < 0)
+               goto sock_err;
+
+       err = sco_init();
+       if (err < 0) {
+               l2cap_exit();
+               goto sock_err;
+       }
 
        return 0;
+
+sock_err:
+       hci_sock_cleanup();
+
+error:
+       sock_unregister(PF_BLUETOOTH);
+       bt_sysfs_cleanup();
+
+       return err;
 }
 
 static void __exit bt_exit(void)
 {
+
+       sco_exit();
+
+       l2cap_exit();
+
        hci_sock_cleanup();
 
        sock_unregister(PF_BLUETOOTH);