Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/p9fs/p9fs_subr.c
39563 views
1
/*-
2
* Copyright (c) 2017 Juniper Networks, Inc.
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
*
25
*/
26
/*-
27
* 9P filesystem subroutines. This file consists of all the Non VFS subroutines.
28
* It contains all of the functions related to the driver submission which form
29
* the upper layer i.e, p9fs driver. This will interact with the client to make
30
* sure we have correct API calls in the header.
31
*/
32
33
#include <sys/cdefs.h>
34
#include <sys/systm.h>
35
#include <sys/limits.h>
36
#include <sys/mount.h>
37
#include <sys/sysctl.h>
38
#include <sys/vnode.h>
39
40
#include "p9fs_proto.h"
41
42
#include <fs/p9fs/p9_client.h>
43
#include <fs/p9fs/p9_debug.h>
44
#include <fs/p9fs/p9_protocol.h>
45
#include <fs/p9fs/p9fs.h>
46
47
int
48
p9fs_proto_dotl(struct p9fs_session *vses)
49
{
50
51
return (vses->flags & P9FS_PROTO_2000L);
52
}
53
54
/* Initialize a p9fs session */
55
struct p9_fid *
56
p9fs_init_session(struct mount *mp, int *error)
57
{
58
struct p9fs_session *vses;
59
struct p9fs_mount *virtmp;
60
struct p9_fid *fid;
61
char *access;
62
63
virtmp = VFSTOP9(mp);
64
vses = &virtmp->p9fs_session;
65
vses->uid = P9_NONUNAME;
66
vses->uname = P9_DEFUNAME;
67
vses->aname = P9_DEFANAME;
68
69
/*
70
* Create the client structure. Call into the driver to create
71
* driver structures for the actual IO transfer.
72
*/
73
vses->clnt = p9_client_create(mp, error, virtmp->mount_tag);
74
75
if (vses->clnt == NULL) {
76
P9_DEBUG(ERROR, "%s: p9_client_create failed\n", __func__);
77
return (NULL);
78
}
79
/*
80
* Find the client version and cache the copy. We will use this copy
81
* throughout FS layer.
82
*/
83
if (p9_is_proto_dotl(vses->clnt))
84
vses->flags |= P9FS_PROTO_2000L;
85
else if (p9_is_proto_dotu(vses->clnt))
86
vses->flags |= P9FS_PROTO_2000U;
87
88
/* Set the access mode */
89
access = vfs_getopts(mp->mnt_optnew, "access", error);
90
if (access == NULL)
91
vses->flags |= P9_ACCESS_USER;
92
else if (!strcmp(access, "any"))
93
vses->flags |= P9_ACCESS_ANY;
94
else if (!strcmp(access, "single"))
95
vses->flags |= P9_ACCESS_SINGLE;
96
else if (!strcmp(access, "user"))
97
vses->flags |= P9_ACCESS_USER;
98
else {
99
P9_DEBUG(ERROR, "%s: unknown access mode\n", __func__);
100
*error = EINVAL;
101
goto out;
102
}
103
104
*error = 0;
105
/* Attach with the backend host*/
106
fid = p9_client_attach(vses->clnt, NULL, vses->uname, P9_NONUNAME,
107
vses->aname, error);
108
vses->mnt_fid = fid;
109
110
if (*error != 0) {
111
P9_DEBUG(ERROR, "%s: attach failed: %d\n", __func__, *error);
112
goto out;
113
}
114
P9_DEBUG(SUBR, "%s: attach successful fid :%p\n", __func__, fid);
115
fid->uid = vses->uid;
116
117
/* initialize the node list for the session */
118
STAILQ_INIT(&vses->virt_node_list);
119
P9FS_LOCK_INIT(vses);
120
121
P9_DEBUG(SUBR, "%s: INIT session successful\n", __func__);
122
123
return (fid);
124
out:
125
p9_client_destroy(vses->clnt);
126
return (NULL);
127
}
128
129
/* Begin to terminate a session */
130
void
131
p9fs_prepare_to_close(struct mount *mp)
132
{
133
struct p9fs_session *vses;
134
struct p9fs_mount *vmp;
135
struct p9fs_node *np, *pnp, *tmp;
136
137
vmp = VFSTOP9(mp);
138
vses = &vmp->p9fs_session;
139
140
/* break the node->parent references */
141
STAILQ_FOREACH_SAFE(np, &vses->virt_node_list, p9fs_node_next, tmp) {
142
if (np->parent && np->parent != np) {
143
pnp = np->parent;
144
np->parent = NULL;
145
vrele(P9FS_NTOV(pnp));
146
}
147
}
148
149
/* We are about to teardown, we dont allow anything other than clunk after this.*/
150
p9_client_begin_disconnect(vses->clnt);
151
}
152
153
/* Shutdown a session */
154
void
155
p9fs_complete_close(struct mount *mp)
156
{
157
struct p9fs_session *vses;
158
struct p9fs_mount *vmp;
159
160
vmp = VFSTOP9(mp);
161
vses = &vmp->p9fs_session;
162
163
/* Finish the close*/
164
p9_client_disconnect(vses->clnt);
165
}
166
167
168
/* Call from unmount. Close the session. */
169
void
170
p9fs_close_session(struct mount *mp)
171
{
172
struct p9fs_session *vses;
173
struct p9fs_mount *vmp;
174
175
vmp = VFSTOP9(mp);
176
vses = &vmp->p9fs_session;
177
178
p9fs_complete_close(mp);
179
/* Clean up the clnt structure. */
180
p9_client_destroy(vses->clnt);
181
P9FS_LOCK_DESTROY(vses);
182
P9_DEBUG(SUBR, "%s: Clean close session .\n", __func__);
183
}
184
185
/*
186
* Remove all the fids of a particular type from a p9fs node
187
* as well as destroy/clunk them.
188
*/
189
void
190
p9fs_fid_remove_all(struct p9fs_node *np, int leave_ofids)
191
{
192
struct p9_fid *fid, *tfid;
193
194
STAILQ_FOREACH_SAFE(fid, &np->vfid_list, fid_next, tfid) {
195
STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next);
196
p9_client_clunk(fid);
197
}
198
199
if (!leave_ofids) {
200
STAILQ_FOREACH_SAFE(fid, &np->vofid_list, fid_next, tfid) {
201
STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next);
202
p9_client_clunk(fid);
203
}
204
}
205
}
206
207
208
/* Remove a fid from its corresponding fid list */
209
void
210
p9fs_fid_remove(struct p9fs_node *np, struct p9_fid *fid, int fid_type)
211
{
212
213
switch (fid_type) {
214
case VFID:
215
P9FS_VFID_LOCK(np);
216
STAILQ_REMOVE(&np->vfid_list, fid, p9_fid, fid_next);
217
P9FS_VFID_UNLOCK(np);
218
break;
219
case VOFID:
220
P9FS_VOFID_LOCK(np);
221
STAILQ_REMOVE(&np->vofid_list, fid, p9_fid, fid_next);
222
P9FS_VOFID_UNLOCK(np);
223
break;
224
}
225
}
226
227
/* Add a fid to the corresponding fid list */
228
void
229
p9fs_fid_add(struct p9fs_node *np, struct p9_fid *fid, int fid_type)
230
{
231
232
switch (fid_type) {
233
case VFID:
234
P9FS_VFID_LOCK(np);
235
STAILQ_INSERT_TAIL(&np->vfid_list, fid, fid_next);
236
P9FS_VFID_UNLOCK(np);
237
break;
238
case VOFID:
239
P9FS_VOFID_LOCK(np);
240
STAILQ_INSERT_TAIL(&np->vofid_list, fid, fid_next);
241
P9FS_VOFID_UNLOCK(np);
242
break;
243
}
244
}
245
246
/* Build the path from root to current directory */
247
static int
248
p9fs_get_full_path(struct p9fs_node *np, char ***names)
249
{
250
int i, n;
251
struct p9fs_node *node;
252
char **wnames;
253
254
n = 0;
255
for (node = np ; (node != NULL) && !IS_ROOT(node) ; node = node->parent)
256
n++;
257
258
if (node == NULL)
259
return (0);
260
261
wnames = malloc(n * sizeof(char *), M_TEMP, M_ZERO|M_WAITOK);
262
263
for (i = n-1, node = np; i >= 0 ; i--, node = node->parent)
264
wnames[i] = node->inode.i_name;
265
266
*names = wnames;
267
return (n);
268
}
269
270
/*
271
* Return TRUE if this fid can be used for the requested mode.
272
*/
273
static int
274
p9fs_compatible_mode(struct p9_fid *fid, int mode)
275
{
276
/*
277
* Return TRUE for an exact match. For OREAD and OWRITE, allow
278
* existing ORDWR fids to match. Only check the low two bits
279
* of mode.
280
*
281
* TODO: figure out if this is correct for O_APPEND
282
*/
283
int fid_mode = fid->mode & 3;
284
if (fid_mode == mode)
285
return (TRUE);
286
if (fid_mode == P9PROTO_ORDWR)
287
return (mode == P9PROTO_OREAD || mode == P9PROTO_OWRITE);
288
return (FALSE);
289
}
290
291
/*
292
* Retrieve fid structure corresponding to a particular
293
* uid and fid type for a p9fs node
294
*/
295
static struct p9_fid *
296
p9fs_get_fid_from_uid(struct p9fs_node *np, uid_t uid, int fid_type, int mode)
297
{
298
struct p9_fid *fid;
299
300
switch (fid_type) {
301
case VFID:
302
P9FS_VFID_LOCK(np);
303
STAILQ_FOREACH(fid, &np->vfid_list, fid_next) {
304
if (fid->uid == uid) {
305
P9FS_VFID_UNLOCK(np);
306
return (fid);
307
}
308
}
309
P9FS_VFID_UNLOCK(np);
310
break;
311
case VOFID:
312
P9FS_VOFID_LOCK(np);
313
STAILQ_FOREACH(fid, &np->vofid_list, fid_next) {
314
if (fid->uid == uid && p9fs_compatible_mode(fid, mode)) {
315
P9FS_VOFID_UNLOCK(np);
316
return (fid);
317
}
318
}
319
P9FS_VOFID_UNLOCK(np);
320
break;
321
}
322
323
return (NULL);
324
}
325
326
/*
327
* Function returns the fid sturcture for a file corresponding to current user id.
328
* First it searches in the fid list of the corresponding p9fs node.
329
* New fid will be created if not already present and added in the corresponding
330
* fid list in the p9fs node.
331
* If the user is not already attached then this will attach the user first
332
* and then create a new fid for this particular file by doing dir walk.
333
*/
334
struct p9_fid *
335
p9fs_get_fid(struct p9_client *clnt, struct p9fs_node *np, struct ucred *cred,
336
int fid_type, int mode, int *error)
337
{
338
uid_t uid;
339
struct p9_fid *fid, *oldfid;
340
struct p9fs_node *root;
341
struct p9fs_session *vses;
342
int i, l, clone;
343
char **wnames = NULL;
344
uint16_t nwnames;
345
346
oldfid = NULL;
347
vses = np->p9fs_ses;
348
349
if (vses->flags & P9_ACCESS_ANY)
350
uid = vses->uid;
351
else if (cred)
352
uid = cred->cr_uid;
353
else
354
uid = 0;
355
356
/*
357
* Search for the fid in corresponding fid list.
358
* We should return NULL for VOFID if it is not present in the list.
359
* Because VOFID should have been created during the file open.
360
* If VFID is not present in the list then we should create one.
361
*/
362
fid = p9fs_get_fid_from_uid(np, uid, fid_type, mode);
363
if (fid != NULL || fid_type == VOFID)
364
return (fid);
365
366
/* Check root if the user is attached */
367
root = &np->p9fs_ses->rnp;
368
fid = p9fs_get_fid_from_uid(root, uid, fid_type, mode);
369
if(fid == NULL) {
370
/* Attach the user */
371
fid = p9_client_attach(clnt, NULL, NULL, uid,
372
vses->aname, error);
373
if (*error != 0)
374
return (NULL);
375
p9fs_fid_add(root, fid, fid_type);
376
}
377
378
/* If we are looking for root then return it */
379
if (IS_ROOT(np))
380
return (fid);
381
382
/* Get full path from root to p9fs node */
383
nwnames = p9fs_get_full_path(np, &wnames);
384
385
/*
386
* Could not get full path.
387
* If p9fs node is not deleted, parent should exist.
388
*/
389
KASSERT(nwnames != 0, ("%s: Directory of %s doesn't exist", __func__, np->inode.i_name));
390
391
clone = 1;
392
i = 0;
393
while (i < nwnames) {
394
l = MIN(nwnames - i, P9_MAXWELEM);
395
396
fid = p9_client_walk(fid, l, wnames, clone, error);
397
if (*error != 0) {
398
if (oldfid)
399
p9_client_clunk(oldfid);
400
fid = NULL;
401
goto bail_out;
402
}
403
oldfid = fid;
404
clone = 0;
405
i += l ;
406
}
407
p9fs_fid_add(np, fid, fid_type);
408
bail_out:
409
free(wnames, M_TEMP);
410
return (fid);
411
}
412
413