mem.c

来自「elinux jffs初始版本 具体了解JFFS的文件系统!」· C语言 代码 · 共 502 行

C
502
字号
/* *  linux/fs/proc/mem.c * *  Copyright (C) 1991, 1992  Linus Torvalds */#include <linux/types.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/mm.h>#include <asm/page.h>#include <asm/segment.h>#include <asm/io.h>#include <asm/pgtable.h>#ifndef NO_MM/* * mem_write isn't really a good idea right now. It needs * to check a lot more: if the process we try to write to  * dies in the middle right now, mem_write will overwrite * kernel memory.. This disables it altogether. */#define mem_write NULLstatic int check_range(struct mm_struct * mm, unsigned long addr, int count){	struct vm_area_struct *vma;	int retval;	vma = find_vma(mm, addr);	if (!vma)		return -EACCES;	if (vma->vm_start > addr)		return -EACCES;	if (!(vma->vm_flags & VM_READ))		return -EACCES;	while ((retval = vma->vm_end - addr) < count) {		struct vm_area_struct *next = vma->vm_next;		if (!next)			break;		if (vma->vm_end != next->vm_start)			break;		if (!(next->vm_flags & VM_READ))			break;		vma = next;	}	if (retval > count)		retval = count;	return retval;}static struct task_struct * get_task(int pid){	struct task_struct * tsk = current;	if (pid != tsk->pid) {		int i;		tsk = NULL;		for (i = 1 ; i < NR_TASKS ; i++)			if (task[i] && task[i]->pid == pid) {				tsk = task[i];				break;			}		/*		 * allow accesses only under the same circumstances		 * that we would allow ptrace to work		 */		if (tsk) {			if (!(tsk->flags & PF_PTRACED)			    || tsk->state != TASK_STOPPED			    || tsk->p_pptr != current)				tsk = NULL;		}	}	return tsk;}static int mem_read(struct inode * inode, struct file * file,char * buf, int count){	pgd_t *page_dir;	pmd_t *page_middle;	pte_t pte;	char * page;	struct task_struct * tsk;	unsigned long addr;	char *tmp;	int i;	if (count < 0)		return -EINVAL;	tsk = get_task(inode->i_ino >> 16);	if (!tsk)		return -ESRCH;	addr = file->f_pos;	count = check_range(tsk->mm, addr, count);	if (count < 0)		return count;	tmp = buf;	while (count > 0) {		if (current->signal & ~current->blocked)			break;		page_dir = pgd_offset(tsk->mm,addr);		if (pgd_none(*page_dir))			break;		if (pgd_bad(*page_dir)) {			printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));			pgd_clear(page_dir);			break;		}		page_middle = pmd_offset(page_dir,addr);		if (pmd_none(*page_middle))			break;		if (pmd_bad(*page_middle)) {			printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));			pmd_clear(page_middle);			break;		}		pte = *pte_offset(page_middle,addr);		if (!pte_present(pte))			break;		page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);		i = PAGE_SIZE-(addr & ~PAGE_MASK);		if (i > count)			i = count;		memcpy_tofs(tmp, page, i);		addr += i;		tmp += i;		count -= i;	}	file->f_pos = addr;	return tmp-buf;}#ifndef mem_writestatic int mem_write(struct inode * inode, struct file * file,char * buf, int count){	pgd_t *page_dir;	pmd_t *page_middle;	pte_t pte;	char * page;	struct task_struct * tsk;	unsigned long addr;	char *tmp;	int i;	if (count < 0)		return -EINVAL;	addr = file->f_pos;	tsk = get_task(inode->i_ino >> 16);	if (!tsk)		return -ESRCH;	tmp = buf;	while (count > 0) {		if (current->signal & ~current->blocked)			break;		page_dir = pgd_offset(tsk,addr);		if (pgd_none(*page_dir))			break;		if (pgd_bad(*page_dir)) {			printk("Bad page dir entry %08lx\n", pgd_val(*page_dir));			pgd_clear(page_dir);			break;		}		page_middle = pmd_offset(page_dir,addr);		if (pmd_none(*page_middle))			break;		if (pmd_bad(*page_middle)) {			printk("Bad page middle entry %08lx\n", pmd_val(*page_middle));			pmd_clear(page_middle);			break;		}		pte = *pte_offset(page_middle,addr);		if (!pte_present(pte))			break;		if (!pte_write(pte))			break;		page = (char *) pte_page(pte) + (addr & ~PAGE_MASK);		i = PAGE_SIZE-(addr & ~PAGE_MASK);		if (i > count)			i = count;		memcpy_fromfs(page, tmp, i);		addr += i;		tmp += i;		count -= i;	}	file->f_pos = addr;	if (tmp != buf)		return tmp-buf;	if (current->signal & ~current->blocked)		return -ERESTARTSYS;	return 0;}#endifstatic int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig){	switch (orig) {		case 0:			file->f_pos = offset;			return file->f_pos;		case 1:			file->f_pos += offset;			return file->f_pos;		default:			return -EINVAL;	}}/* * This isn't really reliable by any means.. */int mem_mmap(struct inode * inode, struct file * file,	     struct vm_area_struct * vma){	struct task_struct *tsk;	pgd_t *src_dir, *dest_dir;	pmd_t *src_middle, *dest_middle;	pte_t *src_table, *dest_table;	unsigned long stmp, dtmp;	struct vm_area_struct *src_vma = NULL;	/* Get the source's task information */	tsk = get_task(inode->i_ino >> 16);	if (!tsk)		return -ESRCH;	/* Ensure that we have a valid source area.  (Has to be mmap'ed and	 have valid page information.)  We can't map shared memory at the	 moment because working out the vm_area_struct & nattach stuff isn't	 worth it. */	src_vma = tsk->mm->mmap;	stmp = vma->vm_offset;	while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) {		while (src_vma && stmp > src_vma->vm_end)			src_vma = src_vma->vm_next;		if (!src_vma || (src_vma->vm_flags & VM_SHM))			return -EINVAL;		src_dir = pgd_offset(tsk->mm, stmp);		if (pgd_none(*src_dir))			return -EINVAL;		if (pgd_bad(*src_dir)) {			printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir));			return -EINVAL;		}		src_middle = pmd_offset(src_dir, stmp);		if (pmd_none(*src_middle))			return -EINVAL;		if (pmd_bad(*src_middle)) {			printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle));			return -EINVAL;		}		src_table = pte_offset(src_middle, stmp);		if (pte_none(*src_table))			return -EINVAL;		if (stmp < src_vma->vm_start) {			if (!(src_vma->vm_flags & VM_GROWSDOWN))				return -EINVAL;			if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur)				return -EINVAL;		}		stmp += PAGE_SIZE;	}	src_vma = tsk->mm->mmap;	stmp    = vma->vm_offset;	dtmp    = vma->vm_start;	flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end);	flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);	while (dtmp < vma->vm_end) {		while (src_vma && stmp > src_vma->vm_end)			src_vma = src_vma->vm_next;		src_dir = pgd_offset(tsk->mm, stmp);		src_middle = pmd_offset(src_dir, stmp);		src_table = pte_offset(src_middle, stmp);		dest_dir = pgd_offset(current->mm, dtmp);		dest_middle = pmd_alloc(dest_dir, dtmp);		if (!dest_middle)			return -ENOMEM;		dest_table = pte_alloc(dest_middle, dtmp);		if (!dest_table)			return -ENOMEM;		if (!pte_present(*src_table))			do_no_page(tsk, src_vma, stmp, 1);		if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table))			do_wp_page(tsk, src_vma, stmp, 1);		set_pte(src_table, pte_mkdirty(*src_table));		set_pte(dest_table, *src_table);		mem_map[MAP_NR(pte_page(*src_table))].count++;		stmp += PAGE_SIZE;		dtmp += PAGE_SIZE;	}	flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end);	flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);	return 0;}static struct file_operations proc_mem_operations = {	mem_lseek,	mem_read,	mem_write,	NULL,		/* mem_readdir */	NULL,		/* mem_select */	NULL,		/* mem_ioctl */	mem_mmap,	/* mmap */	NULL,		/* no special open code */	NULL,		/* no special release code */	NULL		/* can't fsync */};struct inode_operations proc_mem_inode_operations = {	&proc_mem_operations,	/* default base directory file-ops */	NULL,			/* create */	NULL,			/* lookup */	NULL,			/* link */	NULL,			/* unlink */	NULL,			/* symlink */	NULL,			/* mkdir */	NULL,			/* rmdir */	NULL,			/* mknod */	NULL,			/* rename */	NULL,			/* readlink */	NULL,			/* follow_link */	NULL,			/* readpage */	NULL,			/* writepage */	NULL,			/* bmap */	NULL,			/* truncate */	NULL			/* permission */};#else /*NO_MM*//* * mem_write isn't really a good idea right now. It needs * to check a lot more: if the process we try to write to  * dies in the middle right now, mem_write will overwrite * kernel memory.. This disables it altogether. */#define mem_write NULLstatic struct task_struct * get_task(int pid){	struct task_struct * tsk = current;	if (pid != tsk->pid) {		int i;		tsk = NULL;		for (i = 1 ; i < NR_TASKS ; i++)			if (task[i] && task[i]->pid == pid) {				tsk = task[i];				break;			}		/*		 * allow accesses only under the same circumstances		 * that we would allow ptrace to work		 */		if (tsk) {			if (!(tsk->flags & PF_PTRACED)			    || tsk->state != TASK_STOPPED			    || tsk->p_pptr != current)				tsk = NULL;		}	}	return tsk;}static int mem_read(struct inode * inode, struct file * file,char * buf, int count){	struct task_struct * tsk;	unsigned long addr;	char *tmp;	int i;	if (count < 0)		return -EINVAL;	tsk = get_task(inode->i_ino >> 16);	if (!tsk)		return -ESRCH;	addr = file->f_pos;	tmp = buf;	while (count > 0) {		if (current->signal & ~current->blocked)			break;		i = count;		memcpy_tofs(tmp, (void*)addr, i);		addr += i;		tmp += i;		count -= i;	}	file->f_pos = addr;	return tmp-buf;}#ifndef mem_writestatic int mem_write(struct inode * inode, struct file * file,char * buf, int count){	struct task_struct * tsk;	unsigned long addr;	char *tmp;	int i;	if (count < 0)		return -EINVAL;	addr = file->f_pos;	tsk = get_task(inode->i_ino >> 16);	if (!tsk)		return -ESRCH;	tmp = buf;	while (count > 0) {		if (current->signal & ~current->blocked)			break;		i = count;		memcpy_fromfs((void*)addr, tmp, i);		addr += i;		tmp += i;		count -= i;	}	file->f_pos = addr;	if (tmp != buf)		return tmp-buf;	if (current->signal & ~current->blocked)		return -ERESTARTSYS;	return 0;}#endifint mem_mmap(struct inode * inode, struct file * file,	     struct vm_area_struct * vma){	vma->vm_start = vma->vm_offset;	return 0;}static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig){	switch (orig) {		case 0:			file->f_pos = offset;			return file->f_pos;		case 1:			file->f_pos += offset;			return file->f_pos;		default:			return -EINVAL;	}}static struct file_operations proc_mem_operations = {	mem_lseek,	mem_read,	mem_write,	NULL,		/* mem_readdir */	NULL,		/* mem_select */	NULL,		/* mem_ioctl */	mem_mmap,	/* mmap */	NULL,		/* no special open code */	NULL,		/* no special release code */	NULL		/* can't fsync */};struct inode_operations proc_mem_inode_operations = {	&proc_mem_operations,	/* default base directory file-ops */	NULL,			/* create */	NULL,			/* lookup */	NULL,			/* link */	NULL,			/* unlink */	NULL,			/* symlink */	NULL,			/* mkdir */	NULL,			/* rmdir */	NULL,			/* mknod */	NULL,			/* rename */	NULL,			/* readlink */	NULL,			/* follow_link */	NULL,			/* readpage */	NULL,			/* writepage */	NULL,			/* bmap */	NULL,			/* truncate */	NULL			/* permission */};#endif /*NO_MM*/

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?