patch-2.4.23 linux-2.4.23/fs/proc/array.c

Next file: linux-2.4.23/fs/proc/base.c
Previous file: linux-2.4.23/fs/ntfs/super.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.22/fs/proc/array.c linux-2.4.23/fs/proc/array.c
@@ -70,6 +70,7 @@
 #include <linux/smp.h>
 #include <linux/signal.h>
 #include <linux/highmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -511,179 +512,96 @@
 		       size, resident, share, trs, lrs, drs, dt);
 }
 
-/*
- * The way we support synthetic files > 4K
- * - without storing their contents in some buffer and
- * - without walking through the entire synthetic file until we reach the
- *   position of the requested data
- * is to cleverly encode the current position in the file's f_pos field.
- * There is no requirement that a read() call which returns `count' bytes
- * of data increases f_pos by exactly `count'.
- *
- * This idea is Linus' one. Bruno implemented it.
- */
-
-/*
- * For the /proc/<pid>/maps file, we use fixed length records, each containing
- * a single line.
- *
- * f_pos = (number of the vma in the task->mm->mmap list) * PAGE_SIZE
- *         + (index into the line)
- */
-/* for systems with sizeof(void*) == 4: */
-#define MAPS_LINE_FORMAT4	  "%08lx-%08lx %s %08lx %s %lu"
-#define MAPS_LINE_MAX4	49 /* sum of 8  1  8  1 4 1 8 1 5 1 10 1 */
-
-/* for systems with sizeof(void*) == 8: */
-#define MAPS_LINE_FORMAT8	  "%016lx-%016lx %s %016lx %s %lu"
-#define MAPS_LINE_MAX8	73 /* sum of 16  1  16  1 4 1 16 1 5 1 10 1 */
-
-#define MAPS_LINE_FORMAT	(sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8)
-#define MAPS_LINE_MAX	(sizeof(void*) == 4 ?  MAPS_LINE_MAX4 :  MAPS_LINE_MAX8)
-
-static int proc_pid_maps_get_line (char *buf, struct vm_area_struct *map)
-{
-	/* produce the next line */
-	char *line;
-	char str[5];
-	int flags;
-	kdev_t dev;
-	unsigned long ino;
+static int show_map(struct seq_file *m, void *v)
+{
+	struct vm_area_struct *map = v;
+	struct file *file = map->vm_file;
+	int flags = map->vm_flags;
+	unsigned long ino = 0;
+	dev_t dev = 0;
 	int len;
 
-	flags = map->vm_flags;
-
-	str[0] = flags & VM_READ ? 'r' : '-';
-	str[1] = flags & VM_WRITE ? 'w' : '-';
-	str[2] = flags & VM_EXEC ? 'x' : '-';
-	str[3] = flags & VM_MAYSHARE ? 's' : 'p';
-	str[4] = 0;
-
-	dev = 0;
-	ino = 0;
-	if (map->vm_file != NULL) {
-		dev = map->vm_file->f_dentry->d_inode->i_dev;
-		ino = map->vm_file->f_dentry->d_inode->i_ino;
-		line = d_path(map->vm_file->f_dentry,
-			      map->vm_file->f_vfsmnt,
-			      buf, PAGE_SIZE);
-		if (IS_ERR(line))
-			return PTR_ERR(line);
-		buf[PAGE_SIZE-1] = '\n';
-		line -= MAPS_LINE_MAX;
-		if(line < buf)
-			line = buf;
-	} else
-		line = buf;
-
-	len = sprintf(line,
-		      MAPS_LINE_FORMAT,
-		      map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT,
-		      kdevname(dev), ino);
-
-	if(map->vm_file) {
-		int i;
-		for(i = len; i < MAPS_LINE_MAX; i++)
-			line[i] = ' ';
-		len = buf + PAGE_SIZE - line;
-		memmove(buf, line, len);
-	} else
-		line[len++] = '\n';
-	return len;
+	if (file) {
+		struct inode *inode = map->vm_file->f_dentry->d_inode;
+		dev = kdev_t_to_nr(inode->i_sb->s_dev);
+		ino = inode->i_ino;
+	}
+
+	seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+			map->vm_start,
+			map->vm_end,
+			flags & VM_READ ? 'r' : '-',
+			flags & VM_WRITE ? 'w' : '-',
+			flags & VM_EXEC ? 'x' : '-',
+			flags & VM_MAYSHARE ? 's' : 'p',
+			map->vm_pgoff << PAGE_SHIFT,
+			MAJOR(dev), MINOR(dev), ino, &len);
+
+	if (map->vm_file) {
+		len = 25 + sizeof(void*) * 6 - len;
+		if (len < 1)
+			len = 1;
+		seq_printf(m, "%*c", len, ' ');
+		seq_path(m, file->f_vfsmnt, file->f_dentry, "");
+	}
+	seq_putc(m, '\n');
+	return 0;
 }
 
-ssize_t proc_pid_read_maps (struct task_struct *task, struct file * file, char * buf,
-			  size_t count, loff_t *ppos)
+static void *m_start(struct seq_file *m, loff_t *pos)
 {
+	struct task_struct *task = m->private;
 	struct mm_struct *mm;
 	struct vm_area_struct * map;
-	char *tmp, *kbuf;
-	long retval;
-	int off, lineno, loff;
-
-	/* reject calls with out of range parameters immediately */
-	retval = 0;
-	if (*ppos > LONG_MAX)
-		goto out;
-	if (count == 0)
-		goto out;
-	off = (long)*ppos;
-	/*
-	 * We might sleep getting the page, so get it first.
-	 */
-	retval = -ENOMEM;
-	kbuf = (char*)__get_free_page(GFP_KERNEL);
-	if (!kbuf)
-		goto out;
-
-	tmp = (char*)__get_free_page(GFP_KERNEL);
-	if (!tmp)
-		goto out_free1;
+	loff_t l = *pos;
 
 	task_lock(task);
 	mm = task->mm;
 	if (mm)
 		atomic_inc(&mm->mm_users);
 	task_unlock(task);
-	retval = 0;
+
 	if (!mm)
-		goto out_free2;
+		return NULL;
 
 	down_read(&mm->mmap_sem);
 	map = mm->mmap;
-	lineno = 0;
-	loff = 0;
-	if (count > PAGE_SIZE)
-		count = PAGE_SIZE;
-	while (map) {
-		int len;
-		if (off > PAGE_SIZE) {
-			off -= PAGE_SIZE;
-			goto next;
-		}
-		len = proc_pid_maps_get_line(tmp, map);
-		if (len < 0)
-			goto out_unlock;
-		len -= off;
-		if (len > 0) {
-			if (retval+len > count) {
-				/* only partial line transfer possible */
-				len = count - retval;
-				/* save the offset where the next read
-				 * must start */
-				loff = len+off;
-			}
-			memcpy(kbuf+retval, tmp+off, len);
-			retval += len;
-		}
-		off = 0;
-next:
-		if (!loff)
-			lineno++;
-		if (retval >= count)
-			break;
-		if (loff) BUG();
+	while (l-- && map)
 		map = map->vm_next;
+	if (!map) {
+		up_read(&mm->mmap_sem);
+		mmput(mm);
 	}
+	return map;
+}
 
-out_unlock:
-	up_read(&mm->mmap_sem);
-	mmput(mm);
+static void m_stop(struct seq_file *m, void *v)
+{
+	struct vm_area_struct *map = v;
+	if (map) {
+		struct mm_struct *mm = map->vm_mm;
+		up_read(&mm->mmap_sem);
+		mmput(mm);
+	}
+}
 
-	if (retval > count) BUG();
-	if (copy_to_user(buf, kbuf, retval))
-		retval = -EFAULT;
-	else
-		*ppos = (lineno << PAGE_SHIFT) + loff;
-
-out_free2:
-	free_page((unsigned long)tmp);
-out_free1:
-	free_page((unsigned long)kbuf);
-out:
-	return retval;
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct vm_area_struct *map = v;
+	(*pos)++;
+	if (map->vm_next)
+		return map->vm_next;
+	m_stop(m, v);
+	return NULL;
 }
 
+struct seq_operations proc_pid_maps_op = {
+	.start	= m_start,
+	.next	= m_next,
+	.stop	= m_stop,
+	.show	= show_map
+};
+
 #ifdef CONFIG_SMP
 int proc_pid_cpu(struct task_struct *task, char * buffer)
 {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)