Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/fs/p9fs/p9fs_vnops.c
39507 views
1
/*
2
* Copyright (c) 2017-2020 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
/* This file contains VFS file ops for the 9P protocol.
28
* This makes the upper layer of the p9fs driver. These functions interact
29
* with the VFS layer and lower layer of p9fs driver which is 9Pnet. All
30
* the user file operations are handled here.
31
*/
32
#include <sys/cdefs.h>
33
#include <sys/systm.h>
34
#include <sys/bio.h>
35
#include <sys/buf.h>
36
#include <sys/dirent.h>
37
#include <sys/fcntl.h>
38
#include <sys/namei.h>
39
#include <sys/priv.h>
40
#include <sys/stat.h>
41
#include <sys/vnode.h>
42
#include <sys/rwlock.h>
43
#include <sys/vmmeter.h>
44
45
#include <vm/vm.h>
46
#include <vm/vm_extern.h>
47
#include <vm/vm_object.h>
48
#include <vm/vm_page.h>
49
#include <vm/vm_pager.h>
50
#include <vm/vnode_pager.h>
51
52
#include <fs/p9fs/p9_client.h>
53
#include <fs/p9fs/p9_debug.h>
54
#include <fs/p9fs/p9fs.h>
55
#include <fs/p9fs/p9fs_proto.h>
56
57
/* File permissions. */
58
#define IEXEC 0000100 /* Executable. */
59
#define IWRITE 0000200 /* Writeable. */
60
#define IREAD 0000400 /* Readable. */
61
#define ISVTX 0001000 /* Sticky bit. */
62
#define ISGID 0002000 /* Set-gid. */
63
#define ISUID 0004000 /* Set-uid. */
64
65
static MALLOC_DEFINE(M_P9UIOV, "uio", "UIOV structures for strategy in p9fs");
66
extern uma_zone_t p9fs_io_buffer_zone;
67
extern uma_zone_t p9fs_getattr_zone;
68
extern uma_zone_t p9fs_setattr_zone;
69
extern uma_zone_t p9fs_pbuf_zone;
70
/* For the root vnode's vnops. */
71
struct vop_vector p9fs_vnops;
72
73
static uint32_t p9fs_unix2p9_mode(uint32_t mode);
74
75
static void
76
p9fs_itimes(struct vnode *vp)
77
{
78
struct p9fs_node *node;
79
struct timespec ts;
80
struct p9fs_inode *inode;
81
82
node = P9FS_VTON(vp);
83
inode = &node->inode;
84
85
vfs_timestamp(&ts);
86
inode->i_mtime = ts.tv_sec;
87
}
88
89
/*
90
* Cleanup the p9fs node, the in memory representation of a vnode for p9fs.
91
* The cleanup includes invalidating all cache entries for the vnode,
92
* destroying the vobject, removing vnode from hashlist, removing p9fs node
93
* from the list of session p9fs nodes, and disposing of the p9fs node.
94
* Basically it is doing a reverse of what a create/vget does.
95
*/
96
void
97
p9fs_cleanup(struct p9fs_node *np)
98
{
99
struct vnode *vp;
100
struct p9fs_session *vses;
101
102
if (np == NULL)
103
return;
104
105
vp = P9FS_NTOV(np);
106
vses = np->p9fs_ses;
107
108
/* Remove the vnode from hash list if vnode is not already deleted */
109
if ((np->flags & P9FS_NODE_DELETED) == 0)
110
vfs_hash_remove(vp);
111
112
P9FS_LOCK(vses);
113
if ((np->flags & P9FS_NODE_IN_SESSION) != 0) {
114
np->flags &= ~P9FS_NODE_IN_SESSION;
115
STAILQ_REMOVE(&vses->virt_node_list, np, p9fs_node, p9fs_node_next);
116
} else {
117
P9FS_UNLOCK(vses);
118
return;
119
}
120
P9FS_UNLOCK(vses);
121
122
/* Invalidate all entries to a particular vnode. */
123
cache_purge(vp);
124
125
/* Destroy the vm object and flush associated pages. */
126
vnode_destroy_vobject(vp);
127
128
/* Remove all the FID */
129
p9fs_fid_remove_all(np, FALSE);
130
131
/* Dispose all node knowledge.*/
132
p9fs_destroy_node(&np);
133
}
134
135
/*
136
* Reclaim VOP is defined to be called for every vnode. This starts off
137
* the cleanup by clunking(remove the fid on the server) and calls
138
* p9fs_cleanup to free all the resources allocated for p9fs node.
139
*/
140
static int
141
p9fs_reclaim(struct vop_reclaim_args *ap)
142
{
143
struct vnode *vp;
144
struct p9fs_node *np;
145
146
vp = ap->a_vp;
147
np = P9FS_VTON(vp);
148
149
P9_DEBUG(VOPS, "%s: vp:%p node:%p\n", __func__, vp, np);
150
p9fs_cleanup(np);
151
152
return (0);
153
}
154
155
/*
156
* recycle vnodes which are no longer referenced i.e, their usecount is zero
157
*/
158
static int
159
p9fs_inactive(struct vop_inactive_args *ap)
160
{
161
struct vnode *vp;
162
struct p9fs_node *np;
163
164
vp = ap->a_vp;
165
np = P9FS_VTON(vp);
166
167
P9_DEBUG(VOPS, "%s: vp:%p node:%p file:%s\n", __func__, vp, np, np->inode.i_name);
168
if (np->flags & P9FS_NODE_DELETED)
169
vrecycle(vp);
170
171
return (0);
172
}
173
174
struct p9fs_lookup_alloc_arg {
175
struct componentname *cnp;
176
struct p9fs_node *dnp;
177
struct p9_fid *newfid;
178
};
179
180
/* Callback for vn_get_ino */
181
static int
182
p9fs_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp)
183
{
184
struct p9fs_lookup_alloc_arg *p9aa = arg;
185
186
return (p9fs_vget_common(mp, NULL, p9aa->cnp->cn_lkflags, p9aa->dnp,
187
p9aa->newfid, vpp, p9aa->cnp->cn_nameptr));
188
}
189
190
/*
191
* p9fs_lookup is called for every component name that is being searched for.
192
*
193
* I. If component is found on the server, we look for the in-memory
194
* repesentation(vnode) of this component in namecache.
195
* A. If the node is found in the namecache, we check is the vnode is still
196
* valid.
197
* 1. If it is still valid, return vnode.
198
* 2. If it is not valid, we remove this vnode from the name cache and
199
* create a new vnode for the component and return that vnode.
200
* B. If the vnode is not found in the namecache, we look for it in the
201
* hash list.
202
* 1. If the vnode is in the hash list, we check if the vnode is still
203
* valid.
204
* a. If it is still valid, we add that vnode to the namecache for
205
* future lookups and return the vnode.
206
* b. If it is not valid, create a new vnode and p9fs node,
207
* initialize them and return the vnode.
208
* 2. If the vnode is not found in the hash list, we create a new vnode
209
* and p9fs node, initialize them and return the vnode.
210
* II. If the component is not found on the server, an error code is returned.
211
* A. For the creation case, we return EJUSTRETURN so VFS can handle it.
212
* B. For all other cases, ENOENT is returned.
213
*/
214
static int
215
p9fs_lookup(struct vop_lookup_args *ap)
216
{
217
struct vnode *dvp;
218
struct vnode **vpp, *vp;
219
struct componentname *cnp;
220
struct p9fs_node *dnp; /*dir p9_node */
221
struct p9fs_node *np;
222
struct p9fs_session *vses;
223
struct mount *mp; /* Get the mount point */
224
struct p9_fid *dvfid, *newfid;
225
uint64_t flags;
226
int error;
227
struct vattr vattr;
228
char tmpchr;
229
230
dvp = ap->a_dvp;
231
vpp = ap->a_vpp;
232
cnp = ap->a_cnp;
233
dnp = P9FS_VTON(dvp);
234
error = 0;
235
flags = cnp->cn_flags;
236
*vpp = NULL;
237
238
if (dnp == NULL)
239
return (ENOENT);
240
241
if (cnp->cn_nameptr[0] == '.' && cnp->cn_namelen == 1) {
242
vref(dvp);
243
*vpp = dvp;
244
return (0);
245
}
246
247
vses = dnp->p9fs_ses;
248
mp = vses->p9fs_mount;
249
250
/* Do the cache part ourselves */
251
if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) &&
252
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
253
return (EROFS);
254
255
if (dvp->v_type != VDIR)
256
return (ENOTDIR);
257
258
error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, curthread);
259
if (error)
260
return (error);
261
262
/* Do the directory walk on host to check if file exist */
263
dvfid = p9fs_get_fid(vses->clnt, dnp, cnp->cn_cred, VFID, -1, &error);
264
if (error)
265
return (error);
266
267
/*
268
* Save the character present at namelen in nameptr string and
269
* null terminate the character to get the search name for p9_dir_walk
270
* This is done to handle when lookup is for "a" and component
271
* name contains a/b/c
272
*/
273
tmpchr = cnp->cn_nameptr[cnp->cn_namelen];
274
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
275
276
/*
277
* If the client_walk fails, it means the file looking for doesnt exist.
278
* Create the file is the flags are set or just return the error
279
*/
280
newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error);
281
282
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
283
284
if (error != 0 || newfid == NULL) {
285
/* Clunk the newfid if it is not NULL */
286
if (newfid != NULL)
287
p9_client_clunk(newfid);
288
289
if (error != ENOENT)
290
return (error);
291
292
/* The requested file was not found. */
293
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
294
(flags & ISLASTCN)) {
295
296
if (mp->mnt_flag & MNT_RDONLY)
297
return (EROFS);
298
299
error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
300
curthread);
301
if (!error) {
302
return (EJUSTRETURN);
303
}
304
}
305
return (error);
306
}
307
308
/* Look for the entry in the component cache*/
309
error = cache_lookup(dvp, vpp, cnp, NULL, NULL);
310
if (error > 0 && error != ENOENT) {
311
P9_DEBUG(VOPS, "%s: Cache lookup error %d \n", __func__, error);
312
goto out;
313
}
314
315
if (error == -1) {
316
vp = *vpp;
317
/* Check if the entry in cache is stale or not */
318
if ((p9fs_node_cmp(vp, &newfid->qid) == 0) &&
319
((error = VOP_GETATTR(vp, &vattr, cnp->cn_cred)) == 0)) {
320
goto out;
321
}
322
/*
323
* This case, we have an error coming from getattr,
324
* act accordingly.
325
*/
326
cache_purge(vp);
327
if (dvp != vp)
328
vput(vp);
329
else
330
vrele(vp);
331
332
*vpp = NULL;
333
} else if (error == ENOENT) {
334
if (VN_IS_DOOMED(dvp))
335
goto out;
336
if (VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0) {
337
error = ENOENT;
338
goto out;
339
}
340
cache_purge_negative(dvp);
341
}
342
/* Reset values */
343
error = 0;
344
vp = NULL;
345
346
tmpchr = cnp->cn_nameptr[cnp->cn_namelen];
347
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
348
349
/*
350
* Looks like we have found an entry. Now take care of all other cases.
351
*/
352
if (flags & ISDOTDOT) {
353
struct p9fs_lookup_alloc_arg p9aa;
354
p9aa.cnp = cnp;
355
p9aa.dnp = dnp;
356
p9aa.newfid = newfid;
357
error = vn_vget_ino_gen(dvp, p9fs_lookup_alloc, &p9aa, 0, &vp);
358
if (error)
359
goto out;
360
*vpp = vp;
361
} else {
362
/*
363
* client_walk is equivalent to searching a component name in a
364
* directory(fid) here. If new fid is returned, we have found an
365
* entry for this component name so, go and create the rest of
366
* the vnode infra(vget_common) for the returned newfid.
367
*/
368
if ((cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
369
&& (flags & ISLASTCN)) {
370
error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
371
curthread);
372
if (error)
373
goto out;
374
375
error = p9fs_vget_common(mp, NULL, cnp->cn_lkflags,
376
dnp, newfid, &vp, cnp->cn_nameptr);
377
if (error)
378
goto out;
379
380
*vpp = vp;
381
np = P9FS_VTON(vp);
382
if ((dnp->inode.i_mode & ISVTX) &&
383
cnp->cn_cred->cr_uid != 0 &&
384
cnp->cn_cred->cr_uid != dnp->inode.n_uid &&
385
cnp->cn_cred->cr_uid != np->inode.n_uid) {
386
vput(*vpp);
387
*vpp = NULL;
388
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
389
return (EPERM);
390
}
391
} else {
392
error = p9fs_vget_common(mp, NULL, cnp->cn_lkflags,
393
dnp, newfid, &vp, cnp->cn_nameptr);
394
if (error)
395
goto out;
396
*vpp = vp;
397
}
398
}
399
400
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
401
402
/* Store the result the cache if MAKEENTRY is specified in flags */
403
if ((cnp->cn_flags & MAKEENTRY) != 0)
404
cache_enter(dvp, *vpp, cnp);
405
return (error);
406
out:
407
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
408
p9_client_clunk(newfid);
409
return (error);
410
}
411
412
/*
413
* Common creation function for file/directory with respective flags. We first
414
* open the parent directory in order to create the file under it. For this,
415
* as 9P protocol suggests, we need to call client_walk to create the open fid.
416
* Once we have the open fid, the file_create function creates the direntry with
417
* the name and perm specified under the parent dir. If this succeeds (an entry
418
* is created for the new file on the server), we create our metadata for this
419
* file (vnode, p9fs node calling vget). Once we are done, we clunk the open
420
* fid of the parent directory.
421
*/
422
static int
423
create_common(struct p9fs_node *dnp, struct componentname *cnp,
424
char *extension, uint32_t perm, uint8_t mode, struct vnode **vpp)
425
{
426
char tmpchr;
427
struct p9_fid *dvfid, *ofid, *newfid;
428
struct p9fs_session *vses;
429
struct mount *mp;
430
int error;
431
432
P9_DEBUG(VOPS, "%s: name %s\n", __func__, cnp->cn_nameptr);
433
434
vses = dnp->p9fs_ses;
435
mp = vses->p9fs_mount;
436
newfid = NULL;
437
error = 0;
438
439
dvfid = p9fs_get_fid(vses->clnt, dnp, cnp->cn_cred, VFID, -1, &error);
440
if (error != 0)
441
return (error);
442
443
/* Clone the directory fid to create the new file */
444
ofid = p9_client_walk(dvfid, 0, NULL, 1, &error);
445
if (error != 0)
446
return (error);
447
448
/*
449
* Save the character present at namelen in nameptr string and
450
* null terminate the character to get the search name for p9_dir_walk
451
*/
452
tmpchr = cnp->cn_nameptr[cnp->cn_namelen];
453
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
454
455
error = p9_client_file_create(ofid, cnp->cn_nameptr, perm, mode,
456
extension);
457
if (error != 0) {
458
P9_DEBUG(ERROR, "%s: p9_client_fcreate failed %d\n", __func__, error);
459
goto out;
460
}
461
462
/* If its not hardlink only then do the walk, else we are done. */
463
if (!(perm & P9PROTO_DMLINK)) {
464
/*
465
* Do the lookup part and add the vnode, p9fs node. Note that vpp
466
* is filled in here.
467
*/
468
newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error);
469
if (newfid != NULL) {
470
error = p9fs_vget_common(mp, NULL, cnp->cn_lkflags,
471
dnp, newfid, vpp, cnp->cn_nameptr);
472
if (error != 0)
473
goto out;
474
} else {
475
/* Not found return NOENTRY.*/
476
goto out;
477
}
478
479
if ((cnp->cn_flags & MAKEENTRY) != 0)
480
cache_enter(P9FS_NTOV(dnp), *vpp, cnp);
481
}
482
P9_DEBUG(VOPS, "%s: created file under vp %p node %p fid %ju\n",
483
__func__, *vpp, dnp, (uintmax_t)dvfid->fid);
484
/* Clunk the open ofid. */
485
if (ofid != NULL)
486
(void)p9_client_clunk(ofid);
487
488
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
489
return (0);
490
out:
491
if (ofid != NULL)
492
(void)p9_client_clunk(ofid);
493
494
if (newfid != NULL)
495
(void)p9_client_clunk(newfid);
496
497
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
498
return (error);
499
}
500
501
/*
502
* This is the main file creation VOP. Make the permissions of the new
503
* file and call the create_common common code to complete the create.
504
*/
505
static int
506
p9fs_create(struct vop_create_args *ap)
507
{
508
struct vnode *dvp;
509
struct vnode **vpp;
510
struct componentname *cnp;
511
uint32_t mode;
512
struct p9fs_node *dnp;
513
struct p9fs_inode *dinode;
514
uint32_t perm;
515
int ret;
516
517
dvp = ap->a_dvp;
518
vpp = ap->a_vpp;
519
cnp = ap->a_cnp;
520
dnp = P9FS_VTON(dvp);
521
dinode = &dnp->inode;
522
mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
523
perm = p9fs_unix2p9_mode(mode);
524
525
P9_DEBUG(VOPS, "%s: dvp %p\n", __func__, dvp);
526
527
ret = create_common(dnp, cnp, NULL, perm, P9PROTO_ORDWR, vpp);
528
if (ret == 0) {
529
P9FS_INCR_LINKS(dinode);
530
}
531
532
return (ret);
533
}
534
535
/*
536
* p9fs_mkdir is the main directory creation vop. Make the permissions of the new dir
537
* and call the create_common common code to complete the create.
538
*/
539
static int
540
p9fs_mkdir(struct vop_mkdir_args *ap)
541
{
542
struct vnode *dvp;
543
struct vnode **vpp;
544
struct componentname *cnp;
545
uint32_t mode;
546
struct p9fs_node *dnp;
547
struct p9fs_inode *dinode;
548
uint32_t perm;
549
int ret;
550
551
dvp = ap->a_dvp;
552
vpp = ap->a_vpp;
553
cnp = ap->a_cnp;
554
dnp = P9FS_VTON(dvp);
555
dinode = &dnp->inode;
556
mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
557
perm = p9fs_unix2p9_mode(mode | S_IFDIR);
558
559
P9_DEBUG(VOPS, "%s: dvp %p\n", __func__, dvp);
560
561
ret = create_common(dnp, cnp, NULL, perm, P9PROTO_ORDWR, vpp);
562
if (ret == 0)
563
P9FS_INCR_LINKS(dinode);
564
565
return (ret);
566
}
567
568
/*
569
* p9fs_mknod is the main node creation vop. Make the permissions of the new node
570
* and call the create_common common code to complete the create.
571
*/
572
static int
573
p9fs_mknod(struct vop_mknod_args *ap)
574
{
575
struct vnode *dvp;
576
struct vnode **vpp;
577
struct componentname *cnp;
578
uint32_t mode;
579
struct p9fs_node *dnp;
580
struct p9fs_inode *dinode;
581
uint32_t perm;
582
int ret;
583
584
dvp = ap->a_dvp;
585
vpp = ap->a_vpp;
586
cnp = ap->a_cnp;
587
dnp = P9FS_VTON(dvp);
588
dinode = &dnp->inode;
589
mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
590
perm = p9fs_unix2p9_mode(mode);
591
592
P9_DEBUG(VOPS, "%s: dvp %p\n", __func__, dvp);
593
594
ret = create_common(dnp, cnp, NULL, perm, P9PROTO_OREAD, vpp);
595
if (ret == 0) {
596
P9FS_INCR_LINKS(dinode);
597
}
598
599
return (ret);
600
}
601
602
/* Convert open mode permissions to P9 */
603
static int
604
p9fs_uflags_mode(int uflags, int extended)
605
{
606
uint32_t ret;
607
608
/* Convert first to O flags.*/
609
uflags = OFLAGS(uflags);
610
611
switch (uflags & 3) {
612
613
case O_RDONLY:
614
ret = P9PROTO_OREAD;
615
break;
616
617
case O_WRONLY:
618
ret = P9PROTO_OWRITE;
619
break;
620
621
case O_RDWR:
622
ret = P9PROTO_ORDWR;
623
break;
624
}
625
626
if (extended) {
627
if (uflags & O_EXCL)
628
ret |= P9PROTO_OEXCL;
629
630
if (uflags & O_APPEND)
631
ret |= P9PROTO_OAPPEND;
632
}
633
634
return (ret);
635
}
636
637
/*
638
* This is the main open VOP for every file open. If the file is already
639
* open, then increment and return. If there is no open fid for this file,
640
* there needs to be a client_walk which creates a new open fid for this file.
641
* Once we have a open fid, call the open on this file with the mode creating
642
* the vobject.
643
*/
644
static int
645
p9fs_open(struct vop_open_args *ap)
646
{
647
int error;
648
struct vnode *vp;
649
struct p9fs_node *np;
650
struct p9fs_session *vses;
651
struct p9_fid *vofid, *vfid;
652
size_t filesize;
653
uint32_t mode;
654
655
error = 0;
656
vp = ap->a_vp;
657
np = P9FS_VTON(vp);
658
vses = np->p9fs_ses;
659
660
P9_DEBUG(VOPS, "%s: vp %p\n", __func__, vp);
661
662
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK)
663
return (EOPNOTSUPP);
664
665
error = p9fs_reload_stats_dotl(vp, ap->a_cred);
666
if (error != 0)
667
return (error);
668
669
ASSERT_VOP_LOCKED(vp, __func__);
670
/*
671
* Invalidate the pages of the vm_object cache if the file is modified
672
* based on the flag set in reload stats
673
*/
674
if (vp->v_type == VREG && (np->flags & P9FS_NODE_MODIFIED) != 0) {
675
error = vinvalbuf(vp, 0, 0, 0);
676
if (error != 0)
677
return (error);
678
np->flags &= ~P9FS_NODE_MODIFIED;
679
}
680
681
vfid = p9fs_get_fid(vses->clnt, np, ap->a_cred, VFID, -1, &error);
682
if (error != 0)
683
return (error);
684
685
/*
686
* Translate kernel fflags to 9p mode
687
*/
688
mode = p9fs_uflags_mode(ap->a_mode, 1);
689
690
/*
691
* Search the fid in vofid_list for current user. If found increase the open
692
* count and return. If not found clone a new fid and open the file using
693
* that cloned fid.
694
*/
695
vofid = p9fs_get_fid(vses->clnt, np, ap->a_cred, VOFID, mode, &error);
696
if (vofid != NULL) {
697
vofid->v_opens++;
698
return (0);
699
} else {
700
/*vofid is the open fid for this file.*/
701
vofid = p9_client_walk(vfid, 0, NULL, 1, &error);
702
if (error != 0)
703
return (error);
704
}
705
706
error = p9_client_open(vofid, mode);
707
if (error != 0)
708
p9_client_clunk(vofid);
709
else {
710
vofid->v_opens = 1;
711
filesize = np->inode.i_size;
712
vnode_create_vobject(vp, filesize, ap->a_td);
713
p9fs_fid_add(np, vofid, VOFID);
714
}
715
716
return (error);
717
}
718
719
/*
720
* Close the open references. Just reduce the open count on vofid and return.
721
* Let clunking of VOFID happen in p9fs_reclaim.
722
*/
723
static int
724
p9fs_close(struct vop_close_args *ap)
725
{
726
struct vnode *vp;
727
struct p9fs_node *np;
728
struct p9fs_session *vses;
729
struct p9_fid *vofid;
730
int error;
731
732
vp = ap->a_vp;
733
np = P9FS_VTON(vp);
734
735
if (np == NULL)
736
return (0);
737
738
vses = np->p9fs_ses;
739
error = 0;
740
741
P9_DEBUG(VOPS, "%s: file_name %s\n", __func__, np->inode.i_name);
742
743
/*
744
* Translate kernel fflags to 9p mode
745
*/
746
vofid = p9fs_get_fid(vses->clnt, np, ap->a_cred, VOFID,
747
p9fs_uflags_mode(ap->a_fflag, 1), &error);
748
if (vofid == NULL)
749
return (0);
750
751
vofid->v_opens--;
752
753
return (0);
754
}
755
756
/* Helper routine for checking if fileops are possible on this file */
757
static int
758
p9fs_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode)
759
{
760
761
/* Check if we are allowed to write */
762
switch (vap->va_type) {
763
case VDIR:
764
case VLNK:
765
case VREG:
766
/*
767
* Normal nodes: check if we're on a read-only mounted
768
* file system and bail out if we're trying to write.
769
*/
770
if ((mode & VMODIFY_PERMS) && (vp->v_mount->mnt_flag & MNT_RDONLY))
771
return (EROFS);
772
break;
773
case VBLK:
774
case VCHR:
775
case VSOCK:
776
case VFIFO:
777
/*
778
* Special nodes: even on read-only mounted file systems
779
* these are allowed to be written to if permissions allow.
780
*/
781
break;
782
default:
783
/* No idea what this is */
784
return (EINVAL);
785
}
786
787
return (0);
788
}
789
790
/* Check the access permissions of the file. */
791
static int
792
p9fs_access(struct vop_access_args *ap)
793
{
794
struct vnode *vp;
795
accmode_t accmode;
796
struct ucred *cred;
797
struct vattr vap;
798
int error;
799
800
vp = ap->a_vp;
801
accmode = ap->a_accmode;
802
cred = ap->a_cred;
803
804
P9_DEBUG(VOPS, "%s: vp %p\n", __func__, vp);
805
806
/* make sure getattr is working correctly and is defined.*/
807
error = VOP_GETATTR(vp, &vap, cred);
808
if (error != 0)
809
return (error);
810
811
error = p9fs_check_possible(vp, &vap, accmode);
812
if (error != 0)
813
return (error);
814
815
/* Call the Generic Access check in VOPS*/
816
error = vaccess(vp->v_type, vap.va_mode, vap.va_uid, vap.va_gid, accmode,
817
cred);
818
819
820
return (error);
821
}
822
823
/*
824
* Reload the file stats from the server and update the inode structure present
825
* in p9fs node.
826
*/
827
int
828
p9fs_reload_stats_dotl(struct vnode *vp, struct ucred *cred)
829
{
830
struct p9_stat_dotl *stat;
831
int error;
832
struct p9fs_node *node;
833
struct p9fs_session *vses;
834
struct p9_fid *vfid;
835
836
error = 0;
837
node = P9FS_VTON(vp);
838
vses = node->p9fs_ses;
839
840
vfid = p9fs_get_fid(vses->clnt, node, cred, VOFID, P9PROTO_OREAD, &error);
841
if (vfid == NULL) {
842
vfid = p9fs_get_fid(vses->clnt, node, cred, VFID, -1, &error);
843
if (error)
844
return (error);
845
}
846
847
stat = uma_zalloc(p9fs_getattr_zone, M_WAITOK | M_ZERO);
848
849
error = p9_client_getattr(vfid, stat, P9PROTO_STATS_ALL);
850
if (error != 0) {
851
P9_DEBUG(ERROR, "%s: p9_client_getattr failed: %d\n", __func__, error);
852
goto out;
853
}
854
855
/* Init the vnode with the disk info */
856
p9fs_stat_vnode_dotl(stat, vp);
857
out:
858
if (stat != NULL) {
859
uma_zfree(p9fs_getattr_zone, stat);
860
}
861
862
return (error);
863
}
864
865
/*
866
* Read the current inode values into the vap attr. We reload the stats from
867
* the server.
868
*/
869
static int
870
p9fs_getattr_dotl(struct vop_getattr_args *ap)
871
{
872
struct vnode *vp;
873
struct vattr *vap;
874
struct p9fs_node *node;
875
struct p9fs_inode *inode;
876
int error;
877
878
vp = ap->a_vp;
879
vap = ap->a_vap;
880
node = P9FS_VTON(vp);
881
882
if (node == NULL)
883
return (ENOENT);
884
885
inode = &node->inode;
886
887
P9_DEBUG(VOPS, "%s: %u %u\n", __func__, inode->i_mode, IFTOVT(inode->i_mode));
888
889
/* Reload our stats once to get the right values.*/
890
error = p9fs_reload_stats_dotl(vp, ap->a_cred);
891
if (error != 0) {
892
P9_DEBUG(ERROR, "%s: failed: %d\n", __func__, error);
893
return (error);
894
}
895
896
/* Basic info */
897
VATTR_NULL(vap);
898
899
vap->va_atime.tv_sec = inode->i_atime;
900
vap->va_mtime.tv_sec = inode->i_mtime;
901
vap->va_ctime.tv_sec = inode->i_ctime;
902
vap->va_atime.tv_nsec = inode->i_atime_nsec;
903
vap->va_mtime.tv_nsec = inode->i_mtime_nsec;
904
vap->va_ctime.tv_nsec = inode->i_ctime_nsec;
905
vap->va_type = IFTOVT(inode->i_mode);
906
vap->va_mode = inode->i_mode;
907
vap->va_uid = inode->n_uid;
908
vap->va_gid = inode->n_gid;
909
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
910
vap->va_size = inode->i_size;
911
vap->va_nlink = inode->i_links_count;
912
vap->va_blocksize = inode->blksize;
913
vap->va_fileid = inode->i_qid_path;
914
vap->va_flags = inode->i_flags;
915
vap->va_gen = inode->gen;
916
vap->va_filerev = inode->data_version;
917
vap->va_vaflags = 0;
918
vap->va_bytes = inode->blocks * P9PROTO_TGETATTR_BLK;
919
920
return (0);
921
}
922
923
/* Convert a standard FreeBSD permission to P9. */
924
static uint32_t
925
p9fs_unix2p9_mode(uint32_t mode)
926
{
927
uint32_t res;
928
929
res = mode & 0777;
930
if (S_ISDIR(mode))
931
res |= P9PROTO_DMDIR;
932
if (S_ISSOCK(mode))
933
res |= P9PROTO_DMSOCKET;
934
if (S_ISLNK(mode))
935
res |= P9PROTO_DMSYMLINK;
936
if (S_ISFIFO(mode))
937
res |= P9PROTO_DMNAMEDPIPE;
938
if ((mode & S_ISUID) == S_ISUID)
939
res |= P9PROTO_DMSETUID;
940
if ((mode & S_ISGID) == S_ISGID)
941
res |= P9PROTO_DMSETGID;
942
if ((mode & S_ISVTX) == S_ISVTX)
943
res |= P9PROTO_DMSETVTX;
944
945
return (res);
946
}
947
948
/* Update inode with the stats read from server.(9P2000.L version) */
949
int
950
p9fs_stat_vnode_dotl(struct p9_stat_dotl *stat, struct vnode *vp)
951
{
952
struct p9fs_node *np;
953
struct p9fs_inode *inode;
954
955
np = P9FS_VTON(vp);
956
inode = &np->inode;
957
958
ASSERT_VOP_LOCKED(vp, __func__);
959
/* Update the pager size if file size changes on host */
960
if (inode->i_size != stat->st_size) {
961
inode->i_size = stat->st_size;
962
if (vp->v_type == VREG)
963
vnode_pager_setsize(vp, inode->i_size);
964
}
965
966
inode->i_mtime = stat->st_mtime_sec;
967
inode->i_atime = stat->st_atime_sec;
968
inode->i_ctime = stat->st_ctime_sec;
969
inode->i_mtime_nsec = stat->st_mtime_nsec;
970
inode->i_atime_nsec = stat->st_atime_nsec;
971
inode->i_ctime_nsec = stat->st_ctime_nsec;
972
inode->n_uid = stat->st_uid;
973
inode->n_gid = stat->st_gid;
974
inode->i_mode = stat->st_mode;
975
vp->v_type = IFTOVT(inode->i_mode);
976
inode->i_links_count = stat->st_nlink;
977
inode->blksize = stat->st_blksize;
978
inode->blocks = stat->st_blocks;
979
inode->gen = stat->st_gen;
980
inode->data_version = stat->st_data_version;
981
982
ASSERT_VOP_LOCKED(vp, __func__);
983
/* Setting a flag if file changes based on qid version */
984
if (np->vqid.qid_version != stat->qid.version)
985
np->flags |= P9FS_NODE_MODIFIED;
986
memcpy(&np->vqid, &stat->qid, sizeof(stat->qid));
987
988
return (0);
989
}
990
991
/*
992
* Write the current in memory inode stats into persistent stats structure
993
* to write to the server(for linux version).
994
*/
995
static int
996
p9fs_inode_to_iattr(struct p9fs_inode *inode, struct p9_iattr_dotl *p9attr)
997
{
998
p9attr->size = inode->i_size;
999
p9attr->mode = inode->i_mode;
1000
p9attr->uid = inode->n_uid;
1001
p9attr->gid = inode->n_gid;
1002
p9attr->atime_sec = inode->i_atime;
1003
p9attr->atime_nsec = inode->i_atime_nsec;
1004
p9attr->mtime_sec = inode->i_mtime;
1005
p9attr->mtime_nsec = inode->i_mtime_nsec;
1006
1007
return (0);
1008
}
1009
1010
/*
1011
* Modify the ownership of a file whenever the chown is called on the
1012
* file.
1013
*/
1014
static int
1015
p9fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
1016
struct thread *td)
1017
{
1018
struct p9fs_node *np;
1019
struct p9fs_inode *inode;
1020
uid_t ouid;
1021
gid_t ogid;
1022
int error;
1023
1024
np = P9FS_VTON(vp);
1025
inode = &np->inode;
1026
1027
if (uid == (uid_t)VNOVAL)
1028
uid = inode->n_uid;
1029
if (gid == (gid_t)VNOVAL)
1030
gid = inode->n_gid;
1031
/*
1032
* To modify the ownership of a file, must possess VADMIN for that
1033
* file.
1034
*/
1035
if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td)))
1036
return (error);
1037
/*
1038
* To change the owner of a file, or change the group of a file to a
1039
* group of which we are not a member, the caller must have
1040
* privilege.
1041
*/
1042
if (((uid != inode->n_uid && uid != cred->cr_uid) ||
1043
(gid != inode->n_gid && !groupmember(gid, cred))) &&
1044
(error = priv_check_cred(cred, PRIV_VFS_CHOWN)))
1045
return (error);
1046
1047
ogid = inode->n_gid;
1048
ouid = inode->n_uid;
1049
1050
inode->n_gid = gid;
1051
inode->n_uid = uid;
1052
1053
if ((inode->i_mode & (ISUID | ISGID)) &&
1054
(ouid != uid || ogid != gid)) {
1055
1056
if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID))
1057
inode->i_mode &= ~(ISUID | ISGID);
1058
}
1059
P9_DEBUG(VOPS, "%s: vp %p, cred %p, td %p - ret OK\n", __func__, vp, cred, td);
1060
1061
return (0);
1062
}
1063
1064
/*
1065
* Update the in memory inode with all chmod new permissions/mode. Typically a
1066
* setattr is called to update it to server.
1067
*/
1068
static int
1069
p9fs_chmod(struct vnode *vp, uint32_t mode, struct ucred *cred, struct thread *td)
1070
{
1071
struct p9fs_node *np;
1072
struct p9fs_inode *inode;
1073
uint32_t nmode;
1074
int error;
1075
1076
np = P9FS_VTON(vp);
1077
inode = &np->inode;
1078
1079
P9_DEBUG(VOPS, "%s: vp %p, mode %x, cred %p, td %p\n", __func__, vp, mode, cred, td);
1080
/*
1081
* To modify the permissions on a file, must possess VADMIN
1082
* for that file.
1083
*/
1084
if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
1085
return (error);
1086
1087
/*
1088
* Privileged processes may set the sticky bit on non-directories,
1089
* as well as set the setgid bit on a file with a group that the
1090
* process is not a member of. Both of these are allowed in
1091
* jail(8).
1092
*/
1093
if (vp->v_type != VDIR && (mode & S_ISTXT)) {
1094
if (priv_check_cred(cred, PRIV_VFS_STICKYFILE))
1095
return (EFTYPE);
1096
}
1097
if (!groupmember(inode->n_gid, cred) && (mode & ISGID)) {
1098
error = priv_check_cred(cred, PRIV_VFS_SETGID);
1099
if (error != 0)
1100
return (error);
1101
}
1102
1103
/*
1104
* Deny setting setuid if we are not the file owner.
1105
*/
1106
if ((mode & ISUID) && inode->n_uid != cred->cr_uid) {
1107
error = priv_check_cred(cred, PRIV_VFS_ADMIN);
1108
if (error != 0)
1109
return (error);
1110
}
1111
nmode = inode->i_mode;
1112
nmode &= ~ALLPERMS;
1113
nmode |= (mode & ALLPERMS);
1114
inode->i_mode = nmode;
1115
1116
P9_DEBUG(VOPS, "%s: to mode %x %d \n ", __func__, nmode, error);
1117
1118
return (error);
1119
}
1120
1121
/*
1122
* Set the attributes of a file referenced by fid. A valid bitmask is sent
1123
* in request selecting which fields to set
1124
*/
1125
static int
1126
p9fs_setattr_dotl(struct vop_setattr_args *ap)
1127
{
1128
struct vnode *vp;
1129
struct vattr *vap;
1130
struct p9fs_node *node;
1131
struct p9fs_inode *inode;
1132
struct ucred *cred;
1133
struct thread *td;
1134
struct p9_iattr_dotl *p9attr;
1135
struct p9fs_session *vses;
1136
struct p9_fid *vfid;
1137
uint64_t oldfilesize;
1138
int error;
1139
1140
vp = ap->a_vp;
1141
vap = ap->a_vap;
1142
node = P9FS_VTON(vp);
1143
inode = &node->inode;
1144
cred = ap->a_cred;
1145
td = curthread;
1146
vses = node->p9fs_ses;
1147
error = 0;
1148
1149
if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
1150
(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
1151
(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
1152
(vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
1153
P9_DEBUG(ERROR, "%s: unsettable attribute\n", __func__);
1154
return (EINVAL);
1155
}
1156
/* Disallow write attempts on read only filesystem */
1157
if (vp->v_mount->mnt_flag & MNT_RDONLY)
1158
return (EROFS);
1159
1160
/* Setting of flags is not supported */
1161
if (vap->va_flags != VNOVAL)
1162
return (EOPNOTSUPP);
1163
1164
/* Allocate p9attr struct */
1165
p9attr = uma_zalloc(p9fs_setattr_zone, M_WAITOK | M_ZERO);
1166
if (p9attr == NULL)
1167
return (ENOMEM);
1168
1169
/* Check if we need to change the ownership of the file*/
1170
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
1171
P9_DEBUG(VOPS, "%s: vp:%p td:%p uid/gid %x/%x\n", __func__,
1172
vp, td, vap->va_uid, vap->va_gid);
1173
1174
error = p9fs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
1175
p9attr->valid |= P9PROTO_SETATTR_UID | P9PROTO_SETATTR_GID |
1176
P9PROTO_SETATTR_MODE;
1177
if (error)
1178
goto out;
1179
}
1180
1181
/* Check for mode changes */
1182
if (vap->va_mode != (mode_t)VNOVAL) {
1183
P9_DEBUG(VOPS, "%s: vp:%p td:%p mode %x\n", __func__, vp, td,
1184
vap->va_mode);
1185
1186
error = p9fs_chmod(vp, (int)vap->va_mode, cred, td);
1187
p9attr->valid |= P9PROTO_SETATTR_MODE;
1188
if (error)
1189
goto out;
1190
}
1191
1192
/* Update the size of the file and update mtime */
1193
if (vap->va_size != (uint64_t)VNOVAL) {
1194
P9_DEBUG(VOPS, "%s: vp:%p td:%p size:%jx\n", __func__,
1195
vp, td, (uintmax_t)vap->va_size);
1196
switch (vp->v_type) {
1197
case VDIR:
1198
error = EISDIR;
1199
goto out;
1200
case VLNK:
1201
case VREG:
1202
/* Invalidate cached pages of vp */
1203
error = vinvalbuf(vp, 0, 0, 0);
1204
if (error)
1205
goto out;
1206
oldfilesize = inode->i_size;
1207
inode->i_size = vap->va_size;
1208
/* Update the p9fs_inode time */
1209
p9fs_itimes(vp);
1210
p9attr->valid |= P9PROTO_SETATTR_SIZE |
1211
P9PROTO_SETATTR_ATIME |
1212
P9PROTO_SETATTR_MTIME |
1213
P9PROTO_SETATTR_ATIME_SET |
1214
P9PROTO_SETATTR_MTIME_SET ;
1215
break;
1216
default:
1217
goto out;
1218
}
1219
} else if (vap->va_atime.tv_sec != VNOVAL ||
1220
vap->va_mtime.tv_sec != VNOVAL) {
1221
P9_DEBUG(VOPS, "%s: vp:%p td:%p time a/m %jx/%jx/\n",
1222
__func__, vp, td, (uintmax_t)vap->va_atime.tv_sec,
1223
(uintmax_t)vap->va_mtime.tv_sec);
1224
/* Update the p9fs_inode times */
1225
p9fs_itimes(vp);
1226
p9attr->valid |= P9PROTO_SETATTR_ATIME |
1227
P9PROTO_SETATTR_MTIME | P9PROTO_SETATTR_ATIME_SET |
1228
P9PROTO_SETATTR_MTIME_SET;
1229
}
1230
1231
vfid = p9fs_get_fid(vses->clnt, node, cred, VOFID, P9PROTO_OWRITE, &error);
1232
if (vfid == NULL) {
1233
vfid = p9fs_get_fid(vses->clnt, node, cred, VFID, -1, &error);
1234
if (error)
1235
goto out;
1236
}
1237
1238
/* Write the inode structure values into p9attr */
1239
p9fs_inode_to_iattr(inode, p9attr);
1240
error = p9_client_setattr(vfid, p9attr);
1241
if (vap->va_size != (uint64_t)VNOVAL && vp->v_type == VREG) {
1242
if (error)
1243
inode->i_size = oldfilesize;
1244
else
1245
vnode_pager_setsize(vp, inode->i_size);
1246
}
1247
out:
1248
if (p9attr) {
1249
uma_zfree(p9fs_setattr_zone, p9attr);
1250
}
1251
P9_DEBUG(VOPS, "%s: error: %d\n", __func__, error);
1252
return (error);
1253
}
1254
1255
struct open_fid_state {
1256
struct p9_fid *vofid;
1257
int fflags;
1258
int opened;
1259
};
1260
1261
/*
1262
* TODO: change this to take P9PROTO_* mode and avoid routing through
1263
* VOP_OPEN, factoring out implementation of p9fs_open.
1264
*/
1265
static int
1266
p9fs_get_open_fid(struct vnode *vp, int fflags, struct ucred *cr, struct open_fid_state *statep)
1267
{
1268
struct p9fs_node *np;
1269
struct p9fs_session *vses;
1270
struct p9_fid *vofid;
1271
int mode = p9fs_uflags_mode(fflags, TRUE);
1272
int error = 0;
1273
1274
statep->opened = FALSE;
1275
1276
np = P9FS_VTON(vp);
1277
vses = np->p9fs_ses;
1278
vofid = p9fs_get_fid(vses->clnt, np, cr, VOFID, mode, &error);
1279
if (vofid == NULL) {
1280
error = VOP_OPEN(vp, fflags, cr, curthread, NULL);
1281
if (error) {
1282
return (error);
1283
}
1284
vofid = p9fs_get_fid(vses->clnt, np, cr, VOFID, mode, &error);
1285
if (vofid == NULL) {
1286
return (EBADF);
1287
}
1288
statep->fflags = fflags;
1289
statep->opened = TRUE;
1290
}
1291
statep->vofid = vofid;
1292
return (0);
1293
}
1294
1295
static void
1296
p9fs_release_open_fid(struct vnode *vp, struct ucred *cr, struct open_fid_state *statep)
1297
{
1298
if (statep->opened) {
1299
(void) VOP_CLOSE(vp, statep->fflags, cr, curthread);
1300
}
1301
}
1302
1303
/*
1304
* An I/O buffer is used to to do any transfer. The uio is the vfs structure we
1305
* need to copy data into. As long as resid is greater than zero, we call
1306
* client_read to read data from offset(offset into the file) in the open fid
1307
* for the file into the I/O buffer. The data is read into the user data buffer.
1308
*/
1309
static int
1310
p9fs_read(struct vop_read_args *ap)
1311
{
1312
struct vnode *vp;
1313
struct uio *uio;
1314
struct p9fs_node *np;
1315
uint64_t offset;
1316
int64_t ret;
1317
uint64_t resid;
1318
uint32_t count;
1319
int error;
1320
char *io_buffer = NULL;
1321
uint64_t filesize;
1322
struct open_fid_state ostate;
1323
1324
vp = ap->a_vp;
1325
uio = ap->a_uio;
1326
np = P9FS_VTON(vp);
1327
error = 0;
1328
1329
if (VN_ISDEV(vp))
1330
return (EOPNOTSUPP);
1331
if (vp->v_type != VREG)
1332
return (EISDIR);
1333
if (uio->uio_resid == 0)
1334
return (0);
1335
if (uio->uio_offset < 0)
1336
return (EINVAL);
1337
1338
error = p9fs_get_open_fid(vp, FREAD, ap->a_cred, &ostate);
1339
if (error)
1340
return (error);
1341
1342
/* where in the file are we to start reading */
1343
offset = uio->uio_offset;
1344
filesize = np->inode.i_size;
1345
if (uio->uio_offset >= filesize)
1346
goto out;
1347
1348
P9_DEBUG(VOPS, "%s: called %jd at %ju\n",
1349
__func__, (intmax_t)uio->uio_resid, (uintmax_t)uio->uio_offset);
1350
1351
/* Work with a local buffer from the pool for this vop */
1352
1353
io_buffer = uma_zalloc(p9fs_io_buffer_zone, M_WAITOK | M_ZERO);
1354
while ((resid = uio->uio_resid) > 0) {
1355
if (offset >= filesize)
1356
break;
1357
count = MIN(filesize - uio->uio_offset , resid);
1358
if (count == 0)
1359
break;
1360
1361
/* Copy count bytes into the uio */
1362
ret = p9_client_read(ostate.vofid, offset, count, io_buffer);
1363
/*
1364
* This is the only place in the entire p9fs where we check the
1365
* error for < 0 as p9_client_read/write return the number of
1366
* bytes instead of an error code. In this case if ret is < 0,
1367
* it means there is an IO error.
1368
*/
1369
if (ret < 0) {
1370
error = -ret;
1371
goto out;
1372
}
1373
error = uiomove(io_buffer, ret, uio);
1374
if (error != 0)
1375
goto out;
1376
1377
offset += ret;
1378
}
1379
uio->uio_offset = offset;
1380
out:
1381
uma_zfree(p9fs_io_buffer_zone, io_buffer);
1382
p9fs_release_open_fid(vp, ap->a_cred, &ostate);
1383
1384
return (error);
1385
}
1386
1387
/*
1388
* The user buffer contains the data to be written. This data is copied first
1389
* from uio into I/O buffer. This I/O buffer is used to do the client_write to
1390
* the fid of the file starting from the offset given upto count bytes. The
1391
* number of bytes written is returned to the caller.
1392
*/
1393
static int
1394
p9fs_write(struct vop_write_args *ap)
1395
{
1396
struct vnode *vp;
1397
struct uio *uio;
1398
struct p9fs_node *np;
1399
uint64_t off, offset;
1400
int64_t ret;
1401
uint64_t resid, bytes_written;
1402
uint32_t count;
1403
int error, ioflag;
1404
uint64_t file_size;
1405
char *io_buffer = NULL;
1406
struct open_fid_state ostate;
1407
1408
vp = ap->a_vp;
1409
uio = ap->a_uio;
1410
np = P9FS_VTON(vp);
1411
error = 0;
1412
ioflag = ap->a_ioflag;
1413
1414
error = p9fs_get_open_fid(vp, FWRITE, ap->a_cred, &ostate);
1415
if (error)
1416
return (error);
1417
1418
P9_DEBUG(VOPS, "%s: %#zx at %#jx\n",
1419
__func__, uio->uio_resid, (uintmax_t)uio->uio_offset);
1420
1421
if (uio->uio_offset < 0) {
1422
error = EINVAL;
1423
goto out;
1424
}
1425
if (uio->uio_resid == 0)
1426
goto out;
1427
1428
file_size = np->inode.i_size;
1429
1430
switch (vp->v_type) {
1431
case VREG:
1432
if (ioflag & IO_APPEND)
1433
uio->uio_offset = file_size;
1434
break;
1435
case VDIR:
1436
return (EISDIR);
1437
case VLNK:
1438
break;
1439
default:
1440
panic("%s: bad file type vp: %p", __func__, vp);
1441
}
1442
1443
resid = uio->uio_resid;
1444
offset = uio->uio_offset;
1445
bytes_written = 0;
1446
error = 0;
1447
1448
io_buffer = uma_zalloc(p9fs_io_buffer_zone, M_WAITOK | M_ZERO);
1449
while ((resid = uio->uio_resid) > 0) {
1450
off = 0;
1451
count = MIN(resid, P9FS_IOUNIT);
1452
error = uiomove(io_buffer, count, uio);
1453
1454
if (error != 0) {
1455
P9_DEBUG(ERROR, "%s: uiomove failed: %d\n", __func__, error);
1456
goto out;
1457
}
1458
1459
/* While count still exists, keep writing.*/
1460
while (count > 0) {
1461
/* Copy count bytes from the uio */
1462
ret = p9_client_write(ostate.vofid, offset, count,
1463
io_buffer + off);
1464
if (ret < 0) {
1465
if (bytes_written == 0) {
1466
error = -ret;
1467
goto out;
1468
} else {
1469
break;
1470
}
1471
}
1472
P9_DEBUG(VOPS, "%s: write %#zx at %#jx\n",
1473
__func__, uio->uio_resid, (uintmax_t)uio->uio_offset);
1474
1475
off += ret;
1476
offset += ret;
1477
bytes_written += ret;
1478
count -= ret;
1479
}
1480
}
1481
/* Update the fields in the node to reflect the change*/
1482
if (file_size < uio->uio_offset + uio->uio_resid) {
1483
np->inode.i_size = uio->uio_offset + uio->uio_resid;
1484
vnode_pager_setsize(vp, uio->uio_offset + uio->uio_resid);
1485
}
1486
out:
1487
if (io_buffer)
1488
uma_zfree(p9fs_io_buffer_zone, io_buffer);
1489
p9fs_release_open_fid(vp, ap->a_cred, &ostate);
1490
1491
return (error);
1492
}
1493
1494
/*
1495
* Common handler of all removal-related VOPs (e.g. rmdir, rm). Perform the
1496
* client_remove op to send messages to remove the node's fid on the server.
1497
* After that, does a node metadata cleanup on client side.
1498
*/
1499
static int
1500
remove_common(struct p9fs_node *dnp, struct p9fs_node *np, const char *name,
1501
struct ucred *cred)
1502
{
1503
int error;
1504
struct p9fs_session *vses;
1505
struct vnode *vp;
1506
struct p9_fid *vfid;
1507
1508
error = 0;
1509
vses = np->p9fs_ses;
1510
vp = P9FS_NTOV(np);
1511
1512
vfid = p9fs_get_fid(vses->clnt, dnp, cred, VFID, -1, &error);
1513
if (error != 0)
1514
return (error);
1515
1516
error = p9_client_unlink(vfid, name,
1517
np->v_node->v_type == VDIR ? P9PROTO_UNLINKAT_REMOVEDIR : 0);
1518
if (error != 0)
1519
return (error);
1520
1521
/* Remove all non-open fids associated with the vp */
1522
if (np->inode.i_links_count == 1)
1523
p9fs_fid_remove_all(np, TRUE);
1524
1525
/* Invalidate all entries of vnode from name cache and hash list. */
1526
cache_purge(vp);
1527
vfs_hash_remove(vp);
1528
1529
np->flags |= P9FS_NODE_DELETED;
1530
1531
return (error);
1532
}
1533
1534
/* Remove vop for all files. Call common code for remove and adjust links */
1535
static int
1536
p9fs_remove(struct vop_remove_args *ap)
1537
{
1538
struct vnode *vp;
1539
struct p9fs_node *np;
1540
struct vnode *dvp;
1541
struct p9fs_node *dnp;
1542
struct p9fs_inode *dinode;
1543
struct componentname *cnp;
1544
int error;
1545
1546
cnp = ap->a_cnp;
1547
vp = ap->a_vp;
1548
np = P9FS_VTON(vp);
1549
dvp = ap->a_dvp;
1550
dnp = P9FS_VTON(dvp);
1551
dinode = &dnp->inode;
1552
1553
P9_DEBUG(VOPS, "%s: vp %p node %p \n", __func__, vp, np);
1554
1555
if (vp->v_type == VDIR)
1556
return (EISDIR);
1557
1558
error = remove_common(dnp, np, cnp->cn_nameptr, cnp->cn_cred);
1559
if (error == 0)
1560
P9FS_DECR_LINKS(dinode);
1561
1562
return (error);
1563
}
1564
1565
/* Remove vop for all directories. Call common code for remove and adjust links */
1566
static int
1567
p9fs_rmdir(struct vop_rmdir_args *ap)
1568
{
1569
struct vnode *vp;
1570
struct p9fs_node *np;
1571
struct vnode *dvp;
1572
struct p9fs_node *dnp;
1573
struct p9fs_inode *dinode;
1574
struct componentname *cnp;
1575
int error;
1576
1577
cnp = ap->a_cnp;
1578
vp = ap->a_vp;
1579
np = P9FS_VTON(vp);
1580
dvp = ap->a_dvp;
1581
dnp = P9FS_VTON(dvp);
1582
dinode = &dnp->inode;
1583
1584
P9_DEBUG(VOPS, "%s: vp %p node %p \n", __func__, vp, np);
1585
1586
error = remove_common(dnp, np, cnp->cn_nameptr, cnp->cn_cred);
1587
if (error == 0)
1588
P9FS_DECR_LINKS(dinode);
1589
1590
return (error);
1591
}
1592
1593
/*
1594
* Create symlinks. Make the permissions and call create_common code
1595
* for Soft links.
1596
*/
1597
static int
1598
p9fs_symlink(struct vop_symlink_args *ap)
1599
{
1600
struct vnode *dvp;
1601
struct vnode **vpp;
1602
struct vattr *vap;
1603
struct componentname *cnp;
1604
char *symtgt;
1605
struct p9fs_node *dnp;
1606
struct p9fs_session *vses;
1607
struct mount *mp;
1608
struct p9_fid *dvfid, *newfid;
1609
int error;
1610
char tmpchr;
1611
gid_t gid;
1612
1613
dvp = ap->a_dvp;
1614
vpp = ap->a_vpp;
1615
vap = ap->a_vap;
1616
cnp = ap->a_cnp;
1617
symtgt = (char*)(uintptr_t) ap->a_target;
1618
dnp = P9FS_VTON(dvp);
1619
vses = dnp->p9fs_ses;
1620
mp = vses->p9fs_mount;
1621
newfid = NULL;
1622
error = 0;
1623
gid = vap->va_gid;
1624
1625
P9_DEBUG(VOPS, "%s: dvp %p\n", __func__, dvp);
1626
1627
/*
1628
* Save the character present at namelen in nameptr string and
1629
* null terminate the character to get the search name for p9_dir_walk
1630
*/
1631
tmpchr = cnp->cn_nameptr[cnp->cn_namelen];
1632
cnp->cn_nameptr[cnp->cn_namelen] = '\0';
1633
1634
dvfid = p9fs_get_fid(vses->clnt, dnp, cnp->cn_cred, VFID, -1, &error);
1635
if (error != 0)
1636
goto out;
1637
1638
error = p9_create_symlink(dvfid, cnp->cn_nameptr, symtgt, gid);
1639
if (error != 0)
1640
goto out;
1641
1642
/*create vnode for symtgt */
1643
newfid = p9_client_walk(dvfid, 1, &cnp->cn_nameptr, 1, &error);
1644
if (newfid != NULL) {
1645
error = p9fs_vget_common(mp, NULL, cnp->cn_lkflags,
1646
dnp, newfid, vpp, cnp->cn_nameptr);
1647
if (error != 0)
1648
goto out;
1649
} else
1650
goto out;
1651
1652
if ((cnp->cn_flags & MAKEENTRY) != 0) {
1653
cache_enter(P9FS_NTOV(dnp), *vpp, cnp);
1654
}
1655
P9_DEBUG(VOPS, "%s: created file under vp %p node %p fid %ju\n",
1656
__func__, *vpp, dnp, (uintmax_t)dvfid->fid);
1657
1658
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
1659
return (error);
1660
1661
out:
1662
if (newfid != NULL)
1663
p9_client_clunk(newfid);
1664
cnp->cn_nameptr[cnp->cn_namelen] = tmpchr;
1665
return (error);
1666
}
1667
1668
/* Create hard link */
1669
static int
1670
p9fs_link(struct vop_link_args *ap)
1671
{
1672
struct vnode *vp;
1673
struct vnode *tdvp;
1674
struct componentname *cnp;
1675
struct p9fs_node *dnp;
1676
struct p9fs_node *np;
1677
struct p9fs_inode *inode;
1678
struct p9fs_session *vses;
1679
struct p9_fid *dvfid, *oldvfid;
1680
int error;
1681
1682
vp = ap->a_vp;
1683
tdvp = ap->a_tdvp;
1684
cnp = ap->a_cnp;
1685
dnp = P9FS_VTON(tdvp);
1686
np = P9FS_VTON(vp);
1687
inode = &np->inode;
1688
vses = np->p9fs_ses;
1689
error = 0;
1690
1691
P9_DEBUG(VOPS, "%s: tdvp %p vp %p\n", __func__, tdvp, vp);
1692
1693
dvfid = p9fs_get_fid(vses->clnt, dnp, cnp->cn_cred, VFID, -1, &error);
1694
if (error != 0)
1695
return (error);
1696
oldvfid = p9fs_get_fid(vses->clnt, np, cnp->cn_cred, VFID, -1, &error);
1697
if (error != 0)
1698
return (error);
1699
1700
error = p9_create_hardlink(dvfid, oldvfid, cnp->cn_nameptr);
1701
if (error != 0)
1702
return (error);
1703
/* Increment ref count on the inode */
1704
P9FS_INCR_LINKS(inode);
1705
1706
return (0);
1707
}
1708
1709
/* Read contents of the symbolic link */
1710
static int
1711
p9fs_readlink(struct vop_readlink_args *ap)
1712
{
1713
struct vnode *vp;
1714
struct uio *uio;
1715
struct p9fs_node *dnp;
1716
struct p9fs_session *vses;
1717
struct p9_fid *dvfid;
1718
int error, len;
1719
char *target;
1720
1721
vp = ap->a_vp;
1722
uio = ap->a_uio;
1723
dnp = P9FS_VTON(vp);
1724
vses = dnp->p9fs_ses;
1725
error = 0;
1726
1727
P9_DEBUG(VOPS, "%s: vp %p\n", __func__, vp);
1728
1729
dvfid = p9fs_get_fid(vses->clnt, dnp, ap->a_cred, VFID, -1, &error);
1730
if (error != 0)
1731
return (error);
1732
1733
error = p9_readlink(dvfid, &target);
1734
if (error != 0)
1735
return (error);
1736
1737
len = strlen(target);
1738
error = uiomove(target, len, uio);
1739
1740
return (0);
1741
}
1742
1743
/*
1744
* Iterate through a directory. An entire 8k data is read into the I/O buffer.
1745
* This buffer is parsed to make dir entries and fed to the user buffer to
1746
* complete it to the VFS.
1747
*/
1748
static int
1749
p9fs_readdir(struct vop_readdir_args *ap)
1750
{
1751
struct uio *uio;
1752
struct vnode *vp;
1753
struct dirent cde;
1754
int64_t offset;
1755
uint64_t diroffset;
1756
struct p9fs_node *np;
1757
int error;
1758
int32_t count;
1759
struct p9_client *clnt;
1760
struct p9_dirent dent;
1761
char *io_buffer;
1762
struct p9_fid *vofid;
1763
1764
uio = ap->a_uio;
1765
vp = ap->a_vp;
1766
np = P9FS_VTON(ap->a_vp);
1767
offset = 0;
1768
diroffset = 0;
1769
error = 0;
1770
count = 0;
1771
clnt = np->p9fs_ses->clnt;
1772
1773
P9_DEBUG(VOPS, "%s: vp %p, offset %jd, resid %zd\n", __func__, vp, (intmax_t) uio->uio_offset, uio->uio_resid);
1774
1775
if (ap->a_uio->uio_iov->iov_len <= 0)
1776
return (EINVAL);
1777
1778
if (vp->v_type != VDIR)
1779
return (ENOTDIR);
1780
1781
vofid = p9fs_get_fid(clnt, np, ap->a_cred, VOFID, P9PROTO_OREAD, &error);
1782
if (vofid == NULL) {
1783
P9_DEBUG(ERROR, "%s: NULL FID\n", __func__);
1784
return (EBADF);
1785
}
1786
1787
if (ap->a_eofflag != NULL)
1788
*ap->a_eofflag = 0;
1789
1790
io_buffer = uma_zalloc(p9fs_io_buffer_zone, M_WAITOK);
1791
1792
/* We haven't reached the end yet. read more. */
1793
diroffset = uio->uio_offset;
1794
while (uio->uio_resid >= sizeof(struct dirent)) {
1795
/*
1796
* We need to read more data as what is indicated by filesize because
1797
* filesize is based on data stored in struct dirent structure but
1798
* we read data in struct p9_dirent format which has different size.
1799
* Hence we read max data(P9FS_IOUNIT) everytime from host, convert
1800
* it into struct dirent structure and send it back.
1801
*/
1802
count = P9FS_IOUNIT;
1803
bzero(io_buffer, P9FS_MTU);
1804
count = p9_client_readdir(vofid, (char *)io_buffer,
1805
diroffset, count);
1806
1807
if (count == 0) {
1808
if (ap->a_eofflag != NULL)
1809
*ap->a_eofflag = 1;
1810
break;
1811
}
1812
1813
if (count < 0) {
1814
error = EIO;
1815
goto out;
1816
}
1817
1818
offset = 0;
1819
while (offset + QEMU_DIRENTRY_SZ <= count) {
1820
1821
/*
1822
* Read and make sense out of the buffer in one dirent
1823
* This is part of 9p protocol read. This reads one p9_dirent,
1824
* appends it to dirent(FREEBSD specifc) and continues to parse the buffer.
1825
*/
1826
bzero(&dent, sizeof(dent));
1827
offset = p9_dirent_read(clnt, io_buffer, offset, count,
1828
&dent);
1829
if (offset < 0 || offset > count) {
1830
error = EIO;
1831
goto out;
1832
}
1833
1834
bzero(&cde, sizeof(cde));
1835
strncpy(cde.d_name, dent.d_name, dent.len);
1836
cde.d_fileno = dent.qid.path;
1837
cde.d_type = dent.d_type;
1838
cde.d_namlen = dent.len;
1839
cde.d_reclen = GENERIC_DIRSIZ(&cde);
1840
1841
/*
1842
* If there isn't enough space in the uio to return a
1843
* whole dirent, break off read
1844
*/
1845
if (uio->uio_resid < GENERIC_DIRSIZ(&cde))
1846
break;
1847
1848
/* Transfer */
1849
error = uiomove(&cde, GENERIC_DIRSIZ(&cde), uio);
1850
if (error != 0) {
1851
error = EIO;
1852
goto out;
1853
}
1854
diroffset = dent.d_off;
1855
}
1856
}
1857
/* Pass on last transferred offset */
1858
uio->uio_offset = diroffset;
1859
1860
out:
1861
uma_zfree(p9fs_io_buffer_zone, io_buffer);
1862
1863
return (error);
1864
}
1865
1866
static void
1867
p9fs_doio(struct vnode *vp, struct buf *bp, struct p9_fid *vofid, struct ucred *cr)
1868
{
1869
struct uio *uiov;
1870
struct iovec io;
1871
int error;
1872
uint64_t off, offset;
1873
uint64_t filesize;
1874
uint64_t resid;
1875
uint32_t count;
1876
int64_t ret;
1877
struct p9fs_node *np;
1878
char *io_buffer;
1879
1880
error = 0;
1881
np = P9FS_VTON(vp);
1882
1883
filesize = np->inode.i_size;
1884
uiov = malloc(sizeof(struct uio), M_P9UIOV, M_WAITOK);
1885
uiov->uio_iov = &io;
1886
uiov->uio_iovcnt = 1;
1887
uiov->uio_segflg = UIO_SYSSPACE;
1888
io_buffer = uma_zalloc(p9fs_io_buffer_zone, M_WAITOK | M_ZERO);
1889
1890
if (bp->b_iocmd == BIO_READ) {
1891
io.iov_len = uiov->uio_resid = bp->b_bcount;
1892
io.iov_base = bp->b_data;
1893
uiov->uio_rw = UIO_READ;
1894
1895
switch (vp->v_type) {
1896
1897
case VREG:
1898
{
1899
uiov->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
1900
1901
if (uiov->uio_resid) {
1902
int left = uiov->uio_resid;
1903
int nread = bp->b_bcount - left;
1904
1905
if (left > 0)
1906
bzero((char *)bp->b_data + nread, left);
1907
}
1908
/* where in the file are we to start reading */
1909
offset = uiov->uio_offset;
1910
if (uiov->uio_offset >= filesize)
1911
goto out;
1912
1913
while ((resid = uiov->uio_resid) > 0) {
1914
if (offset >= filesize)
1915
break;
1916
count = min(filesize - uiov->uio_offset, resid);
1917
if (count == 0)
1918
break;
1919
1920
P9_DEBUG(VOPS, "%s: read called %#zx at %#jx\n",
1921
__func__, uiov->uio_resid, (uintmax_t)uiov->uio_offset);
1922
1923
/* Copy count bytes into the uio */
1924
ret = p9_client_read(vofid, offset, count, io_buffer);
1925
error = uiomove(io_buffer, ret, uiov);
1926
1927
if (error != 0)
1928
goto out;
1929
offset += ret;
1930
}
1931
break;
1932
}
1933
default:
1934
printf("vfs: type %x unexpected\n", vp->v_type);
1935
break;
1936
}
1937
} else {
1938
if (bp->b_dirtyend > bp->b_dirtyoff) {
1939
io.iov_len = uiov->uio_resid = bp->b_dirtyend - bp->b_dirtyoff;
1940
uiov->uio_offset = ((off_t)bp->b_blkno) * PAGE_SIZE + bp->b_dirtyoff;
1941
io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
1942
uiov->uio_rw = UIO_WRITE;
1943
1944
if (uiov->uio_offset < 0) {
1945
error = EINVAL;
1946
goto out;
1947
}
1948
1949
if (uiov->uio_resid == 0)
1950
goto out;
1951
1952
resid = uiov->uio_resid;
1953
offset = uiov->uio_offset;
1954
error = 0;
1955
1956
while ((resid = uiov->uio_resid) > 0) {
1957
off = 0;
1958
count = MIN(resid, P9FS_IOUNIT);
1959
error = uiomove(io_buffer, count, uiov);
1960
if (error != 0) {
1961
goto out;
1962
}
1963
1964
while (count > 0) {
1965
/* Copy count bytes from the uio */
1966
ret = p9_client_write(vofid, offset, count,
1967
io_buffer + off);
1968
if (ret < 0)
1969
goto out;
1970
1971
P9_DEBUG(VOPS, "%s: write called %#zx at %#jx\n",
1972
__func__, uiov->uio_resid, (uintmax_t)uiov->uio_offset);
1973
off += ret;
1974
offset += ret;
1975
count -= ret;
1976
}
1977
}
1978
1979
/* Update the fields in the node to reflect the change */
1980
if (filesize < uiov->uio_offset + uiov->uio_resid) {
1981
np->inode.i_size = uiov->uio_offset + uiov->uio_resid;
1982
vnode_pager_setsize(vp, uiov->uio_offset + uiov->uio_resid);
1983
/* update the modified timers. */
1984
p9fs_itimes(vp);
1985
}
1986
} else {
1987
bp->b_resid = 0;
1988
goto out1;
1989
}
1990
}
1991
out:
1992
/* Set the error */
1993
if (error != 0) {
1994
bp->b_error = error;
1995
bp->b_ioflags |= BIO_ERROR;
1996
}
1997
bp->b_resid = uiov->uio_resid;
1998
out1:
1999
bufdone(bp);
2000
uma_zfree(p9fs_io_buffer_zone, io_buffer);
2001
free(uiov, M_P9UIOV);
2002
}
2003
2004
/*
2005
* The I/O buffer is mapped to a uio and a client_write/client_read is performed
2006
* the same way as p9fs_read and p9fs_write.
2007
*/
2008
static int
2009
p9fs_strategy(struct vop_strategy_args *ap)
2010
{
2011
struct vnode *vp;
2012
struct buf *bp;
2013
struct ucred *cr;
2014
int error;
2015
struct open_fid_state ostate;
2016
2017
vp = ap->a_vp;
2018
bp = ap->a_bp;
2019
error = 0;
2020
2021
P9_DEBUG(VOPS, "%s: vp %p, iocmd %d\n ", __func__, vp, bp->b_iocmd);
2022
2023
if (bp->b_iocmd == BIO_READ)
2024
cr = bp->b_rcred;
2025
else
2026
cr = bp->b_wcred;
2027
2028
error = p9fs_get_open_fid(vp, bp->b_iocmd == BIO_READ ? FREAD : FWRITE, cr, &ostate);
2029
if (error) {
2030
P9_DEBUG(ERROR, "%s: p9fs_get_open_fid failed: %d\n", __func__, error);
2031
bp->b_error = error;
2032
bp->b_ioflags |= BIO_ERROR;
2033
bufdone(bp);
2034
return (0);
2035
}
2036
2037
p9fs_doio(vp, bp, ostate.vofid, cr);
2038
p9fs_release_open_fid(vp, cr, &ostate);
2039
2040
return (0);
2041
}
2042
2043
/* Rename a file */
2044
static int
2045
p9fs_rename(struct vop_rename_args *ap)
2046
{
2047
struct vnode *tvp;
2048
struct vnode *tdvp;
2049
struct vnode *fvp;
2050
struct vnode *fdvp;
2051
struct componentname *tcnp;
2052
struct componentname *fcnp;
2053
struct p9fs_node *tdnode;
2054
struct p9fs_node *fdnode;
2055
struct p9fs_inode *fdinode;
2056
struct p9fs_node *fnode;
2057
struct p9fs_inode *finode;
2058
struct p9fs_session *vses;
2059
struct p9fs_node *tnode;
2060
struct p9fs_inode *tinode;
2061
struct p9_fid *olddirvfid, *newdirvfid ;
2062
int error;
2063
2064
tvp = ap->a_tvp;
2065
tdvp = ap->a_tdvp;
2066
fvp = ap->a_fvp;
2067
fdvp = ap->a_fdvp;
2068
tcnp = ap->a_tcnp;
2069
fcnp = ap->a_fcnp;
2070
tdnode = P9FS_VTON(tdvp);
2071
fdnode = P9FS_VTON(fdvp);
2072
fdinode = &fdnode->inode;
2073
fnode = P9FS_VTON(fvp);
2074
finode = &fnode->inode;
2075
vses = fnode->p9fs_ses;
2076
error = 0;
2077
2078
P9_DEBUG(VOPS, "%s: tvp %p, tdvp %p, fvp %p, fdvp %p\n ", __func__, tvp, tdvp, fvp, fdvp);
2079
2080
/* Check for cross mount operation */
2081
if (fvp->v_mount != tdvp->v_mount ||
2082
(tvp && (fvp->v_mount != tvp->v_mount))) {
2083
error = EXDEV;
2084
goto out;
2085
}
2086
2087
/* warning if you are renaming to the same name */
2088
if (fvp == tvp)
2089
error = 0;
2090
2091
olddirvfid = p9fs_get_fid(vses->clnt, fdnode, fcnp->cn_cred, VFID, -1, &error);
2092
if (error != 0)
2093
goto out;
2094
newdirvfid = p9fs_get_fid(vses->clnt, tdnode, tcnp->cn_cred, VFID, -1, &error);
2095
if (error != 0)
2096
goto out;
2097
2098
error = p9_client_renameat(olddirvfid, fcnp->cn_nameptr, newdirvfid, tcnp->cn_nameptr);
2099
if (error != 0)
2100
goto out;
2101
2102
/*
2103
* decrement the link count on the "from" file whose name is going
2104
* to be changed if its a directory
2105
*/
2106
if (fvp->v_type == VDIR) {
2107
if (tvp && tvp->v_type == VDIR)
2108
cache_purge(tdvp);
2109
P9FS_DECR_LINKS(fdinode);
2110
cache_purge(fdvp);
2111
}
2112
2113
/* Taking exclusive lock on the from node before decrementing the link count */
2114
if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
2115
goto out;
2116
P9FS_DECR_LINKS(finode);
2117
VOP_UNLOCK(fvp);
2118
2119
if (tvp) {
2120
tnode = P9FS_VTON(tvp);
2121
tinode = &tnode->inode;
2122
P9FS_DECR_LINKS(tinode);
2123
}
2124
2125
out:
2126
if (tdvp == tvp)
2127
vrele(tdvp);
2128
else
2129
vput(tdvp);
2130
if (tvp)
2131
vput(tvp);
2132
vrele(fdvp);
2133
vrele(fvp);
2134
return (error);
2135
}
2136
2137
/*
2138
* Put VM pages, synchronously.
2139
* XXX: like smbfs, cannot use vop_stdputpages due to mapping requirement
2140
*/
2141
static int
2142
p9fs_putpages(struct vop_putpages_args *ap)
2143
{
2144
struct uio uio;
2145
struct iovec iov;
2146
int i, error, npages, count;
2147
off_t offset;
2148
int *rtvals;
2149
struct vnode *vp;
2150
struct thread *td;
2151
struct ucred *cred;
2152
struct p9fs_node *np;
2153
vm_page_t *pages;
2154
vm_offset_t kva;
2155
struct buf *bp;
2156
2157
vp = ap->a_vp;
2158
np = P9FS_VTON(vp);
2159
td = curthread;
2160
cred = curthread->td_ucred;
2161
pages = ap->a_m;
2162
count = ap->a_count;
2163
rtvals = ap->a_rtvals;
2164
npages = btoc(count);
2165
offset = IDX_TO_OFF(pages[0]->pindex);
2166
2167
/*
2168
* When putting pages, do not extend file past EOF.
2169
*/
2170
if (offset + count > np->inode.i_size) {
2171
count = np->inode.i_size - offset;
2172
if (count < 0)
2173
count = 0;
2174
}
2175
2176
for (i = 0; i < npages; i++)
2177
rtvals[i] = VM_PAGER_ERROR;
2178
2179
bp = uma_zalloc(p9fs_pbuf_zone, M_WAITOK);
2180
kva = (vm_offset_t) bp->b_data;
2181
pmap_qenter(kva, pages, npages);
2182
2183
VM_CNT_INC(v_vnodeout);
2184
VM_CNT_ADD(v_vnodepgsout, count);
2185
2186
iov.iov_base = (caddr_t) kva;
2187
iov.iov_len = count;
2188
uio.uio_iov = &iov;
2189
uio.uio_iovcnt = 1;
2190
uio.uio_offset = offset;
2191
uio.uio_resid = count;
2192
uio.uio_segflg = UIO_SYSSPACE;
2193
uio.uio_rw = UIO_WRITE;
2194
uio.uio_td = td;
2195
2196
P9_DEBUG(VOPS, "of=%jd,resid=%zd\n", (intmax_t)uio.uio_offset, uio.uio_resid);
2197
2198
error = VOP_WRITE(vp, &uio, vnode_pager_putpages_ioflags(ap->a_sync),
2199
cred);
2200
2201
pmap_qremove(kva, npages);
2202
uma_zfree(p9fs_pbuf_zone, bp);
2203
2204
if (error == 0)
2205
vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid,
2206
np->inode.i_size - offset, npages * PAGE_SIZE);
2207
2208
return (rtvals[0]);
2209
}
2210
2211
struct vop_vector p9fs_vnops = {
2212
.vop_default = &default_vnodeops,
2213
.vop_lookup = p9fs_lookup,
2214
.vop_open = p9fs_open,
2215
.vop_close = p9fs_close,
2216
.vop_access = p9fs_access,
2217
.vop_getattr = p9fs_getattr_dotl,
2218
.vop_setattr = p9fs_setattr_dotl,
2219
.vop_reclaim = p9fs_reclaim,
2220
.vop_inactive = p9fs_inactive,
2221
.vop_readdir = p9fs_readdir,
2222
.vop_create = p9fs_create,
2223
.vop_mknod = p9fs_mknod,
2224
.vop_read = p9fs_read,
2225
.vop_write = p9fs_write,
2226
.vop_remove = p9fs_remove,
2227
.vop_mkdir = p9fs_mkdir,
2228
.vop_rmdir = p9fs_rmdir,
2229
.vop_strategy = p9fs_strategy,
2230
.vop_symlink = p9fs_symlink,
2231
.vop_rename = p9fs_rename,
2232
.vop_link = p9fs_link,
2233
.vop_readlink = p9fs_readlink,
2234
.vop_putpages = p9fs_putpages,
2235
};
2236
VFS_VOP_VECTOR_REGISTER(p9fs_vnops);
2237
2238