Commit 2d9318a1 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] seq_path() for /proc/pid/maps

This converts /proc/pid/maps to use of seq_file, cleans the issues with
d_path() overflows as a side effect.

It's incremental to seq_path() patch.
parent 32ccd2b6
...@@ -322,21 +322,23 @@ static int proc_permission(struct inode *inode, int mask) ...@@ -322,21 +322,23 @@ static int proc_permission(struct inode *inode, int mask)
return proc_check_root(inode); return proc_check_root(inode);
} }
extern ssize_t proc_pid_read_maps(struct task_struct *, struct file *, extern struct seq_operations proc_pid_maps_op;
char *, size_t, loff_t *); static int maps_open(struct inode *inode, struct file *file)
static ssize_t pid_maps_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{ {
struct inode * inode = file->f_dentry->d_inode;
struct task_struct *task = proc_task(inode); struct task_struct *task = proc_task(inode);
ssize_t res; int ret = seq_open(file, &proc_pid_maps_op);
if (!ret) {
res = proc_pid_read_maps(task, file, buf, count, ppos); struct seq_file *m = file->private_data;
return res; m->private = task;
}
return ret;
} }
static struct file_operations proc_maps_operations = { static struct file_operations proc_maps_operations = {
.read = pid_maps_read, .open = maps_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
}; };
extern struct seq_operations mounts_op; extern struct seq_operations mounts_op;
......
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
char *task_mem(struct mm_struct *mm, char *buffer) char *task_mem(struct mm_struct *mm, char *buffer)
...@@ -75,167 +75,86 @@ int task_statm(struct mm_struct *mm, int *shared, int *text, ...@@ -75,167 +75,86 @@ int task_statm(struct mm_struct *mm, int *shared, int *text,
return size; return size;
} }
/* static int show_map(struct seq_file *m, void *v)
* 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 %02x:%02x %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 %02x:%02x %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 */ struct vm_area_struct *map = v;
char *line; struct file *file = map->vm_file;
char str[5]; int flags = map->vm_flags;
int flags; unsigned long ino = 0;
dev_t dev; dev_t dev = 0;
unsigned long ino;
int len; int len;
flags = map->vm_flags; if (file) {
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) {
struct inode *inode = map->vm_file->f_dentry->d_inode; struct inode *inode = map->vm_file->f_dentry->d_inode;
dev = inode->i_sb->s_dev; dev = inode->i_sb->s_dev;
ino = inode->i_ino; ino = inode->i_ino;
line = d_path(map->vm_file->f_dentry, }
map->vm_file->f_vfsmnt,
buf, PAGE_SIZE); seq_printf(m, "%0*lx-%0*lx %c%c%c%c %0*lx %02x:%02x %lu %n",
buf[PAGE_SIZE-1] = '\n'; 2*sizeof(void*), map->vm_start,
line -= MAPS_LINE_MAX; 2*sizeof(void*), map->vm_end,
if(line < buf) flags & VM_READ ? 'r' : '-',
line = buf; flags & VM_WRITE ? 'w' : '-',
} else flags & VM_EXEC ? 'x' : '-',
line = buf; flags & VM_MAYSHARE ? 's' : 'p',
2*sizeof(void*), map->vm_pgoff << PAGE_SHIFT,
len = sprintf(line, MAJOR(dev), MINOR(dev), ino, &len);
MAPS_LINE_FORMAT,
map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, if (map->vm_file) {
MAJOR(dev), MINOR(dev), ino); len = 25 + sizeof(void*) * 6 - len;
if (len < 1)
if(map->vm_file) { len = 1;
int i; seq_printf(m, "%*c", len, ' ');
for(i = len; i < MAPS_LINE_MAX; i++) seq_path(m, file->f_vfsmnt, file->f_dentry, " \t\n\\");
line[i] = ' '; }
len = buf + PAGE_SIZE - line; seq_putc(m, '\n');
memmove(buf, line, len); return 0;
} else
line[len++] = '\n';
return len;
} }
ssize_t proc_pid_read_maps(struct task_struct *task, struct file *file, static void *m_start(struct seq_file *m, loff_t *pos)
char *buf, size_t count, loff_t *ppos)
{ {
struct mm_struct *mm; struct task_struct *task = m->private;
struct mm_struct *mm = get_task_mm(task);
struct vm_area_struct * map; struct vm_area_struct * map;
char *tmp, *kbuf; loff_t l = *pos;
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;
mm = get_task_mm(task);
retval = 0;
if (!mm) if (!mm)
goto out_free2; return NULL;
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
map = mm->mmap; map = mm->mmap;
lineno = 0; while (l-- && map)
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);
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();
map = map->vm_next; map = map->vm_next;
if (!map) {
up_read(&mm->mmap_sem);
mmput(mm);
}
return map;
}
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);
} }
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
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment