1 /* $NetBSD: node.c,v 1.20 2008/08/22 17:44:14 pooka Exp $ */
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: node.c,v 1.20 2008/08/22 17:44:14 pooka Exp $");
39 #include "ninepuffs.h"
40 #include "nineproto.h"
43 nodecmp(struct puffs_usermount
*pu
, struct puffs_node
*pn
, void *arg
)
45 struct vattr
*vap
= &pn
->pn_va
;
46 struct qid9p
*qid
= arg
;
48 if (vap
->va_fileid
== qid
->qidpath
&& vap
->va_gen
== qid
->qidvers
)
55 do_getattr(struct puffs_usermount
*pu
, struct puffs_node
*pn
, struct vattr
*vap
)
58 struct p9pnode
*p9n
= pn
->pn_data
;
60 p9pbuf_put_1(pb
, P9PROTO_T_STAT
);
61 p9pbuf_put_2(pb
, tag
);
62 p9pbuf_put_4(pb
, p9n
->fid_base
);
65 rv
= proto_expect_stat(pb
, vap
);
72 puffs9p_node_getattr(struct puffs_usermount
*pu
, void *opc
, struct vattr
*vap
,
73 const struct puffs_cred
*pcr
)
75 struct puffs_node
*pn
= opc
;
78 rv
= do_getattr(pu
, pn
, &pn
->pn_va
);
80 memcpy(vap
, &pn
->pn_va
, sizeof(struct vattr
));
85 puffs9p_node_lookup(struct puffs_usermount
*pu
, void *opc
, struct puffs_newinfo
*pni
,
86 const struct puffs_cn
*pcn
)
90 struct puffs_node
*pn
, *pn_dir
= opc
;
91 struct p9pnode
*p9n_dir
= pn_dir
->pn_data
;
92 p9ptag_t tfid
= NEXTFID(p9p
);
96 p9pbuf_put_1(pb
, P9PROTO_T_WALK
);
97 p9pbuf_put_2(pb
, tag
);
98 p9pbuf_put_4(pb
, p9n_dir
->fid_base
);
99 p9pbuf_put_4(pb
, tfid
);
101 p9pbuf_put_str(pb
, pcn
->pcn_name
);
104 rv
= proto_expect_walk_nqids(pb
, &nqid
);
113 if ((rv
= proto_getqid(pb
, &newqid
)))
116 /* we get the parent vers in walk(?) compensate */
117 p9pbuf_recycleout(pb
);
119 p9pbuf_put_1(pb
, P9PROTO_T_STAT
);
120 p9pbuf_put_2(pb
, tag
);
121 p9pbuf_put_4(pb
, tfid
);
123 if ((rv
= proto_expect_stat(pb
, &va
)) != 0) {
124 proto_cc_clunkfid(pu
, tfid
, 0);
128 if (newqid
.qidpath
!= va
.va_fileid
) {
129 proto_cc_clunkfid(pu
, tfid
, 0);
133 newqid
.qidvers
= va
.va_gen
;
135 pn
= puffs_pn_nodewalk(pu
, nodecmp
, &newqid
);
137 pn
= newp9pnode_qid(pu
, &newqid
, tfid
);
139 proto_cc_clunkfid(pu
, tfid
, 0);
141 memcpy(&pn
->pn_va
, &va
, sizeof(va
));
143 puffs_newinfo_setcookie(pni
, pn
);
144 puffs_newinfo_setvtype(pni
, pn
->pn_va
.va_type
);
145 puffs_newinfo_setsize(pni
, pn
->pn_va
.va_size
);
146 puffs_newinfo_setrdev(pni
, pn
->pn_va
.va_rdev
);
153 * Problem is that 9P doesn't allow seeking into a directory. So we
154 * maintain a list of active fids for any given directory. They
155 * start living at the first read and exist either until the directory
156 * is closed or until they reach the end.
159 puffs9p_node_readdir(struct puffs_usermount
*pu
, void *opc
, struct dirent
*dent
,
160 off_t
*readoff
, size_t *reslen
, const struct puffs_cred
*pcr
,
161 int *eofflag
, off_t
*cookies
, size_t *ncookies
)
164 struct puffs_node
*pn
= opc
;
165 struct p9pnode
*p9n
= pn
->pn_data
;
172 rv
= getdfwithoffset(pu
, p9n
, *readoff
, &dfp
);
177 p9pbuf_put_1(pb
, P9PROTO_T_READ
);
178 p9pbuf_put_2(pb
, tag
);
179 p9pbuf_put_4(pb
, dfp
->fid
);
180 p9pbuf_put_8(pb
, *readoff
);
181 p9pbuf_put_4(pb
, *reslen
); /* XXX */
184 p9pbuf_get_4(pb
, &count
);
187 * if count is 0, assume we at end-of-dir. dfp is no longer
197 if ((rv
= proto_getstat(pb
, &va
, &name
, &statsize
))) {
199 * If there was an error, it's unlikely we'll be
200 * coming back, so just nuke the dfp. If we do
201 * come back for some strange reason, we'll just
208 puffs_nextdent(&dent
, name
, va
.va_fileid
,
209 puffs_vtype2dt(va
.va_type
), reslen
);
212 *readoff
+= statsize
;
213 dfp
->seekoff
+= statsize
;
224 puffs9p_node_setattr(struct puffs_usermount
*pu
, void *opc
,
225 const struct vattr
*va
, const struct puffs_cred
*pcr
)
228 struct puffs_node
*pn
= opc
;
229 struct p9pnode
*p9n
= pn
->pn_data
;
231 p9pbuf_put_1(pb
, P9PROTO_T_WSTAT
);
232 p9pbuf_put_2(pb
, tag
);
233 p9pbuf_put_4(pb
, p9n
->fid_base
);
234 proto_make_stat(pb
, va
, NULL
, pn
->pn_va
.va_type
);
237 if (p9pbuf_get_type(pb
) != P9PROTO_R_WSTAT
)
245 * Ok, time to get clever. There are two possible cases: we are
246 * opening a file or we are opening a directory.
248 * If it's a directory, don't bother opening it here, but rather
249 * wait until readdir, since it's probable we need to be able to
250 * open a directory there in any case.
252 * If it's a regular file, open it here with whatever credentials
253 * we happen to have. Let the upper layers of the kernel worry
254 * about permission control.
257 puffs9p_node_open(struct puffs_usermount
*pu
, void *opc
, int mode
,
258 const struct puffs_cred
*pcr
)
260 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
261 struct puffs9p
*p9p
= puffs_getspecific(pu
);
262 struct puffs_node
*pn
= opc
;
263 struct p9pnode
*p9n
= pn
->pn_data
;
267 puffs_setback(pcc
, PUFFS_SETBACK_INACT_N1
);
268 if (pn
->pn_va
.va_type
!= VDIR
) {
269 if (mode
& FREAD
&& p9n
->fid_read
== P9P_INVALFID
) {
271 error
= proto_cc_open(pu
, p9n
->fid_base
, nfid
,
275 p9n
->fid_read
= nfid
;
277 if (mode
& FWRITE
&& p9n
->fid_write
== P9P_INVALFID
) {
279 error
= proto_cc_open(pu
, p9n
->fid_base
, nfid
,
280 P9PROTO_OMODE_WRITE
);
283 p9n
->fid_write
= nfid
;
291 puffs9p_node_inactive(struct puffs_usermount
*pu
, void *opc
)
293 struct puffs_node
*pn
= opc
;
294 struct p9pnode
*p9n
= pn
->pn_data
;
296 if (pn
->pn_va
.va_type
== VDIR
) {
299 if (p9n
->fid_read
!= P9P_INVALFID
) {
300 proto_cc_clunkfid(pu
, p9n
->fid_read
, 0);
301 p9n
->fid_read
= P9P_INVALFID
;
303 if (p9n
->fid_write
!= P9P_INVALFID
) {
304 proto_cc_clunkfid(pu
, p9n
->fid_write
, 0);
305 p9n
->fid_write
= P9P_INVALFID
;
313 puffs9p_node_read(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
314 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
318 struct puffs_node
*pn
= opc
;
319 struct p9pnode
*p9n
= pn
->pn_data
;
324 while (*resid
> 0 && (uint64_t)(offset
+nread
) < pn
->pn_va
.va_size
) {
325 p9pbuf_put_1(pb
, P9PROTO_T_READ
);
326 p9pbuf_put_2(pb
, tag
);
327 p9pbuf_put_4(pb
, p9n
->fid_read
);
328 p9pbuf_put_8(pb
, offset
+nread
);
329 p9pbuf_put_4(pb
, MIN((uint32_t)*resid
,p9p
->maxreq
-24));
332 if (p9pbuf_get_type(pb
) != P9PROTO_R_READ
) {
337 p9pbuf_get_4(pb
, &count
);
338 if ((rv
= p9pbuf_read_data(pb
, buf
+ nread
, count
)))
347 p9pbuf_recycleout(pb
);
355 puffs9p_node_write(struct puffs_usermount
*pu
, void *opc
, uint8_t *buf
,
356 off_t offset
, size_t *resid
, const struct puffs_cred
*cred
,
360 struct puffs_node
*pn
= opc
;
361 struct p9pnode
*p9n
= pn
->pn_data
;
362 uint32_t chunk
, count
;
365 if (ioflag
& PUFFS_IO_APPEND
)
366 offset
= pn
->pn_va
.va_size
;
370 chunk
= MIN(*resid
, p9p
->maxreq
-32);
372 p9pbuf_put_1(pb
, P9PROTO_T_WRITE
);
373 p9pbuf_put_2(pb
, tag
);
374 p9pbuf_put_4(pb
, p9n
->fid_write
);
375 p9pbuf_put_8(pb
, offset
+nwrite
);
376 p9pbuf_put_4(pb
, chunk
);
377 p9pbuf_write_data(pb
, buf
+nwrite
, chunk
);
380 if (p9pbuf_get_type(pb
) != P9PROTO_R_WRITE
) {
385 p9pbuf_get_4(pb
, &count
);
389 if (count
!= chunk
) {
394 p9pbuf_recycleout(pb
);
402 nodecreate(struct puffs_usermount
*pu
, struct puffs_node
*pn
,
403 struct puffs_newinfo
*pni
, const char *name
,
404 const struct vattr
*vap
, uint32_t dirbit
)
407 struct puffs_node
*pn_new
;
408 struct p9pnode
*p9n
= pn
->pn_data
;
409 p9pfid_t nfid
= NEXTFID(p9p
);
419 rv
= proto_cc_dupfid(pu
, p9n
->fid_base
, nfid
);
423 p9pbuf_put_1(pb
, P9PROTO_T_CREATE
);
424 p9pbuf_put_2(pb
, tag
);
425 p9pbuf_put_4(pb
, nfid
);
426 p9pbuf_put_str(pb
, name
);
427 p9pbuf_put_4(pb
, dirbit
| (vap
->va_mode
& 0777));
431 rv
= proto_expect_qid(pb
, P9PROTO_R_CREATE
, &nqid
);
436 * Now, little problem here: create returns an *open* fid.
437 * So, clunk it and walk the parent directory to get a fid
438 * which is not open for I/O yet.
440 proto_cc_clunkfid(pu
, nfid
, 0);
443 p9pbuf_recycleout(pb
);
444 p9pbuf_put_1(pb
, P9PROTO_T_WALK
);
445 p9pbuf_put_2(pb
, tag
);
446 p9pbuf_put_4(pb
, p9n
->fid_base
);
447 p9pbuf_put_4(pb
, nfid
);
449 p9pbuf_put_str(pb
, name
);
453 * someone removed it already? try again
454 * note: this is kind of lose/lose
456 if (p9pbuf_get_type(pb
) != P9PROTO_R_WALK
)
459 pn_new
= newp9pnode_va(pu
, vap
, nfid
);
460 qid2vattr(&pn_new
->pn_va
, &nqid
);
461 puffs_newinfo_setcookie(pni
, pn_new
);
468 puffs9p_node_create(struct puffs_usermount
*pu
, void *opc
, struct puffs_newinfo
*pni
,
469 const struct puffs_cn
*pcn
, const struct vattr
*va
)
472 return nodecreate(pu
, opc
, pni
, pcn
->pcn_name
, va
, 0);
476 puffs9p_node_mkdir(struct puffs_usermount
*pu
, void *opc
, struct puffs_newinfo
*pni
,
477 const struct puffs_cn
*pcn
, const struct vattr
*va
)
480 return nodecreate(pu
, opc
, pni
, pcn
->pcn_name
,
481 va
, P9PROTO_CPERM_DIR
);
485 * Need to be a bit clever again: the fid is clunked no matter if
486 * the remove succeeds or not. Re-getting a fid would be way too
487 * difficult in case the remove failed for a valid reason (directory
488 * not empty etcetc.). So walk ourselves another fid to prod the
492 noderemove(struct puffs_usermount
*pu
, struct puffs_node
*pn
)
495 struct p9pnode
*p9n
= pn
->pn_data
;
496 p9pfid_t testfid
= NEXTFID(p9p
);
498 rv
= proto_cc_dupfid(pu
, p9n
->fid_base
, testfid
);
502 p9pbuf_put_1(pb
, P9PROTO_T_REMOVE
);
503 p9pbuf_put_2(pb
, tag
);
504 p9pbuf_put_4(pb
, testfid
);
507 * XXX: error handling isn't very robust, but doom is impending
508 * anyway, so just accept we're going belly up and play dead
512 if (p9pbuf_get_type(pb
) != P9PROTO_R_REMOVE
) {
515 proto_cc_clunkfid(pu
, p9n
->fid_base
, 0);
516 p9n
->fid_base
= P9P_INVALFID
;
522 puffs_setback(pcc
, PUFFS_SETBACK_NOREF_N2
);
528 puffs9p_node_remove(struct puffs_usermount
*pu
, void *opc
, void *targ
,
529 const struct puffs_cn
*pcn
)
531 struct puffs_node
*pn
= targ
;
533 if (pn
->pn_va
.va_type
== VDIR
)
536 return noderemove(pu
, pn
);
540 puffs9p_node_rmdir(struct puffs_usermount
*pu
, void *opc
, void *targ
,
541 const struct puffs_cn
*pcn
)
543 struct puffs_node
*pn
= targ
;
545 if (pn
->pn_va
.va_type
!= VDIR
)
548 return noderemove(pu
, pn
);
552 * 9P supports renames only for files within a directory
553 * from what I could tell. So just support in-directory renames
557 puffs9p_node_rename(struct puffs_usermount
*pu
, void *opc
, void *src
,
558 const struct puffs_cn
*pcn_src
, void *targ_dir
, void *targ
,
559 const struct puffs_cn
*pcn_targ
)
562 struct puffs_node
*pn_src
= src
;
563 struct p9pnode
*p9n_src
= pn_src
->pn_data
;
565 if (opc
!= targ_dir
) {
570 /* 9P doesn't allow to overwrite in rename */
572 struct puffs_node
*pn_targ
= targ
;
574 rv
= noderemove(pu
, pn_targ
->pn_data
);
579 p9pbuf_put_1(pb
, P9PROTO_T_WSTAT
);
580 p9pbuf_put_2(pb
, tag
);
581 p9pbuf_put_4(pb
, p9n_src
->fid_base
);
582 proto_make_stat(pb
, NULL
, pcn_targ
->pcn_name
, pn_src
->pn_va
.va_type
);
585 if (p9pbuf_get_type(pb
) != P9PROTO_R_WSTAT
)
596 * - "you're not fooling anyone you know, you'll be stone dead in a minute
597 * - "he says he's not quite dead"
598 * - "isn't there anything you could do?"
603 puffs9p_node_reclaim(struct puffs_usermount
*pu
, void *opc
)
605 struct puffs_node
*pn
= opc
;
606 struct p9pnode
*p9n
= pn
->pn_data
;
608 assert(LIST_EMPTY(&p9n
->dir_openlist
));
609 assert(p9n
->fid_read
== P9P_INVALFID
&& p9n
->fid_write
== P9P_INVALFID
);
611 proto_cc_clunkfid(pu
, p9n
->fid_base
, 0);