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