root/fs/proc/mem.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_range
  2. mem_read
  3. mem_write
  4. mem_lseek
  5. mem_mmap

   1 /*
   2  *  linux/fs/proc/mem.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 #include <linux/types.h>
   8 #include <linux/errno.h>
   9 #include <linux/sched.h>
  10 #include <linux/kernel.h>
  11 #include <linux/mm.h>
  12 
  13 #include <asm/page.h>
  14 #include <asm/segment.h>
  15 #include <asm/io.h>
  16 #include <asm/pgtable.h>
  17 
  18 /*
  19  * mem_write isn't really a good idea right now. It needs
  20  * to check a lot more: if the process we try to write to 
  21  * dies in the middle right now, mem_write will overwrite
  22  * kernel memory.. This disables it altogether.
  23  */
  24 #define mem_write NULL
  25 
  26 static int check_range(struct task_struct * tsk, unsigned long addr, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28         struct vm_area_struct *vma;
  29         int retval;
  30 
  31         vma = find_vma(tsk, addr);
  32         if (!vma)
  33                 return -EACCES;
  34         if (vma->vm_start > addr)
  35                 return -EACCES;
  36         if (!(vma->vm_flags & VM_READ))
  37                 return -EACCES;
  38         while ((retval = vma->vm_end - addr) < count) {
  39                 struct vm_area_struct *next = vma->vm_next;
  40                 if (!next)
  41                         break;
  42                 if (vma->vm_end != next->vm_start)
  43                         break;
  44                 if (!(next->vm_flags & VM_READ))
  45                         break;
  46                 vma = next;
  47         }
  48         if (retval > count)
  49                 retval = count;
  50         return retval;
  51 }
  52 
  53 static int mem_read(struct inode * inode, struct file * file,char * buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55         pgd_t *page_dir;
  56         pmd_t *page_middle;
  57         pte_t pte;
  58         char * page;
  59         struct task_struct * tsk;
  60         unsigned long addr, pid;
  61         char *tmp;
  62         int i;
  63 
  64         if (count < 0)
  65                 return -EINVAL;
  66         pid = inode->i_ino;
  67         pid >>= 16;
  68         tsk = NULL;
  69         for (i = 1 ; i < NR_TASKS ; i++)
  70                 if (task[i] && task[i]->pid == pid) {
  71                         tsk = task[i];
  72                         break;
  73                 }
  74         if (!tsk)
  75                 return -EACCES;
  76         addr = file->f_pos;
  77         count = check_range(tsk, addr, count);
  78         if (count < 0)
  79                 return count;
  80         tmp = buf;
  81         while (count > 0) {
  82                 if (current->signal & ~current->blocked)
  83                         break;
  84                 page_dir = pgd_offset(tsk->mm,addr);
  85                 if (pgd_none(*page_dir))
  86                         break;
  87                 if (pgd_bad(*page_dir)) {
  88                         printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
  89                         pgd_clear(page_dir);
  90                         break;
  91                 }
  92                 page_middle = pmd_offset(page_dir,addr);
  93                 if (pmd_none(*page_middle))
  94                         break;
  95                 if (pmd_bad(*page_middle)) {
  96                         printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
  97                         pmd_clear(page_middle);
  98                         break;
  99                 }
 100                 pte = *pte_offset(page_middle,addr);
 101                 if (!pte_present(pte))
 102                         break;
 103                 page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
 104                 i = PAGE_SIZE-(addr & ~PAGE_MASK);
 105                 if (i > count)
 106                         i = count;
 107                 memcpy_tofs(tmp, page, i);
 108                 addr += i;
 109                 tmp += i;
 110                 count -= i;
 111         }
 112         file->f_pos = addr;
 113         return tmp-buf;
 114 }
 115 
 116 #ifndef mem_write
 117 
 118 static int mem_write(struct inode * inode, struct file * file,char * buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120         pgd_t *page_dir;
 121         pmd_t *page_middle;
 122         pte_t pte;
 123         char * page;
 124         struct task_struct * tsk;
 125         unsigned long addr, pid;
 126         char *tmp;
 127         int i;
 128 
 129         if (count < 0)
 130                 return -EINVAL;
 131         addr = file->f_pos;
 132         pid = inode->i_ino;
 133         pid >>= 16;
 134         tsk = NULL;
 135         for (i = 1 ; i < NR_TASKS ; i++)
 136                 if (task[i] && task[i]->pid == pid) {
 137                         tsk = task[i];
 138                         break;
 139                 }
 140         if (!tsk)
 141                 return -EACCES;
 142         tmp = buf;
 143         while (count > 0) {
 144                 if (current->signal & ~current->blocked)
 145                         break;
 146                 page_dir = pgd_offset(tsk,addr);
 147                 if (pgd_none(*page_dir))
 148                         break;
 149                 if (pgd_bad(*page_dir)) {
 150                         printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));
 151                         pgd_clear(page_dir);
 152                         break;
 153                 }
 154                 page_middle = pmd_offset(page_dir,addr);
 155                 if (pmd_none(*page_middle))
 156                         break;
 157                 if (pmd_bad(*page_middle)) {
 158                         printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));
 159                         pmd_clear(page_middle);
 160                         break;
 161                 }
 162                 pte = *pte_offset(page_middle,addr);
 163                 if (!pte_present(pte))
 164                         break;
 165                 if (!pte_write(pte))
 166                         break;
 167                 page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);
 168                 i = PAGE_SIZE-(addr & ~PAGE_MASK);
 169                 if (i > count)
 170                         i = count;
 171                 memcpy_fromfs(page, tmp, i);
 172                 addr += i;
 173                 tmp += i;
 174                 count -= i;
 175         }
 176         file->f_pos = addr;
 177         if (tmp != buf)
 178                 return tmp-buf;
 179         if (current->signal & ~current->blocked)
 180                 return -ERESTARTSYS;
 181         return 0;
 182 }
 183 
 184 #endif
 185 
 186 static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
     /* [previous][next][first][last][top][bottom][index][help] */
 187 {
 188         switch (orig) {
 189                 case 0:
 190                         file->f_pos = offset;
 191                         return file->f_pos;
 192                 case 1:
 193                         file->f_pos += offset;
 194                         return file->f_pos;
 195                 default:
 196                         return -EINVAL;
 197         }
 198 }
 199 
 200 /*
 201  * This isn't really reliable by any means..
 202  */
 203 int mem_mmap(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 204              struct vm_area_struct * vma)
 205 {
 206         struct task_struct *tsk;
 207         pgd_t *src_dir, *dest_dir;
 208         pmd_t *src_middle, *dest_middle;
 209         pte_t *src_table, *dest_table;
 210         unsigned long stmp, dtmp;
 211         struct vm_area_struct *src_vma = NULL;
 212         int i;
 213 
 214         /* Get the source's task information */
 215 
 216         tsk = NULL;
 217         for (i = 1 ; i < NR_TASKS ; i++)
 218                 if (task[i] && task[i]->pid == (inode->i_ino >> 16)) {
 219                         tsk = task[i];
 220                         break;
 221                 }
 222 
 223         if (!tsk)
 224                 return -EACCES;
 225 
 226         /* Ensure that we have a valid source area.  (Has to be mmap'ed and
 227          have valid page information.)  We can't map shared memory at the
 228          moment because working out the vm_area_struct & nattach stuff isn't
 229          worth it. */
 230 
 231         src_vma = tsk->mm->mmap;
 232         stmp = vma->vm_offset;
 233         while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {
 234                 while (src_vma && stmp > src_vma->vm_end)
 235                         src_vma = src_vma->vm_next;
 236                 if (!src_vma || (src_vma->vm_flags & VM_SHM))
 237                         return -EINVAL;
 238 
 239                 src_dir = pgd_offset(tsk->mm, stmp);
 240                 if (pgd_none(*src_dir))
 241                         return -EINVAL;
 242                 if (pgd_bad(*src_dir)) {
 243                         printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir));
 244                         return -EINVAL;
 245                 }
 246                 src_middle = pmd_offset(src_dir, stmp);
 247                 if (pmd_none(*src_middle))
 248                         return -EINVAL;
 249                 if (pmd_bad(*src_middle)) {
 250                         printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle));
 251                         return -EINVAL;
 252                 }
 253                 src_table = pte_offset(src_middle, stmp);
 254                 if (pte_none(*src_table))
 255                         return -EINVAL;
 256 
 257                 if (stmp < src_vma->vm_start) {
 258                         if (!(src_vma->vm_flags & VM_GROWSDOWN))
 259                                 return -EINVAL;
 260                         if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)
 261                                 return -EINVAL;
 262                 }
 263                 stmp += PAGE_SIZE;
 264         }
 265 
 266         src_vma = tsk->mm->mmap;
 267         stmp    = vma->vm_offset;
 268         dtmp    = vma->vm_start;
 269 
 270         while (dtmp < vma->vm_end) {
 271                 while (src_vma && stmp > src_vma->vm_end)
 272                         src_vma = src_vma->vm_next;
 273 
 274                 src_dir = pgd_offset(tsk->mm, stmp);
 275                 src_middle = pmd_offset(src_dir, stmp);
 276                 src_table = pte_offset(src_middle, stmp);
 277 
 278                 dest_dir = pgd_offset(current->mm, dtmp);
 279                 dest_middle = pmd_alloc(dest_dir, dtmp);
 280                 if (!dest_middle)
 281                         return -ENOMEM;
 282                 dest_table = pte_alloc(dest_middle, dtmp);
 283                 if (!dest_table)
 284                         return -ENOMEM;
 285 
 286                 if (!pte_present(*src_table))
 287                         do_no_page(tsk, src_vma, stmp, 1);
 288 
 289                 if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))
 290                         do_wp_page(tsk, src_vma, stmp, 1);
 291 
 292                 set_pte(src_table, pte_mkdirty(*src_table));
 293                 set_pte(dest_table, *src_table);
 294                 mem_map[MAP_NR(pte_page(*src_table))].count++;
 295 
 296                 stmp += PAGE_SIZE;
 297                 dtmp += PAGE_SIZE;
 298         }
 299 
 300         invalidate_range(vma->vm_mm, vma->vm_start, vma->vm_end);
 301         invalidate_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
 302         return 0;
 303 }
 304 
 305 static struct file_operations proc_mem_operations = {
 306         mem_lseek,
 307         mem_read,
 308         mem_write,
 309         NULL,           /* mem_readdir */
 310         NULL,           /* mem_select */
 311         NULL,           /* mem_ioctl */
 312         mem_mmap,       /* mmap */
 313         NULL,           /* no special open code */
 314         NULL,           /* no special release code */
 315         NULL            /* can't fsync */
 316 };
 317 
 318 struct inode_operations proc_mem_inode_operations = {
 319         &proc_mem_operations,   /* default base directory file-ops */
 320         NULL,                   /* create */
 321         NULL,                   /* lookup */
 322         NULL,                   /* link */
 323         NULL,                   /* unlink */
 324         NULL,                   /* symlink */
 325         NULL,                   /* mkdir */
 326         NULL,                   /* rmdir */
 327         NULL,                   /* mknod */
 328         NULL,                   /* rename */
 329         NULL,                   /* readlink */
 330         NULL,                   /* follow_link */
 331         NULL,                   /* readpage */
 332         NULL,                   /* writepage */
 333         NULL,                   /* bmap */
 334         NULL,                   /* truncate */
 335         NULL                    /* permission */
 336 };

/* [previous][next][first][last][top][bottom][index][help] */