vt: lock the accent table

First step to debletcherising the vt console layer - pick a victim and fix
the locking

This is a nice simple object with its own rules so lets pick it out for
treatment. The user of the table already has a lock so we will also use the
same lock for updates.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index a605549..bdf838d 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1452,3 +1452,165 @@
 
 	return 0;
 }
+
+/* Ioctl support code */
+
+/**
+ *	vt_do_diacrit		-	diacritical table updates
+ *	@cmd: ioctl request
+ *	@up: pointer to user data for ioctl
+ *	@perm: permissions check computed by caller
+ *
+ *	Update the diacritical tables atomically and safely. Lock them
+ *	against simultaneous keypresses
+ */
+int vt_do_diacrit(unsigned int cmd, void __user *up, int perm)
+{
+	struct kbdiacrs __user *a = up;
+	unsigned long flags;
+	int asize;
+	int ret = 0;
+
+	switch (cmd) {
+	case KDGKBDIACR:
+	{
+		struct kbdiacr *diacr;
+		int i;
+
+		diacr = kmalloc(MAX_DIACR * sizeof(struct kbdiacr),
+								GFP_KERNEL);
+		if (diacr == NULL)
+			return -ENOMEM;
+
+		/* Lock the diacriticals table, make a copy and then
+		   copy it after we unlock */
+		spin_lock_irqsave(&kbd_event_lock, flags);
+
+		asize = accent_table_size;
+		for (i = 0; i < asize; i++) {
+			diacr[i].diacr = conv_uni_to_8bit(
+						accent_table[i].diacr);
+			diacr[i].base = conv_uni_to_8bit(
+						accent_table[i].base);
+			diacr[i].result = conv_uni_to_8bit(
+						accent_table[i].result);
+		}
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+
+		if (put_user(asize, &a->kb_cnt))
+			ret = -EFAULT;
+		else  if (copy_to_user(a->kbdiacr, diacr,
+				asize * sizeof(struct kbdiacr)))
+			ret = -EFAULT;
+		kfree(diacr);
+		return ret;
+	}
+	case KDGKBDIACRUC:
+	{
+		struct kbdiacrsuc __user *a = up;
+		void *buf;
+
+		buf = kmalloc(MAX_DIACR * sizeof(struct kbdiacruc),
+								GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+
+		/* Lock the diacriticals table, make a copy and then
+		   copy it after we unlock */
+		spin_lock_irqsave(&kbd_event_lock, flags);
+
+		asize = accent_table_size;
+		memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
+
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+
+		if (put_user(asize, &a->kb_cnt))
+			ret = -EFAULT;
+		else if (copy_to_user(a->kbdiacruc, buf,
+				asize*sizeof(struct kbdiacruc)))
+			ret = -EFAULT;
+		kfree(buf);
+		return ret;
+	}
+
+	case KDSKBDIACR:
+	{
+		struct kbdiacrs __user *a = up;
+		struct kbdiacr *diacr = NULL;
+		unsigned int ct;
+		int i;
+
+		if (!perm)
+			return -EPERM;
+		if (get_user(ct, &a->kb_cnt))
+			return -EFAULT;
+		if (ct >= MAX_DIACR)
+			return -EINVAL;
+
+		if (ct) {
+			diacr = kmalloc(sizeof(struct kbdiacr) * ct,
+								GFP_KERNEL);
+			if (diacr == NULL)
+				return -ENOMEM;
+
+			if (copy_from_user(diacr, a->kbdiacr,
+					sizeof(struct kbdiacr) * ct)) {
+				kfree(diacr);
+				return -EFAULT;
+			}
+		}
+
+		spin_lock_irqsave(&kbd_event_lock, flags);
+		accent_table_size = ct;
+		for (i = 0; i < ct; i++) {
+			accent_table[i].diacr =
+					conv_8bit_to_uni(diacr[i].diacr);
+			accent_table[i].base =
+					conv_8bit_to_uni(diacr[i].base);
+			accent_table[i].result =
+					conv_8bit_to_uni(diacr[i].result);
+		}
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		kfree(diacr);
+		return 0;
+	}
+
+	case KDSKBDIACRUC:
+	{
+		struct kbdiacrsuc __user *a = up;
+		unsigned int ct;
+		void *buf = NULL;
+
+		if (!perm)
+			return -EPERM;
+
+		if (get_user(ct, &a->kb_cnt))
+			return -EFAULT;
+
+		if (ct >= MAX_DIACR)
+			return -EINVAL;
+
+		if (ct) {
+			buf = kmalloc(ct * sizeof(struct kbdiacruc),
+								GFP_KERNEL);
+			if (buf == NULL)
+				return -ENOMEM;
+
+			if (copy_from_user(buf, a->kbdiacruc,
+					ct * sizeof(struct kbdiacruc))) {
+				kfree(buf);
+				return -EFAULT;
+			}
+		} 
+		spin_lock_irqsave(&kbd_event_lock, flags);
+		if (ct)
+			memcpy(accent_table, buf,
+					ct * sizeof(struct kbdiacruc));
+		accent_table_size = ct;
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		kfree(buf);
+		return 0;
+	}
+	}
+	return ret;
+}