📄 hostfs_kern.c
字号:
/* * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL * * Ported the filesystem routines to 2.5. * 2003-02-10 Petr Baudis <pasky@ucw.cz> */#include <linux/fs.h>#include <linux/module.h>#include <linux/mm.h>#include <linux/pagemap.h>#include <linux/statfs.h>#include "hostfs.h"#include "init.h"#include "kern.h"struct hostfs_inode_info { char *host_filename; int fd; int mode; struct inode vfs_inode;};static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode){ return list_entry(inode, struct hostfs_inode_info, vfs_inode);}#define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_path.dentry->d_inode)int hostfs_d_delete(struct dentry *dentry){ return 1;}struct dentry_operations hostfs_dentry_ops = { .d_delete = hostfs_d_delete,};/* Changed in hostfs_args before the kernel starts running */static char *root_ino = "";static int append = 0;#define HOSTFS_SUPER_MAGIC 0x00c0ffeestatic const struct inode_operations hostfs_iops;static const struct inode_operations hostfs_dir_iops;static const struct address_space_operations hostfs_link_aops;#ifndef MODULEstatic int __init hostfs_args(char *options, int *add){ char *ptr; ptr = strchr(options, ','); if (ptr != NULL) *ptr++ = '\0'; if (*options != '\0') root_ino = options; options = ptr; while (options) { ptr = strchr(options, ','); if (ptr != NULL) *ptr++ = '\0'; if (*options != '\0') { if (!strcmp(options, "append")) append = 1; else printf("hostfs_args - unsupported option - %s\n", options); } options = ptr; } return 0;}__uml_setup("hostfs=", hostfs_args,"hostfs=<root dir>,<flags>,...\n"" This is used to set hostfs parameters. The root directory argument\n"" is used to confine all hostfs mounts to within the specified directory\n"" tree on the host. If this isn't specified, then a user inside UML can\n"" mount anything on the host that's accessible to the user that's running\n"" it.\n"" The only flag currently supported is 'append', which specifies that all\n"" files opened by hostfs will be opened in append mode.\n\n");#endifstatic char *dentry_name(struct dentry *dentry, int extra){ struct dentry *parent; char *root, *name; int len; len = 0; parent = dentry; while (parent->d_parent != parent) { len += parent->d_name.len + 1; parent = parent->d_parent; } root = HOSTFS_I(parent->d_inode)->host_filename; len += strlen(root); name = kmalloc(len + extra + 1, GFP_KERNEL); if (name == NULL) return NULL; name[len] = '\0'; parent = dentry; while (parent->d_parent != parent) { len -= parent->d_name.len + 1; name[len] = '/'; strncpy(&name[len + 1], parent->d_name.name, parent->d_name.len); parent = parent->d_parent; } strncpy(name, root, strlen(root)); return name;}static char *inode_name(struct inode *ino, int extra){ struct dentry *dentry; dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); return dentry_name(dentry, extra);}static int read_name(struct inode *ino, char *name){ /* * The non-int inode fields are copied into ints by stat_file and * then copied into the inode because passing the actual pointers * in and having them treated as int * breaks on big-endian machines */ int err; int i_mode, i_nlink, i_blksize; unsigned long long i_size; unsigned long long i_ino; unsigned long long i_blocks; err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid, &ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime, &ino->i_ctime, &i_blksize, &i_blocks, -1); if (err) return err; ino->i_ino = i_ino; ino->i_mode = i_mode; ino->i_nlink = i_nlink; ino->i_size = i_size; ino->i_blocks = i_blocks; return 0;}static char *follow_link(char *link){ int len, n; char *name, *resolved, *end; len = 64; while (1) { n = -ENOMEM; name = kmalloc(len, GFP_KERNEL); if (name == NULL) goto out; n = do_readlink(link, name, len); if (n < len) break; len *= 2; kfree(name); } if (n < 0) goto out_free; if (*name == '/') return name; end = strrchr(link, '/'); if (end == NULL) return name; *(end + 1) = '\0'; len = strlen(link) + strlen(name) + 1; resolved = kmalloc(len, GFP_KERNEL); if (resolved == NULL) { n = -ENOMEM; goto out_free; } sprintf(resolved, "%s%s", link, name); kfree(name); kfree(link); return resolved; out_free: kfree(name); out: return ERR_PTR(n);}static int read_inode(struct inode *ino){ char *name; int err = 0; /* * Unfortunately, we are called from iget() when we don't have a dentry * allocated yet. */ if (list_empty(&ino->i_dentry)) goto out; err = -ENOMEM; name = inode_name(ino, 0); if (name == NULL) goto out; if (file_type(name, NULL, NULL) == OS_TYPE_SYMLINK) { name = follow_link(name); if (IS_ERR(name)) { err = PTR_ERR(name); goto out; } } err = read_name(ino, name); kfree(name); out: return err;}int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf){ /* * do_statfs uses struct statfs64 internally, but the linux kernel * struct statfs still has 32-bit versions for most of these fields, * so we convert them here */ int err; long long f_blocks; long long f_bfree; long long f_bavail; long long f_files; long long f_ffree; err = do_statfs(HOSTFS_I(dentry->d_sb->s_root->d_inode)->host_filename, &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), &sf->f_namelen, sf->f_spare); if (err) return err; sf->f_blocks = f_blocks; sf->f_bfree = f_bfree; sf->f_bavail = f_bavail; sf->f_files = f_files; sf->f_ffree = f_ffree; sf->f_type = HOSTFS_SUPER_MAGIC; return 0;}static struct inode *hostfs_alloc_inode(struct super_block *sb){ struct hostfs_inode_info *hi; hi = kmalloc(sizeof(*hi), GFP_KERNEL); if (hi == NULL) return NULL; *hi = ((struct hostfs_inode_info) { .host_filename = NULL, .fd = -1, .mode = 0 }); inode_init_once(&hi->vfs_inode); return &hi->vfs_inode;}static void hostfs_delete_inode(struct inode *inode){ truncate_inode_pages(&inode->i_data, 0); if (HOSTFS_I(inode)->fd != -1) { close_file(&HOSTFS_I(inode)->fd); HOSTFS_I(inode)->fd = -1; } clear_inode(inode);}static void hostfs_destroy_inode(struct inode *inode){ kfree(HOSTFS_I(inode)->host_filename); /* * XXX: This should not happen, probably. The check is here for * additional safety. */ if (HOSTFS_I(inode)->fd != -1) { close_file(&HOSTFS_I(inode)->fd); printk(KERN_DEBUG "Closing host fd in .destroy_inode\n"); } kfree(HOSTFS_I(inode));}static void hostfs_read_inode(struct inode *inode){ read_inode(inode);}static const struct super_operations hostfs_sbops = { .alloc_inode = hostfs_alloc_inode, .drop_inode = generic_delete_inode, .delete_inode = hostfs_delete_inode, .destroy_inode = hostfs_destroy_inode, .read_inode = hostfs_read_inode, .statfs = hostfs_statfs,};int hostfs_readdir(struct file *file, void *ent, filldir_t filldir){ void *dir; char *name; unsigned long long next, ino; int error, len; name = dentry_name(file->f_path.dentry, 0); if (name == NULL) return -ENOMEM; dir = open_dir(name, &error); kfree(name); if (dir == NULL) return -error; next = file->f_pos; while ((name = read_dir(dir, &next, &ino, &len)) != NULL) { error = (*filldir)(ent, name, len, file->f_pos, ino, DT_UNKNOWN); if (error) break; file->f_pos = next; } close_dir(dir); return 0;}int hostfs_file_open(struct inode *ino, struct file *file){ char *name; int mode = 0, r = 0, w = 0, fd; mode = file->f_mode & (FMODE_READ | FMODE_WRITE); if ((mode & HOSTFS_I(ino)->mode) == mode) return 0; /* * The file may already have been opened, but with the wrong access, * so this resets things and reopens the file with the new access. */ if (HOSTFS_I(ino)->fd != -1) { close_file(&HOSTFS_I(ino)->fd); HOSTFS_I(ino)->fd = -1; } HOSTFS_I(ino)->mode |= mode; if (HOSTFS_I(ino)->mode & FMODE_READ) r = 1; if (HOSTFS_I(ino)->mode & FMODE_WRITE) w = 1; if (w) r = 1; name = dentry_name(file->f_path.dentry, 0); if (name == NULL) return -ENOMEM; fd = open_file(name, r, w, append); kfree(name); if (fd < 0) return fd; FILE_HOSTFS_I(file)->fd = fd; return 0;}int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync){ return fsync_file(HOSTFS_I(dentry->d_inode)->fd, datasync);}static const struct file_operations hostfs_file_fops = { .llseek = generic_file_llseek, .read = do_sync_read, .splice_read = generic_file_splice_read, .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .write = do_sync_write, .mmap = generic_file_mmap, .open = hostfs_file_open, .release = NULL, .fsync = hostfs_fsync,};static const struct file_operations hostfs_dir_fops = { .llseek = generic_file_llseek, .readdir = hostfs_readdir, .read = generic_read_dir,};int hostfs_writepage(struct page *page, struct writeback_control *wbc){ struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; char *buffer; unsigned long long base; int count = PAGE_CACHE_SIZE; int end_index = inode->i_size >> PAGE_CACHE_SHIFT; int err; if (page->index >= end_index) count = inode->i_size & (PAGE_CACHE_SIZE-1); buffer = kmap(page); base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); if (err != count) { ClearPageUptodate(page); goto out; } if (base > inode->i_size) inode->i_size = base; if (PageError(page)) ClearPageError(page); err = 0; out: kunmap(page); unlock_page(page); return err;}int hostfs_readpage(struct file *file, struct page *page){ char *buffer; long long start; int err = 0; start = (long long) page->index << PAGE_CACHE_SHIFT; buffer = kmap(page); err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, PAGE_CACHE_SIZE); if (err < 0) goto out; memset(&buffer[err], 0, PAGE_CACHE_SIZE - err); flush_dcache_page(page); SetPageUptodate(page); if (PageError(page)) ClearPageError(page); err = 0; out: kunmap(page); unlock_page(page); return err;}int hostfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata){ pgoff_t index = pos >> PAGE_CACHE_SHIFT; *pagep = __grab_cache_page(mapping, index); if (!*pagep) return -ENOMEM; return 0;}int hostfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata){ struct inode *inode = mapping->host; void *buffer; unsigned from = pos & (PAGE_CACHE_SIZE - 1); int err; buffer = kmap(page); err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied); kunmap(page); if (!PageUptodate(page) && err == PAGE_CACHE_SIZE) SetPageUptodate(page); /* * If err > 0, write_file has added err to pos, so we are comparing * i_size against the last byte written. */ if (err > 0 && (pos > inode->i_size)) inode->i_size = pos; unlock_page(page); page_cache_release(page); return err;}static const struct address_space_operations hostfs_aops = { .writepage = hostfs_writepage, .readpage = hostfs_readpage,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -