netfilter: ebtables: abort if next_offset is too small
[safe/jmp/linux-2.6] / net / rfkill / core.c
index 868d79f..c218e07 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/rfkill.h>
+#include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/miscdevice.h>
 #include <linux/wait.h>
@@ -56,7 +57,6 @@ struct rfkill {
        u32                     idx;
 
        bool                    registered;
-       bool                    suspended;
        bool                    persistent;
 
        const struct rfkill_ops *ops;
@@ -224,7 +224,7 @@ static void rfkill_send_events(struct rfkill *rfkill, enum rfkill_operation op)
 
 static void rfkill_event(struct rfkill *rfkill)
 {
-       if (!rfkill->registered || rfkill->suspended)
+       if (!rfkill->registered)
                return;
 
        kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE);
@@ -508,19 +508,32 @@ bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked)
        blocked = blocked || hwblock;
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
-       if (!rfkill->registered) {
-               rfkill->persistent = true;
-       } else {
-               if (prev != blocked && !hwblock)
-                       schedule_work(&rfkill->uevent_work);
+       if (!rfkill->registered)
+               return blocked;
 
-               rfkill_led_trigger_event(rfkill);
-       }
+       if (prev != blocked && !hwblock)
+               schedule_work(&rfkill->uevent_work);
+
+       rfkill_led_trigger_event(rfkill);
 
        return blocked;
 }
 EXPORT_SYMBOL(rfkill_set_sw_state);
 
+void rfkill_init_sw_state(struct rfkill *rfkill, bool blocked)
+{
+       unsigned long flags;
+
+       BUG_ON(!rfkill);
+       BUG_ON(rfkill->registered);
+
+       spin_lock_irqsave(&rfkill->lock, flags);
+       __rfkill_set_sw_state(rfkill, blocked);
+       rfkill->persistent = true;
+       spin_unlock_irqrestore(&rfkill->lock, flags);
+}
+EXPORT_SYMBOL(rfkill_init_sw_state);
+
 void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
 {
        unsigned long flags;
@@ -537,6 +550,10 @@ void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw)
        swprev = !!(rfkill->state & RFKILL_BLOCK_SW);
        hwprev = !!(rfkill->state & RFKILL_BLOCK_HW);
        __rfkill_set_sw_state(rfkill, sw);
+       if (hw)
+               rfkill->state |= RFKILL_BLOCK_HW;
+       else
+               rfkill->state &= ~RFKILL_BLOCK_HW;
 
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
@@ -562,6 +579,8 @@ static ssize_t rfkill_name_show(struct device *dev,
 
 static const char *rfkill_get_type_str(enum rfkill_type type)
 {
+       BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_FM + 1);
+
        switch (type) {
        case RFKILL_TYPE_WLAN:
                return "wlan";
@@ -573,11 +592,13 @@ static const char *rfkill_get_type_str(enum rfkill_type type)
                return "wimax";
        case RFKILL_TYPE_WWAN:
                return "wwan";
+       case RFKILL_TYPE_GPS:
+               return "gps";
+       case RFKILL_TYPE_FM:
+               return "fm";
        default:
                BUG();
        }
-
-       BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_WWAN + 1);
 }
 
 static ssize_t rfkill_type_show(struct device *dev,
@@ -598,6 +619,15 @@ static ssize_t rfkill_idx_show(struct device *dev,
        return sprintf(buf, "%d\n", rfkill->idx);
 }
 
+static ssize_t rfkill_persistent_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct rfkill *rfkill = to_rfkill(dev);
+
+       return sprintf(buf, "%d\n", rfkill->persistent);
+}
+
 static u8 user_state_from_blocked(unsigned long state)
 {
        if (state & RFKILL_BLOCK_HW)
@@ -627,15 +657,26 @@ static ssize_t rfkill_state_store(struct device *dev,
                                  struct device_attribute *attr,
                                  const char *buf, size_t count)
 {
-       /*
-        * The intention was that userspace can only take control over
-        * a given device when/if rfkill-input doesn't control it due
-        * to user_claim. Since user_claim is currently unsupported,
-        * we never support changing the state from userspace -- this
-        * can be implemented again later.
-        */
+       struct rfkill *rfkill = to_rfkill(dev);
+       unsigned long state;
+       int err;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
 
-       return -EPERM;
+       err = strict_strtoul(buf, 0, &state);
+       if (err)
+               return err;
+
+       if (state != RFKILL_USER_STATE_SOFT_BLOCKED &&
+           state != RFKILL_USER_STATE_UNBLOCKED)
+               return -EINVAL;
+
+       mutex_lock(&rfkill_global_mutex);
+       rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED);
+       mutex_unlock(&rfkill_global_mutex);
+
+       return err ?: count;
 }
 
 static ssize_t rfkill_claim_show(struct device *dev,
@@ -656,6 +697,7 @@ static struct device_attribute rfkill_dev_attrs[] = {
        __ATTR(name, S_IRUGO, rfkill_name_show, NULL),
        __ATTR(type, S_IRUGO, rfkill_type_show, NULL),
        __ATTR(index, S_IRUGO, rfkill_idx_show, NULL),
+       __ATTR(persistent, S_IRUGO, rfkill_persistent_show, NULL),
        __ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store),
        __ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store),
        __ATTR_NULL
@@ -718,8 +760,6 @@ static int rfkill_suspend(struct device *dev, pm_message_t state)
 
        rfkill_pause_polling(rfkill);
 
-       rfkill->suspended = true;
-
        return 0;
 }
 
@@ -728,10 +768,10 @@ static int rfkill_resume(struct device *dev)
        struct rfkill *rfkill = to_rfkill(dev);
        bool cur;
 
-       cur = !!(rfkill->state & RFKILL_BLOCK_SW);
-       rfkill_set_block(rfkill, cur);
-
-       rfkill->suspended = false;
+       if (!rfkill->persistent) {
+               cur = !!(rfkill->state & RFKILL_BLOCK_SW);
+               rfkill_set_block(rfkill, cur);
+       }
 
        rfkill_resume_polling(rfkill);
 
@@ -1056,10 +1096,16 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
        struct rfkill_event ev;
 
        /* we don't need the 'hard' variable but accept it */
-       if (count < sizeof(ev) - 1)
+       if (count < RFKILL_EVENT_SIZE_V1 - 1)
                return -EINVAL;
 
-       if (copy_from_user(&ev, buf, sizeof(ev) - 1))
+       /*
+        * Copy as much data as we can accept into our 'ev' buffer,
+        * but tell userspace how much we've copied so it can determine
+        * our API version even in a write() call, if it cares.
+        */
+       count = min(count, sizeof(ev));
+       if (copy_from_user(&ev, buf, count))
                return -EFAULT;
 
        if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL)
@@ -1145,6 +1191,7 @@ static long rfkill_fop_ioctl(struct file *file, unsigned int cmd,
 #endif
 
 static const struct file_operations rfkill_fops = {
+       .owner          = THIS_MODULE,
        .open           = rfkill_fop_open,
        .read           = rfkill_fop_read,
        .write          = rfkill_fop_write,