📄 shmem.c
字号:
return shmem_mknod(dir, dentry, mode | S_IFREG, 0);}/* * Link a file.. */static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry){ struct inode *inode = old_dentry->d_inode; if (S_ISDIR(inode->i_mode)) return -EPERM; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_nlink++; atomic_inc(&inode->i_count); /* New dentry reference */ dget(dentry); /* Extra pinning count for the created dentry */ d_instantiate(dentry, inode); return 0;}static inline int shmem_positive(struct dentry *dentry){ return dentry->d_inode && !d_unhashed(dentry);}/* * Check that a directory is empty (this works * for regular files too, they'll just always be * considered empty..). * * Note that an empty directory can still have * children, they just all have to be negative.. */static int shmem_empty(struct dentry *dentry){ struct list_head *list; spin_lock(&dcache_lock); list = dentry->d_subdirs.next; while (list != &dentry->d_subdirs) { struct dentry *de = list_entry(list, struct dentry, d_child); if (shmem_positive(de)) { spin_unlock(&dcache_lock); return 0; } list = list->next; } spin_unlock(&dcache_lock); return 1;}static int shmem_unlink(struct inode * dir, struct dentry *dentry){ struct inode *inode = dentry->d_inode; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; inode->i_nlink--; dput(dentry); /* Undo the count from "create" - this does all the work */ return 0;}static int shmem_rmdir(struct inode * dir, struct dentry *dentry){ if (!shmem_empty(dentry)) return -ENOTEMPTY; dir->i_nlink--; return shmem_unlink(dir, dentry);}/* * The VFS layer already does all the dentry stuff for rename, * we just have to decrement the usage count for the target if * it exists so that the VFS layer correctly free's it when it * gets overwritten. */static int shmem_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry){ int error = -ENOTEMPTY; if (shmem_empty(new_dentry)) { struct inode *inode = new_dentry->d_inode; if (inode) { inode->i_ctime = CURRENT_TIME; inode->i_nlink--; dput(new_dentry); } error = 0; old_dentry->d_inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; } return error;}static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname){ int error; int len; struct inode *inode; struct page *page; char *kaddr; struct shmem_inode_info * info; error = shmem_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0); if (error) return error; len = strlen(symname) + 1; if (len > PAGE_CACHE_SIZE) return -ENAMETOOLONG; inode = dentry->d_inode; info = SHMEM_I(inode); inode->i_size = len-1; if (len <= sizeof(struct shmem_inode_info)) { /* do it inline */ memcpy(info, symname, len); inode->i_op = &shmem_symlink_inline_operations; } else { spin_lock (&shmem_ilock); list_add (&info->list, &shmem_inodes); spin_unlock (&shmem_ilock); down(&info->sem); page = shmem_getpage_locked(info, inode, 0); if (IS_ERR(page)) { up(&info->sem); return PTR_ERR(page); } kaddr = kmap(page); memcpy(kaddr, symname, len); kunmap(page); SetPageDirty(page); UnlockPage(page); page_cache_release(page); up(&info->sem); inode->i_op = &shmem_symlink_inode_operations; } dir->i_ctime = dir->i_mtime = CURRENT_TIME; return 0;}static int shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen){ return vfs_readlink(dentry,buffer,buflen, (const char *)SHMEM_I(dentry->d_inode));}static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd){ return vfs_follow_link(nd, (const char *)SHMEM_I(dentry->d_inode));}static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen){ struct page * page; int res = shmem_getpage(dentry->d_inode, 0, &page); if (res) return res; res = vfs_readlink(dentry,buffer,buflen, kmap(page)); kunmap(page); page_cache_release(page); return res;}static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd){ struct page * page; int res = shmem_getpage(dentry->d_inode, 0, &page); if (res) return res; res = vfs_follow_link(nd, kmap(page)); kunmap(page); page_cache_release(page); return res;}static struct inode_operations shmem_symlink_inline_operations = { readlink: shmem_readlink_inline, follow_link: shmem_follow_link_inline,};static struct inode_operations shmem_symlink_inode_operations = { truncate: shmem_truncate, readlink: shmem_readlink, follow_link: shmem_follow_link,};static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long * blocks, unsigned long *inodes){ char *this_char, *value, *rest; this_char = NULL; if ( options ) this_char = strtok(options,","); for ( ; this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { *value++ = 0; } else { printk(KERN_ERR "tmpfs: No value for mount option '%s'\n", this_char); return 1; } if (!strcmp(this_char,"size")) { unsigned long long size; size = memparse(value,&rest); if (*rest) goto bad_val; *blocks = size >> PAGE_CACHE_SHIFT; } else if (!strcmp(this_char,"nr_blocks")) { *blocks = memparse(value,&rest); if (*rest) goto bad_val; } else if (!strcmp(this_char,"nr_inodes")) { *inodes = memparse(value,&rest); if (*rest) goto bad_val; } else if (!strcmp(this_char,"mode")) { if (!mode) continue; *mode = simple_strtoul(value,&rest,8); if (*rest) goto bad_val; } else if (!strcmp(this_char,"uid")) { if (!uid) continue; *uid = simple_strtoul(value,&rest,0); if (*rest) goto bad_val; } else if (!strcmp(this_char,"gid")) { if (!gid) continue; *gid = simple_strtoul(value,&rest,0); if (*rest) goto bad_val; } else { printk(KERN_ERR "tmpfs: Bad mount option %s\n", this_char); return 1; } } return 0;bad_val: printk(KERN_ERR "tmpfs: Bad value '%s' for mount option '%s'\n", value, this_char); return 1;}static int shmem_remount_fs (struct super_block *sb, int *flags, char *data){ struct shmem_sb_info *sbinfo = &sb->u.shmem_sb; unsigned long max_blocks = sbinfo->max_blocks; unsigned long max_inodes = sbinfo->max_inodes; if (shmem_parse_options (data, NULL, NULL, NULL, &max_blocks, &max_inodes)) return -EINVAL; return shmem_set_size(sbinfo, max_blocks, max_inodes);}int shmem_sync_file(struct file * file, struct dentry *dentry, int datasync){ return 0;}#endifstatic struct super_block *shmem_read_super(struct super_block * sb, void * data, int silent){ struct inode * inode; struct dentry * root; unsigned long blocks, inodes; int mode = S_IRWXUGO | S_ISVTX; uid_t uid = current->fsuid; gid_t gid = current->fsgid; struct shmem_sb_info *sbinfo = SHMEM_SB(sb); struct sysinfo si; /* * Per default we only allow half of the physical ram per * tmpfs instance */ si_meminfo(&si); blocks = inodes = si.totalram / 2;#ifdef CONFIG_TMPFS if (shmem_parse_options (data, &mode, &uid, &gid, &blocks, &inodes)) return NULL;#endif spin_lock_init (&sbinfo->stat_lock); sbinfo->max_blocks = blocks; sbinfo->free_blocks = blocks; sbinfo->max_inodes = inodes; sbinfo->free_inodes = inodes; sb->s_maxbytes = (unsigned long long) SHMEM_MAX_BLOCKS << PAGE_CACHE_SHIFT; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = TMPFS_MAGIC; sb->s_op = &shmem_ops; inode = shmem_get_inode(sb, S_IFDIR | mode, 0); if (!inode) return NULL; inode->i_uid = uid; inode->i_gid = gid; root = d_alloc_root(inode); if (!root) { iput(inode); return NULL; } sb->s_root = root; return sb;}static struct address_space_operations shmem_aops = { writepage: shmem_writepage,};static struct file_operations shmem_file_operations = { mmap: shmem_mmap,#ifdef CONFIG_TMPFS read: shmem_file_read, write: shmem_file_write, fsync: shmem_sync_file,#endif};static struct inode_operations shmem_inode_operations = { truncate: shmem_truncate,};static struct file_operations shmem_dir_operations = { read: generic_read_dir, readdir: dcache_readdir,#ifdef CONFIG_TMPFS fsync: shmem_sync_file,#endif};static struct inode_operations shmem_dir_inode_operations = {#ifdef CONFIG_TMPFS create: shmem_create, lookup: shmem_lookup, link: shmem_link, unlink: shmem_unlink, symlink: shmem_symlink, mkdir: shmem_mkdir, rmdir: shmem_rmdir, mknod: shmem_mknod, rename: shmem_rename,#endif};static struct super_operations shmem_ops = {#ifdef CONFIG_TMPFS statfs: shmem_statfs, remount_fs: shmem_remount_fs,#endif delete_inode: shmem_delete_inode, put_inode: force_delete, };static struct vm_operations_struct shmem_vm_ops = { nopage: shmem_nopage,};#ifdef CONFIG_TMPFS/* type "shm" will be tagged obsolete in 2.5 */static DECLARE_FSTYPE(shmem_fs_type, "shm", shmem_read_super, FS_LITTER);static DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER);#elsestatic DECLARE_FSTYPE(tmpfs_fs_type, "tmpfs", shmem_read_super, FS_LITTER|FS_NOMOUNT);#endifstatic struct vfsmount *shm_mnt;static int __init init_shmem_fs(void){ int error; struct vfsmount * res; if ((error = register_filesystem(&tmpfs_fs_type))) { printk (KERN_ERR "Could not register tmpfs\n"); return error; }#ifdef CONFIG_TMPFS if ((error = register_filesystem(&shmem_fs_type))) { printk (KERN_ERR "Could not register shm fs\n"); return error; } devfs_mk_dir (NULL, "shm", NULL);#endif res = kern_mount(&tmpfs_fs_type); if (IS_ERR (res)) { printk (KERN_ERR "could not kern_mount tmpfs\n"); unregister_filesystem(&tmpfs_fs_type); return PTR_ERR(res); } shm_mnt = res; /* The internal instance should not do size checking */ if ((error = shmem_set_size(SHMEM_SB(res->mnt_sb), ULONG_MAX, ULONG_MAX))) printk (KERN_ERR "could not set limits on internal tmpfs\n"); return 0;}static void __exit exit_shmem_fs(void){#ifdef CONFIG_TMPFS unregister_filesystem(&shmem_fs_type);#endif unregister_filesystem(&tmpfs_fs_type); mntput(shm_mnt);}module_init(init_shmem_fs)module_exit(exit_shmem_fs)/* * shmem_file_setup - get an unlinked file living in shmem fs * * @name: name for dentry (to be seen in /proc/<pid>/maps * @size: size to be set for the file * */struct file *shmem_file_setup(char * name, loff_t size){ int error; struct file *file; struct inode * inode; struct dentry *dentry, *root; struct qstr this; int vm_enough_memory(long pages); if (size > (unsigned long long) SHMEM_MAX_BLOCKS << PAGE_CACHE_SHIFT) return ERR_PTR(-EINVAL); if (!vm_enough_memory((size) >> PAGE_CACHE_SHIFT)) return ERR_PTR(-ENOMEM); this.name = name; this.len = strlen(name); this.hash = 0; /* will go */ root = shm_mnt->mnt_root; dentry = d_alloc(root, &this); if (!dentry) return ERR_PTR(-ENOMEM); error = -ENFILE; file = get_empty_filp(); if (!file) goto put_dentry; error = -ENOSPC; inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0); if (!inode) goto close_file; d_instantiate(dentry, inode); dentry->d_inode->i_size = size; shmem_truncate(inode); file->f_vfsmnt = mntget(shm_mnt); file->f_dentry = dentry; file->f_op = &shmem_file_operations; file->f_mode = FMODE_WRITE | FMODE_READ; inode->i_nlink = 0; /* It is unlinked */ return(file);close_file: put_filp(file);put_dentry: dput (dentry); return ERR_PTR(error); }/* * shmem_zero_setup - setup a shared anonymous mapping * * @vma: the vma to be mmapped is prepared by do_mmap_pgoff */int shmem_zero_setup(struct vm_area_struct *vma){ struct file *file; loff_t size = vma->vm_end - vma->vm_start; file = shmem_file_setup("dev/zero", size); if (IS_ERR(file)) return PTR_ERR(file); if (vma->vm_file) fput (vma->vm_file); vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; return 0;}EXPORT_SYMBOL(shmem_file_setup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -