Bluetooth: notify userspace of security level change
[linux-flexiantxendom0-3.2.10.git] / net / bluetooth / af_bluetooth.c
index 8add9b4..6fb68a9 100644 (file)
@@ -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);
 
@@ -199,15 +197,14 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
 
        BT_DBG("parent %p", parent);
 
-       local_bh_disable();
        list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
                sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
 
-               bh_lock_sock(sk);
+               lock_sock(sk);
 
                /* FIXME: Is this check still needed */
                if (sk->sk_state == BT_CLOSED) {
-                       bh_unlock_sock(sk);
+                       release_sock(sk);
                        bt_accept_unlink(sk);
                        continue;
                }
@@ -218,14 +215,12 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
                        if (newsock)
                                sock_graft(sk, newsock);
 
-                       bh_unlock_sock(sk);
-                       local_bh_enable();
+                       release_sock(sk);
                        return sk;
                }
 
-               bh_unlock_sock(sk);
+               release_sock(sk);
        }
-       local_bh_enable();
 
        return NULL;
 }
@@ -349,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;
@@ -361,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;
@@ -429,7 +450,7 @@ unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wa
                        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);
@@ -494,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;
@@ -510,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;
 }