| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | *  This file contains functions assisting in mapping VFS to 9P2000 | 
|---|
| 4 | * | 
|---|
| 5 | *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com> | 
|---|
| 6 | *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | 
|---|
| 7 | */ | 
|---|
| 8 |  | 
|---|
| 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 10 |  | 
|---|
| 11 | #include <linux/module.h> | 
|---|
| 12 | #include <linux/errno.h> | 
|---|
| 13 | #include <linux/fs.h> | 
|---|
| 14 | #include <linux/sched.h> | 
|---|
| 15 | #include <linux/cred.h> | 
|---|
| 16 | #include <linux/parser.h> | 
|---|
| 17 | #include <linux/slab.h> | 
|---|
| 18 | #include <linux/seq_file.h> | 
|---|
| 19 | #include <net/9p/9p.h> | 
|---|
| 20 | #include <net/9p/client.h> | 
|---|
| 21 | #include <net/9p/transport.h> | 
|---|
| 22 | #include "v9fs.h" | 
|---|
| 23 | #include "v9fs_vfs.h" | 
|---|
| 24 | #include "cache.h" | 
|---|
| 25 |  | 
|---|
| 26 | static DEFINE_SPINLOCK(v9fs_sessionlist_lock); | 
|---|
| 27 | static LIST_HEAD(v9fs_sessionlist); | 
|---|
| 28 | struct kmem_cache *v9fs_inode_cache; | 
|---|
| 29 |  | 
|---|
| 30 | /* | 
|---|
| 31 | * Option Parsing (code inspired by NFS code) | 
|---|
| 32 | *  NOTE: each transport will parse its own options | 
|---|
| 33 | */ | 
|---|
| 34 |  | 
|---|
| 35 | enum { | 
|---|
| 36 | /* Options that take integer arguments */ | 
|---|
| 37 | Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, | 
|---|
| 38 | /* String options */ | 
|---|
| 39 | Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag, | 
|---|
| 40 | /* Options that take no arguments */ | 
|---|
| 41 | Opt_nodevmap, Opt_noxattr, Opt_directio, Opt_ignoreqv, | 
|---|
| 42 | /* Access options */ | 
|---|
| 43 | Opt_access, Opt_posixacl, | 
|---|
| 44 | /* Lock timeout option */ | 
|---|
| 45 | Opt_locktimeout, | 
|---|
| 46 | /* Error token */ | 
|---|
| 47 | Opt_err | 
|---|
| 48 | }; | 
|---|
| 49 |  | 
|---|
| 50 | static const match_table_t tokens = { | 
|---|
| 51 | {Opt_debug, "debug=%x"}, | 
|---|
| 52 | {.token: Opt_dfltuid, .pattern: "dfltuid=%u"}, | 
|---|
| 53 | {.token: Opt_dfltgid, .pattern: "dfltgid=%u"}, | 
|---|
| 54 | {.token: Opt_afid, .pattern: "afid=%u"}, | 
|---|
| 55 | {.token: Opt_uname, .pattern: "uname=%s"}, | 
|---|
| 56 | {.token: Opt_remotename, .pattern: "aname=%s"}, | 
|---|
| 57 | {.token: Opt_nodevmap, .pattern: "nodevmap"}, | 
|---|
| 58 | {.token: Opt_noxattr, .pattern: "noxattr"}, | 
|---|
| 59 | {.token: Opt_directio, .pattern: "directio"}, | 
|---|
| 60 | {.token: Opt_ignoreqv, .pattern: "ignoreqv"}, | 
|---|
| 61 | {.token: Opt_cache, .pattern: "cache=%s"}, | 
|---|
| 62 | {.token: Opt_cachetag, .pattern: "cachetag=%s"}, | 
|---|
| 63 | {.token: Opt_access, .pattern: "access=%s"}, | 
|---|
| 64 | {.token: Opt_posixacl, .pattern: "posixacl"}, | 
|---|
| 65 | {.token: Opt_locktimeout, .pattern: "locktimeout=%u"}, | 
|---|
| 66 | {.token: Opt_err, NULL} | 
|---|
| 67 | }; | 
|---|
| 68 |  | 
|---|
| 69 | /* Interpret mount options for cache mode */ | 
|---|
| 70 | static int get_cache_mode(char *s) | 
|---|
| 71 | { | 
|---|
| 72 | int version = -EINVAL; | 
|---|
| 73 |  | 
|---|
| 74 | if (!strcmp(s, "loose")) { | 
|---|
| 75 | version = CACHE_SC_LOOSE; | 
|---|
| 76 | p9_debug(P9_DEBUG_9P, "Cache mode: loose\n"); | 
|---|
| 77 | } else if (!strcmp(s, "fscache")) { | 
|---|
| 78 | version = CACHE_SC_FSCACHE; | 
|---|
| 79 | p9_debug(P9_DEBUG_9P, "Cache mode: fscache\n"); | 
|---|
| 80 | } else if (!strcmp(s, "mmap")) { | 
|---|
| 81 | version = CACHE_SC_MMAP; | 
|---|
| 82 | p9_debug(P9_DEBUG_9P, "Cache mode: mmap\n"); | 
|---|
| 83 | } else if (!strcmp(s, "readahead")) { | 
|---|
| 84 | version = CACHE_SC_READAHEAD; | 
|---|
| 85 | p9_debug(P9_DEBUG_9P, "Cache mode: readahead\n"); | 
|---|
| 86 | } else if (!strcmp(s, "none")) { | 
|---|
| 87 | version = CACHE_SC_NONE; | 
|---|
| 88 | p9_debug(P9_DEBUG_9P, "Cache mode: none\n"); | 
|---|
| 89 | } else if (kstrtoint(s, base: 0, res: &version) != 0) { | 
|---|
| 90 | version = -EINVAL; | 
|---|
| 91 | pr_info( "Unknown Cache mode or invalid value %s\n", s); | 
|---|
| 92 | } | 
|---|
| 93 | return version; | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 | /* | 
|---|
| 97 | * Display the mount options in /proc/mounts. | 
|---|
| 98 | */ | 
|---|
| 99 | int v9fs_show_options(struct seq_file *m, struct dentry *root) | 
|---|
| 100 | { | 
|---|
| 101 | struct v9fs_session_info *v9ses = root->d_sb->s_fs_info; | 
|---|
| 102 |  | 
|---|
| 103 | if (v9ses->debug) | 
|---|
| 104 | seq_printf(m, fmt: ",debug=%x", v9ses->debug); | 
|---|
| 105 | if (!uid_eq(left: v9ses->dfltuid, V9FS_DEFUID)) | 
|---|
| 106 | seq_printf(m, fmt: ",dfltuid=%u", | 
|---|
| 107 | from_kuid_munged(to: &init_user_ns, kuid: v9ses->dfltuid)); | 
|---|
| 108 | if (!gid_eq(left: v9ses->dfltgid, V9FS_DEFGID)) | 
|---|
| 109 | seq_printf(m, fmt: ",dfltgid=%u", | 
|---|
| 110 | from_kgid_munged(to: &init_user_ns, kgid: v9ses->dfltgid)); | 
|---|
| 111 | if (v9ses->afid != ~0) | 
|---|
| 112 | seq_printf(m, fmt: ",afid=%u", v9ses->afid); | 
|---|
| 113 | if (strcmp(v9ses->uname, V9FS_DEFUSER) != 0) | 
|---|
| 114 | seq_printf(m, fmt: ",uname=%s", v9ses->uname); | 
|---|
| 115 | if (strcmp(v9ses->aname, V9FS_DEFANAME) != 0) | 
|---|
| 116 | seq_printf(m, fmt: ",aname=%s", v9ses->aname); | 
|---|
| 117 | if (v9ses->nodev) | 
|---|
| 118 | seq_puts(m, s: ",nodevmap"); | 
|---|
| 119 | if (v9ses->cache) | 
|---|
| 120 | seq_printf(m, fmt: ",cache=%x", v9ses->cache); | 
|---|
| 121 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 122 | if (v9ses->cachetag && (v9ses->cache & CACHE_FSCACHE)) | 
|---|
| 123 | seq_printf(m, ",cachetag=%s", v9ses->cachetag); | 
|---|
| 124 | #endif | 
|---|
| 125 |  | 
|---|
| 126 | switch (v9ses->flags & V9FS_ACCESS_MASK) { | 
|---|
| 127 | case V9FS_ACCESS_USER: | 
|---|
| 128 | seq_puts(m, s: ",access=user"); | 
|---|
| 129 | break; | 
|---|
| 130 | case V9FS_ACCESS_ANY: | 
|---|
| 131 | seq_puts(m, s: ",access=any"); | 
|---|
| 132 | break; | 
|---|
| 133 | case V9FS_ACCESS_CLIENT: | 
|---|
| 134 | seq_puts(m, s: ",access=client"); | 
|---|
| 135 | break; | 
|---|
| 136 | case V9FS_ACCESS_SINGLE: | 
|---|
| 137 | seq_printf(m, fmt: ",access=%u", | 
|---|
| 138 | from_kuid_munged(to: &init_user_ns, kuid: v9ses->uid)); | 
|---|
| 139 | break; | 
|---|
| 140 | } | 
|---|
| 141 |  | 
|---|
| 142 | if (v9ses->flags & V9FS_IGNORE_QV) | 
|---|
| 143 | seq_puts(m, s: ",ignoreqv"); | 
|---|
| 144 | if (v9ses->flags & V9FS_DIRECT_IO) | 
|---|
| 145 | seq_puts(m, s: ",directio"); | 
|---|
| 146 | if (v9ses->flags & V9FS_POSIX_ACL) | 
|---|
| 147 | seq_puts(m, s: ",posixacl"); | 
|---|
| 148 |  | 
|---|
| 149 | if (v9ses->flags & V9FS_NO_XATTR) | 
|---|
| 150 | seq_puts(m, s: ",noxattr"); | 
|---|
| 151 |  | 
|---|
| 152 | return p9_show_client_options(m, clnt: v9ses->clnt); | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | /** | 
|---|
| 156 | * v9fs_parse_options - parse mount options into session structure | 
|---|
| 157 | * @v9ses: existing v9fs session information | 
|---|
| 158 | * @opts: The mount option string | 
|---|
| 159 | * | 
|---|
| 160 | * Return 0 upon success, -ERRNO upon failure. | 
|---|
| 161 | */ | 
|---|
| 162 |  | 
|---|
| 163 | static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) | 
|---|
| 164 | { | 
|---|
| 165 | char *options, *tmp_options; | 
|---|
| 166 | substring_t args[MAX_OPT_ARGS]; | 
|---|
| 167 | char *p; | 
|---|
| 168 | int option = 0; | 
|---|
| 169 | char *s; | 
|---|
| 170 | int ret = 0; | 
|---|
| 171 |  | 
|---|
| 172 | /* setup defaults */ | 
|---|
| 173 | v9ses->afid = ~0; | 
|---|
| 174 | v9ses->debug = 0; | 
|---|
| 175 | v9ses->cache = CACHE_NONE; | 
|---|
| 176 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 177 | v9ses->cachetag = NULL; | 
|---|
| 178 | #endif | 
|---|
| 179 | v9ses->session_lock_timeout = P9_LOCK_TIMEOUT; | 
|---|
| 180 |  | 
|---|
| 181 | if (!opts) | 
|---|
| 182 | return 0; | 
|---|
| 183 |  | 
|---|
| 184 | tmp_options = kstrdup(s: opts, GFP_KERNEL); | 
|---|
| 185 | if (!tmp_options) { | 
|---|
| 186 | ret = -ENOMEM; | 
|---|
| 187 | goto fail_option_alloc; | 
|---|
| 188 | } | 
|---|
| 189 | options = tmp_options; | 
|---|
| 190 |  | 
|---|
| 191 | while ((p = strsep(&options, ",")) != NULL) { | 
|---|
| 192 | int token, r; | 
|---|
| 193 |  | 
|---|
| 194 | if (!*p) | 
|---|
| 195 | continue; | 
|---|
| 196 |  | 
|---|
| 197 | token = match_token(p, table: tokens, args); | 
|---|
| 198 | switch (token) { | 
|---|
| 199 | case Opt_debug: | 
|---|
| 200 | r = match_int(&args[0], result: &option); | 
|---|
| 201 | if (r < 0) { | 
|---|
| 202 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 203 | "integer field, but no integer?\n"); | 
|---|
| 204 | ret = r; | 
|---|
| 205 | } else { | 
|---|
| 206 | v9ses->debug = option; | 
|---|
| 207 | #ifdef CONFIG_NET_9P_DEBUG | 
|---|
| 208 | p9_debug_level = option; | 
|---|
| 209 | #endif | 
|---|
| 210 | } | 
|---|
| 211 | break; | 
|---|
| 212 |  | 
|---|
| 213 | case Opt_dfltuid: | 
|---|
| 214 | r = match_int(&args[0], result: &option); | 
|---|
| 215 | if (r < 0) { | 
|---|
| 216 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 217 | "integer field, but no integer?\n"); | 
|---|
| 218 | ret = r; | 
|---|
| 219 | continue; | 
|---|
| 220 | } | 
|---|
| 221 | v9ses->dfltuid = make_kuid(from: current_user_ns(), uid: option); | 
|---|
| 222 | if (!uid_valid(uid: v9ses->dfltuid)) { | 
|---|
| 223 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 224 | "uid field, but not a uid?\n"); | 
|---|
| 225 | ret = -EINVAL; | 
|---|
| 226 | } | 
|---|
| 227 | break; | 
|---|
| 228 | case Opt_dfltgid: | 
|---|
| 229 | r = match_int(&args[0], result: &option); | 
|---|
| 230 | if (r < 0) { | 
|---|
| 231 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 232 | "integer field, but no integer?\n"); | 
|---|
| 233 | ret = r; | 
|---|
| 234 | continue; | 
|---|
| 235 | } | 
|---|
| 236 | v9ses->dfltgid = make_kgid(from: current_user_ns(), gid: option); | 
|---|
| 237 | if (!gid_valid(gid: v9ses->dfltgid)) { | 
|---|
| 238 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 239 | "gid field, but not a gid?\n"); | 
|---|
| 240 | ret = -EINVAL; | 
|---|
| 241 | } | 
|---|
| 242 | break; | 
|---|
| 243 | case Opt_afid: | 
|---|
| 244 | r = match_int(&args[0], result: &option); | 
|---|
| 245 | if (r < 0) { | 
|---|
| 246 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 247 | "integer field, but no integer?\n"); | 
|---|
| 248 | ret = r; | 
|---|
| 249 | } else { | 
|---|
| 250 | v9ses->afid = option; | 
|---|
| 251 | } | 
|---|
| 252 | break; | 
|---|
| 253 | case Opt_uname: | 
|---|
| 254 | kfree(objp: v9ses->uname); | 
|---|
| 255 | v9ses->uname = match_strdup(&args[0]); | 
|---|
| 256 | if (!v9ses->uname) { | 
|---|
| 257 | ret = -ENOMEM; | 
|---|
| 258 | goto free_and_return; | 
|---|
| 259 | } | 
|---|
| 260 | break; | 
|---|
| 261 | case Opt_remotename: | 
|---|
| 262 | kfree(objp: v9ses->aname); | 
|---|
| 263 | v9ses->aname = match_strdup(&args[0]); | 
|---|
| 264 | if (!v9ses->aname) { | 
|---|
| 265 | ret = -ENOMEM; | 
|---|
| 266 | goto free_and_return; | 
|---|
| 267 | } | 
|---|
| 268 | break; | 
|---|
| 269 | case Opt_nodevmap: | 
|---|
| 270 | v9ses->nodev = 1; | 
|---|
| 271 | break; | 
|---|
| 272 | case Opt_noxattr: | 
|---|
| 273 | v9ses->flags |= V9FS_NO_XATTR; | 
|---|
| 274 | break; | 
|---|
| 275 | case Opt_directio: | 
|---|
| 276 | v9ses->flags |= V9FS_DIRECT_IO; | 
|---|
| 277 | break; | 
|---|
| 278 | case Opt_ignoreqv: | 
|---|
| 279 | v9ses->flags |= V9FS_IGNORE_QV; | 
|---|
| 280 | break; | 
|---|
| 281 | case Opt_cachetag: | 
|---|
| 282 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 283 | kfree(v9ses->cachetag); | 
|---|
| 284 | v9ses->cachetag = match_strdup(&args[0]); | 
|---|
| 285 | if (!v9ses->cachetag) { | 
|---|
| 286 | ret = -ENOMEM; | 
|---|
| 287 | goto free_and_return; | 
|---|
| 288 | } | 
|---|
| 289 | #endif | 
|---|
| 290 | break; | 
|---|
| 291 | case Opt_cache: | 
|---|
| 292 | s = match_strdup(&args[0]); | 
|---|
| 293 | if (!s) { | 
|---|
| 294 | ret = -ENOMEM; | 
|---|
| 295 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 296 | "problem allocating copy of cache arg\n"); | 
|---|
| 297 | goto free_and_return; | 
|---|
| 298 | } | 
|---|
| 299 | r = get_cache_mode(s); | 
|---|
| 300 | if (r < 0) | 
|---|
| 301 | ret = r; | 
|---|
| 302 | else | 
|---|
| 303 | v9ses->cache = r; | 
|---|
| 304 |  | 
|---|
| 305 | kfree(objp: s); | 
|---|
| 306 | break; | 
|---|
| 307 |  | 
|---|
| 308 | case Opt_access: | 
|---|
| 309 | s = match_strdup(&args[0]); | 
|---|
| 310 | if (!s) { | 
|---|
| 311 | ret = -ENOMEM; | 
|---|
| 312 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 313 | "problem allocating copy of access arg\n"); | 
|---|
| 314 | goto free_and_return; | 
|---|
| 315 | } | 
|---|
| 316 |  | 
|---|
| 317 | v9ses->flags &= ~V9FS_ACCESS_MASK; | 
|---|
| 318 | if (strcmp(s, "user") == 0) | 
|---|
| 319 | v9ses->flags |= V9FS_ACCESS_USER; | 
|---|
| 320 | else if (strcmp(s, "any") == 0) | 
|---|
| 321 | v9ses->flags |= V9FS_ACCESS_ANY; | 
|---|
| 322 | else if (strcmp(s, "client") == 0) { | 
|---|
| 323 | v9ses->flags |= V9FS_ACCESS_CLIENT; | 
|---|
| 324 | } else { | 
|---|
| 325 | uid_t uid; | 
|---|
| 326 |  | 
|---|
| 327 | v9ses->flags |= V9FS_ACCESS_SINGLE; | 
|---|
| 328 | r = kstrtouint(s, base: 10, res: &uid); | 
|---|
| 329 | if (r) { | 
|---|
| 330 | ret = r; | 
|---|
| 331 | pr_info( "Unknown access argument %s: %d\n", | 
|---|
| 332 | s, r); | 
|---|
| 333 | kfree(objp: s); | 
|---|
| 334 | continue; | 
|---|
| 335 | } | 
|---|
| 336 | v9ses->uid = make_kuid(from: current_user_ns(), uid); | 
|---|
| 337 | if (!uid_valid(uid: v9ses->uid)) { | 
|---|
| 338 | ret = -EINVAL; | 
|---|
| 339 | pr_info( "Unknown uid %s\n", s); | 
|---|
| 340 | } | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | kfree(objp: s); | 
|---|
| 344 | break; | 
|---|
| 345 |  | 
|---|
| 346 | case Opt_posixacl: | 
|---|
| 347 | #ifdef CONFIG_9P_FS_POSIX_ACL | 
|---|
| 348 | v9ses->flags |= V9FS_POSIX_ACL; | 
|---|
| 349 | #else | 
|---|
| 350 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 351 | "Not defined CONFIG_9P_FS_POSIX_ACL. Ignoring posixacl option\n"); | 
|---|
| 352 | #endif | 
|---|
| 353 | break; | 
|---|
| 354 |  | 
|---|
| 355 | case Opt_locktimeout: | 
|---|
| 356 | r = match_int(&args[0], result: &option); | 
|---|
| 357 | if (r < 0) { | 
|---|
| 358 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 359 | "integer field, but no integer?\n"); | 
|---|
| 360 | ret = r; | 
|---|
| 361 | continue; | 
|---|
| 362 | } | 
|---|
| 363 | if (option < 1) { | 
|---|
| 364 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 365 | "locktimeout must be a greater than zero integer.\n"); | 
|---|
| 366 | ret = -EINVAL; | 
|---|
| 367 | continue; | 
|---|
| 368 | } | 
|---|
| 369 | v9ses->session_lock_timeout = (long)option * HZ; | 
|---|
| 370 | break; | 
|---|
| 371 |  | 
|---|
| 372 | default: | 
|---|
| 373 | continue; | 
|---|
| 374 | } | 
|---|
| 375 | } | 
|---|
| 376 |  | 
|---|
| 377 | free_and_return: | 
|---|
| 378 | kfree(objp: tmp_options); | 
|---|
| 379 | fail_option_alloc: | 
|---|
| 380 | return ret; | 
|---|
| 381 | } | 
|---|
| 382 |  | 
|---|
| 383 | /** | 
|---|
| 384 | * v9fs_session_init - initialize session | 
|---|
| 385 | * @v9ses: session information structure | 
|---|
| 386 | * @dev_name: device being mounted | 
|---|
| 387 | * @data: options | 
|---|
| 388 | * | 
|---|
| 389 | */ | 
|---|
| 390 |  | 
|---|
| 391 | struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, | 
|---|
| 392 | const char *dev_name, char *data) | 
|---|
| 393 | { | 
|---|
| 394 | struct p9_fid *fid; | 
|---|
| 395 | int rc = -ENOMEM; | 
|---|
| 396 |  | 
|---|
| 397 | v9ses->uname = kstrdup(V9FS_DEFUSER, GFP_KERNEL); | 
|---|
| 398 | if (!v9ses->uname) | 
|---|
| 399 | goto err_names; | 
|---|
| 400 |  | 
|---|
| 401 | v9ses->aname = kstrdup(V9FS_DEFANAME, GFP_KERNEL); | 
|---|
| 402 | if (!v9ses->aname) | 
|---|
| 403 | goto err_names; | 
|---|
| 404 | init_rwsem(&v9ses->rename_sem); | 
|---|
| 405 |  | 
|---|
| 406 | v9ses->uid = INVALID_UID; | 
|---|
| 407 | v9ses->dfltuid = V9FS_DEFUID; | 
|---|
| 408 | v9ses->dfltgid = V9FS_DEFGID; | 
|---|
| 409 |  | 
|---|
| 410 | v9ses->clnt = p9_client_create(dev_name, options: data); | 
|---|
| 411 | if (IS_ERR(ptr: v9ses->clnt)) { | 
|---|
| 412 | rc = PTR_ERR(ptr: v9ses->clnt); | 
|---|
| 413 | p9_debug(P9_DEBUG_ERROR, "problem initializing 9p client\n"); | 
|---|
| 414 | goto err_names; | 
|---|
| 415 | } | 
|---|
| 416 |  | 
|---|
| 417 | v9ses->flags = V9FS_ACCESS_USER; | 
|---|
| 418 |  | 
|---|
| 419 | if (p9_is_proto_dotl(clnt: v9ses->clnt)) { | 
|---|
| 420 | v9ses->flags = V9FS_ACCESS_CLIENT; | 
|---|
| 421 | v9ses->flags |= V9FS_PROTO_2000L; | 
|---|
| 422 | } else if (p9_is_proto_dotu(clnt: v9ses->clnt)) { | 
|---|
| 423 | v9ses->flags |= V9FS_PROTO_2000U; | 
|---|
| 424 | } | 
|---|
| 425 |  | 
|---|
| 426 | rc = v9fs_parse_options(v9ses, opts: data); | 
|---|
| 427 | if (rc < 0) | 
|---|
| 428 | goto err_clnt; | 
|---|
| 429 |  | 
|---|
| 430 | v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; | 
|---|
| 431 |  | 
|---|
| 432 | if (!v9fs_proto_dotl(v9ses) && | 
|---|
| 433 | ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { | 
|---|
| 434 | /* | 
|---|
| 435 | * We support ACCESS_CLIENT only for dotl. | 
|---|
| 436 | * Fall back to ACCESS_USER | 
|---|
| 437 | */ | 
|---|
| 438 | v9ses->flags &= ~V9FS_ACCESS_MASK; | 
|---|
| 439 | v9ses->flags |= V9FS_ACCESS_USER; | 
|---|
| 440 | } | 
|---|
| 441 | /* FIXME: for legacy mode, fall back to V9FS_ACCESS_ANY */ | 
|---|
| 442 | if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && | 
|---|
| 443 | ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { | 
|---|
| 444 |  | 
|---|
| 445 | v9ses->flags &= ~V9FS_ACCESS_MASK; | 
|---|
| 446 | v9ses->flags |= V9FS_ACCESS_ANY; | 
|---|
| 447 | v9ses->uid = INVALID_UID; | 
|---|
| 448 | } | 
|---|
| 449 | if (!v9fs_proto_dotl(v9ses) || | 
|---|
| 450 | !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { | 
|---|
| 451 | /* | 
|---|
| 452 | * We support ACL checks on client only if the protocol is | 
|---|
| 453 | * 9P2000.L and access is V9FS_ACCESS_CLIENT. | 
|---|
| 454 | */ | 
|---|
| 455 | v9ses->flags &= ~V9FS_ACL_MASK; | 
|---|
| 456 | } | 
|---|
| 457 |  | 
|---|
| 458 | fid = p9_client_attach(clnt: v9ses->clnt, NULL, uname: v9ses->uname, INVALID_UID, | 
|---|
| 459 | aname: v9ses->aname); | 
|---|
| 460 | if (IS_ERR(ptr: fid)) { | 
|---|
| 461 | rc = PTR_ERR(ptr: fid); | 
|---|
| 462 | p9_debug(P9_DEBUG_ERROR, "cannot attach\n"); | 
|---|
| 463 | goto err_clnt; | 
|---|
| 464 | } | 
|---|
| 465 |  | 
|---|
| 466 | if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) | 
|---|
| 467 | fid->uid = v9ses->uid; | 
|---|
| 468 | else | 
|---|
| 469 | fid->uid = INVALID_UID; | 
|---|
| 470 |  | 
|---|
| 471 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 472 | /* register the session for caching */ | 
|---|
| 473 | if (v9ses->cache & CACHE_FSCACHE) { | 
|---|
| 474 | rc = v9fs_cache_session_get_cookie(v9ses, dev_name); | 
|---|
| 475 | if (rc < 0) | 
|---|
| 476 | goto err_clnt; | 
|---|
| 477 | } | 
|---|
| 478 | #endif | 
|---|
| 479 | spin_lock(lock: &v9fs_sessionlist_lock); | 
|---|
| 480 | list_add(new: &v9ses->slist, head: &v9fs_sessionlist); | 
|---|
| 481 | spin_unlock(lock: &v9fs_sessionlist_lock); | 
|---|
| 482 |  | 
|---|
| 483 | return fid; | 
|---|
| 484 |  | 
|---|
| 485 | err_clnt: | 
|---|
| 486 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 487 | kfree(v9ses->cachetag); | 
|---|
| 488 | #endif | 
|---|
| 489 | p9_client_destroy(clnt: v9ses->clnt); | 
|---|
| 490 | err_names: | 
|---|
| 491 | kfree(objp: v9ses->uname); | 
|---|
| 492 | kfree(objp: v9ses->aname); | 
|---|
| 493 | return ERR_PTR(error: rc); | 
|---|
| 494 | } | 
|---|
| 495 |  | 
|---|
| 496 | /** | 
|---|
| 497 | * v9fs_session_close - shutdown a session | 
|---|
| 498 | * @v9ses: session information structure | 
|---|
| 499 | * | 
|---|
| 500 | */ | 
|---|
| 501 |  | 
|---|
| 502 | void v9fs_session_close(struct v9fs_session_info *v9ses) | 
|---|
| 503 | { | 
|---|
| 504 | if (v9ses->clnt) { | 
|---|
| 505 | p9_client_destroy(clnt: v9ses->clnt); | 
|---|
| 506 | v9ses->clnt = NULL; | 
|---|
| 507 | } | 
|---|
| 508 |  | 
|---|
| 509 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 510 | fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false); | 
|---|
| 511 | kfree(v9ses->cachetag); | 
|---|
| 512 | #endif | 
|---|
| 513 | kfree(objp: v9ses->uname); | 
|---|
| 514 | kfree(objp: v9ses->aname); | 
|---|
| 515 |  | 
|---|
| 516 | spin_lock(lock: &v9fs_sessionlist_lock); | 
|---|
| 517 | list_del(entry: &v9ses->slist); | 
|---|
| 518 | spin_unlock(lock: &v9fs_sessionlist_lock); | 
|---|
| 519 | } | 
|---|
| 520 |  | 
|---|
| 521 | /** | 
|---|
| 522 | * v9fs_session_cancel - terminate a session | 
|---|
| 523 | * @v9ses: session to terminate | 
|---|
| 524 | * | 
|---|
| 525 | * mark transport as disconnected and cancel all pending requests. | 
|---|
| 526 | */ | 
|---|
| 527 |  | 
|---|
| 528 | void v9fs_session_cancel(struct v9fs_session_info *v9ses) | 
|---|
| 529 | { | 
|---|
| 530 | p9_debug(P9_DEBUG_ERROR, "cancel session %p\n", v9ses); | 
|---|
| 531 | p9_client_disconnect(clnt: v9ses->clnt); | 
|---|
| 532 | } | 
|---|
| 533 |  | 
|---|
| 534 | /** | 
|---|
| 535 | * v9fs_session_begin_cancel - Begin terminate of a session | 
|---|
| 536 | * @v9ses: session to terminate | 
|---|
| 537 | * | 
|---|
| 538 | * After this call we don't allow any request other than clunk. | 
|---|
| 539 | */ | 
|---|
| 540 |  | 
|---|
| 541 | void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses) | 
|---|
| 542 | { | 
|---|
| 543 | p9_debug(P9_DEBUG_ERROR, "begin cancel session %p\n", v9ses); | 
|---|
| 544 | p9_client_begin_disconnect(clnt: v9ses->clnt); | 
|---|
| 545 | } | 
|---|
| 546 |  | 
|---|
| 547 | static struct kobject *v9fs_kobj; | 
|---|
| 548 |  | 
|---|
| 549 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 550 | /* | 
|---|
| 551 | * List caches associated with a session | 
|---|
| 552 | */ | 
|---|
| 553 | static ssize_t caches_show(struct kobject *kobj, | 
|---|
| 554 | struct kobj_attribute *attr, | 
|---|
| 555 | char *buf) | 
|---|
| 556 | { | 
|---|
| 557 | ssize_t n = 0, count = 0, limit = PAGE_SIZE; | 
|---|
| 558 | struct v9fs_session_info *v9ses; | 
|---|
| 559 |  | 
|---|
| 560 | spin_lock(&v9fs_sessionlist_lock); | 
|---|
| 561 | list_for_each_entry(v9ses, &v9fs_sessionlist, slist) { | 
|---|
| 562 | if (v9ses->cachetag) { | 
|---|
| 563 | n = snprintf(buf + count, limit, "%s\n", v9ses->cachetag); | 
|---|
| 564 | if (n < 0) { | 
|---|
| 565 | count = n; | 
|---|
| 566 | break; | 
|---|
| 567 | } | 
|---|
| 568 |  | 
|---|
| 569 | count += n; | 
|---|
| 570 | limit -= n; | 
|---|
| 571 | } | 
|---|
| 572 | } | 
|---|
| 573 |  | 
|---|
| 574 | spin_unlock(&v9fs_sessionlist_lock); | 
|---|
| 575 | return count; | 
|---|
| 576 | } | 
|---|
| 577 |  | 
|---|
| 578 | static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches); | 
|---|
| 579 | #endif /* CONFIG_9P_FSCACHE */ | 
|---|
| 580 |  | 
|---|
| 581 | static struct attribute *v9fs_attrs[] = { | 
|---|
| 582 | #ifdef CONFIG_9P_FSCACHE | 
|---|
| 583 | &v9fs_attr_cache.attr, | 
|---|
| 584 | #endif | 
|---|
| 585 | NULL, | 
|---|
| 586 | }; | 
|---|
| 587 |  | 
|---|
| 588 | static const struct attribute_group v9fs_attr_group = { | 
|---|
| 589 | .attrs = v9fs_attrs, | 
|---|
| 590 | }; | 
|---|
| 591 |  | 
|---|
| 592 | /** | 
|---|
| 593 | * v9fs_sysfs_init - Initialize the v9fs sysfs interface | 
|---|
| 594 | * | 
|---|
| 595 | */ | 
|---|
| 596 |  | 
|---|
| 597 | static int __init v9fs_sysfs_init(void) | 
|---|
| 598 | { | 
|---|
| 599 | int ret; | 
|---|
| 600 |  | 
|---|
| 601 | v9fs_kobj = kobject_create_and_add(name: "9p", parent: fs_kobj); | 
|---|
| 602 | if (!v9fs_kobj) | 
|---|
| 603 | return -ENOMEM; | 
|---|
| 604 |  | 
|---|
| 605 | ret = sysfs_create_group(kobj: v9fs_kobj, grp: &v9fs_attr_group); | 
|---|
| 606 | if (ret) { | 
|---|
| 607 | kobject_put(kobj: v9fs_kobj); | 
|---|
| 608 | return ret; | 
|---|
| 609 | } | 
|---|
| 610 |  | 
|---|
| 611 | return 0; | 
|---|
| 612 | } | 
|---|
| 613 |  | 
|---|
| 614 | /** | 
|---|
| 615 | * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface | 
|---|
| 616 | * | 
|---|
| 617 | */ | 
|---|
| 618 |  | 
|---|
| 619 | static void v9fs_sysfs_cleanup(void) | 
|---|
| 620 | { | 
|---|
| 621 | sysfs_remove_group(kobj: v9fs_kobj, grp: &v9fs_attr_group); | 
|---|
| 622 | kobject_put(kobj: v9fs_kobj); | 
|---|
| 623 | } | 
|---|
| 624 |  | 
|---|
| 625 | static void v9fs_inode_init_once(void *foo) | 
|---|
| 626 | { | 
|---|
| 627 | struct v9fs_inode *v9inode = (struct v9fs_inode *)foo; | 
|---|
| 628 |  | 
|---|
| 629 | memset(s: &v9inode->qid, c: 0, n: sizeof(v9inode->qid)); | 
|---|
| 630 | inode_init_once(&v9inode->netfs.inode); | 
|---|
| 631 | } | 
|---|
| 632 |  | 
|---|
| 633 | /** | 
|---|
| 634 | * v9fs_init_inode_cache - initialize a cache for 9P | 
|---|
| 635 | * Returns 0 on success. | 
|---|
| 636 | */ | 
|---|
| 637 | static int v9fs_init_inode_cache(void) | 
|---|
| 638 | { | 
|---|
| 639 | v9fs_inode_cache = kmem_cache_create( "v9fs_inode_cache", | 
|---|
| 640 | sizeof(struct v9fs_inode), | 
|---|
| 641 | 0, (SLAB_RECLAIM_ACCOUNT| | 
|---|
| 642 | SLAB_ACCOUNT), | 
|---|
| 643 | v9fs_inode_init_once); | 
|---|
| 644 | if (!v9fs_inode_cache) | 
|---|
| 645 | return -ENOMEM; | 
|---|
| 646 |  | 
|---|
| 647 | return 0; | 
|---|
| 648 | } | 
|---|
| 649 |  | 
|---|
| 650 | /** | 
|---|
| 651 | * v9fs_destroy_inode_cache - destroy the cache of 9P inode | 
|---|
| 652 | * | 
|---|
| 653 | */ | 
|---|
| 654 | static void v9fs_destroy_inode_cache(void) | 
|---|
| 655 | { | 
|---|
| 656 | /* | 
|---|
| 657 | * Make sure all delayed rcu free inodes are flushed before we | 
|---|
| 658 | * destroy cache. | 
|---|
| 659 | */ | 
|---|
| 660 | rcu_barrier(); | 
|---|
| 661 | kmem_cache_destroy(s: v9fs_inode_cache); | 
|---|
| 662 | } | 
|---|
| 663 |  | 
|---|
| 664 | /** | 
|---|
| 665 | * init_v9fs - Initialize module | 
|---|
| 666 | * | 
|---|
| 667 | */ | 
|---|
| 668 |  | 
|---|
| 669 | static int __init init_v9fs(void) | 
|---|
| 670 | { | 
|---|
| 671 | int err; | 
|---|
| 672 |  | 
|---|
| 673 | pr_info( "Installing v9fs 9p2000 file system support\n"); | 
|---|
| 674 | /* TODO: Setup list of registered transport modules */ | 
|---|
| 675 |  | 
|---|
| 676 | err = v9fs_init_inode_cache(); | 
|---|
| 677 | if (err < 0) { | 
|---|
| 678 | pr_err( "Failed to register v9fs for caching\n"); | 
|---|
| 679 | return err; | 
|---|
| 680 | } | 
|---|
| 681 |  | 
|---|
| 682 | err = v9fs_sysfs_init(); | 
|---|
| 683 | if (err < 0) { | 
|---|
| 684 | pr_err( "Failed to register with sysfs\n"); | 
|---|
| 685 | goto out_cache; | 
|---|
| 686 | } | 
|---|
| 687 | err = register_filesystem(&v9fs_fs_type); | 
|---|
| 688 | if (err < 0) { | 
|---|
| 689 | pr_err( "Failed to register filesystem\n"); | 
|---|
| 690 | goto out_sysfs_cleanup; | 
|---|
| 691 | } | 
|---|
| 692 |  | 
|---|
| 693 | return 0; | 
|---|
| 694 |  | 
|---|
| 695 | out_sysfs_cleanup: | 
|---|
| 696 | v9fs_sysfs_cleanup(); | 
|---|
| 697 |  | 
|---|
| 698 | out_cache: | 
|---|
| 699 | v9fs_destroy_inode_cache(); | 
|---|
| 700 |  | 
|---|
| 701 | return err; | 
|---|
| 702 | } | 
|---|
| 703 |  | 
|---|
| 704 | /** | 
|---|
| 705 | * exit_v9fs - shutdown module | 
|---|
| 706 | * | 
|---|
| 707 | */ | 
|---|
| 708 |  | 
|---|
| 709 | static void __exit exit_v9fs(void) | 
|---|
| 710 | { | 
|---|
| 711 | v9fs_sysfs_cleanup(); | 
|---|
| 712 | v9fs_destroy_inode_cache(); | 
|---|
| 713 | unregister_filesystem(&v9fs_fs_type); | 
|---|
| 714 | } | 
|---|
| 715 |  | 
|---|
| 716 | module_init(init_v9fs) | 
|---|
| 717 | module_exit(exit_v9fs) | 
|---|
| 718 |  | 
|---|
| 719 | MODULE_AUTHOR( "Latchesar Ionkov <lucho@ionkov.net>"); | 
|---|
| 720 | MODULE_AUTHOR( "Eric Van Hensbergen <ericvh@gmail.com>"); | 
|---|
| 721 | MODULE_AUTHOR( "Ron Minnich <rminnich@lanl.gov>"); | 
|---|
| 722 | MODULE_DESCRIPTION( "9P Client File System"); | 
|---|
| 723 | MODULE_LICENSE( "GPL"); | 
|---|
| 724 |  | 
|---|