162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains functions assisting in mapping VFS to 9P2000 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004-2008 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/sched.h> 1562306a36Sopenharmony_ci#include <linux/cred.h> 1662306a36Sopenharmony_ci#include <linux/parser.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/seq_file.h> 1962306a36Sopenharmony_ci#include <net/9p/9p.h> 2062306a36Sopenharmony_ci#include <net/9p/client.h> 2162306a36Sopenharmony_ci#include <net/9p/transport.h> 2262306a36Sopenharmony_ci#include "v9fs.h" 2362306a36Sopenharmony_ci#include "v9fs_vfs.h" 2462306a36Sopenharmony_ci#include "cache.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(v9fs_sessionlist_lock); 2762306a36Sopenharmony_cistatic LIST_HEAD(v9fs_sessionlist); 2862306a36Sopenharmony_cistruct kmem_cache *v9fs_inode_cache; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Option Parsing (code inspired by NFS code) 3262306a36Sopenharmony_ci * NOTE: each transport will parse its own options 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum { 3662306a36Sopenharmony_ci /* Options that take integer arguments */ 3762306a36Sopenharmony_ci Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, 3862306a36Sopenharmony_ci /* String options */ 3962306a36Sopenharmony_ci Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag, 4062306a36Sopenharmony_ci /* Options that take no arguments */ 4162306a36Sopenharmony_ci Opt_nodevmap, Opt_noxattr, Opt_directio, Opt_ignoreqv, 4262306a36Sopenharmony_ci /* Access options */ 4362306a36Sopenharmony_ci Opt_access, Opt_posixacl, 4462306a36Sopenharmony_ci /* Lock timeout option */ 4562306a36Sopenharmony_ci Opt_locktimeout, 4662306a36Sopenharmony_ci /* Error token */ 4762306a36Sopenharmony_ci Opt_err 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const match_table_t tokens = { 5162306a36Sopenharmony_ci {Opt_debug, "debug=%x"}, 5262306a36Sopenharmony_ci {Opt_dfltuid, "dfltuid=%u"}, 5362306a36Sopenharmony_ci {Opt_dfltgid, "dfltgid=%u"}, 5462306a36Sopenharmony_ci {Opt_afid, "afid=%u"}, 5562306a36Sopenharmony_ci {Opt_uname, "uname=%s"}, 5662306a36Sopenharmony_ci {Opt_remotename, "aname=%s"}, 5762306a36Sopenharmony_ci {Opt_nodevmap, "nodevmap"}, 5862306a36Sopenharmony_ci {Opt_noxattr, "noxattr"}, 5962306a36Sopenharmony_ci {Opt_directio, "directio"}, 6062306a36Sopenharmony_ci {Opt_ignoreqv, "ignoreqv"}, 6162306a36Sopenharmony_ci {Opt_cache, "cache=%s"}, 6262306a36Sopenharmony_ci {Opt_cachetag, "cachetag=%s"}, 6362306a36Sopenharmony_ci {Opt_access, "access=%s"}, 6462306a36Sopenharmony_ci {Opt_posixacl, "posixacl"}, 6562306a36Sopenharmony_ci {Opt_locktimeout, "locktimeout=%u"}, 6662306a36Sopenharmony_ci {Opt_err, NULL} 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Interpret mount options for cache mode */ 7062306a36Sopenharmony_cistatic int get_cache_mode(char *s) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int version = -EINVAL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!strcmp(s, "loose")) { 7562306a36Sopenharmony_ci version = CACHE_SC_LOOSE; 7662306a36Sopenharmony_ci p9_debug(P9_DEBUG_9P, "Cache mode: loose\n"); 7762306a36Sopenharmony_ci } else if (!strcmp(s, "fscache")) { 7862306a36Sopenharmony_ci version = CACHE_SC_FSCACHE; 7962306a36Sopenharmony_ci p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n"); 8062306a36Sopenharmony_ci } else if (!strcmp(s, "mmap")) { 8162306a36Sopenharmony_ci version = CACHE_SC_MMAP; 8262306a36Sopenharmony_ci p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n"); 8362306a36Sopenharmony_ci } else if (!strcmp(s, "readahead")) { 8462306a36Sopenharmony_ci version = CACHE_SC_READAHEAD; 8562306a36Sopenharmony_ci p9_debug(P9_DEBUG_9P, "Cache mode: readahead\n"); 8662306a36Sopenharmony_ci } else if (!strcmp(s, "none")) { 8762306a36Sopenharmony_ci version = CACHE_SC_NONE; 8862306a36Sopenharmony_ci p9_debug(P9_DEBUG_9P, "Cache mode: none\n"); 8962306a36Sopenharmony_ci } else if (kstrtoint(s, 0, &version) != 0) { 9062306a36Sopenharmony_ci version = -EINVAL; 9162306a36Sopenharmony_ci pr_info("Unknown Cache mode or invalid value %s\n", s); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return version; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * Display the mount options in /proc/mounts. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ciint v9fs_show_options(struct seq_file *m, struct dentry *root) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct v9fs_session_info *v9ses = root->d_sb->s_fs_info; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (v9ses->debug) 10462306a36Sopenharmony_ci seq_printf(m, ",debug=%x", v9ses->debug); 10562306a36Sopenharmony_ci if (!uid_eq(v9ses->dfltuid, V9FS_DEFUID)) 10662306a36Sopenharmony_ci seq_printf(m, ",dfltuid=%u", 10762306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, v9ses->dfltuid)); 10862306a36Sopenharmony_ci if (!gid_eq(v9ses->dfltgid, V9FS_DEFGID)) 10962306a36Sopenharmony_ci seq_printf(m, ",dfltgid=%u", 11062306a36Sopenharmony_ci from_kgid_munged(&init_user_ns, v9ses->dfltgid)); 11162306a36Sopenharmony_ci if (v9ses->afid != ~0) 11262306a36Sopenharmony_ci seq_printf(m, ",afid=%u", v9ses->afid); 11362306a36Sopenharmony_ci if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0) 11462306a36Sopenharmony_ci seq_printf(m, ",uname=%s", v9ses->uname); 11562306a36Sopenharmony_ci if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0) 11662306a36Sopenharmony_ci seq_printf(m, ",aname=%s", v9ses->aname); 11762306a36Sopenharmony_ci if (v9ses->nodev) 11862306a36Sopenharmony_ci seq_puts(m, ",nodevmap"); 11962306a36Sopenharmony_ci if (v9ses->cache) 12062306a36Sopenharmony_ci seq_printf(m, ",cache=%x", v9ses->cache); 12162306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 12262306a36Sopenharmony_ci if (v9ses->cachetag && (v9ses->cache & CACHE_FSCACHE)) 12362306a36Sopenharmony_ci seq_printf(m, ",cachetag=%s", v9ses->cachetag); 12462306a36Sopenharmony_ci#endif 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci switch (v9ses->flags & V9FS_ACCESS_MASK) { 12762306a36Sopenharmony_ci case V9FS_ACCESS_USER: 12862306a36Sopenharmony_ci seq_puts(m, ",access=user"); 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci case V9FS_ACCESS_ANY: 13162306a36Sopenharmony_ci seq_puts(m, ",access=any"); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case V9FS_ACCESS_CLIENT: 13462306a36Sopenharmony_ci seq_puts(m, ",access=client"); 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case V9FS_ACCESS_SINGLE: 13762306a36Sopenharmony_ci seq_printf(m, ",access=%u", 13862306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, v9ses->uid)); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (v9ses->flags & V9FS_IGNORE_QV) 14362306a36Sopenharmony_ci seq_puts(m, ",ignoreqv"); 14462306a36Sopenharmony_ci if (v9ses->flags & V9FS_DIRECT_IO) 14562306a36Sopenharmony_ci seq_puts(m, ",directio"); 14662306a36Sopenharmony_ci if (v9ses->flags & V9FS_POSIX_ACL) 14762306a36Sopenharmony_ci seq_puts(m, ",posixacl"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (v9ses->flags & V9FS_NO_XATTR) 15062306a36Sopenharmony_ci seq_puts(m, ",noxattr"); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return p9_show_client_options(m, v9ses->clnt); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/** 15662306a36Sopenharmony_ci * v9fs_parse_options - parse mount options into session structure 15762306a36Sopenharmony_ci * @v9ses: existing v9fs session information 15862306a36Sopenharmony_ci * @opts: The mount option string 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Return 0 upon success, -ERRNO upon failure. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci char *options, *tmp_options; 16662306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 16762306a36Sopenharmony_ci char *p; 16862306a36Sopenharmony_ci int option = 0; 16962306a36Sopenharmony_ci char *s; 17062306a36Sopenharmony_ci int ret = 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* setup defaults */ 17362306a36Sopenharmony_ci v9ses->afid = ~0; 17462306a36Sopenharmony_ci v9ses->debug = 0; 17562306a36Sopenharmony_ci v9ses->cache = CACHE_NONE; 17662306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 17762306a36Sopenharmony_ci v9ses->cachetag = NULL; 17862306a36Sopenharmony_ci#endif 17962306a36Sopenharmony_ci v9ses->session_lock_timeout = P9_LOCK_TIMEOUT; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!opts) 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci tmp_options = kstrdup(opts, GFP_KERNEL); 18562306a36Sopenharmony_ci if (!tmp_options) { 18662306a36Sopenharmony_ci ret = -ENOMEM; 18762306a36Sopenharmony_ci goto fail_option_alloc; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci options = tmp_options; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci while ((p = strsep(&options, ",")) != NULL) { 19262306a36Sopenharmony_ci int token, r; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!*p) 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci token = match_token(p, tokens, args); 19862306a36Sopenharmony_ci switch (token) { 19962306a36Sopenharmony_ci case Opt_debug: 20062306a36Sopenharmony_ci r = match_int(&args[0], &option); 20162306a36Sopenharmony_ci if (r < 0) { 20262306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 20362306a36Sopenharmony_ci "integer field, but no integer?\n"); 20462306a36Sopenharmony_ci ret = r; 20562306a36Sopenharmony_ci } else { 20662306a36Sopenharmony_ci v9ses->debug = option; 20762306a36Sopenharmony_ci#ifdef CONFIG_NET_9P_DEBUG 20862306a36Sopenharmony_ci p9_debug_level = option; 20962306a36Sopenharmony_ci#endif 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci case Opt_dfltuid: 21462306a36Sopenharmony_ci r = match_int(&args[0], &option); 21562306a36Sopenharmony_ci if (r < 0) { 21662306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 21762306a36Sopenharmony_ci "integer field, but no integer?\n"); 21862306a36Sopenharmony_ci ret = r; 21962306a36Sopenharmony_ci continue; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci v9ses->dfltuid = make_kuid(current_user_ns(), option); 22262306a36Sopenharmony_ci if (!uid_valid(v9ses->dfltuid)) { 22362306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 22462306a36Sopenharmony_ci "uid field, but not a uid?\n"); 22562306a36Sopenharmony_ci ret = -EINVAL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case Opt_dfltgid: 22962306a36Sopenharmony_ci r = match_int(&args[0], &option); 23062306a36Sopenharmony_ci if (r < 0) { 23162306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 23262306a36Sopenharmony_ci "integer field, but no integer?\n"); 23362306a36Sopenharmony_ci ret = r; 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci v9ses->dfltgid = make_kgid(current_user_ns(), option); 23762306a36Sopenharmony_ci if (!gid_valid(v9ses->dfltgid)) { 23862306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 23962306a36Sopenharmony_ci "gid field, but not a gid?\n"); 24062306a36Sopenharmony_ci ret = -EINVAL; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case Opt_afid: 24462306a36Sopenharmony_ci r = match_int(&args[0], &option); 24562306a36Sopenharmony_ci if (r < 0) { 24662306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 24762306a36Sopenharmony_ci "integer field, but no integer?\n"); 24862306a36Sopenharmony_ci ret = r; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci v9ses->afid = option; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci case Opt_uname: 25462306a36Sopenharmony_ci kfree(v9ses->uname); 25562306a36Sopenharmony_ci v9ses->uname = match_strdup(&args[0]); 25662306a36Sopenharmony_ci if (!v9ses->uname) { 25762306a36Sopenharmony_ci ret = -ENOMEM; 25862306a36Sopenharmony_ci goto free_and_return; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case Opt_remotename: 26262306a36Sopenharmony_ci kfree(v9ses->aname); 26362306a36Sopenharmony_ci v9ses->aname = match_strdup(&args[0]); 26462306a36Sopenharmony_ci if (!v9ses->aname) { 26562306a36Sopenharmony_ci ret = -ENOMEM; 26662306a36Sopenharmony_ci goto free_and_return; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case Opt_nodevmap: 27062306a36Sopenharmony_ci v9ses->nodev = 1; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case Opt_noxattr: 27362306a36Sopenharmony_ci v9ses->flags |= V9FS_NO_XATTR; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case Opt_directio: 27662306a36Sopenharmony_ci v9ses->flags |= V9FS_DIRECT_IO; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case Opt_ignoreqv: 27962306a36Sopenharmony_ci v9ses->flags |= V9FS_IGNORE_QV; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case Opt_cachetag: 28262306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 28362306a36Sopenharmony_ci kfree(v9ses->cachetag); 28462306a36Sopenharmony_ci v9ses->cachetag = match_strdup(&args[0]); 28562306a36Sopenharmony_ci if (!v9ses->cachetag) { 28662306a36Sopenharmony_ci ret = -ENOMEM; 28762306a36Sopenharmony_ci goto free_and_return; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case Opt_cache: 29262306a36Sopenharmony_ci s = match_strdup(&args[0]); 29362306a36Sopenharmony_ci if (!s) { 29462306a36Sopenharmony_ci ret = -ENOMEM; 29562306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 29662306a36Sopenharmony_ci "problem allocating copy of cache arg\n"); 29762306a36Sopenharmony_ci goto free_and_return; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci r = get_cache_mode(s); 30062306a36Sopenharmony_ci if (r < 0) 30162306a36Sopenharmony_ci ret = r; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci v9ses->cache = r; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci kfree(s); 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci case Opt_access: 30962306a36Sopenharmony_ci s = match_strdup(&args[0]); 31062306a36Sopenharmony_ci if (!s) { 31162306a36Sopenharmony_ci ret = -ENOMEM; 31262306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 31362306a36Sopenharmony_ci "problem allocating copy of access arg\n"); 31462306a36Sopenharmony_ci goto free_and_return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci v9ses->flags &= ~V9FS_ACCESS_MASK; 31862306a36Sopenharmony_ci if (strcmp(s, "user") == 0) 31962306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_USER; 32062306a36Sopenharmony_ci else if (strcmp(s, "any") == 0) 32162306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_ANY; 32262306a36Sopenharmony_ci else if (strcmp(s, "client") == 0) { 32362306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_CLIENT; 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci uid_t uid; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_SINGLE; 32862306a36Sopenharmony_ci r = kstrtouint(s, 10, &uid); 32962306a36Sopenharmony_ci if (r) { 33062306a36Sopenharmony_ci ret = r; 33162306a36Sopenharmony_ci pr_info("Unknown access argument %s: %d\n", 33262306a36Sopenharmony_ci s, r); 33362306a36Sopenharmony_ci kfree(s); 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci v9ses->uid = make_kuid(current_user_ns(), uid); 33762306a36Sopenharmony_ci if (!uid_valid(v9ses->uid)) { 33862306a36Sopenharmony_ci ret = -EINVAL; 33962306a36Sopenharmony_ci pr_info("Unknown uid %s\n", s); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci kfree(s); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci case Opt_posixacl: 34762306a36Sopenharmony_ci#ifdef CONFIG_9P_FS_POSIX_ACL 34862306a36Sopenharmony_ci v9ses->flags |= V9FS_POSIX_ACL; 34962306a36Sopenharmony_ci#else 35062306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 35162306a36Sopenharmony_ci "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n"); 35262306a36Sopenharmony_ci#endif 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci case Opt_locktimeout: 35662306a36Sopenharmony_ci r = match_int(&args[0], &option); 35762306a36Sopenharmony_ci if (r < 0) { 35862306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 35962306a36Sopenharmony_ci "integer field, but no integer?\n"); 36062306a36Sopenharmony_ci ret = r; 36162306a36Sopenharmony_ci continue; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci if (option < 1) { 36462306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, 36562306a36Sopenharmony_ci "locktimeout must be a greater than zero integer.\n"); 36662306a36Sopenharmony_ci ret = -EINVAL; 36762306a36Sopenharmony_ci continue; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci v9ses->session_lock_timeout = (long)option * HZ; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci continue; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cifree_and_return: 37862306a36Sopenharmony_ci kfree(tmp_options); 37962306a36Sopenharmony_cifail_option_alloc: 38062306a36Sopenharmony_ci return ret; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/** 38462306a36Sopenharmony_ci * v9fs_session_init - initialize session 38562306a36Sopenharmony_ci * @v9ses: session information structure 38662306a36Sopenharmony_ci * @dev_name: device being mounted 38762306a36Sopenharmony_ci * @data: options 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistruct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, 39262306a36Sopenharmony_ci const char *dev_name, char *data) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct p9_fid *fid; 39562306a36Sopenharmony_ci int rc = -ENOMEM; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL); 39862306a36Sopenharmony_ci if (!v9ses->uname) 39962306a36Sopenharmony_ci goto err_names; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL); 40262306a36Sopenharmony_ci if (!v9ses->aname) 40362306a36Sopenharmony_ci goto err_names; 40462306a36Sopenharmony_ci init_rwsem(&v9ses->rename_sem); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci v9ses->uid = INVALID_UID; 40762306a36Sopenharmony_ci v9ses->dfltuid = V9FS_DEFUID; 40862306a36Sopenharmony_ci v9ses->dfltgid = V9FS_DEFGID; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci v9ses->clnt = p9_client_create(dev_name, data); 41162306a36Sopenharmony_ci if (IS_ERR(v9ses->clnt)) { 41262306a36Sopenharmony_ci rc = PTR_ERR(v9ses->clnt); 41362306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n"); 41462306a36Sopenharmony_ci goto err_names; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci v9ses->flags = V9FS_ACCESS_USER; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (p9_is_proto_dotl(v9ses->clnt)) { 42062306a36Sopenharmony_ci v9ses->flags = V9FS_ACCESS_CLIENT; 42162306a36Sopenharmony_ci v9ses->flags |= V9FS_PROTO_2000L; 42262306a36Sopenharmony_ci } else if (p9_is_proto_dotu(v9ses->clnt)) { 42362306a36Sopenharmony_ci v9ses->flags |= V9FS_PROTO_2000U; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci rc = v9fs_parse_options(v9ses, data); 42762306a36Sopenharmony_ci if (rc < 0) 42862306a36Sopenharmony_ci goto err_clnt; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!v9fs_proto_dotl(v9ses) && 43362306a36Sopenharmony_ci ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * We support ACCESS_CLIENT only for dotl. 43662306a36Sopenharmony_ci * Fall back to ACCESS_USER 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci v9ses->flags &= ~V9FS_ACCESS_MASK; 43962306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_USER; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci /*FIXME !! */ 44262306a36Sopenharmony_ci /* for legacy mode, fall back to V9FS_ACCESS_ANY */ 44362306a36Sopenharmony_ci if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && 44462306a36Sopenharmony_ci ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci v9ses->flags &= ~V9FS_ACCESS_MASK; 44762306a36Sopenharmony_ci v9ses->flags |= V9FS_ACCESS_ANY; 44862306a36Sopenharmony_ci v9ses->uid = INVALID_UID; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci if (!v9fs_proto_dotl(v9ses) || 45162306a36Sopenharmony_ci !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * We support ACL checks on clinet only if the protocol is 45462306a36Sopenharmony_ci * 9P2000.L and access is V9FS_ACCESS_CLIENT. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci v9ses->flags &= ~V9FS_ACL_MASK; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID, 46062306a36Sopenharmony_ci v9ses->aname); 46162306a36Sopenharmony_ci if (IS_ERR(fid)) { 46262306a36Sopenharmony_ci rc = PTR_ERR(fid); 46362306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "cannot attach\n"); 46462306a36Sopenharmony_ci goto err_clnt; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) 46862306a36Sopenharmony_ci fid->uid = v9ses->uid; 46962306a36Sopenharmony_ci else 47062306a36Sopenharmony_ci fid->uid = INVALID_UID; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 47362306a36Sopenharmony_ci /* register the session for caching */ 47462306a36Sopenharmony_ci if (v9ses->cache & CACHE_FSCACHE) { 47562306a36Sopenharmony_ci rc = v9fs_cache_session_get_cookie(v9ses, dev_name); 47662306a36Sopenharmony_ci if (rc < 0) 47762306a36Sopenharmony_ci goto err_clnt; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci#endif 48062306a36Sopenharmony_ci spin_lock(&v9fs_sessionlist_lock); 48162306a36Sopenharmony_ci list_add(&v9ses->slist, &v9fs_sessionlist); 48262306a36Sopenharmony_ci spin_unlock(&v9fs_sessionlist_lock); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return fid; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cierr_clnt: 48762306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 48862306a36Sopenharmony_ci kfree(v9ses->cachetag); 48962306a36Sopenharmony_ci#endif 49062306a36Sopenharmony_ci p9_client_destroy(v9ses->clnt); 49162306a36Sopenharmony_cierr_names: 49262306a36Sopenharmony_ci kfree(v9ses->uname); 49362306a36Sopenharmony_ci kfree(v9ses->aname); 49462306a36Sopenharmony_ci return ERR_PTR(rc); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/** 49862306a36Sopenharmony_ci * v9fs_session_close - shutdown a session 49962306a36Sopenharmony_ci * @v9ses: session information structure 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_civoid v9fs_session_close(struct v9fs_session_info *v9ses) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci if (v9ses->clnt) { 50662306a36Sopenharmony_ci p9_client_destroy(v9ses->clnt); 50762306a36Sopenharmony_ci v9ses->clnt = NULL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 51162306a36Sopenharmony_ci fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false); 51262306a36Sopenharmony_ci kfree(v9ses->cachetag); 51362306a36Sopenharmony_ci#endif 51462306a36Sopenharmony_ci kfree(v9ses->uname); 51562306a36Sopenharmony_ci kfree(v9ses->aname); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci spin_lock(&v9fs_sessionlist_lock); 51862306a36Sopenharmony_ci list_del(&v9ses->slist); 51962306a36Sopenharmony_ci spin_unlock(&v9fs_sessionlist_lock); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/** 52362306a36Sopenharmony_ci * v9fs_session_cancel - terminate a session 52462306a36Sopenharmony_ci * @v9ses: session to terminate 52562306a36Sopenharmony_ci * 52662306a36Sopenharmony_ci * mark transport as disconnected and cancel all pending requests. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid v9fs_session_cancel(struct v9fs_session_info *v9ses) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses); 53262306a36Sopenharmony_ci p9_client_disconnect(v9ses->clnt); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/** 53662306a36Sopenharmony_ci * v9fs_session_begin_cancel - Begin terminate of a session 53762306a36Sopenharmony_ci * @v9ses: session to terminate 53862306a36Sopenharmony_ci * 53962306a36Sopenharmony_ci * After this call we don't allow any request other than clunk. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_civoid v9fs_session_begin_cancel(struct v9fs_session_info *v9ses) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses); 54562306a36Sopenharmony_ci p9_client_begin_disconnect(v9ses->clnt); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic struct kobject *v9fs_kobj; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 55162306a36Sopenharmony_ci/* 55262306a36Sopenharmony_ci * List caches associated with a session 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_cistatic ssize_t caches_show(struct kobject *kobj, 55562306a36Sopenharmony_ci struct kobj_attribute *attr, 55662306a36Sopenharmony_ci char *buf) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci ssize_t n = 0, count = 0, limit = PAGE_SIZE; 55962306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci spin_lock(&v9fs_sessionlist_lock); 56262306a36Sopenharmony_ci list_for_each_entry(v9ses, &v9fs_sessionlist, slist) { 56362306a36Sopenharmony_ci if (v9ses->cachetag) { 56462306a36Sopenharmony_ci n = snprintf(buf, limit, "%s\n", v9ses->cachetag); 56562306a36Sopenharmony_ci if (n < 0) { 56662306a36Sopenharmony_ci count = n; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci count += n; 57162306a36Sopenharmony_ci limit -= n; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci spin_unlock(&v9fs_sessionlist_lock); 57662306a36Sopenharmony_ci return count; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches); 58062306a36Sopenharmony_ci#endif /* CONFIG_9P_FSCACHE */ 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic struct attribute *v9fs_attrs[] = { 58362306a36Sopenharmony_ci#ifdef CONFIG_9P_FSCACHE 58462306a36Sopenharmony_ci &v9fs_attr_cache.attr, 58562306a36Sopenharmony_ci#endif 58662306a36Sopenharmony_ci NULL, 58762306a36Sopenharmony_ci}; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic const struct attribute_group v9fs_attr_group = { 59062306a36Sopenharmony_ci .attrs = v9fs_attrs, 59162306a36Sopenharmony_ci}; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/** 59462306a36Sopenharmony_ci * v9fs_sysfs_init - Initialize the v9fs sysfs interface 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int __init v9fs_sysfs_init(void) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci v9fs_kobj = kobject_create_and_add("9p", fs_kobj); 60162306a36Sopenharmony_ci if (!v9fs_kobj) 60262306a36Sopenharmony_ci return -ENOMEM; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) { 60562306a36Sopenharmony_ci kobject_put(v9fs_kobj); 60662306a36Sopenharmony_ci return -ENOMEM; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void v9fs_sysfs_cleanup(void) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci sysfs_remove_group(v9fs_kobj, &v9fs_attr_group); 62062306a36Sopenharmony_ci kobject_put(v9fs_kobj); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void v9fs_inode_init_once(void *foo) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct v9fs_inode *v9inode = (struct v9fs_inode *)foo; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci memset(&v9inode->qid, 0, sizeof(v9inode->qid)); 62862306a36Sopenharmony_ci inode_init_once(&v9inode->netfs.inode); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/** 63262306a36Sopenharmony_ci * v9fs_init_inode_cache - initialize a cache for 9P 63362306a36Sopenharmony_ci * Returns 0 on success. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_cistatic int v9fs_init_inode_cache(void) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache", 63862306a36Sopenharmony_ci sizeof(struct v9fs_inode), 63962306a36Sopenharmony_ci 0, (SLAB_RECLAIM_ACCOUNT| 64062306a36Sopenharmony_ci SLAB_MEM_SPREAD|SLAB_ACCOUNT), 64162306a36Sopenharmony_ci v9fs_inode_init_once); 64262306a36Sopenharmony_ci if (!v9fs_inode_cache) 64362306a36Sopenharmony_ci return -ENOMEM; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/** 64962306a36Sopenharmony_ci * v9fs_destroy_inode_cache - destroy the cache of 9P inode 65062306a36Sopenharmony_ci * 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_cistatic void v9fs_destroy_inode_cache(void) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 65662306a36Sopenharmony_ci * destroy cache. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci rcu_barrier(); 65962306a36Sopenharmony_ci kmem_cache_destroy(v9fs_inode_cache); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int v9fs_cache_register(void) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci int ret; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = v9fs_init_inode_cache(); 66762306a36Sopenharmony_ci if (ret < 0) 66862306a36Sopenharmony_ci return ret; 66962306a36Sopenharmony_ci return ret; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void v9fs_cache_unregister(void) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci v9fs_destroy_inode_cache(); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/** 67862306a36Sopenharmony_ci * init_v9fs - Initialize module 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci */ 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int __init init_v9fs(void) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci pr_info("Installing v9fs 9p2000 file system support\n"); 68762306a36Sopenharmony_ci /* TODO: Setup list of registered trasnport modules */ 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci err = v9fs_cache_register(); 69062306a36Sopenharmony_ci if (err < 0) { 69162306a36Sopenharmony_ci pr_err("Failed to register v9fs for caching\n"); 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci err = v9fs_sysfs_init(); 69662306a36Sopenharmony_ci if (err < 0) { 69762306a36Sopenharmony_ci pr_err("Failed to register with sysfs\n"); 69862306a36Sopenharmony_ci goto out_cache; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci err = register_filesystem(&v9fs_fs_type); 70162306a36Sopenharmony_ci if (err < 0) { 70262306a36Sopenharmony_ci pr_err("Failed to register filesystem\n"); 70362306a36Sopenharmony_ci goto out_sysfs_cleanup; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ciout_sysfs_cleanup: 70962306a36Sopenharmony_ci v9fs_sysfs_cleanup(); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ciout_cache: 71262306a36Sopenharmony_ci v9fs_cache_unregister(); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return err; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/** 71862306a36Sopenharmony_ci * exit_v9fs - shutdown module 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic void __exit exit_v9fs(void) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci v9fs_sysfs_cleanup(); 72562306a36Sopenharmony_ci v9fs_cache_unregister(); 72662306a36Sopenharmony_ci unregister_filesystem(&v9fs_fs_type); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cimodule_init(init_v9fs) 73062306a36Sopenharmony_cimodule_exit(exit_v9fs) 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ciMODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>"); 73362306a36Sopenharmony_ciMODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); 73462306a36Sopenharmony_ciMODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>"); 73562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 736