162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 362306a36Sopenharmony_ci * Licensed under the GPL 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Ported the filesystem routines to 2.5. 662306a36Sopenharmony_ci * 2003-02-10 Petr Baudis <pasky@ucw.cz> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/magic.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/pagemap.h> 1462306a36Sopenharmony_ci#include <linux/statfs.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/seq_file.h> 1762306a36Sopenharmony_ci#include <linux/writeback.h> 1862306a36Sopenharmony_ci#include <linux/mount.h> 1962306a36Sopenharmony_ci#include <linux/namei.h> 2062306a36Sopenharmony_ci#include "hostfs.h" 2162306a36Sopenharmony_ci#include <init.h> 2262306a36Sopenharmony_ci#include <kern.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct hostfs_inode_info { 2562306a36Sopenharmony_ci int fd; 2662306a36Sopenharmony_ci fmode_t mode; 2762306a36Sopenharmony_ci struct inode vfs_inode; 2862306a36Sopenharmony_ci struct mutex open_mutex; 2962306a36Sopenharmony_ci dev_t dev; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return list_entry(inode, struct hostfs_inode_info, vfs_inode); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file)) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic struct kmem_cache *hostfs_inode_cache; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Changed in hostfs_args before the kernel starts running */ 4262306a36Sopenharmony_cistatic char *root_ino = ""; 4362306a36Sopenharmony_cistatic int append = 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct inode_operations hostfs_iops; 4662306a36Sopenharmony_cistatic const struct inode_operations hostfs_dir_iops; 4762306a36Sopenharmony_cistatic const struct inode_operations hostfs_link_iops; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifndef MODULE 5062306a36Sopenharmony_cistatic int __init hostfs_args(char *options, int *add) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci char *ptr; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ptr = strchr(options, ','); 5562306a36Sopenharmony_ci if (ptr != NULL) 5662306a36Sopenharmony_ci *ptr++ = '\0'; 5762306a36Sopenharmony_ci if (*options != '\0') 5862306a36Sopenharmony_ci root_ino = options; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci options = ptr; 6162306a36Sopenharmony_ci while (options) { 6262306a36Sopenharmony_ci ptr = strchr(options, ','); 6362306a36Sopenharmony_ci if (ptr != NULL) 6462306a36Sopenharmony_ci *ptr++ = '\0'; 6562306a36Sopenharmony_ci if (*options != '\0') { 6662306a36Sopenharmony_ci if (!strcmp(options, "append")) 6762306a36Sopenharmony_ci append = 1; 6862306a36Sopenharmony_ci else printf("hostfs_args - unsupported option - %s\n", 6962306a36Sopenharmony_ci options); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci options = ptr; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci__uml_setup("hostfs=", hostfs_args, 7762306a36Sopenharmony_ci"hostfs=<root dir>,<flags>,...\n" 7862306a36Sopenharmony_ci" This is used to set hostfs parameters. The root directory argument\n" 7962306a36Sopenharmony_ci" is used to confine all hostfs mounts to within the specified directory\n" 8062306a36Sopenharmony_ci" tree on the host. If this isn't specified, then a user inside UML can\n" 8162306a36Sopenharmony_ci" mount anything on the host that's accessible to the user that's running\n" 8262306a36Sopenharmony_ci" it.\n" 8362306a36Sopenharmony_ci" The only flag currently supported is 'append', which specifies that all\n" 8462306a36Sopenharmony_ci" files opened by hostfs will be opened in append mode.\n\n" 8562306a36Sopenharmony_ci); 8662306a36Sopenharmony_ci#endif 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic char *__dentry_name(struct dentry *dentry, char *name) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci char *p = dentry_path_raw(dentry, name, PATH_MAX); 9162306a36Sopenharmony_ci char *root; 9262306a36Sopenharmony_ci size_t len; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci root = dentry->d_sb->s_fs_info; 9562306a36Sopenharmony_ci len = strlen(root); 9662306a36Sopenharmony_ci if (IS_ERR(p)) { 9762306a36Sopenharmony_ci __putname(name); 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * This function relies on the fact that dentry_path_raw() will place 10362306a36Sopenharmony_ci * the path name at the end of the provided buffer. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci BUG_ON(p + strlen(p) + 1 != name + PATH_MAX); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci strscpy(name, root, PATH_MAX); 10862306a36Sopenharmony_ci if (len > p - name) { 10962306a36Sopenharmony_ci __putname(name); 11062306a36Sopenharmony_ci return NULL; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (p > name + len) 11462306a36Sopenharmony_ci strcpy(name + len, p); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return name; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic char *dentry_name(struct dentry *dentry) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci char *name = __getname(); 12262306a36Sopenharmony_ci if (!name) 12362306a36Sopenharmony_ci return NULL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return __dentry_name(dentry, name); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic char *inode_name(struct inode *ino) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct dentry *dentry; 13162306a36Sopenharmony_ci char *name; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci dentry = d_find_alias(ino); 13462306a36Sopenharmony_ci if (!dentry) 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci name = dentry_name(dentry); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dput(dentry); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return name; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic char *follow_link(char *link) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci char *name, *resolved, *end; 14762306a36Sopenharmony_ci int n; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci name = kmalloc(PATH_MAX, GFP_KERNEL); 15062306a36Sopenharmony_ci if (!name) { 15162306a36Sopenharmony_ci n = -ENOMEM; 15262306a36Sopenharmony_ci goto out_free; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci n = hostfs_do_readlink(link, name, PATH_MAX); 15662306a36Sopenharmony_ci if (n < 0) 15762306a36Sopenharmony_ci goto out_free; 15862306a36Sopenharmony_ci else if (n == PATH_MAX) { 15962306a36Sopenharmony_ci n = -E2BIG; 16062306a36Sopenharmony_ci goto out_free; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (*name == '/') 16462306a36Sopenharmony_ci return name; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci end = strrchr(link, '/'); 16762306a36Sopenharmony_ci if (end == NULL) 16862306a36Sopenharmony_ci return name; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci *(end + 1) = '\0'; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci resolved = kasprintf(GFP_KERNEL, "%s%s", link, name); 17362306a36Sopenharmony_ci if (resolved == NULL) { 17462306a36Sopenharmony_ci n = -ENOMEM; 17562306a36Sopenharmony_ci goto out_free; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci kfree(name); 17962306a36Sopenharmony_ci return resolved; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci out_free: 18262306a36Sopenharmony_ci kfree(name); 18362306a36Sopenharmony_ci return ERR_PTR(n); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * do_statfs uses struct statfs64 internally, but the linux kernel 19062306a36Sopenharmony_ci * struct statfs still has 32-bit versions for most of these fields, 19162306a36Sopenharmony_ci * so we convert them here 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci int err; 19462306a36Sopenharmony_ci long long f_blocks; 19562306a36Sopenharmony_ci long long f_bfree; 19662306a36Sopenharmony_ci long long f_bavail; 19762306a36Sopenharmony_ci long long f_files; 19862306a36Sopenharmony_ci long long f_ffree; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci err = do_statfs(dentry->d_sb->s_fs_info, 20162306a36Sopenharmony_ci &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, 20262306a36Sopenharmony_ci &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), 20362306a36Sopenharmony_ci &sf->f_namelen); 20462306a36Sopenharmony_ci if (err) 20562306a36Sopenharmony_ci return err; 20662306a36Sopenharmony_ci sf->f_blocks = f_blocks; 20762306a36Sopenharmony_ci sf->f_bfree = f_bfree; 20862306a36Sopenharmony_ci sf->f_bavail = f_bavail; 20962306a36Sopenharmony_ci sf->f_files = f_files; 21062306a36Sopenharmony_ci sf->f_ffree = f_ffree; 21162306a36Sopenharmony_ci sf->f_type = HOSTFS_SUPER_MAGIC; 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic struct inode *hostfs_alloc_inode(struct super_block *sb) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct hostfs_inode_info *hi; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci hi = alloc_inode_sb(sb, hostfs_inode_cache, GFP_KERNEL_ACCOUNT); 22062306a36Sopenharmony_ci if (hi == NULL) 22162306a36Sopenharmony_ci return NULL; 22262306a36Sopenharmony_ci hi->fd = -1; 22362306a36Sopenharmony_ci hi->mode = 0; 22462306a36Sopenharmony_ci hi->dev = 0; 22562306a36Sopenharmony_ci inode_init_once(&hi->vfs_inode); 22662306a36Sopenharmony_ci mutex_init(&hi->open_mutex); 22762306a36Sopenharmony_ci return &hi->vfs_inode; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void hostfs_evict_inode(struct inode *inode) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 23362306a36Sopenharmony_ci clear_inode(inode); 23462306a36Sopenharmony_ci if (HOSTFS_I(inode)->fd != -1) { 23562306a36Sopenharmony_ci close_file(&HOSTFS_I(inode)->fd); 23662306a36Sopenharmony_ci HOSTFS_I(inode)->fd = -1; 23762306a36Sopenharmony_ci HOSTFS_I(inode)->dev = 0; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void hostfs_free_inode(struct inode *inode) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci kmem_cache_free(hostfs_inode_cache, HOSTFS_I(inode)); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int hostfs_show_options(struct seq_file *seq, struct dentry *root) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci const char *root_path = root->d_sb->s_fs_info; 24962306a36Sopenharmony_ci size_t offset = strlen(root_ino) + 1; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (strlen(root_path) > offset) 25262306a36Sopenharmony_ci seq_show_option(seq, root_path + offset, NULL); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (append) 25562306a36Sopenharmony_ci seq_puts(seq, ",append"); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const struct super_operations hostfs_sbops = { 26162306a36Sopenharmony_ci .alloc_inode = hostfs_alloc_inode, 26262306a36Sopenharmony_ci .free_inode = hostfs_free_inode, 26362306a36Sopenharmony_ci .drop_inode = generic_delete_inode, 26462306a36Sopenharmony_ci .evict_inode = hostfs_evict_inode, 26562306a36Sopenharmony_ci .statfs = hostfs_statfs, 26662306a36Sopenharmony_ci .show_options = hostfs_show_options, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int hostfs_readdir(struct file *file, struct dir_context *ctx) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci void *dir; 27262306a36Sopenharmony_ci char *name; 27362306a36Sopenharmony_ci unsigned long long next, ino; 27462306a36Sopenharmony_ci int error, len; 27562306a36Sopenharmony_ci unsigned int type; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci name = dentry_name(file->f_path.dentry); 27862306a36Sopenharmony_ci if (name == NULL) 27962306a36Sopenharmony_ci return -ENOMEM; 28062306a36Sopenharmony_ci dir = open_dir(name, &error); 28162306a36Sopenharmony_ci __putname(name); 28262306a36Sopenharmony_ci if (dir == NULL) 28362306a36Sopenharmony_ci return -error; 28462306a36Sopenharmony_ci next = ctx->pos; 28562306a36Sopenharmony_ci seek_dir(dir, next); 28662306a36Sopenharmony_ci while ((name = read_dir(dir, &next, &ino, &len, &type)) != NULL) { 28762306a36Sopenharmony_ci if (!dir_emit(ctx, name, len, ino, type)) 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci ctx->pos = next; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci close_dir(dir); 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int hostfs_open(struct inode *ino, struct file *file) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci char *name; 29862306a36Sopenharmony_ci fmode_t mode; 29962306a36Sopenharmony_ci int err; 30062306a36Sopenharmony_ci int r, w, fd; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci mode = file->f_mode & (FMODE_READ | FMODE_WRITE); 30362306a36Sopenharmony_ci if ((mode & HOSTFS_I(ino)->mode) == mode) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci mode |= HOSTFS_I(ino)->mode; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciretry: 30962306a36Sopenharmony_ci r = w = 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (mode & FMODE_READ) 31262306a36Sopenharmony_ci r = 1; 31362306a36Sopenharmony_ci if (mode & FMODE_WRITE) 31462306a36Sopenharmony_ci r = w = 1; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci name = dentry_name(file_dentry(file)); 31762306a36Sopenharmony_ci if (name == NULL) 31862306a36Sopenharmony_ci return -ENOMEM; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci fd = open_file(name, r, w, append); 32162306a36Sopenharmony_ci __putname(name); 32262306a36Sopenharmony_ci if (fd < 0) 32362306a36Sopenharmony_ci return fd; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mutex_lock(&HOSTFS_I(ino)->open_mutex); 32662306a36Sopenharmony_ci /* somebody else had handled it first? */ 32762306a36Sopenharmony_ci if ((mode & HOSTFS_I(ino)->mode) == mode) { 32862306a36Sopenharmony_ci mutex_unlock(&HOSTFS_I(ino)->open_mutex); 32962306a36Sopenharmony_ci close_file(&fd); 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if ((mode | HOSTFS_I(ino)->mode) != mode) { 33362306a36Sopenharmony_ci mode |= HOSTFS_I(ino)->mode; 33462306a36Sopenharmony_ci mutex_unlock(&HOSTFS_I(ino)->open_mutex); 33562306a36Sopenharmony_ci close_file(&fd); 33662306a36Sopenharmony_ci goto retry; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci if (HOSTFS_I(ino)->fd == -1) { 33962306a36Sopenharmony_ci HOSTFS_I(ino)->fd = fd; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci err = replace_file(fd, HOSTFS_I(ino)->fd); 34262306a36Sopenharmony_ci close_file(&fd); 34362306a36Sopenharmony_ci if (err < 0) { 34462306a36Sopenharmony_ci mutex_unlock(&HOSTFS_I(ino)->open_mutex); 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci HOSTFS_I(ino)->mode = mode; 34962306a36Sopenharmony_ci mutex_unlock(&HOSTFS_I(ino)->open_mutex); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int hostfs_file_release(struct inode *inode, struct file *file) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci filemap_write_and_wait(inode->i_mapping); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int hostfs_fsync(struct file *file, loff_t start, loff_t end, 36262306a36Sopenharmony_ci int datasync) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct inode *inode = file->f_mapping->host; 36562306a36Sopenharmony_ci int ret; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = file_write_and_wait_range(file, start, end); 36862306a36Sopenharmony_ci if (ret) 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci inode_lock(inode); 37262306a36Sopenharmony_ci ret = fsync_file(HOSTFS_I(inode)->fd, datasync); 37362306a36Sopenharmony_ci inode_unlock(inode); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic const struct file_operations hostfs_file_fops = { 37962306a36Sopenharmony_ci .llseek = generic_file_llseek, 38062306a36Sopenharmony_ci .splice_read = filemap_splice_read, 38162306a36Sopenharmony_ci .splice_write = iter_file_splice_write, 38262306a36Sopenharmony_ci .read_iter = generic_file_read_iter, 38362306a36Sopenharmony_ci .write_iter = generic_file_write_iter, 38462306a36Sopenharmony_ci .mmap = generic_file_mmap, 38562306a36Sopenharmony_ci .open = hostfs_open, 38662306a36Sopenharmony_ci .release = hostfs_file_release, 38762306a36Sopenharmony_ci .fsync = hostfs_fsync, 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const struct file_operations hostfs_dir_fops = { 39162306a36Sopenharmony_ci .llseek = generic_file_llseek, 39262306a36Sopenharmony_ci .iterate_shared = hostfs_readdir, 39362306a36Sopenharmony_ci .read = generic_read_dir, 39462306a36Sopenharmony_ci .open = hostfs_open, 39562306a36Sopenharmony_ci .fsync = hostfs_fsync, 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int hostfs_writepage(struct page *page, struct writeback_control *wbc) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct address_space *mapping = page->mapping; 40162306a36Sopenharmony_ci struct inode *inode = mapping->host; 40262306a36Sopenharmony_ci char *buffer; 40362306a36Sopenharmony_ci loff_t base = page_offset(page); 40462306a36Sopenharmony_ci int count = PAGE_SIZE; 40562306a36Sopenharmony_ci int end_index = inode->i_size >> PAGE_SHIFT; 40662306a36Sopenharmony_ci int err; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (page->index >= end_index) 40962306a36Sopenharmony_ci count = inode->i_size & (PAGE_SIZE-1); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci buffer = kmap_local_page(page); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); 41462306a36Sopenharmony_ci if (err != count) { 41562306a36Sopenharmony_ci if (err >= 0) 41662306a36Sopenharmony_ci err = -EIO; 41762306a36Sopenharmony_ci mapping_set_error(mapping, err); 41862306a36Sopenharmony_ci goto out; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (base > inode->i_size) 42262306a36Sopenharmony_ci inode->i_size = base; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci err = 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci out: 42762306a36Sopenharmony_ci kunmap_local(buffer); 42862306a36Sopenharmony_ci unlock_page(page); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return err; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int hostfs_read_folio(struct file *file, struct folio *folio) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct page *page = &folio->page; 43662306a36Sopenharmony_ci char *buffer; 43762306a36Sopenharmony_ci loff_t start = page_offset(page); 43862306a36Sopenharmony_ci int bytes_read, ret = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci buffer = kmap_local_page(page); 44162306a36Sopenharmony_ci bytes_read = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, 44262306a36Sopenharmony_ci PAGE_SIZE); 44362306a36Sopenharmony_ci if (bytes_read < 0) { 44462306a36Sopenharmony_ci ClearPageUptodate(page); 44562306a36Sopenharmony_ci SetPageError(page); 44662306a36Sopenharmony_ci ret = bytes_read; 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci memset(buffer + bytes_read, 0, PAGE_SIZE - bytes_read); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ClearPageError(page); 45362306a36Sopenharmony_ci SetPageUptodate(page); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci out: 45662306a36Sopenharmony_ci flush_dcache_page(page); 45762306a36Sopenharmony_ci kunmap_local(buffer); 45862306a36Sopenharmony_ci unlock_page(page); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int hostfs_write_begin(struct file *file, struct address_space *mapping, 46462306a36Sopenharmony_ci loff_t pos, unsigned len, 46562306a36Sopenharmony_ci struct page **pagep, void **fsdata) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci pgoff_t index = pos >> PAGE_SHIFT; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci *pagep = grab_cache_page_write_begin(mapping, index); 47062306a36Sopenharmony_ci if (!*pagep) 47162306a36Sopenharmony_ci return -ENOMEM; 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int hostfs_write_end(struct file *file, struct address_space *mapping, 47662306a36Sopenharmony_ci loff_t pos, unsigned len, unsigned copied, 47762306a36Sopenharmony_ci struct page *page, void *fsdata) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct inode *inode = mapping->host; 48062306a36Sopenharmony_ci void *buffer; 48162306a36Sopenharmony_ci unsigned from = pos & (PAGE_SIZE - 1); 48262306a36Sopenharmony_ci int err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci buffer = kmap_local_page(page); 48562306a36Sopenharmony_ci err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied); 48662306a36Sopenharmony_ci kunmap_local(buffer); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!PageUptodate(page) && err == PAGE_SIZE) 48962306a36Sopenharmony_ci SetPageUptodate(page); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* 49262306a36Sopenharmony_ci * If err > 0, write_file has added err to pos, so we are comparing 49362306a36Sopenharmony_ci * i_size against the last byte written. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci if (err > 0 && (pos > inode->i_size)) 49662306a36Sopenharmony_ci inode->i_size = pos; 49762306a36Sopenharmony_ci unlock_page(page); 49862306a36Sopenharmony_ci put_page(page); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return err; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic const struct address_space_operations hostfs_aops = { 50462306a36Sopenharmony_ci .writepage = hostfs_writepage, 50562306a36Sopenharmony_ci .read_folio = hostfs_read_folio, 50662306a36Sopenharmony_ci .dirty_folio = filemap_dirty_folio, 50762306a36Sopenharmony_ci .write_begin = hostfs_write_begin, 50862306a36Sopenharmony_ci .write_end = hostfs_write_end, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int hostfs_inode_update(struct inode *ino, const struct hostfs_stat *st) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci set_nlink(ino, st->nlink); 51462306a36Sopenharmony_ci i_uid_write(ino, st->uid); 51562306a36Sopenharmony_ci i_gid_write(ino, st->gid); 51662306a36Sopenharmony_ci ino->i_atime = 51762306a36Sopenharmony_ci (struct timespec64){ st->atime.tv_sec, st->atime.tv_nsec }; 51862306a36Sopenharmony_ci ino->i_mtime = 51962306a36Sopenharmony_ci (struct timespec64){ st->mtime.tv_sec, st->mtime.tv_nsec }; 52062306a36Sopenharmony_ci inode_set_ctime(ino, st->ctime.tv_sec, st->ctime.tv_nsec); 52162306a36Sopenharmony_ci ino->i_size = st->size; 52262306a36Sopenharmony_ci ino->i_blocks = st->blocks; 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int hostfs_inode_set(struct inode *ino, void *data) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct hostfs_stat *st = data; 52962306a36Sopenharmony_ci dev_t rdev; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Reencode maj and min with the kernel encoding.*/ 53262306a36Sopenharmony_ci rdev = MKDEV(st->maj, st->min); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci switch (st->mode & S_IFMT) { 53562306a36Sopenharmony_ci case S_IFLNK: 53662306a36Sopenharmony_ci ino->i_op = &hostfs_link_iops; 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci case S_IFDIR: 53962306a36Sopenharmony_ci ino->i_op = &hostfs_dir_iops; 54062306a36Sopenharmony_ci ino->i_fop = &hostfs_dir_fops; 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci case S_IFCHR: 54362306a36Sopenharmony_ci case S_IFBLK: 54462306a36Sopenharmony_ci case S_IFIFO: 54562306a36Sopenharmony_ci case S_IFSOCK: 54662306a36Sopenharmony_ci init_special_inode(ino, st->mode & S_IFMT, rdev); 54762306a36Sopenharmony_ci ino->i_op = &hostfs_iops; 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci case S_IFREG: 55062306a36Sopenharmony_ci ino->i_op = &hostfs_iops; 55162306a36Sopenharmony_ci ino->i_fop = &hostfs_file_fops; 55262306a36Sopenharmony_ci ino->i_mapping->a_ops = &hostfs_aops; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci default: 55562306a36Sopenharmony_ci return -EIO; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci HOSTFS_I(ino)->dev = st->dev; 55962306a36Sopenharmony_ci ino->i_ino = st->ino; 56062306a36Sopenharmony_ci ino->i_mode = st->mode; 56162306a36Sopenharmony_ci return hostfs_inode_update(ino, st); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int hostfs_inode_test(struct inode *inode, void *data) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci const struct hostfs_stat *st = data; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == st->dev; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic struct inode *hostfs_iget(struct super_block *sb, char *name) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct inode *inode; 57462306a36Sopenharmony_ci struct hostfs_stat st; 57562306a36Sopenharmony_ci int err = stat_file(name, &st, -1); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (err) 57862306a36Sopenharmony_ci return ERR_PTR(err); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci inode = iget5_locked(sb, st.ino, hostfs_inode_test, hostfs_inode_set, 58162306a36Sopenharmony_ci &st); 58262306a36Sopenharmony_ci if (!inode) 58362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (inode->i_state & I_NEW) { 58662306a36Sopenharmony_ci unlock_new_inode(inode); 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci spin_lock(&inode->i_lock); 58962306a36Sopenharmony_ci hostfs_inode_update(inode, &st); 59062306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return inode; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, 59762306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct inode *inode; 60062306a36Sopenharmony_ci char *name; 60162306a36Sopenharmony_ci int fd; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci name = dentry_name(dentry); 60462306a36Sopenharmony_ci if (name == NULL) 60562306a36Sopenharmony_ci return -ENOMEM; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci fd = file_create(name, mode & 0777); 60862306a36Sopenharmony_ci if (fd < 0) { 60962306a36Sopenharmony_ci __putname(name); 61062306a36Sopenharmony_ci return fd; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci inode = hostfs_iget(dir->i_sb, name); 61462306a36Sopenharmony_ci __putname(name); 61562306a36Sopenharmony_ci if (IS_ERR(inode)) 61662306a36Sopenharmony_ci return PTR_ERR(inode); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci HOSTFS_I(inode)->fd = fd; 61962306a36Sopenharmony_ci HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; 62062306a36Sopenharmony_ci d_instantiate(dentry, inode); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, 62562306a36Sopenharmony_ci unsigned int flags) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct inode *inode = NULL; 62862306a36Sopenharmony_ci char *name; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci name = dentry_name(dentry); 63162306a36Sopenharmony_ci if (name == NULL) 63262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci inode = hostfs_iget(ino->i_sb, name); 63562306a36Sopenharmony_ci __putname(name); 63662306a36Sopenharmony_ci if (IS_ERR(inode)) { 63762306a36Sopenharmony_ci if (PTR_ERR(inode) == -ENOENT) 63862306a36Sopenharmony_ci inode = NULL; 63962306a36Sopenharmony_ci else 64062306a36Sopenharmony_ci return ERR_CAST(inode); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return d_splice_alias(inode, dentry); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int hostfs_link(struct dentry *to, struct inode *ino, 64762306a36Sopenharmony_ci struct dentry *from) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci char *from_name, *to_name; 65062306a36Sopenharmony_ci int err; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if ((from_name = dentry_name(from)) == NULL) 65362306a36Sopenharmony_ci return -ENOMEM; 65462306a36Sopenharmony_ci to_name = dentry_name(to); 65562306a36Sopenharmony_ci if (to_name == NULL) { 65662306a36Sopenharmony_ci __putname(from_name); 65762306a36Sopenharmony_ci return -ENOMEM; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci err = link_file(to_name, from_name); 66062306a36Sopenharmony_ci __putname(from_name); 66162306a36Sopenharmony_ci __putname(to_name); 66262306a36Sopenharmony_ci return err; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int hostfs_unlink(struct inode *ino, struct dentry *dentry) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci char *file; 66862306a36Sopenharmony_ci int err; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (append) 67162306a36Sopenharmony_ci return -EPERM; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if ((file = dentry_name(dentry)) == NULL) 67462306a36Sopenharmony_ci return -ENOMEM; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci err = unlink_file(file); 67762306a36Sopenharmony_ci __putname(file); 67862306a36Sopenharmony_ci return err; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int hostfs_symlink(struct mnt_idmap *idmap, struct inode *ino, 68262306a36Sopenharmony_ci struct dentry *dentry, const char *to) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci char *file; 68562306a36Sopenharmony_ci int err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if ((file = dentry_name(dentry)) == NULL) 68862306a36Sopenharmony_ci return -ENOMEM; 68962306a36Sopenharmony_ci err = make_symlink(file, to); 69062306a36Sopenharmony_ci __putname(file); 69162306a36Sopenharmony_ci return err; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int hostfs_mkdir(struct mnt_idmap *idmap, struct inode *ino, 69562306a36Sopenharmony_ci struct dentry *dentry, umode_t mode) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci char *file; 69862306a36Sopenharmony_ci int err; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if ((file = dentry_name(dentry)) == NULL) 70162306a36Sopenharmony_ci return -ENOMEM; 70262306a36Sopenharmony_ci err = do_mkdir(file, mode); 70362306a36Sopenharmony_ci __putname(file); 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int hostfs_rmdir(struct inode *ino, struct dentry *dentry) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci char *file; 71062306a36Sopenharmony_ci int err; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if ((file = dentry_name(dentry)) == NULL) 71362306a36Sopenharmony_ci return -ENOMEM; 71462306a36Sopenharmony_ci err = hostfs_do_rmdir(file); 71562306a36Sopenharmony_ci __putname(file); 71662306a36Sopenharmony_ci return err; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int hostfs_mknod(struct mnt_idmap *idmap, struct inode *dir, 72062306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, dev_t dev) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct inode *inode; 72362306a36Sopenharmony_ci char *name; 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci name = dentry_name(dentry); 72762306a36Sopenharmony_ci if (name == NULL) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); 73162306a36Sopenharmony_ci if (err) { 73262306a36Sopenharmony_ci __putname(name); 73362306a36Sopenharmony_ci return err; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci inode = hostfs_iget(dir->i_sb, name); 73762306a36Sopenharmony_ci __putname(name); 73862306a36Sopenharmony_ci if (IS_ERR(inode)) 73962306a36Sopenharmony_ci return PTR_ERR(inode); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci d_instantiate(dentry, inode); 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int hostfs_rename2(struct mnt_idmap *idmap, 74662306a36Sopenharmony_ci struct inode *old_dir, struct dentry *old_dentry, 74762306a36Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 74862306a36Sopenharmony_ci unsigned int flags) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci char *old_name, *new_name; 75162306a36Sopenharmony_ci int err; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) 75462306a36Sopenharmony_ci return -EINVAL; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci old_name = dentry_name(old_dentry); 75762306a36Sopenharmony_ci if (old_name == NULL) 75862306a36Sopenharmony_ci return -ENOMEM; 75962306a36Sopenharmony_ci new_name = dentry_name(new_dentry); 76062306a36Sopenharmony_ci if (new_name == NULL) { 76162306a36Sopenharmony_ci __putname(old_name); 76262306a36Sopenharmony_ci return -ENOMEM; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci if (!flags) 76562306a36Sopenharmony_ci err = rename_file(old_name, new_name); 76662306a36Sopenharmony_ci else 76762306a36Sopenharmony_ci err = rename2_file(old_name, new_name, flags); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci __putname(old_name); 77062306a36Sopenharmony_ci __putname(new_name); 77162306a36Sopenharmony_ci return err; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int hostfs_permission(struct mnt_idmap *idmap, 77562306a36Sopenharmony_ci struct inode *ino, int desired) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci char *name; 77862306a36Sopenharmony_ci int r = 0, w = 0, x = 0, err; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (desired & MAY_NOT_BLOCK) 78162306a36Sopenharmony_ci return -ECHILD; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (desired & MAY_READ) r = 1; 78462306a36Sopenharmony_ci if (desired & MAY_WRITE) w = 1; 78562306a36Sopenharmony_ci if (desired & MAY_EXEC) x = 1; 78662306a36Sopenharmony_ci name = inode_name(ino); 78762306a36Sopenharmony_ci if (name == NULL) 78862306a36Sopenharmony_ci return -ENOMEM; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) || 79162306a36Sopenharmony_ci S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode)) 79262306a36Sopenharmony_ci err = 0; 79362306a36Sopenharmony_ci else 79462306a36Sopenharmony_ci err = access_file(name, r, w, x); 79562306a36Sopenharmony_ci __putname(name); 79662306a36Sopenharmony_ci if (!err) 79762306a36Sopenharmony_ci err = generic_permission(&nop_mnt_idmap, ino, desired); 79862306a36Sopenharmony_ci return err; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int hostfs_setattr(struct mnt_idmap *idmap, 80262306a36Sopenharmony_ci struct dentry *dentry, struct iattr *attr) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 80562306a36Sopenharmony_ci struct hostfs_iattr attrs; 80662306a36Sopenharmony_ci char *name; 80762306a36Sopenharmony_ci int err; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci int fd = HOSTFS_I(inode)->fd; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci err = setattr_prepare(&nop_mnt_idmap, dentry, attr); 81262306a36Sopenharmony_ci if (err) 81362306a36Sopenharmony_ci return err; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (append) 81662306a36Sopenharmony_ci attr->ia_valid &= ~ATTR_SIZE; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci attrs.ia_valid = 0; 81962306a36Sopenharmony_ci if (attr->ia_valid & ATTR_MODE) { 82062306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_MODE; 82162306a36Sopenharmony_ci attrs.ia_mode = attr->ia_mode; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci if (attr->ia_valid & ATTR_UID) { 82462306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_UID; 82562306a36Sopenharmony_ci attrs.ia_uid = from_kuid(&init_user_ns, attr->ia_uid); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci if (attr->ia_valid & ATTR_GID) { 82862306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_GID; 82962306a36Sopenharmony_ci attrs.ia_gid = from_kgid(&init_user_ns, attr->ia_gid); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci if (attr->ia_valid & ATTR_SIZE) { 83262306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_SIZE; 83362306a36Sopenharmony_ci attrs.ia_size = attr->ia_size; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci if (attr->ia_valid & ATTR_ATIME) { 83662306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_ATIME; 83762306a36Sopenharmony_ci attrs.ia_atime = (struct hostfs_timespec) 83862306a36Sopenharmony_ci { attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec }; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci if (attr->ia_valid & ATTR_MTIME) { 84162306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_MTIME; 84262306a36Sopenharmony_ci attrs.ia_mtime = (struct hostfs_timespec) 84362306a36Sopenharmony_ci { attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec }; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci if (attr->ia_valid & ATTR_CTIME) { 84662306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_CTIME; 84762306a36Sopenharmony_ci attrs.ia_ctime = (struct hostfs_timespec) 84862306a36Sopenharmony_ci { attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec }; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci if (attr->ia_valid & ATTR_ATIME_SET) { 85162306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci if (attr->ia_valid & ATTR_MTIME_SET) { 85462306a36Sopenharmony_ci attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci name = dentry_name(dentry); 85762306a36Sopenharmony_ci if (name == NULL) 85862306a36Sopenharmony_ci return -ENOMEM; 85962306a36Sopenharmony_ci err = set_attr(name, &attrs, fd); 86062306a36Sopenharmony_ci __putname(name); 86162306a36Sopenharmony_ci if (err) 86262306a36Sopenharmony_ci return err; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if ((attr->ia_valid & ATTR_SIZE) && 86562306a36Sopenharmony_ci attr->ia_size != i_size_read(inode)) 86662306a36Sopenharmony_ci truncate_setsize(inode, attr->ia_size); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, attr); 86962306a36Sopenharmony_ci mark_inode_dirty(inode); 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic const struct inode_operations hostfs_iops = { 87462306a36Sopenharmony_ci .permission = hostfs_permission, 87562306a36Sopenharmony_ci .setattr = hostfs_setattr, 87662306a36Sopenharmony_ci}; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic const struct inode_operations hostfs_dir_iops = { 87962306a36Sopenharmony_ci .create = hostfs_create, 88062306a36Sopenharmony_ci .lookup = hostfs_lookup, 88162306a36Sopenharmony_ci .link = hostfs_link, 88262306a36Sopenharmony_ci .unlink = hostfs_unlink, 88362306a36Sopenharmony_ci .symlink = hostfs_symlink, 88462306a36Sopenharmony_ci .mkdir = hostfs_mkdir, 88562306a36Sopenharmony_ci .rmdir = hostfs_rmdir, 88662306a36Sopenharmony_ci .mknod = hostfs_mknod, 88762306a36Sopenharmony_ci .rename = hostfs_rename2, 88862306a36Sopenharmony_ci .permission = hostfs_permission, 88962306a36Sopenharmony_ci .setattr = hostfs_setattr, 89062306a36Sopenharmony_ci}; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic const char *hostfs_get_link(struct dentry *dentry, 89362306a36Sopenharmony_ci struct inode *inode, 89462306a36Sopenharmony_ci struct delayed_call *done) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci char *link; 89762306a36Sopenharmony_ci if (!dentry) 89862306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 89962306a36Sopenharmony_ci link = kmalloc(PATH_MAX, GFP_KERNEL); 90062306a36Sopenharmony_ci if (link) { 90162306a36Sopenharmony_ci char *path = dentry_name(dentry); 90262306a36Sopenharmony_ci int err = -ENOMEM; 90362306a36Sopenharmony_ci if (path) { 90462306a36Sopenharmony_ci err = hostfs_do_readlink(path, link, PATH_MAX); 90562306a36Sopenharmony_ci if (err == PATH_MAX) 90662306a36Sopenharmony_ci err = -E2BIG; 90762306a36Sopenharmony_ci __putname(path); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci if (err < 0) { 91062306a36Sopenharmony_ci kfree(link); 91162306a36Sopenharmony_ci return ERR_PTR(err); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci } else { 91462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci set_delayed_call(done, kfree_link, link); 91862306a36Sopenharmony_ci return link; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic const struct inode_operations hostfs_link_iops = { 92262306a36Sopenharmony_ci .get_link = hostfs_get_link, 92362306a36Sopenharmony_ci}; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct inode *root_inode; 92862306a36Sopenharmony_ci char *host_root_path, *req_root = d; 92962306a36Sopenharmony_ci int err; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci sb->s_blocksize = 1024; 93262306a36Sopenharmony_ci sb->s_blocksize_bits = 10; 93362306a36Sopenharmony_ci sb->s_magic = HOSTFS_SUPER_MAGIC; 93462306a36Sopenharmony_ci sb->s_op = &hostfs_sbops; 93562306a36Sopenharmony_ci sb->s_d_op = &simple_dentry_operations; 93662306a36Sopenharmony_ci sb->s_maxbytes = MAX_LFS_FILESIZE; 93762306a36Sopenharmony_ci err = super_setup_bdi(sb); 93862306a36Sopenharmony_ci if (err) 93962306a36Sopenharmony_ci return err; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* NULL is printed as '(null)' by printf(): avoid that. */ 94262306a36Sopenharmony_ci if (req_root == NULL) 94362306a36Sopenharmony_ci req_root = ""; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci sb->s_fs_info = host_root_path = 94662306a36Sopenharmony_ci kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root); 94762306a36Sopenharmony_ci if (host_root_path == NULL) 94862306a36Sopenharmony_ci return -ENOMEM; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci root_inode = hostfs_iget(sb, host_root_path); 95162306a36Sopenharmony_ci if (IS_ERR(root_inode)) 95262306a36Sopenharmony_ci return PTR_ERR(root_inode); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (S_ISLNK(root_inode->i_mode)) { 95562306a36Sopenharmony_ci char *name; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci iput(root_inode); 95862306a36Sopenharmony_ci name = follow_link(host_root_path); 95962306a36Sopenharmony_ci if (IS_ERR(name)) 96062306a36Sopenharmony_ci return PTR_ERR(name); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci root_inode = hostfs_iget(sb, name); 96362306a36Sopenharmony_ci kfree(name); 96462306a36Sopenharmony_ci if (IS_ERR(root_inode)) 96562306a36Sopenharmony_ci return PTR_ERR(root_inode); 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci sb->s_root = d_make_root(root_inode); 96962306a36Sopenharmony_ci if (sb->s_root == NULL) 97062306a36Sopenharmony_ci return -ENOMEM; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic struct dentry *hostfs_read_sb(struct file_system_type *type, 97662306a36Sopenharmony_ci int flags, const char *dev_name, 97762306a36Sopenharmony_ci void *data) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci return mount_nodev(type, flags, data, hostfs_fill_sb_common); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic void hostfs_kill_sb(struct super_block *s) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci kill_anon_super(s); 98562306a36Sopenharmony_ci kfree(s->s_fs_info); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic struct file_system_type hostfs_type = { 98962306a36Sopenharmony_ci .owner = THIS_MODULE, 99062306a36Sopenharmony_ci .name = "hostfs", 99162306a36Sopenharmony_ci .mount = hostfs_read_sb, 99262306a36Sopenharmony_ci .kill_sb = hostfs_kill_sb, 99362306a36Sopenharmony_ci .fs_flags = 0, 99462306a36Sopenharmony_ci}; 99562306a36Sopenharmony_ciMODULE_ALIAS_FS("hostfs"); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic int __init init_hostfs(void) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci hostfs_inode_cache = KMEM_CACHE(hostfs_inode_info, 0); 100062306a36Sopenharmony_ci if (!hostfs_inode_cache) 100162306a36Sopenharmony_ci return -ENOMEM; 100262306a36Sopenharmony_ci return register_filesystem(&hostfs_type); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic void __exit exit_hostfs(void) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci unregister_filesystem(&hostfs_type); 100862306a36Sopenharmony_ci kmem_cache_destroy(hostfs_inode_cache); 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cimodule_init(init_hostfs) 101262306a36Sopenharmony_cimodule_exit(exit_hostfs) 101362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1014