162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains vfs inode ops for the 9P2000 protocol. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> 662306a36Sopenharmony_ci * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/file.h> 1562306a36Sopenharmony_ci#include <linux/pagemap.h> 1662306a36Sopenharmony_ci#include <linux/stat.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/namei.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/xattr.h> 2262306a36Sopenharmony_ci#include <linux/posix_acl.h> 2362306a36Sopenharmony_ci#include <net/9p/9p.h> 2462306a36Sopenharmony_ci#include <net/9p/client.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "v9fs.h" 2762306a36Sopenharmony_ci#include "v9fs_vfs.h" 2862306a36Sopenharmony_ci#include "fid.h" 2962306a36Sopenharmony_ci#include "cache.h" 3062306a36Sopenharmony_ci#include "xattr.h" 3162306a36Sopenharmony_ci#include "acl.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic const struct inode_operations v9fs_dir_inode_operations; 3462306a36Sopenharmony_cistatic const struct inode_operations v9fs_dir_inode_operations_dotu; 3562306a36Sopenharmony_cistatic const struct inode_operations v9fs_file_inode_operations; 3662306a36Sopenharmony_cistatic const struct inode_operations v9fs_symlink_inode_operations; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/** 3962306a36Sopenharmony_ci * unixmode2p9mode - convert unix mode bits to plan 9 4062306a36Sopenharmony_ci * @v9ses: v9fs session information 4162306a36Sopenharmony_ci * @mode: mode to convert 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic u32 unixmode2p9mode(struct v9fs_session_info *v9ses, umode_t mode) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int res; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci res = mode & 0777; 5062306a36Sopenharmony_ci if (S_ISDIR(mode)) 5162306a36Sopenharmony_ci res |= P9_DMDIR; 5262306a36Sopenharmony_ci if (v9fs_proto_dotu(v9ses)) { 5362306a36Sopenharmony_ci if (v9ses->nodev == 0) { 5462306a36Sopenharmony_ci if (S_ISSOCK(mode)) 5562306a36Sopenharmony_ci res |= P9_DMSOCKET; 5662306a36Sopenharmony_ci if (S_ISFIFO(mode)) 5762306a36Sopenharmony_ci res |= P9_DMNAMEDPIPE; 5862306a36Sopenharmony_ci if (S_ISBLK(mode)) 5962306a36Sopenharmony_ci res |= P9_DMDEVICE; 6062306a36Sopenharmony_ci if (S_ISCHR(mode)) 6162306a36Sopenharmony_ci res |= P9_DMDEVICE; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if ((mode & S_ISUID) == S_ISUID) 6562306a36Sopenharmony_ci res |= P9_DMSETUID; 6662306a36Sopenharmony_ci if ((mode & S_ISGID) == S_ISGID) 6762306a36Sopenharmony_ci res |= P9_DMSETGID; 6862306a36Sopenharmony_ci if ((mode & S_ISVTX) == S_ISVTX) 6962306a36Sopenharmony_ci res |= P9_DMSETVTX; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci return res; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * p9mode2perm- convert plan9 mode bits to unix permission bits 7662306a36Sopenharmony_ci * @v9ses: v9fs session information 7762306a36Sopenharmony_ci * @stat: p9_wstat from which mode need to be derived 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic int p9mode2perm(struct v9fs_session_info *v9ses, 8162306a36Sopenharmony_ci struct p9_wstat *stat) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int res; 8462306a36Sopenharmony_ci int mode = stat->mode; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci res = mode & S_IALLUGO; 8762306a36Sopenharmony_ci if (v9fs_proto_dotu(v9ses)) { 8862306a36Sopenharmony_ci if ((mode & P9_DMSETUID) == P9_DMSETUID) 8962306a36Sopenharmony_ci res |= S_ISUID; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if ((mode & P9_DMSETGID) == P9_DMSETGID) 9262306a36Sopenharmony_ci res |= S_ISGID; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if ((mode & P9_DMSETVTX) == P9_DMSETVTX) 9562306a36Sopenharmony_ci res |= S_ISVTX; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci return res; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * p9mode2unixmode- convert plan9 mode bits to unix mode bits 10262306a36Sopenharmony_ci * @v9ses: v9fs session information 10362306a36Sopenharmony_ci * @stat: p9_wstat from which mode need to be derived 10462306a36Sopenharmony_ci * @rdev: major number, minor number in case of device files. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic umode_t p9mode2unixmode(struct v9fs_session_info *v9ses, 10862306a36Sopenharmony_ci struct p9_wstat *stat, dev_t *rdev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int res, r; 11162306a36Sopenharmony_ci u32 mode = stat->mode; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci *rdev = 0; 11462306a36Sopenharmony_ci res = p9mode2perm(v9ses, stat); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if ((mode & P9_DMDIR) == P9_DMDIR) 11762306a36Sopenharmony_ci res |= S_IFDIR; 11862306a36Sopenharmony_ci else if ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses))) 11962306a36Sopenharmony_ci res |= S_IFLNK; 12062306a36Sopenharmony_ci else if ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses)) 12162306a36Sopenharmony_ci && (v9ses->nodev == 0)) 12262306a36Sopenharmony_ci res |= S_IFSOCK; 12362306a36Sopenharmony_ci else if ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses)) 12462306a36Sopenharmony_ci && (v9ses->nodev == 0)) 12562306a36Sopenharmony_ci res |= S_IFIFO; 12662306a36Sopenharmony_ci else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses)) 12762306a36Sopenharmony_ci && (v9ses->nodev == 0)) { 12862306a36Sopenharmony_ci char type = 0; 12962306a36Sopenharmony_ci int major = -1, minor = -1; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci r = sscanf(stat->extension, "%c %i %i", &type, &major, &minor); 13262306a36Sopenharmony_ci if (r != 3) { 13362306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 13462306a36Sopenharmony_ci "invalid device string, umode will be bogus: %s\n", 13562306a36Sopenharmony_ci stat->extension); 13662306a36Sopenharmony_ci return res; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci switch (type) { 13962306a36Sopenharmony_ci case 'c': 14062306a36Sopenharmony_ci res |= S_IFCHR; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci case 'b': 14362306a36Sopenharmony_ci res |= S_IFBLK; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci default: 14662306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "Unknown special type %c %s\n", 14762306a36Sopenharmony_ci type, stat->extension); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci *rdev = MKDEV(major, minor); 15062306a36Sopenharmony_ci } else 15162306a36Sopenharmony_ci res |= S_IFREG; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return res; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits 15862306a36Sopenharmony_ci * @uflags: flags to convert 15962306a36Sopenharmony_ci * @extended: if .u extensions are active 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciint v9fs_uflags2omode(int uflags, int extended) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci switch (uflags&3) { 16762306a36Sopenharmony_ci default: 16862306a36Sopenharmony_ci case O_RDONLY: 16962306a36Sopenharmony_ci ret = P9_OREAD; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci case O_WRONLY: 17362306a36Sopenharmony_ci ret = P9_OWRITE; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci case O_RDWR: 17762306a36Sopenharmony_ci ret = P9_ORDWR; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (extended) { 18262306a36Sopenharmony_ci if (uflags & O_EXCL) 18362306a36Sopenharmony_ci ret |= P9_OEXCL; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (uflags & O_APPEND) 18662306a36Sopenharmony_ci ret |= P9_OAPPEND; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * v9fs_blank_wstat - helper function to setup a 9P stat structure 19462306a36Sopenharmony_ci * @wstat: structure to initialize 19562306a36Sopenharmony_ci * 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid 19962306a36Sopenharmony_civ9fs_blank_wstat(struct p9_wstat *wstat) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci wstat->type = ~0; 20262306a36Sopenharmony_ci wstat->dev = ~0; 20362306a36Sopenharmony_ci wstat->qid.type = ~0; 20462306a36Sopenharmony_ci wstat->qid.version = ~0; 20562306a36Sopenharmony_ci *((long long *)&wstat->qid.path) = ~0; 20662306a36Sopenharmony_ci wstat->mode = ~0; 20762306a36Sopenharmony_ci wstat->atime = ~0; 20862306a36Sopenharmony_ci wstat->mtime = ~0; 20962306a36Sopenharmony_ci wstat->length = ~0; 21062306a36Sopenharmony_ci wstat->name = NULL; 21162306a36Sopenharmony_ci wstat->uid = NULL; 21262306a36Sopenharmony_ci wstat->gid = NULL; 21362306a36Sopenharmony_ci wstat->muid = NULL; 21462306a36Sopenharmony_ci wstat->n_uid = INVALID_UID; 21562306a36Sopenharmony_ci wstat->n_gid = INVALID_GID; 21662306a36Sopenharmony_ci wstat->n_muid = INVALID_UID; 21762306a36Sopenharmony_ci wstat->extension = NULL; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/** 22162306a36Sopenharmony_ci * v9fs_alloc_inode - helper function to allocate an inode 22262306a36Sopenharmony_ci * @sb: The superblock to allocate the inode from 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistruct inode *v9fs_alloc_inode(struct super_block *sb) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct v9fs_inode *v9inode; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci v9inode = alloc_inode_sb(sb, v9fs_inode_cache, GFP_KERNEL); 22962306a36Sopenharmony_ci if (!v9inode) 23062306a36Sopenharmony_ci return NULL; 23162306a36Sopenharmony_ci v9inode->cache_validity = 0; 23262306a36Sopenharmony_ci mutex_init(&v9inode->v_mutex); 23362306a36Sopenharmony_ci return &v9inode->netfs.inode; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/** 23762306a36Sopenharmony_ci * v9fs_free_inode - destroy an inode 23862306a36Sopenharmony_ci * @inode: The inode to be freed 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_civoid v9fs_free_inode(struct inode *inode) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci kmem_cache_free(v9fs_inode_cache, V9FS_I(inode)); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* 24762306a36Sopenharmony_ci * Set parameters for the netfs library 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_civoid v9fs_set_netfs_context(struct inode *inode) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 25262306a36Sopenharmony_ci netfs_inode_init(&v9inode->netfs, &v9fs_req_ops); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciint v9fs_init_inode(struct v9fs_session_info *v9ses, 25662306a36Sopenharmony_ci struct inode *inode, umode_t mode, dev_t rdev) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int err = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci inode_init_owner(&nop_mnt_idmap, inode, NULL, mode); 26162306a36Sopenharmony_ci inode->i_blocks = 0; 26262306a36Sopenharmony_ci inode->i_rdev = rdev; 26362306a36Sopenharmony_ci inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode); 26462306a36Sopenharmony_ci inode->i_mapping->a_ops = &v9fs_addr_operations; 26562306a36Sopenharmony_ci inode->i_private = NULL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (mode & S_IFMT) { 26862306a36Sopenharmony_ci case S_IFIFO: 26962306a36Sopenharmony_ci case S_IFBLK: 27062306a36Sopenharmony_ci case S_IFCHR: 27162306a36Sopenharmony_ci case S_IFSOCK: 27262306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) { 27362306a36Sopenharmony_ci inode->i_op = &v9fs_file_inode_operations_dotl; 27462306a36Sopenharmony_ci } else if (v9fs_proto_dotu(v9ses)) { 27562306a36Sopenharmony_ci inode->i_op = &v9fs_file_inode_operations; 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 27862306a36Sopenharmony_ci "special files without extended mode\n"); 27962306a36Sopenharmony_ci err = -EINVAL; 28062306a36Sopenharmony_ci goto error; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci init_special_inode(inode, inode->i_mode, inode->i_rdev); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case S_IFREG: 28562306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) { 28662306a36Sopenharmony_ci inode->i_op = &v9fs_file_inode_operations_dotl; 28762306a36Sopenharmony_ci inode->i_fop = &v9fs_file_operations_dotl; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci inode->i_op = &v9fs_file_inode_operations; 29062306a36Sopenharmony_ci inode->i_fop = &v9fs_file_operations; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case S_IFLNK: 29562306a36Sopenharmony_ci if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) { 29662306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 29762306a36Sopenharmony_ci "extended modes used with legacy protocol\n"); 29862306a36Sopenharmony_ci err = -EINVAL; 29962306a36Sopenharmony_ci goto error; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) 30362306a36Sopenharmony_ci inode->i_op = &v9fs_symlink_inode_operations_dotl; 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci inode->i_op = &v9fs_symlink_inode_operations; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci case S_IFDIR: 30962306a36Sopenharmony_ci inc_nlink(inode); 31062306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) 31162306a36Sopenharmony_ci inode->i_op = &v9fs_dir_inode_operations_dotl; 31262306a36Sopenharmony_ci else if (v9fs_proto_dotu(v9ses)) 31362306a36Sopenharmony_ci inode->i_op = &v9fs_dir_inode_operations_dotu; 31462306a36Sopenharmony_ci else 31562306a36Sopenharmony_ci inode->i_op = &v9fs_dir_inode_operations; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) 31862306a36Sopenharmony_ci inode->i_fop = &v9fs_dir_operations_dotl; 31962306a36Sopenharmony_ci else 32062306a36Sopenharmony_ci inode->i_fop = &v9fs_dir_operations; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci default: 32462306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "BAD mode 0x%hx S_IFMT 0x%x\n", 32562306a36Sopenharmony_ci mode, mode & S_IFMT); 32662306a36Sopenharmony_ci err = -EINVAL; 32762306a36Sopenharmony_ci goto error; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_cierror: 33062306a36Sopenharmony_ci return err; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/** 33562306a36Sopenharmony_ci * v9fs_get_inode - helper function to setup an inode 33662306a36Sopenharmony_ci * @sb: superblock 33762306a36Sopenharmony_ci * @mode: mode to setup inode with 33862306a36Sopenharmony_ci * @rdev: The device numbers to set 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistruct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t rdev) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int err; 34462306a36Sopenharmony_ci struct inode *inode; 34562306a36Sopenharmony_ci struct v9fs_session_info *v9ses = sb->s_fs_info; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "super block: %p mode: %ho\n", sb, mode); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci inode = new_inode(sb); 35062306a36Sopenharmony_ci if (!inode) { 35162306a36Sopenharmony_ci pr_warn("%s (%d): Problem allocating inode\n", 35262306a36Sopenharmony_ci __func__, task_pid_nr(current)); 35362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci err = v9fs_init_inode(v9ses, inode, mode, rdev); 35662306a36Sopenharmony_ci if (err) { 35762306a36Sopenharmony_ci iput(inode); 35862306a36Sopenharmony_ci return ERR_PTR(err); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci v9fs_set_netfs_context(inode); 36162306a36Sopenharmony_ci return inode; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/** 36562306a36Sopenharmony_ci * v9fs_evict_inode - Remove an inode from the inode cache 36662306a36Sopenharmony_ci * @inode: inode to release 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_civoid v9fs_evict_inode(struct inode *inode) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct v9fs_inode __maybe_unused *v9inode = V9FS_I(inode); 37262306a36Sopenharmony_ci __le32 __maybe_unused version; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 37762306a36Sopenharmony_ci version = cpu_to_le32(v9inode->qid.version); 37862306a36Sopenharmony_ci fscache_clear_inode_writeback(v9fs_inode_cookie(v9inode), inode, 37962306a36Sopenharmony_ci &version); 38062306a36Sopenharmony_ci#endif 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci clear_inode(inode); 38362306a36Sopenharmony_ci filemap_fdatawrite(&inode->i_data); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 38662306a36Sopenharmony_ci fscache_relinquish_cookie(v9fs_inode_cookie(v9inode), false); 38762306a36Sopenharmony_ci#endif 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int v9fs_test_inode(struct inode *inode, void *data) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int umode; 39362306a36Sopenharmony_ci dev_t rdev; 39462306a36Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 39562306a36Sopenharmony_ci struct p9_wstat *st = (struct p9_wstat *)data; 39662306a36Sopenharmony_ci struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci umode = p9mode2unixmode(v9ses, st, &rdev); 39962306a36Sopenharmony_ci /* don't match inode of different type */ 40062306a36Sopenharmony_ci if (inode_wrong_type(inode, umode)) 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* compare qid details */ 40462306a36Sopenharmony_ci if (memcmp(&v9inode->qid.version, 40562306a36Sopenharmony_ci &st->qid.version, sizeof(v9inode->qid.version))) 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (v9inode->qid.type != st->qid.type) 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (v9inode->qid.path != st->qid.path) 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci return 1; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int v9fs_test_new_inode(struct inode *inode, void *data) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int v9fs_set_inode(struct inode *inode, void *data) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 42462306a36Sopenharmony_ci struct p9_wstat *st = (struct p9_wstat *)data; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci memcpy(&v9inode->qid, &st->qid, sizeof(st->qid)); 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic struct inode *v9fs_qid_iget(struct super_block *sb, 43162306a36Sopenharmony_ci struct p9_qid *qid, 43262306a36Sopenharmony_ci struct p9_wstat *st, 43362306a36Sopenharmony_ci int new) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci dev_t rdev; 43662306a36Sopenharmony_ci int retval; 43762306a36Sopenharmony_ci umode_t umode; 43862306a36Sopenharmony_ci unsigned long i_ino; 43962306a36Sopenharmony_ci struct inode *inode; 44062306a36Sopenharmony_ci struct v9fs_session_info *v9ses = sb->s_fs_info; 44162306a36Sopenharmony_ci int (*test)(struct inode *inode, void *data); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (new) 44462306a36Sopenharmony_ci test = v9fs_test_new_inode; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci test = v9fs_test_inode; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci i_ino = v9fs_qid2ino(qid); 44962306a36Sopenharmony_ci inode = iget5_locked(sb, i_ino, test, v9fs_set_inode, st); 45062306a36Sopenharmony_ci if (!inode) 45162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 45262306a36Sopenharmony_ci if (!(inode->i_state & I_NEW)) 45362306a36Sopenharmony_ci return inode; 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * initialize the inode with the stat info 45662306a36Sopenharmony_ci * FIXME!! we may need support for stale inodes 45762306a36Sopenharmony_ci * later. 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci inode->i_ino = i_ino; 46062306a36Sopenharmony_ci umode = p9mode2unixmode(v9ses, st, &rdev); 46162306a36Sopenharmony_ci retval = v9fs_init_inode(v9ses, inode, umode, rdev); 46262306a36Sopenharmony_ci if (retval) 46362306a36Sopenharmony_ci goto error; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci v9fs_stat2inode(st, inode, sb, 0); 46662306a36Sopenharmony_ci v9fs_set_netfs_context(inode); 46762306a36Sopenharmony_ci v9fs_cache_inode_get_cookie(inode); 46862306a36Sopenharmony_ci unlock_new_inode(inode); 46962306a36Sopenharmony_ci return inode; 47062306a36Sopenharmony_cierror: 47162306a36Sopenharmony_ci iget_failed(inode); 47262306a36Sopenharmony_ci return ERR_PTR(retval); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistruct inode * 47762306a36Sopenharmony_civ9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, 47862306a36Sopenharmony_ci struct super_block *sb, int new) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct p9_wstat *st; 48162306a36Sopenharmony_ci struct inode *inode = NULL; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci st = p9_client_stat(fid); 48462306a36Sopenharmony_ci if (IS_ERR(st)) 48562306a36Sopenharmony_ci return ERR_CAST(st); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci inode = v9fs_qid_iget(sb, &st->qid, st, new); 48862306a36Sopenharmony_ci p9stat_free(st); 48962306a36Sopenharmony_ci kfree(st); 49062306a36Sopenharmony_ci return inode; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * v9fs_at_to_dotl_flags- convert Linux specific AT flags to 49562306a36Sopenharmony_ci * plan 9 AT flag. 49662306a36Sopenharmony_ci * @flags: flags to convert 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_cistatic int v9fs_at_to_dotl_flags(int flags) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci int rflags = 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (flags & AT_REMOVEDIR) 50362306a36Sopenharmony_ci rflags |= P9_DOTL_AT_REMOVEDIR; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return rflags; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/** 50962306a36Sopenharmony_ci * v9fs_dec_count - helper functon to drop i_nlink. 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * If a directory had nlink <= 2 (including . and ..), then we should not drop 51262306a36Sopenharmony_ci * the link count, which indicates the underlying exported fs doesn't maintain 51362306a36Sopenharmony_ci * nlink accurately. e.g. 51462306a36Sopenharmony_ci * - overlayfs sets nlink to 1 for merged dir 51562306a36Sopenharmony_ci * - ext4 (with dir_nlink feature enabled) sets nlink to 1 if a dir has more 51662306a36Sopenharmony_ci * than EXT4_LINK_MAX (65000) links. 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * @inode: inode whose nlink is being dropped 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_cistatic void v9fs_dec_count(struct inode *inode) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) 52362306a36Sopenharmony_ci drop_nlink(inode); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/** 52762306a36Sopenharmony_ci * v9fs_remove - helper function to remove files and directories 52862306a36Sopenharmony_ci * @dir: directory inode that is being deleted 52962306a36Sopenharmony_ci * @dentry: dentry that is being deleted 53062306a36Sopenharmony_ci * @flags: removing a directory 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct inode *inode; 53762306a36Sopenharmony_ci int retval = -EOPNOTSUPP; 53862306a36Sopenharmony_ci struct p9_fid *v9fid, *dfid; 53962306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %x\n", 54262306a36Sopenharmony_ci dir, dentry, flags); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(dir); 54562306a36Sopenharmony_ci inode = d_inode(dentry); 54662306a36Sopenharmony_ci dfid = v9fs_parent_fid(dentry); 54762306a36Sopenharmony_ci if (IS_ERR(dfid)) { 54862306a36Sopenharmony_ci retval = PTR_ERR(dfid); 54962306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", retval); 55062306a36Sopenharmony_ci return retval; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) 55362306a36Sopenharmony_ci retval = p9_client_unlinkat(dfid, dentry->d_name.name, 55462306a36Sopenharmony_ci v9fs_at_to_dotl_flags(flags)); 55562306a36Sopenharmony_ci p9_fid_put(dfid); 55662306a36Sopenharmony_ci if (retval == -EOPNOTSUPP) { 55762306a36Sopenharmony_ci /* Try the one based on path */ 55862306a36Sopenharmony_ci v9fid = v9fs_fid_clone(dentry); 55962306a36Sopenharmony_ci if (IS_ERR(v9fid)) 56062306a36Sopenharmony_ci return PTR_ERR(v9fid); 56162306a36Sopenharmony_ci retval = p9_client_remove(v9fid); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci if (!retval) { 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * directories on unlink should have zero 56662306a36Sopenharmony_ci * link count 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci if (flags & AT_REMOVEDIR) { 56962306a36Sopenharmony_ci clear_nlink(inode); 57062306a36Sopenharmony_ci v9fs_dec_count(dir); 57162306a36Sopenharmony_ci } else 57262306a36Sopenharmony_ci v9fs_dec_count(inode); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci v9fs_invalidate_inode_attr(inode); 57562306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* invalidate all fids associated with dentry */ 57862306a36Sopenharmony_ci /* NOTE: This will not include open fids */ 57962306a36Sopenharmony_ci dentry->d_op->d_release(dentry); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci return retval; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/** 58562306a36Sopenharmony_ci * v9fs_create - Create a file 58662306a36Sopenharmony_ci * @v9ses: session information 58762306a36Sopenharmony_ci * @dir: directory that dentry is being created in 58862306a36Sopenharmony_ci * @dentry: dentry that is being created 58962306a36Sopenharmony_ci * @extension: 9p2000.u extension string to support devices, etc. 59062306a36Sopenharmony_ci * @perm: create permissions 59162306a36Sopenharmony_ci * @mode: open mode 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_cistatic struct p9_fid * 59562306a36Sopenharmony_civ9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, 59662306a36Sopenharmony_ci struct dentry *dentry, char *extension, u32 perm, u8 mode) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci int err; 59962306a36Sopenharmony_ci const unsigned char *name; 60062306a36Sopenharmony_ci struct p9_fid *dfid, *ofid = NULL, *fid = NULL; 60162306a36Sopenharmony_ci struct inode *inode; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci name = dentry->d_name.name; 60662306a36Sopenharmony_ci dfid = v9fs_parent_fid(dentry); 60762306a36Sopenharmony_ci if (IS_ERR(dfid)) { 60862306a36Sopenharmony_ci err = PTR_ERR(dfid); 60962306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err); 61062306a36Sopenharmony_ci return ERR_PTR(err); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* clone a fid to use for creation */ 61462306a36Sopenharmony_ci ofid = clone_fid(dfid); 61562306a36Sopenharmony_ci if (IS_ERR(ofid)) { 61662306a36Sopenharmony_ci err = PTR_ERR(ofid); 61762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); 61862306a36Sopenharmony_ci goto error; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci err = p9_client_fcreate(ofid, name, perm, mode, extension); 62262306a36Sopenharmony_ci if (err < 0) { 62362306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err); 62462306a36Sopenharmony_ci goto error; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (!(perm & P9_DMLINK)) { 62862306a36Sopenharmony_ci /* now walk from the parent so we can get unopened fid */ 62962306a36Sopenharmony_ci fid = p9_client_walk(dfid, 1, &name, 1); 63062306a36Sopenharmony_ci if (IS_ERR(fid)) { 63162306a36Sopenharmony_ci err = PTR_ERR(fid); 63262306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, 63362306a36Sopenharmony_ci "p9_client_walk failed %d\n", err); 63462306a36Sopenharmony_ci goto error; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci /* 63762306a36Sopenharmony_ci * instantiate inode and assign the unopened fid to the dentry 63862306a36Sopenharmony_ci */ 63962306a36Sopenharmony_ci inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); 64062306a36Sopenharmony_ci if (IS_ERR(inode)) { 64162306a36Sopenharmony_ci err = PTR_ERR(inode); 64262306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, 64362306a36Sopenharmony_ci "inode creation failed %d\n", err); 64462306a36Sopenharmony_ci goto error; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci v9fs_fid_add(dentry, &fid); 64762306a36Sopenharmony_ci d_instantiate(dentry, inode); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci p9_fid_put(dfid); 65062306a36Sopenharmony_ci return ofid; 65162306a36Sopenharmony_cierror: 65262306a36Sopenharmony_ci p9_fid_put(dfid); 65362306a36Sopenharmony_ci p9_fid_put(ofid); 65462306a36Sopenharmony_ci p9_fid_put(fid); 65562306a36Sopenharmony_ci return ERR_PTR(err); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/** 65962306a36Sopenharmony_ci * v9fs_vfs_create - VFS hook to create a regular file 66062306a36Sopenharmony_ci * @idmap: idmap of the mount 66162306a36Sopenharmony_ci * @dir: The parent directory 66262306a36Sopenharmony_ci * @dentry: The name of file to be created 66362306a36Sopenharmony_ci * @mode: The UNIX file mode to set 66462306a36Sopenharmony_ci * @excl: True if the file must not yet exist 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * open(.., O_CREAT) is handled in v9fs_vfs_atomic_open(). This is only called 66762306a36Sopenharmony_ci * for mknod(2). 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int 67262306a36Sopenharmony_civ9fs_vfs_create(struct mnt_idmap *idmap, struct inode *dir, 67362306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, bool excl) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 67662306a36Sopenharmony_ci u32 perm = unixmode2p9mode(v9ses, mode); 67762306a36Sopenharmony_ci struct p9_fid *fid; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* P9_OEXCL? */ 68062306a36Sopenharmony_ci fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_ORDWR); 68162306a36Sopenharmony_ci if (IS_ERR(fid)) 68262306a36Sopenharmony_ci return PTR_ERR(fid); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 68562306a36Sopenharmony_ci p9_fid_put(fid); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/** 69162306a36Sopenharmony_ci * v9fs_vfs_mkdir - VFS mkdir hook to create a directory 69262306a36Sopenharmony_ci * @idmap: idmap of the mount 69362306a36Sopenharmony_ci * @dir: inode that is being unlinked 69462306a36Sopenharmony_ci * @dentry: dentry that is being unlinked 69562306a36Sopenharmony_ci * @mode: mode for new directory 69662306a36Sopenharmony_ci * 69762306a36Sopenharmony_ci */ 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int v9fs_vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, 70062306a36Sopenharmony_ci struct dentry *dentry, umode_t mode) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci int err; 70362306a36Sopenharmony_ci u32 perm; 70462306a36Sopenharmony_ci struct p9_fid *fid; 70562306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry); 70862306a36Sopenharmony_ci err = 0; 70962306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(dir); 71062306a36Sopenharmony_ci perm = unixmode2p9mode(v9ses, mode | S_IFDIR); 71162306a36Sopenharmony_ci fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD); 71262306a36Sopenharmony_ci if (IS_ERR(fid)) { 71362306a36Sopenharmony_ci err = PTR_ERR(fid); 71462306a36Sopenharmony_ci fid = NULL; 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci inc_nlink(dir); 71762306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (fid) 72162306a36Sopenharmony_ci p9_fid_put(fid); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return err; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/** 72762306a36Sopenharmony_ci * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode 72862306a36Sopenharmony_ci * @dir: inode that is being walked from 72962306a36Sopenharmony_ci * @dentry: dentry that is being walked to? 73062306a36Sopenharmony_ci * @flags: lookup flags (unused) 73162306a36Sopenharmony_ci * 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistruct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, 73562306a36Sopenharmony_ci unsigned int flags) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct dentry *res; 73862306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 73962306a36Sopenharmony_ci struct p9_fid *dfid, *fid; 74062306a36Sopenharmony_ci struct inode *inode; 74162306a36Sopenharmony_ci const unsigned char *name; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "dir: %p dentry: (%pd) %p flags: %x\n", 74462306a36Sopenharmony_ci dir, dentry, dentry, flags); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (dentry->d_name.len > NAME_MAX) 74762306a36Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(dir); 75062306a36Sopenharmony_ci /* We can walk d_parent because we hold the dir->i_mutex */ 75162306a36Sopenharmony_ci dfid = v9fs_parent_fid(dentry); 75262306a36Sopenharmony_ci if (IS_ERR(dfid)) 75362306a36Sopenharmony_ci return ERR_CAST(dfid); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* 75662306a36Sopenharmony_ci * Make sure we don't use a wrong inode due to parallel 75762306a36Sopenharmony_ci * unlink. For cached mode create calls request for new 75862306a36Sopenharmony_ci * inode. But with cache disabled, lookup should do this. 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ci name = dentry->d_name.name; 76162306a36Sopenharmony_ci fid = p9_client_walk(dfid, 1, &name, 1); 76262306a36Sopenharmony_ci p9_fid_put(dfid); 76362306a36Sopenharmony_ci if (fid == ERR_PTR(-ENOENT)) 76462306a36Sopenharmony_ci inode = NULL; 76562306a36Sopenharmony_ci else if (IS_ERR(fid)) 76662306a36Sopenharmony_ci inode = ERR_CAST(fid); 76762306a36Sopenharmony_ci else if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) 76862306a36Sopenharmony_ci inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb); 76962306a36Sopenharmony_ci else 77062306a36Sopenharmony_ci inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); 77162306a36Sopenharmony_ci /* 77262306a36Sopenharmony_ci * If we had a rename on the server and a parallel lookup 77362306a36Sopenharmony_ci * for the new name, then make sure we instantiate with 77462306a36Sopenharmony_ci * the new name. ie look up for a/b, while on server somebody 77562306a36Sopenharmony_ci * moved b under k and client parallely did a lookup for 77662306a36Sopenharmony_ci * k/b. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci res = d_splice_alias(inode, dentry); 77962306a36Sopenharmony_ci if (!IS_ERR(fid)) { 78062306a36Sopenharmony_ci if (!res) 78162306a36Sopenharmony_ci v9fs_fid_add(dentry, &fid); 78262306a36Sopenharmony_ci else if (!IS_ERR(res)) 78362306a36Sopenharmony_ci v9fs_fid_add(res, &fid); 78462306a36Sopenharmony_ci else 78562306a36Sopenharmony_ci p9_fid_put(fid); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci return res; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int 79162306a36Sopenharmony_civ9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, 79262306a36Sopenharmony_ci struct file *file, unsigned int flags, umode_t mode) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci int err; 79562306a36Sopenharmony_ci u32 perm; 79662306a36Sopenharmony_ci struct v9fs_inode __maybe_unused *v9inode; 79762306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 79862306a36Sopenharmony_ci struct p9_fid *fid; 79962306a36Sopenharmony_ci struct dentry *res = NULL; 80062306a36Sopenharmony_ci struct inode *inode; 80162306a36Sopenharmony_ci int p9_omode; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (d_in_lookup(dentry)) { 80462306a36Sopenharmony_ci res = v9fs_vfs_lookup(dir, dentry, 0); 80562306a36Sopenharmony_ci if (IS_ERR(res)) 80662306a36Sopenharmony_ci return PTR_ERR(res); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (res) 80962306a36Sopenharmony_ci dentry = res; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* Only creates */ 81362306a36Sopenharmony_ci if (!(flags & O_CREAT) || d_really_is_positive(dentry)) 81462306a36Sopenharmony_ci return finish_no_open(file, res); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(dir); 81762306a36Sopenharmony_ci perm = unixmode2p9mode(v9ses, mode); 81862306a36Sopenharmony_ci p9_omode = v9fs_uflags2omode(flags, v9fs_proto_dotu(v9ses)); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if ((v9ses->cache & CACHE_WRITEBACK) && (p9_omode & P9_OWRITE)) { 82162306a36Sopenharmony_ci p9_omode = (p9_omode & ~P9_OWRITE) | P9_ORDWR; 82262306a36Sopenharmony_ci p9_debug(P9_DEBUG_CACHE, 82362306a36Sopenharmony_ci "write-only file with writeback enabled, creating w/ O_RDWR\n"); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci fid = v9fs_create(v9ses, dir, dentry, NULL, perm, p9_omode); 82662306a36Sopenharmony_ci if (IS_ERR(fid)) { 82762306a36Sopenharmony_ci err = PTR_ERR(fid); 82862306a36Sopenharmony_ci goto error; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 83262306a36Sopenharmony_ci inode = d_inode(dentry); 83362306a36Sopenharmony_ci v9inode = V9FS_I(inode); 83462306a36Sopenharmony_ci err = finish_open(file, dentry, generic_file_open); 83562306a36Sopenharmony_ci if (err) 83662306a36Sopenharmony_ci goto error; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci file->private_data = fid; 83962306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 84062306a36Sopenharmony_ci if (v9ses->cache & CACHE_FSCACHE) 84162306a36Sopenharmony_ci fscache_use_cookie(v9fs_inode_cookie(v9inode), 84262306a36Sopenharmony_ci file->f_mode & FMODE_WRITE); 84362306a36Sopenharmony_ci#endif 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci v9fs_fid_add_modes(fid, v9ses->flags, v9ses->cache, file->f_flags); 84662306a36Sopenharmony_ci v9fs_open_fid_add(inode, &fid); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci file->f_mode |= FMODE_CREATED; 84962306a36Sopenharmony_ciout: 85062306a36Sopenharmony_ci dput(res); 85162306a36Sopenharmony_ci return err; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cierror: 85462306a36Sopenharmony_ci p9_fid_put(fid); 85562306a36Sopenharmony_ci goto out; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci/** 85962306a36Sopenharmony_ci * v9fs_vfs_unlink - VFS unlink hook to delete an inode 86062306a36Sopenharmony_ci * @i: inode that is being unlinked 86162306a36Sopenharmony_ci * @d: dentry that is being unlinked 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci */ 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciint v9fs_vfs_unlink(struct inode *i, struct dentry *d) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci return v9fs_remove(i, d, 0); 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/** 87162306a36Sopenharmony_ci * v9fs_vfs_rmdir - VFS unlink hook to delete a directory 87262306a36Sopenharmony_ci * @i: inode that is being unlinked 87362306a36Sopenharmony_ci * @d: dentry that is being unlinked 87462306a36Sopenharmony_ci * 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciint v9fs_vfs_rmdir(struct inode *i, struct dentry *d) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci return v9fs_remove(i, d, AT_REMOVEDIR); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/** 88362306a36Sopenharmony_ci * v9fs_vfs_rename - VFS hook to rename an inode 88462306a36Sopenharmony_ci * @idmap: The idmap of the mount 88562306a36Sopenharmony_ci * @old_dir: old dir inode 88662306a36Sopenharmony_ci * @old_dentry: old dentry 88762306a36Sopenharmony_ci * @new_dir: new dir inode 88862306a36Sopenharmony_ci * @new_dentry: new dentry 88962306a36Sopenharmony_ci * @flags: RENAME_* flags 89062306a36Sopenharmony_ci * 89162306a36Sopenharmony_ci */ 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ciint 89462306a36Sopenharmony_civ9fs_vfs_rename(struct mnt_idmap *idmap, struct inode *old_dir, 89562306a36Sopenharmony_ci struct dentry *old_dentry, struct inode *new_dir, 89662306a36Sopenharmony_ci struct dentry *new_dentry, unsigned int flags) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci int retval; 89962306a36Sopenharmony_ci struct inode *old_inode; 90062306a36Sopenharmony_ci struct inode *new_inode; 90162306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 90262306a36Sopenharmony_ci struct p9_fid *oldfid = NULL, *dfid = NULL; 90362306a36Sopenharmony_ci struct p9_fid *olddirfid = NULL; 90462306a36Sopenharmony_ci struct p9_fid *newdirfid = NULL; 90562306a36Sopenharmony_ci struct p9_wstat wstat; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (flags) 90862306a36Sopenharmony_ci return -EINVAL; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "\n"); 91162306a36Sopenharmony_ci old_inode = d_inode(old_dentry); 91262306a36Sopenharmony_ci new_inode = d_inode(new_dentry); 91362306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(old_inode); 91462306a36Sopenharmony_ci oldfid = v9fs_fid_lookup(old_dentry); 91562306a36Sopenharmony_ci if (IS_ERR(oldfid)) 91662306a36Sopenharmony_ci return PTR_ERR(oldfid); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci dfid = v9fs_parent_fid(old_dentry); 91962306a36Sopenharmony_ci olddirfid = clone_fid(dfid); 92062306a36Sopenharmony_ci p9_fid_put(dfid); 92162306a36Sopenharmony_ci dfid = NULL; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (IS_ERR(olddirfid)) { 92462306a36Sopenharmony_ci retval = PTR_ERR(olddirfid); 92562306a36Sopenharmony_ci goto error; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dfid = v9fs_parent_fid(new_dentry); 92962306a36Sopenharmony_ci newdirfid = clone_fid(dfid); 93062306a36Sopenharmony_ci p9_fid_put(dfid); 93162306a36Sopenharmony_ci dfid = NULL; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (IS_ERR(newdirfid)) { 93462306a36Sopenharmony_ci retval = PTR_ERR(newdirfid); 93562306a36Sopenharmony_ci goto error; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci down_write(&v9ses->rename_sem); 93962306a36Sopenharmony_ci if (v9fs_proto_dotl(v9ses)) { 94062306a36Sopenharmony_ci retval = p9_client_renameat(olddirfid, old_dentry->d_name.name, 94162306a36Sopenharmony_ci newdirfid, new_dentry->d_name.name); 94262306a36Sopenharmony_ci if (retval == -EOPNOTSUPP) 94362306a36Sopenharmony_ci retval = p9_client_rename(oldfid, newdirfid, 94462306a36Sopenharmony_ci new_dentry->d_name.name); 94562306a36Sopenharmony_ci if (retval != -EOPNOTSUPP) 94662306a36Sopenharmony_ci goto error_locked; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci if (old_dentry->d_parent != new_dentry->d_parent) { 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * 9P .u can only handle file rename in the same directory 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "old dir and new dir are different\n"); 95462306a36Sopenharmony_ci retval = -EXDEV; 95562306a36Sopenharmony_ci goto error_locked; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci v9fs_blank_wstat(&wstat); 95862306a36Sopenharmony_ci wstat.muid = v9ses->uname; 95962306a36Sopenharmony_ci wstat.name = new_dentry->d_name.name; 96062306a36Sopenharmony_ci retval = p9_client_wstat(oldfid, &wstat); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cierror_locked: 96362306a36Sopenharmony_ci if (!retval) { 96462306a36Sopenharmony_ci if (new_inode) { 96562306a36Sopenharmony_ci if (S_ISDIR(new_inode->i_mode)) 96662306a36Sopenharmony_ci clear_nlink(new_inode); 96762306a36Sopenharmony_ci else 96862306a36Sopenharmony_ci v9fs_dec_count(new_inode); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci if (S_ISDIR(old_inode->i_mode)) { 97162306a36Sopenharmony_ci if (!new_inode) 97262306a36Sopenharmony_ci inc_nlink(new_dir); 97362306a36Sopenharmony_ci v9fs_dec_count(old_dir); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci v9fs_invalidate_inode_attr(old_inode); 97662306a36Sopenharmony_ci v9fs_invalidate_inode_attr(old_dir); 97762306a36Sopenharmony_ci v9fs_invalidate_inode_attr(new_dir); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* successful rename */ 98062306a36Sopenharmony_ci d_move(old_dentry, new_dentry); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci up_write(&v9ses->rename_sem); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cierror: 98562306a36Sopenharmony_ci p9_fid_put(newdirfid); 98662306a36Sopenharmony_ci p9_fid_put(olddirfid); 98762306a36Sopenharmony_ci p9_fid_put(oldfid); 98862306a36Sopenharmony_ci return retval; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/** 99262306a36Sopenharmony_ci * v9fs_vfs_getattr - retrieve file metadata 99362306a36Sopenharmony_ci * @idmap: idmap of the mount 99462306a36Sopenharmony_ci * @path: Object to query 99562306a36Sopenharmony_ci * @stat: metadata structure to populate 99662306a36Sopenharmony_ci * @request_mask: Mask of STATX_xxx flags indicating the caller's interests 99762306a36Sopenharmony_ci * @flags: AT_STATX_xxx setting 99862306a36Sopenharmony_ci * 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic int 100262306a36Sopenharmony_civ9fs_vfs_getattr(struct mnt_idmap *idmap, const struct path *path, 100362306a36Sopenharmony_ci struct kstat *stat, u32 request_mask, unsigned int flags) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 100662306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 100762306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 100862306a36Sopenharmony_ci struct p9_fid *fid; 100962306a36Sopenharmony_ci struct p9_wstat *st; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry); 101262306a36Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 101362306a36Sopenharmony_ci if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) { 101462306a36Sopenharmony_ci generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci } else if (v9ses->cache & CACHE_WRITEBACK) { 101762306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 101862306a36Sopenharmony_ci int retval = filemap_fdatawrite(inode->i_mapping); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (retval) 102162306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 102262306a36Sopenharmony_ci "flushing writeback during getattr returned %d\n", retval); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci fid = v9fs_fid_lookup(dentry); 102662306a36Sopenharmony_ci if (IS_ERR(fid)) 102762306a36Sopenharmony_ci return PTR_ERR(fid); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci st = p9_client_stat(fid); 103062306a36Sopenharmony_ci p9_fid_put(fid); 103162306a36Sopenharmony_ci if (IS_ERR(st)) 103262306a36Sopenharmony_ci return PTR_ERR(st); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci v9fs_stat2inode(st, d_inode(dentry), dentry->d_sb, 0); 103562306a36Sopenharmony_ci generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(dentry), stat); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci p9stat_free(st); 103862306a36Sopenharmony_ci kfree(st); 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci/** 104362306a36Sopenharmony_ci * v9fs_vfs_setattr - set file metadata 104462306a36Sopenharmony_ci * @idmap: idmap of the mount 104562306a36Sopenharmony_ci * @dentry: file whose metadata to set 104662306a36Sopenharmony_ci * @iattr: metadata assignment structure 104762306a36Sopenharmony_ci * 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic int v9fs_vfs_setattr(struct mnt_idmap *idmap, 105162306a36Sopenharmony_ci struct dentry *dentry, struct iattr *iattr) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci int retval, use_dentry = 0; 105462306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 105562306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 105662306a36Sopenharmony_ci struct p9_fid *fid = NULL; 105762306a36Sopenharmony_ci struct p9_wstat wstat; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "\n"); 106062306a36Sopenharmony_ci retval = setattr_prepare(&nop_mnt_idmap, dentry, iattr); 106162306a36Sopenharmony_ci if (retval) 106262306a36Sopenharmony_ci return retval; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 106562306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_FILE) { 106662306a36Sopenharmony_ci fid = iattr->ia_file->private_data; 106762306a36Sopenharmony_ci WARN_ON(!fid); 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci if (!fid) { 107062306a36Sopenharmony_ci fid = v9fs_fid_lookup(dentry); 107162306a36Sopenharmony_ci use_dentry = 1; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci if (IS_ERR(fid)) 107462306a36Sopenharmony_ci return PTR_ERR(fid); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci v9fs_blank_wstat(&wstat); 107762306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_MODE) 107862306a36Sopenharmony_ci wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_MTIME) 108162306a36Sopenharmony_ci wstat.mtime = iattr->ia_mtime.tv_sec; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_ATIME) 108462306a36Sopenharmony_ci wstat.atime = iattr->ia_atime.tv_sec; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_SIZE) 108762306a36Sopenharmony_ci wstat.length = iattr->ia_size; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (v9fs_proto_dotu(v9ses)) { 109062306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_UID) 109162306a36Sopenharmony_ci wstat.n_uid = iattr->ia_uid; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (iattr->ia_valid & ATTR_GID) 109462306a36Sopenharmony_ci wstat.n_gid = iattr->ia_gid; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* Write all dirty data */ 109862306a36Sopenharmony_ci if (d_is_reg(dentry)) { 109962306a36Sopenharmony_ci retval = filemap_fdatawrite(inode->i_mapping); 110062306a36Sopenharmony_ci if (retval) 110162306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 110262306a36Sopenharmony_ci "flushing writeback during setattr returned %d\n", retval); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci retval = p9_client_wstat(fid, &wstat); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (use_dentry) 110862306a36Sopenharmony_ci p9_fid_put(fid); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (retval < 0) 111162306a36Sopenharmony_ci return retval; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if ((iattr->ia_valid & ATTR_SIZE) && 111462306a36Sopenharmony_ci iattr->ia_size != i_size_read(inode)) { 111562306a36Sopenharmony_ci truncate_setsize(inode, iattr->ia_size); 111662306a36Sopenharmony_ci truncate_pagecache(inode, iattr->ia_size); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 111962306a36Sopenharmony_ci if (v9ses->cache & CACHE_FSCACHE) { 112062306a36Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci fscache_resize_cookie(v9fs_inode_cookie(v9inode), iattr->ia_size); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci#endif 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci v9fs_invalidate_inode_attr(inode); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, iattr); 113062306a36Sopenharmony_ci mark_inode_dirty(inode); 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/** 113562306a36Sopenharmony_ci * v9fs_stat2inode - populate an inode structure with mistat info 113662306a36Sopenharmony_ci * @stat: Plan 9 metadata (mistat) structure 113762306a36Sopenharmony_ci * @inode: inode to populate 113862306a36Sopenharmony_ci * @sb: superblock of filesystem 113962306a36Sopenharmony_ci * @flags: control flags (e.g. V9FS_STAT2INODE_KEEP_ISIZE) 114062306a36Sopenharmony_ci * 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_civoid 114462306a36Sopenharmony_civ9fs_stat2inode(struct p9_wstat *stat, struct inode *inode, 114562306a36Sopenharmony_ci struct super_block *sb, unsigned int flags) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci umode_t mode; 114862306a36Sopenharmony_ci struct v9fs_session_info *v9ses = sb->s_fs_info; 114962306a36Sopenharmony_ci struct v9fs_inode *v9inode = V9FS_I(inode); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci set_nlink(inode, 1); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci inode->i_atime.tv_sec = stat->atime; 115462306a36Sopenharmony_ci inode->i_mtime.tv_sec = stat->mtime; 115562306a36Sopenharmony_ci inode_set_ctime(inode, stat->mtime, 0); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci inode->i_uid = v9ses->dfltuid; 115862306a36Sopenharmony_ci inode->i_gid = v9ses->dfltgid; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (v9fs_proto_dotu(v9ses)) { 116162306a36Sopenharmony_ci inode->i_uid = stat->n_uid; 116262306a36Sopenharmony_ci inode->i_gid = stat->n_gid; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) { 116562306a36Sopenharmony_ci if (v9fs_proto_dotu(v9ses)) { 116662306a36Sopenharmony_ci unsigned int i_nlink; 116762306a36Sopenharmony_ci /* 116862306a36Sopenharmony_ci * Hadlink support got added later to the .u extension. 116962306a36Sopenharmony_ci * So there can be a server out there that doesn't 117062306a36Sopenharmony_ci * support this even with .u extension. That would 117162306a36Sopenharmony_ci * just leave us with stat->extension being an empty 117262306a36Sopenharmony_ci * string, though. 117362306a36Sopenharmony_ci */ 117462306a36Sopenharmony_ci /* HARDLINKCOUNT %u */ 117562306a36Sopenharmony_ci if (sscanf(stat->extension, 117662306a36Sopenharmony_ci " HARDLINKCOUNT %u", &i_nlink) == 1) 117762306a36Sopenharmony_ci set_nlink(inode, i_nlink); 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci mode = p9mode2perm(v9ses, stat); 118162306a36Sopenharmony_ci mode |= inode->i_mode & ~S_IALLUGO; 118262306a36Sopenharmony_ci inode->i_mode = mode; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) 118562306a36Sopenharmony_ci v9fs_i_size_write(inode, stat->length); 118662306a36Sopenharmony_ci /* not real number of blocks, but 512 byte ones ... */ 118762306a36Sopenharmony_ci inode->i_blocks = (stat->length + 512 - 1) >> 9; 118862306a36Sopenharmony_ci v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci/** 119262306a36Sopenharmony_ci * v9fs_qid2ino - convert qid into inode number 119362306a36Sopenharmony_ci * @qid: qid to hash 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * BUG: potential for inode number collisions? 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ciino_t v9fs_qid2ino(struct p9_qid *qid) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci u64 path = qid->path + 2; 120162306a36Sopenharmony_ci ino_t i = 0; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (sizeof(ino_t) == sizeof(path)) 120462306a36Sopenharmony_ci memcpy(&i, &path, sizeof(ino_t)); 120562306a36Sopenharmony_ci else 120662306a36Sopenharmony_ci i = (ino_t) (path ^ (path >> 32)); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return i; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci/** 121262306a36Sopenharmony_ci * v9fs_vfs_get_link - follow a symlink path 121362306a36Sopenharmony_ci * @dentry: dentry for symlink 121462306a36Sopenharmony_ci * @inode: inode for symlink 121562306a36Sopenharmony_ci * @done: delayed call for when we are done with the return value 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic const char *v9fs_vfs_get_link(struct dentry *dentry, 121962306a36Sopenharmony_ci struct inode *inode, 122062306a36Sopenharmony_ci struct delayed_call *done) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 122362306a36Sopenharmony_ci struct p9_fid *fid; 122462306a36Sopenharmony_ci struct p9_wstat *st; 122562306a36Sopenharmony_ci char *res; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!dentry) 122862306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 123162306a36Sopenharmony_ci if (!v9fs_proto_dotu(v9ses)) 123262306a36Sopenharmony_ci return ERR_PTR(-EBADF); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); 123562306a36Sopenharmony_ci fid = v9fs_fid_lookup(dentry); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (IS_ERR(fid)) 123862306a36Sopenharmony_ci return ERR_CAST(fid); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci st = p9_client_stat(fid); 124162306a36Sopenharmony_ci p9_fid_put(fid); 124262306a36Sopenharmony_ci if (IS_ERR(st)) 124362306a36Sopenharmony_ci return ERR_CAST(st); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (!(st->mode & P9_DMSYMLINK)) { 124662306a36Sopenharmony_ci p9stat_free(st); 124762306a36Sopenharmony_ci kfree(st); 124862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci res = st->extension; 125162306a36Sopenharmony_ci st->extension = NULL; 125262306a36Sopenharmony_ci if (strlen(res) >= PATH_MAX) 125362306a36Sopenharmony_ci res[PATH_MAX - 1] = '\0'; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci p9stat_free(st); 125662306a36Sopenharmony_ci kfree(st); 125762306a36Sopenharmony_ci set_delayed_call(done, kfree_link, res); 125862306a36Sopenharmony_ci return res; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci/** 126262306a36Sopenharmony_ci * v9fs_vfs_mkspecial - create a special file 126362306a36Sopenharmony_ci * @dir: inode to create special file in 126462306a36Sopenharmony_ci * @dentry: dentry to create 126562306a36Sopenharmony_ci * @perm: mode to create special file 126662306a36Sopenharmony_ci * @extension: 9p2000.u format extension string representing special file 126762306a36Sopenharmony_ci * 126862306a36Sopenharmony_ci */ 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, 127162306a36Sopenharmony_ci u32 perm, const char *extension) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct p9_fid *fid; 127462306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(dir); 127762306a36Sopenharmony_ci if (!v9fs_proto_dotu(v9ses)) { 127862306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "not extended\n"); 127962306a36Sopenharmony_ci return -EPERM; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm, 128362306a36Sopenharmony_ci P9_OREAD); 128462306a36Sopenharmony_ci if (IS_ERR(fid)) 128562306a36Sopenharmony_ci return PTR_ERR(fid); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 128862306a36Sopenharmony_ci p9_fid_put(fid); 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/** 129362306a36Sopenharmony_ci * v9fs_vfs_symlink - helper function to create symlinks 129462306a36Sopenharmony_ci * @idmap: idmap of the mount 129562306a36Sopenharmony_ci * @dir: directory inode containing symlink 129662306a36Sopenharmony_ci * @dentry: dentry for symlink 129762306a36Sopenharmony_ci * @symname: symlink data 129862306a36Sopenharmony_ci * 129962306a36Sopenharmony_ci * See Also: 9P2000.u RFC for more information 130062306a36Sopenharmony_ci * 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic int 130462306a36Sopenharmony_civ9fs_vfs_symlink(struct mnt_idmap *idmap, struct inode *dir, 130562306a36Sopenharmony_ci struct dentry *dentry, const char *symname) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, " %lu,%pd,%s\n", 130862306a36Sopenharmony_ci dir->i_ino, dentry, symname); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname); 131162306a36Sopenharmony_ci} 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci#define U32_MAX_DIGITS 10 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci/** 131662306a36Sopenharmony_ci * v9fs_vfs_link - create a hardlink 131762306a36Sopenharmony_ci * @old_dentry: dentry for file to link to 131862306a36Sopenharmony_ci * @dir: inode destination for new link 131962306a36Sopenharmony_ci * @dentry: dentry for link 132062306a36Sopenharmony_ci * 132162306a36Sopenharmony_ci */ 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic int 132462306a36Sopenharmony_civ9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, 132562306a36Sopenharmony_ci struct dentry *dentry) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci int retval; 132862306a36Sopenharmony_ci char name[1 + U32_MAX_DIGITS + 2]; /* sign + number + \n + \0 */ 132962306a36Sopenharmony_ci struct p9_fid *oldfid; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, " %lu,%pd,%pd\n", 133262306a36Sopenharmony_ci dir->i_ino, dentry, old_dentry); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci oldfid = v9fs_fid_clone(old_dentry); 133562306a36Sopenharmony_ci if (IS_ERR(oldfid)) 133662306a36Sopenharmony_ci return PTR_ERR(oldfid); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci sprintf(name, "%d\n", oldfid->fid); 133962306a36Sopenharmony_ci retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name); 134062306a36Sopenharmony_ci if (!retval) { 134162306a36Sopenharmony_ci v9fs_refresh_inode(oldfid, d_inode(old_dentry)); 134262306a36Sopenharmony_ci v9fs_invalidate_inode_attr(dir); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci p9_fid_put(oldfid); 134562306a36Sopenharmony_ci return retval; 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci/** 134962306a36Sopenharmony_ci * v9fs_vfs_mknod - create a special file 135062306a36Sopenharmony_ci * @idmap: idmap of the mount 135162306a36Sopenharmony_ci * @dir: inode destination for new link 135262306a36Sopenharmony_ci * @dentry: dentry for file 135362306a36Sopenharmony_ci * @mode: mode for creation 135462306a36Sopenharmony_ci * @rdev: device associated with special file 135562306a36Sopenharmony_ci * 135662306a36Sopenharmony_ci */ 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic int 135962306a36Sopenharmony_civ9fs_vfs_mknod(struct mnt_idmap *idmap, struct inode *dir, 136062306a36Sopenharmony_ci struct dentry *dentry, umode_t mode, dev_t rdev) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); 136362306a36Sopenharmony_ci int retval; 136462306a36Sopenharmony_ci char name[2 + U32_MAX_DIGITS + 1 + U32_MAX_DIGITS + 1]; 136562306a36Sopenharmony_ci u32 perm; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %x MAJOR: %u MINOR: %u\n", 136862306a36Sopenharmony_ci dir->i_ino, dentry, mode, 136962306a36Sopenharmony_ci MAJOR(rdev), MINOR(rdev)); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* build extension */ 137262306a36Sopenharmony_ci if (S_ISBLK(mode)) 137362306a36Sopenharmony_ci sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev)); 137462306a36Sopenharmony_ci else if (S_ISCHR(mode)) 137562306a36Sopenharmony_ci sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev)); 137662306a36Sopenharmony_ci else 137762306a36Sopenharmony_ci *name = 0; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci perm = unixmode2p9mode(v9ses, mode); 138062306a36Sopenharmony_ci retval = v9fs_vfs_mkspecial(dir, dentry, perm, name); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci return retval; 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ciint v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci int umode; 138862306a36Sopenharmony_ci dev_t rdev; 138962306a36Sopenharmony_ci struct p9_wstat *st; 139062306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 139162306a36Sopenharmony_ci unsigned int flags; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 139462306a36Sopenharmony_ci st = p9_client_stat(fid); 139562306a36Sopenharmony_ci if (IS_ERR(st)) 139662306a36Sopenharmony_ci return PTR_ERR(st); 139762306a36Sopenharmony_ci /* 139862306a36Sopenharmony_ci * Don't update inode if the file type is different 139962306a36Sopenharmony_ci */ 140062306a36Sopenharmony_ci umode = p9mode2unixmode(v9ses, st, &rdev); 140162306a36Sopenharmony_ci if (inode_wrong_type(inode, umode)) 140262306a36Sopenharmony_ci goto out; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* 140562306a36Sopenharmony_ci * We don't want to refresh inode->i_size, 140662306a36Sopenharmony_ci * because we may have cached data 140762306a36Sopenharmony_ci */ 140862306a36Sopenharmony_ci flags = (v9ses->cache & CACHE_LOOSE) ? 140962306a36Sopenharmony_ci V9FS_STAT2INODE_KEEP_ISIZE : 0; 141062306a36Sopenharmony_ci v9fs_stat2inode(st, inode, inode->i_sb, flags); 141162306a36Sopenharmony_ciout: 141262306a36Sopenharmony_ci p9stat_free(st); 141362306a36Sopenharmony_ci kfree(st); 141462306a36Sopenharmony_ci return 0; 141562306a36Sopenharmony_ci} 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_cistatic const struct inode_operations v9fs_dir_inode_operations_dotu = { 141862306a36Sopenharmony_ci .create = v9fs_vfs_create, 141962306a36Sopenharmony_ci .lookup = v9fs_vfs_lookup, 142062306a36Sopenharmony_ci .atomic_open = v9fs_vfs_atomic_open, 142162306a36Sopenharmony_ci .symlink = v9fs_vfs_symlink, 142262306a36Sopenharmony_ci .link = v9fs_vfs_link, 142362306a36Sopenharmony_ci .unlink = v9fs_vfs_unlink, 142462306a36Sopenharmony_ci .mkdir = v9fs_vfs_mkdir, 142562306a36Sopenharmony_ci .rmdir = v9fs_vfs_rmdir, 142662306a36Sopenharmony_ci .mknod = v9fs_vfs_mknod, 142762306a36Sopenharmony_ci .rename = v9fs_vfs_rename, 142862306a36Sopenharmony_ci .getattr = v9fs_vfs_getattr, 142962306a36Sopenharmony_ci .setattr = v9fs_vfs_setattr, 143062306a36Sopenharmony_ci}; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic const struct inode_operations v9fs_dir_inode_operations = { 143362306a36Sopenharmony_ci .create = v9fs_vfs_create, 143462306a36Sopenharmony_ci .lookup = v9fs_vfs_lookup, 143562306a36Sopenharmony_ci .atomic_open = v9fs_vfs_atomic_open, 143662306a36Sopenharmony_ci .unlink = v9fs_vfs_unlink, 143762306a36Sopenharmony_ci .mkdir = v9fs_vfs_mkdir, 143862306a36Sopenharmony_ci .rmdir = v9fs_vfs_rmdir, 143962306a36Sopenharmony_ci .mknod = v9fs_vfs_mknod, 144062306a36Sopenharmony_ci .rename = v9fs_vfs_rename, 144162306a36Sopenharmony_ci .getattr = v9fs_vfs_getattr, 144262306a36Sopenharmony_ci .setattr = v9fs_vfs_setattr, 144362306a36Sopenharmony_ci}; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_cistatic const struct inode_operations v9fs_file_inode_operations = { 144662306a36Sopenharmony_ci .getattr = v9fs_vfs_getattr, 144762306a36Sopenharmony_ci .setattr = v9fs_vfs_setattr, 144862306a36Sopenharmony_ci}; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic const struct inode_operations v9fs_symlink_inode_operations = { 145162306a36Sopenharmony_ci .get_link = v9fs_vfs_get_link, 145262306a36Sopenharmony_ci .getattr = v9fs_vfs_getattr, 145362306a36Sopenharmony_ci .setattr = v9fs_vfs_setattr, 145462306a36Sopenharmony_ci}; 145562306a36Sopenharmony_ci 1456