patch-2.1.4 linux/fs/namei.c

Next file: linux/fs/ncpfs/inode.c
Previous file: linux/fs/minix/inode.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.3/linux/fs/namei.c linux/fs/namei.c
@@ -8,8 +8,6 @@
  * Some corrections by tytso.
  */
 
-#include <asm/segment.h>
-
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -18,69 +16,73 @@
 #include <linux/stat.h>
 #include <linux/mm.h>
 
+#include <asm/segment.h>
+
 #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
 
 /*
+ * How long a filename can we get from user space?
+ *  -EFAULT if invalid area
+ *  0 if ok (ENAMETOOLONG before EFAULT)
+ *  >0 EFAULT after xx bytes
+ */
+static inline int get_max_filename(unsigned long address)
+{
+	struct vm_area_struct * vma;
+
+	if (get_fs() == KERNEL_DS)
+		return 0;
+	vma = find_vma(current->mm, address);
+	if (!vma || vma->vm_start > address || !(vma->vm_flags & VM_READ))
+		return -EFAULT;
+	address = vma->vm_end - address;
+	if (address > PAGE_SIZE)
+		return 0;
+	if (vma->vm_next && vma->vm_next->vm_start == vma->vm_end &&
+	   (vma->vm_next->vm_flags & VM_READ))
+		return 0;
+	return address;
+}
+
+/*
  * In order to reduce some races, while at the same time doing additional
  * checking and hopefully speeding things up, we copy filenames to the
  * kernel data space before using them..
  *
  * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
  */
-static inline int do_getname(const char * filename, char *buf)
+int getname(const char * filename, char **result)
 {
-	int error, maxlen = PAGE_SIZE;
-	int c;
-
-	error = -ENAMETOOLONG;
-	if (get_fs() != KERNEL_DS) {
-		error = -EFAULT;
-		if (TASK_SIZE <= (unsigned long) filename)
-			return error;
-		maxlen = TASK_SIZE - (unsigned long) filename;
-		if (maxlen >= PAGE_SIZE) {
-			maxlen = PAGE_SIZE;
-			error = -ENAMETOOLONG;
-		}
+	int i, error;
+	unsigned long page;
+	char * tmp, c;
+
+	i = get_max_filename((unsigned long) filename);
+	if (i < 0)
+		return i;
+	error = -EFAULT;
+	if (!i) {
+		error = -ENAMETOOLONG;
+		i = PAGE_SIZE;
 	}
-
-	c = (unsigned char) get_user(filename++);
+	get_user(c, filename++);
 	if (!c)
 		return -ENOENT;
-
-	while (--maxlen) {
-		*(buf++) = c;
-		c = get_user(filename++);
+	if(!(page = __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	*result = tmp = (char *) page;
+	while (--i) {
+		*(tmp++) = c;
+		get_user(c, filename++);
 		if (!c) {
-			*buf = '\0';
+			*tmp = '\0';
 			return 0;
 		}
 	}
+	free_page(page);
 	return error;
 }
 
-int getname(const char *filename, char **result)
-{
-	int error;
-	unsigned long page = __get_free_page(GFP_KERNEL);
-
-	error = -ENOMEM;
-	if (page) {
-		error = -EFAULT;
-		if (!exception()) {
-			int retval = do_getname(filename, (char *) page);
-			end_exception();
-			if (!retval) {
-				*result = (char *) page;
-				return 0;
-			}
-			error = retval;
-		}
-		free_page(page);
-	}
-	return error;
-}
-		
 void putname(char * name)
 {
 	free_page((unsigned long) name);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov