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)
return proc_check_root(inode);
}
extern ssize_t proc_pid_read_maps(struct task_struct *, struct file *,
char *, size_t, loff_t *);
static ssize_t pid_maps_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
extern struct seq_operations proc_pid_maps_op;
static int maps_open(struct inode *inode, struct file *file)
{
struct inode * inode = file->f_dentry->d_inode;
struct task_struct *task = proc_task(inode);
ssize_t res;
res = proc_pid_read_maps(task, file, buf, count, ppos);
return res;
int ret = seq_open(file, &proc_pid_maps_op);
if (!ret) {
struct seq_file *m = file->private_data;
m->private = task;
}
return ret;
}
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;
......
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>
char *task_mem(struct mm_struct *mm, char *buffer)
......@@ -75,167 +75,86 @@ int task_statm(struct mm_struct *mm, int *shared, int *text,
return size;
}
/*
* 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)
static int show_map(struct seq_file *m, void *v)
{
/* produce the next line */
char *line;
char str[5];
int flags;
dev_t dev;
unsigned long ino;
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) {
if (file) {
struct inode *inode = map->vm_file->f_dentry->d_inode;
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
line = d_path(map->vm_file->f_dentry,
map->vm_file->f_vfsmnt,
buf, PAGE_SIZE);
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,
MAJOR(dev), MINOR(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;
}
seq_printf(m, "%0*lx-%0*lx %c%c%c%c %0*lx %02x:%02x %lu %n",
2*sizeof(void*), map->vm_start,
2*sizeof(void*), map->vm_end,
flags & VM_READ ? 'r' : '-',
flags & VM_WRITE ? 'w' : '-',
flags & VM_EXEC ? 'x' : '-',
flags & VM_MAYSHARE ? 's' : 'p',
2*sizeof(void*), 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, " \t\n\\");
}
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 mm_struct *mm;
struct task_struct *task = m->private;
struct mm_struct *mm = get_task_mm(task);
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;
mm = get_task_mm(task);
retval = 0;
loff_t l = *pos;
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);
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;
}
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
};
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