162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hypervisor filesystem for Linux on s390. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2006, 2008 662306a36Sopenharmony_ci * Author(s): Michael Holzheu <holzheu@de.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define KMSG_COMPONENT "hypfs" 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/fs_context.h> 1662306a36Sopenharmony_ci#include <linux/fs_parser.h> 1762306a36Sopenharmony_ci#include <linux/namei.h> 1862306a36Sopenharmony_ci#include <linux/vfs.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/pagemap.h> 2162306a36Sopenharmony_ci#include <linux/time.h> 2262306a36Sopenharmony_ci#include <linux/sysfs.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/kobject.h> 2562306a36Sopenharmony_ci#include <linux/seq_file.h> 2662306a36Sopenharmony_ci#include <linux/uio.h> 2762306a36Sopenharmony_ci#include <asm/ebcdic.h> 2862306a36Sopenharmony_ci#include "hypfs.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define HYPFS_MAGIC 0x687970 /* ASCII 'hyp' */ 3162306a36Sopenharmony_ci#define TMP_SIZE 64 /* size of temporary buffers */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct dentry *hypfs_create_update_file(struct dentry *dir); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct hypfs_sb_info { 3662306a36Sopenharmony_ci kuid_t uid; /* uid used for files and dirs */ 3762306a36Sopenharmony_ci kgid_t gid; /* gid used for files and dirs */ 3862306a36Sopenharmony_ci struct dentry *update_file; /* file to trigger update */ 3962306a36Sopenharmony_ci time64_t last_update; /* last update, CLOCK_MONOTONIC time */ 4062306a36Sopenharmony_ci struct mutex lock; /* lock to protect update process */ 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct file_operations hypfs_file_ops; 4462306a36Sopenharmony_cistatic struct file_system_type hypfs_type; 4562306a36Sopenharmony_cistatic const struct super_operations hypfs_s_ops; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* start of list of all dentries, which have to be deleted on update */ 4862306a36Sopenharmony_cistatic struct dentry *hypfs_last_dentry; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void hypfs_update_update(struct super_block *sb) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct hypfs_sb_info *sb_info = sb->s_fs_info; 5362306a36Sopenharmony_ci struct inode *inode = d_inode(sb_info->update_file); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci sb_info->last_update = ktime_get_seconds(); 5662306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* directory tree removal functions */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void hypfs_add_dentry(struct dentry *dentry) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci dentry->d_fsdata = hypfs_last_dentry; 6462306a36Sopenharmony_ci hypfs_last_dentry = dentry; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void hypfs_remove(struct dentry *dentry) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct dentry *parent; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci parent = dentry->d_parent; 7262306a36Sopenharmony_ci inode_lock(d_inode(parent)); 7362306a36Sopenharmony_ci if (simple_positive(dentry)) { 7462306a36Sopenharmony_ci if (d_is_dir(dentry)) 7562306a36Sopenharmony_ci simple_rmdir(d_inode(parent), dentry); 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci simple_unlink(d_inode(parent), dentry); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci d_drop(dentry); 8062306a36Sopenharmony_ci dput(dentry); 8162306a36Sopenharmony_ci inode_unlock(d_inode(parent)); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void hypfs_delete_tree(struct dentry *root) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci while (hypfs_last_dentry) { 8762306a36Sopenharmony_ci struct dentry *next_dentry; 8862306a36Sopenharmony_ci next_dentry = hypfs_last_dentry->d_fsdata; 8962306a36Sopenharmony_ci hypfs_remove(hypfs_last_dentry); 9062306a36Sopenharmony_ci hypfs_last_dentry = next_dentry; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct inode *hypfs_make_inode(struct super_block *sb, umode_t mode) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct inode *ret = new_inode(sb); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (ret) { 9962306a36Sopenharmony_ci struct hypfs_sb_info *hypfs_info = sb->s_fs_info; 10062306a36Sopenharmony_ci ret->i_ino = get_next_ino(); 10162306a36Sopenharmony_ci ret->i_mode = mode; 10262306a36Sopenharmony_ci ret->i_uid = hypfs_info->uid; 10362306a36Sopenharmony_ci ret->i_gid = hypfs_info->gid; 10462306a36Sopenharmony_ci ret->i_atime = ret->i_mtime = inode_set_ctime_current(ret); 10562306a36Sopenharmony_ci if (S_ISDIR(mode)) 10662306a36Sopenharmony_ci set_nlink(ret, 2); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void hypfs_evict_inode(struct inode *inode) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci clear_inode(inode); 11462306a36Sopenharmony_ci kfree(inode->i_private); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int hypfs_open(struct inode *inode, struct file *filp) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci char *data = file_inode(filp)->i_private; 12062306a36Sopenharmony_ci struct hypfs_sb_info *fs_info; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (filp->f_mode & FMODE_WRITE) { 12362306a36Sopenharmony_ci if (!(inode->i_mode & S_IWUGO)) 12462306a36Sopenharmony_ci return -EACCES; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci if (filp->f_mode & FMODE_READ) { 12762306a36Sopenharmony_ci if (!(inode->i_mode & S_IRUGO)) 12862306a36Sopenharmony_ci return -EACCES; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci fs_info = inode->i_sb->s_fs_info; 13262306a36Sopenharmony_ci if(data) { 13362306a36Sopenharmony_ci mutex_lock(&fs_info->lock); 13462306a36Sopenharmony_ci filp->private_data = kstrdup(data, GFP_KERNEL); 13562306a36Sopenharmony_ci if (!filp->private_data) { 13662306a36Sopenharmony_ci mutex_unlock(&fs_info->lock); 13762306a36Sopenharmony_ci return -ENOMEM; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci mutex_unlock(&fs_info->lock); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return nonseekable_open(inode, filp); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic ssize_t hypfs_read_iter(struct kiocb *iocb, struct iov_iter *to) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct file *file = iocb->ki_filp; 14762306a36Sopenharmony_ci char *data = file->private_data; 14862306a36Sopenharmony_ci size_t available = strlen(data); 14962306a36Sopenharmony_ci loff_t pos = iocb->ki_pos; 15062306a36Sopenharmony_ci size_t count; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (pos < 0) 15362306a36Sopenharmony_ci return -EINVAL; 15462306a36Sopenharmony_ci if (pos >= available || !iov_iter_count(to)) 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci count = copy_to_iter(data + pos, available - pos, to); 15762306a36Sopenharmony_ci if (!count) 15862306a36Sopenharmony_ci return -EFAULT; 15962306a36Sopenharmony_ci iocb->ki_pos = pos + count; 16062306a36Sopenharmony_ci file_accessed(file); 16162306a36Sopenharmony_ci return count; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic ssize_t hypfs_write_iter(struct kiocb *iocb, struct iov_iter *from) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int rc; 16762306a36Sopenharmony_ci struct super_block *sb = file_inode(iocb->ki_filp)->i_sb; 16862306a36Sopenharmony_ci struct hypfs_sb_info *fs_info = sb->s_fs_info; 16962306a36Sopenharmony_ci size_t count = iov_iter_count(from); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * Currently we only allow one update per second for two reasons: 17362306a36Sopenharmony_ci * 1. diag 204 is VERY expensive 17462306a36Sopenharmony_ci * 2. If several processes do updates in parallel and then read the 17562306a36Sopenharmony_ci * hypfs data, the likelihood of collisions is reduced, if we restrict 17662306a36Sopenharmony_ci * the minimum update interval. A collision occurs, if during the 17762306a36Sopenharmony_ci * data gathering of one process another process triggers an update 17862306a36Sopenharmony_ci * If the first process wants to ensure consistent data, it has 17962306a36Sopenharmony_ci * to restart data collection in this case. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci mutex_lock(&fs_info->lock); 18262306a36Sopenharmony_ci if (fs_info->last_update == ktime_get_seconds()) { 18362306a36Sopenharmony_ci rc = -EBUSY; 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci hypfs_delete_tree(sb->s_root); 18762306a36Sopenharmony_ci if (MACHINE_IS_VM) 18862306a36Sopenharmony_ci rc = hypfs_vm_create_files(sb->s_root); 18962306a36Sopenharmony_ci else 19062306a36Sopenharmony_ci rc = hypfs_diag_create_files(sb->s_root); 19162306a36Sopenharmony_ci if (rc) { 19262306a36Sopenharmony_ci pr_err("Updating the hypfs tree failed\n"); 19362306a36Sopenharmony_ci hypfs_delete_tree(sb->s_root); 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci hypfs_update_update(sb); 19762306a36Sopenharmony_ci rc = count; 19862306a36Sopenharmony_ci iov_iter_advance(from, count); 19962306a36Sopenharmony_ciout: 20062306a36Sopenharmony_ci mutex_unlock(&fs_info->lock); 20162306a36Sopenharmony_ci return rc; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int hypfs_release(struct inode *inode, struct file *filp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci kfree(filp->private_data); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cienum { Opt_uid, Opt_gid, }; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic const struct fs_parameter_spec hypfs_fs_parameters[] = { 21362306a36Sopenharmony_ci fsparam_u32("gid", Opt_gid), 21462306a36Sopenharmony_ci fsparam_u32("uid", Opt_uid), 21562306a36Sopenharmony_ci {} 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int hypfs_parse_param(struct fs_context *fc, struct fs_parameter *param) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct hypfs_sb_info *hypfs_info = fc->s_fs_info; 22162306a36Sopenharmony_ci struct fs_parse_result result; 22262306a36Sopenharmony_ci kuid_t uid; 22362306a36Sopenharmony_ci kgid_t gid; 22462306a36Sopenharmony_ci int opt; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci opt = fs_parse(fc, hypfs_fs_parameters, param, &result); 22762306a36Sopenharmony_ci if (opt < 0) 22862306a36Sopenharmony_ci return opt; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (opt) { 23162306a36Sopenharmony_ci case Opt_uid: 23262306a36Sopenharmony_ci uid = make_kuid(current_user_ns(), result.uint_32); 23362306a36Sopenharmony_ci if (!uid_valid(uid)) 23462306a36Sopenharmony_ci return invalf(fc, "Unknown uid"); 23562306a36Sopenharmony_ci hypfs_info->uid = uid; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case Opt_gid: 23862306a36Sopenharmony_ci gid = make_kgid(current_user_ns(), result.uint_32); 23962306a36Sopenharmony_ci if (!gid_valid(gid)) 24062306a36Sopenharmony_ci return invalf(fc, "Unknown gid"); 24162306a36Sopenharmony_ci hypfs_info->gid = gid; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int hypfs_show_options(struct seq_file *s, struct dentry *root) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct hypfs_sb_info *hypfs_info = root->d_sb->s_fs_info; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci seq_printf(s, ",uid=%u", from_kuid_munged(&init_user_ns, hypfs_info->uid)); 25262306a36Sopenharmony_ci seq_printf(s, ",gid=%u", from_kgid_munged(&init_user_ns, hypfs_info->gid)); 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int hypfs_fill_super(struct super_block *sb, struct fs_context *fc) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct hypfs_sb_info *sbi = sb->s_fs_info; 25962306a36Sopenharmony_ci struct inode *root_inode; 26062306a36Sopenharmony_ci struct dentry *root_dentry, *update_file; 26162306a36Sopenharmony_ci int rc; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sb->s_blocksize = PAGE_SIZE; 26462306a36Sopenharmony_ci sb->s_blocksize_bits = PAGE_SHIFT; 26562306a36Sopenharmony_ci sb->s_magic = HYPFS_MAGIC; 26662306a36Sopenharmony_ci sb->s_op = &hypfs_s_ops; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci root_inode = hypfs_make_inode(sb, S_IFDIR | 0755); 26962306a36Sopenharmony_ci if (!root_inode) 27062306a36Sopenharmony_ci return -ENOMEM; 27162306a36Sopenharmony_ci root_inode->i_op = &simple_dir_inode_operations; 27262306a36Sopenharmony_ci root_inode->i_fop = &simple_dir_operations; 27362306a36Sopenharmony_ci sb->s_root = root_dentry = d_make_root(root_inode); 27462306a36Sopenharmony_ci if (!root_dentry) 27562306a36Sopenharmony_ci return -ENOMEM; 27662306a36Sopenharmony_ci if (MACHINE_IS_VM) 27762306a36Sopenharmony_ci rc = hypfs_vm_create_files(root_dentry); 27862306a36Sopenharmony_ci else 27962306a36Sopenharmony_ci rc = hypfs_diag_create_files(root_dentry); 28062306a36Sopenharmony_ci if (rc) 28162306a36Sopenharmony_ci return rc; 28262306a36Sopenharmony_ci update_file = hypfs_create_update_file(root_dentry); 28362306a36Sopenharmony_ci if (IS_ERR(update_file)) 28462306a36Sopenharmony_ci return PTR_ERR(update_file); 28562306a36Sopenharmony_ci sbi->update_file = update_file; 28662306a36Sopenharmony_ci hypfs_update_update(sb); 28762306a36Sopenharmony_ci pr_info("Hypervisor filesystem mounted\n"); 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic int hypfs_get_tree(struct fs_context *fc) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci return get_tree_single(fc, hypfs_fill_super); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void hypfs_free_fc(struct fs_context *fc) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci kfree(fc->s_fs_info); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic const struct fs_context_operations hypfs_context_ops = { 30262306a36Sopenharmony_ci .free = hypfs_free_fc, 30362306a36Sopenharmony_ci .parse_param = hypfs_parse_param, 30462306a36Sopenharmony_ci .get_tree = hypfs_get_tree, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int hypfs_init_fs_context(struct fs_context *fc) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct hypfs_sb_info *sbi; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL); 31262306a36Sopenharmony_ci if (!sbi) 31362306a36Sopenharmony_ci return -ENOMEM; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci mutex_init(&sbi->lock); 31662306a36Sopenharmony_ci sbi->uid = current_uid(); 31762306a36Sopenharmony_ci sbi->gid = current_gid(); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci fc->s_fs_info = sbi; 32062306a36Sopenharmony_ci fc->ops = &hypfs_context_ops; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void hypfs_kill_super(struct super_block *sb) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct hypfs_sb_info *sb_info = sb->s_fs_info; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (sb->s_root) 32962306a36Sopenharmony_ci hypfs_delete_tree(sb->s_root); 33062306a36Sopenharmony_ci if (sb_info && sb_info->update_file) 33162306a36Sopenharmony_ci hypfs_remove(sb_info->update_file); 33262306a36Sopenharmony_ci kfree(sb->s_fs_info); 33362306a36Sopenharmony_ci sb->s_fs_info = NULL; 33462306a36Sopenharmony_ci kill_litter_super(sb); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic struct dentry *hypfs_create_file(struct dentry *parent, const char *name, 33862306a36Sopenharmony_ci char *data, umode_t mode) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct dentry *dentry; 34162306a36Sopenharmony_ci struct inode *inode; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci inode_lock(d_inode(parent)); 34462306a36Sopenharmony_ci dentry = lookup_one_len(name, parent, strlen(name)); 34562306a36Sopenharmony_ci if (IS_ERR(dentry)) { 34662306a36Sopenharmony_ci dentry = ERR_PTR(-ENOMEM); 34762306a36Sopenharmony_ci goto fail; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci inode = hypfs_make_inode(parent->d_sb, mode); 35062306a36Sopenharmony_ci if (!inode) { 35162306a36Sopenharmony_ci dput(dentry); 35262306a36Sopenharmony_ci dentry = ERR_PTR(-ENOMEM); 35362306a36Sopenharmony_ci goto fail; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (S_ISREG(mode)) { 35662306a36Sopenharmony_ci inode->i_fop = &hypfs_file_ops; 35762306a36Sopenharmony_ci if (data) 35862306a36Sopenharmony_ci inode->i_size = strlen(data); 35962306a36Sopenharmony_ci else 36062306a36Sopenharmony_ci inode->i_size = 0; 36162306a36Sopenharmony_ci } else if (S_ISDIR(mode)) { 36262306a36Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 36362306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 36462306a36Sopenharmony_ci inc_nlink(d_inode(parent)); 36562306a36Sopenharmony_ci } else 36662306a36Sopenharmony_ci BUG(); 36762306a36Sopenharmony_ci inode->i_private = data; 36862306a36Sopenharmony_ci d_instantiate(dentry, inode); 36962306a36Sopenharmony_ci dget(dentry); 37062306a36Sopenharmony_cifail: 37162306a36Sopenharmony_ci inode_unlock(d_inode(parent)); 37262306a36Sopenharmony_ci return dentry; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistruct dentry *hypfs_mkdir(struct dentry *parent, const char *name) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct dentry *dentry; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci dentry = hypfs_create_file(parent, name, NULL, S_IFDIR | DIR_MODE); 38062306a36Sopenharmony_ci if (IS_ERR(dentry)) 38162306a36Sopenharmony_ci return dentry; 38262306a36Sopenharmony_ci hypfs_add_dentry(dentry); 38362306a36Sopenharmony_ci return dentry; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic struct dentry *hypfs_create_update_file(struct dentry *dir) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct dentry *dentry; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci dentry = hypfs_create_file(dir, "update", NULL, 39162306a36Sopenharmony_ci S_IFREG | UPDATE_FILE_MODE); 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * We do not put the update file on the 'delete' list with 39462306a36Sopenharmony_ci * hypfs_add_dentry(), since it should not be removed when the tree 39562306a36Sopenharmony_ci * is updated. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci return dentry; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistruct dentry *hypfs_create_u64(struct dentry *dir, 40162306a36Sopenharmony_ci const char *name, __u64 value) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci char *buffer; 40462306a36Sopenharmony_ci char tmp[TMP_SIZE]; 40562306a36Sopenharmony_ci struct dentry *dentry; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci snprintf(tmp, TMP_SIZE, "%llu\n", (unsigned long long int)value); 40862306a36Sopenharmony_ci buffer = kstrdup(tmp, GFP_KERNEL); 40962306a36Sopenharmony_ci if (!buffer) 41062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41162306a36Sopenharmony_ci dentry = 41262306a36Sopenharmony_ci hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); 41362306a36Sopenharmony_ci if (IS_ERR(dentry)) { 41462306a36Sopenharmony_ci kfree(buffer); 41562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci hypfs_add_dentry(dentry); 41862306a36Sopenharmony_ci return dentry; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistruct dentry *hypfs_create_str(struct dentry *dir, 42262306a36Sopenharmony_ci const char *name, char *string) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci char *buffer; 42562306a36Sopenharmony_ci struct dentry *dentry; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci buffer = kmalloc(strlen(string) + 2, GFP_KERNEL); 42862306a36Sopenharmony_ci if (!buffer) 42962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43062306a36Sopenharmony_ci sprintf(buffer, "%s\n", string); 43162306a36Sopenharmony_ci dentry = 43262306a36Sopenharmony_ci hypfs_create_file(dir, name, buffer, S_IFREG | REG_FILE_MODE); 43362306a36Sopenharmony_ci if (IS_ERR(dentry)) { 43462306a36Sopenharmony_ci kfree(buffer); 43562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci hypfs_add_dentry(dentry); 43862306a36Sopenharmony_ci return dentry; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic const struct file_operations hypfs_file_ops = { 44262306a36Sopenharmony_ci .open = hypfs_open, 44362306a36Sopenharmony_ci .release = hypfs_release, 44462306a36Sopenharmony_ci .read_iter = hypfs_read_iter, 44562306a36Sopenharmony_ci .write_iter = hypfs_write_iter, 44662306a36Sopenharmony_ci .llseek = no_llseek, 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct file_system_type hypfs_type = { 45062306a36Sopenharmony_ci .owner = THIS_MODULE, 45162306a36Sopenharmony_ci .name = "s390_hypfs", 45262306a36Sopenharmony_ci .init_fs_context = hypfs_init_fs_context, 45362306a36Sopenharmony_ci .parameters = hypfs_fs_parameters, 45462306a36Sopenharmony_ci .kill_sb = hypfs_kill_super 45562306a36Sopenharmony_ci}; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct super_operations hypfs_s_ops = { 45862306a36Sopenharmony_ci .statfs = simple_statfs, 45962306a36Sopenharmony_ci .evict_inode = hypfs_evict_inode, 46062306a36Sopenharmony_ci .show_options = hypfs_show_options, 46162306a36Sopenharmony_ci}; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ciint __init __hypfs_fs_init(void) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int rc; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci rc = sysfs_create_mount_point(hypervisor_kobj, "s390"); 46862306a36Sopenharmony_ci if (rc) 46962306a36Sopenharmony_ci return rc; 47062306a36Sopenharmony_ci rc = register_filesystem(&hypfs_type); 47162306a36Sopenharmony_ci if (rc) 47262306a36Sopenharmony_ci goto fail; 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_cifail: 47562306a36Sopenharmony_ci sysfs_remove_mount_point(hypervisor_kobj, "s390"); 47662306a36Sopenharmony_ci return rc; 47762306a36Sopenharmony_ci} 478