node.c revision 1.46 1 /* $NetBSD: node.c,v 1.46 2007/11/27 11:31:21 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2006 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
14 *
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
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: node.c,v 1.46 2007/11/27 11:31:21 pooka Exp $");
31 #endif /* !lint */
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37
38 #include "psshfs.h"
39 #include "sftp_proto.h"
40
41 int
42 psshfs_node_lookup(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni,
43 const struct puffs_cn *pcn)
44 {
45 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
46 struct psshfs_ctx *pctx = puffs_getspecific(pu);
47 struct puffs_node *pn_dir = opc;
48 struct psshfs_node *psn, *psn_dir = pn_dir->pn_data;
49 struct puffs_node *pn;
50 struct psshfs_dir *pd;
51 struct vattr va;
52 int rv;
53
54 if (PCNISDOTDOT(pcn)) {
55 psn = psn_dir->parent->pn_data;
56 psn->stat &= ~PSN_RECLAIMED;
57
58 puffs_newinfo_setcookie(pni, psn_dir->parent);
59 puffs_newinfo_setvtype(pni, VDIR);
60 return 0;
61 }
62
63 rv = sftp_readdir(pcc, pctx, pn_dir);
64 if (rv) {
65 if (rv != EPERM)
66 return rv;
67
68 /*
69 * Can't read the directory. We still might be
70 * able to find the node with getattr in -r+x dirs
71 */
72 rv = getpathattr(pcc, PCNPATH(pcn), &va);
73 if (rv)
74 return rv;
75
76 /* guess */
77 if (va.va_type == VDIR)
78 va.va_nlink = 2;
79 else
80 va.va_nlink = 1;
81
82 pn = allocnode(pu, pn_dir, pcn->pcn_name, &va);
83 psn = pn->pn_data;
84 psn->attrread = time(NULL);
85 } else {
86 pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name);
87 if (!pd) {
88 return ENOENT;
89 }
90
91 if (pd->entry)
92 pn = pd->entry;
93 else
94 pn = makenode(pu, pn_dir, pd, &pd->va);
95 psn = pn->pn_data;
96 }
97
98 psn->stat &= ~PSN_RECLAIMED;
99
100 puffs_newinfo_setcookie(pni, pn);
101 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
102 puffs_newinfo_setsize(pni, pn->pn_va.va_size);
103
104 return 0;
105 }
106
107 int
108 psshfs_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *vap,
109 const struct puffs_cred *pcr)
110 {
111 struct puffs_node *pn = opc;
112 int rv;
113
114 rv = getnodeattr(pcc, pn);
115 if (rv)
116 return rv;
117
118 memcpy(vap, &pn->pn_va, sizeof(struct vattr));
119
120 return 0;
121 }
122
123 int
124 psshfs_node_setattr(struct puffs_cc *pcc, void *opc,
125 const struct vattr *va, const struct puffs_cred *pcr)
126 {
127 PSSHFSAUTOVAR(pcc);
128 struct vattr kludgeva;
129 struct puffs_node *pn = opc;
130
131 psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn));
132
133 memcpy(&kludgeva, va, sizeof(struct vattr));
134
135 /* XXX: kludge due to openssh server implementation */
136 if (va->va_atime.tv_sec != PUFFS_VNOVAL
137 && va->va_mtime.tv_sec == PUFFS_VNOVAL) {
138 if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL)
139 kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec;
140 else
141 kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec;
142 }
143 if (va->va_mtime.tv_sec != PUFFS_VNOVAL
144 && va->va_atime.tv_sec == PUFFS_VNOVAL) {
145 if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL)
146 kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec;
147 else
148 kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec;
149 }
150
151 psbuf_put_vattr(pb, &kludgeva);
152 GETRESPONSE(pb);
153
154 rv = psbuf_expect_status(pb);
155 if (rv == 0)
156 puffs_setvattr(&pn->pn_va, &kludgeva);
157
158 out:
159 PSSHFSRETURN(rv);
160 }
161
162 int
163 psshfs_node_create(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni,
164 const struct puffs_cn *pcn, const struct vattr *va)
165 {
166 PSSHFSAUTOVAR(pcc);
167 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
168 struct puffs_node *pn = opc;
169 struct puffs_node *pn_new;
170 char *fhand = NULL;
171 uint32_t fhandlen;
172
173 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
174 if (!pn_new) {
175 rv = ENOMEM;
176 goto out;
177 }
178
179 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn));
180 psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
181 psbuf_put_vattr(pb, va);
182 GETRESPONSE(pb);
183
184 rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
185 if (rv == 0)
186 puffs_newinfo_setcookie(pni, pn_new);
187 else
188 goto out;
189
190 reqid = NEXTREQ(pctx);
191 psbuf_recycleout(pb);
192 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
193 JUSTSEND(pb);
194 free(fhand);
195 return 0;
196
197 out:
198 free(fhand);
199 PSSHFSRETURN(rv);
200 }
201
202 int
203 psshfs_node_mmap(struct puffs_cc* pcc, void *opc, vm_prot_t prot,
204 const struct puffs_cred *pcr)
205 {
206 struct puffs_node *pn = opc;
207 struct psshfs_node *psn = pn->pn_data;
208
209 if (prot & (VM_PROT_READ | VM_PROT_EXECUTE))
210 psn->stat |= PSN_READMAP;
211 if (prot & VM_PROT_WRITE)
212 psn->stat |= PSN_WRITEMAP;
213
214 return 0;
215 }
216
217 int
218 psshfs_node_open(struct puffs_cc *pcc, void *opc, int mode,
219 const struct puffs_cred *pcr)
220 {
221 PSSHFSAUTOVAR(pcc);
222 struct vattr va;
223 struct puffs_node *pn = opc;
224 struct psshfs_node *psn = pn->pn_data;
225
226 if (pn->pn_va.va_type == VDIR)
227 goto out;
228
229 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1);
230 puffs_vattr_null(&va);
231 if (mode & FREAD && psn->fhand_r == NULL) {
232 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn));
233 psbuf_put_4(pb, SSH_FXF_READ);
234 psbuf_put_vattr(pb, &va);
235 GETRESPONSE(pb);
236
237 rv = psbuf_expect_handle(pb, &psn->fhand_r, &psn->fhand_r_len);
238 if (rv)
239 goto out;
240 psbuf_recycleout(pb);
241 }
242 if (mode & FWRITE && psn->fhand_w == NULL) {
243 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn));
244 psbuf_put_4(pb, SSH_FXF_WRITE);
245 psbuf_put_vattr(pb, &va);
246 GETRESPONSE(pb);
247
248 rv = psbuf_expect_handle(pb, &psn->fhand_w, &psn->fhand_w_len);
249 if (rv)
250 goto out;
251 }
252
253 out:
254 PSSHFSRETURN(rv);
255 }
256
257 static void
258 closehandles(struct puffs_cc *pcc, struct psshfs_node *psn)
259 {
260 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
261 struct psshfs_ctx *pctx = puffs_cc_getspecific(pcc);
262 struct puffs_framebuf *pb1, *pb2;
263 uint32_t reqid = NEXTREQ(pctx);
264 int rv; /* macro magic */
265
266 if ((psn->stat & PSN_READMAP) == 0 && psn->fhand_r) {
267 pb1 = psbuf_makeout();
268 psbuf_req_data(pb1, SSH_FXP_CLOSE, reqid,
269 psn->fhand_r, psn->fhand_r_len);
270 JUSTSEND(pb1);
271 free(psn->fhand_r);
272 psn->fhand_r = NULL;
273 }
274 if ((psn->stat & PSN_WRITEMAP) == 0 && psn->fhand_w) {
275 pb2 = psbuf_makeout();
276 psbuf_req_data(pb2, SSH_FXP_CLOSE, reqid,
277 psn->fhand_w, psn->fhand_w_len);
278 JUSTSEND(pb2);
279 free(psn->fhand_w);
280 psn->fhand_w = NULL;
281 }
282
283 out:
284 return;
285 }
286
287 int
288 psshfs_node_inactive(struct puffs_cc *pcc, void *opc)
289 {
290 struct puffs_node *pn = opc;
291 struct psshfs_node *psn = pn->pn_data;
292
293 psn->stat &= ~(PSN_READMAP | PSN_WRITEMAP);
294 closehandles(pcc, psn);
295
296 return 0;
297 }
298
299 int
300 psshfs_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
301 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
302 int *eofflag, off_t *cookies, size_t *ncookies)
303 {
304 struct psshfs_ctx *pctx = puffs_cc_getspecific(pcc);
305 struct puffs_node *pn = opc;
306 struct psshfs_node *psn = pn->pn_data;
307 struct psshfs_dir *pd;
308 int i, rv, set_readdir;
309
310 if (psn->stat & PSN_READDIR) {
311 struct psshfs_wait pw;
312
313 set_readdir = 0;
314 pw.pw_cc = pcc;
315 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
316 puffs_cc_yield(pcc);
317 TAILQ_REMOVE(&psn->pw, &pw, pw_entries);
318 } else {
319 psn->stat |= PSN_READDIR;
320 set_readdir = 1;
321 }
322
323 *ncookies = 0;
324 rv = sftp_readdir(pcc, pctx, pn);
325 if (rv)
326 goto out;
327
328 /* find next dirent */
329 for (i = *readoff;;i++) {
330 if (i >= psn->dentnext)
331 goto out;
332 pd = &psn->dir[i];
333 if (pd->valid)
334 break;
335 }
336
337 for (;;) {
338 *readoff = i;
339 if (!puffs_nextdent(&dent, pd->entryname,
340 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) {
341 rv = 0;
342 goto out;
343 }
344
345 /* find next entry, store possible nfs key */
346 do {
347 if (++i >= psn->dentnext)
348 goto out;
349 pd = &psn->dir[i];
350 } while (pd->valid == 0);
351 PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i);
352 }
353
354 out:
355 if (rv == 0) {
356 if (i >= psn->dentnext)
357 *eofflag = 1;
358
359 *readoff = i;
360 }
361
362 if (set_readdir) {
363 struct psshfs_wait *pw;
364
365 /* all will likely run to completion because of cache */
366 TAILQ_FOREACH(pw, &psn->pw, pw_entries)
367 puffs_cc_schedule(pw->pw_cc);
368
369 psn->stat &= ~PSN_READDIR;
370 }
371
372 return rv;
373 }
374
375 int
376 psshfs_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
377 off_t offset, size_t *resid, const struct puffs_cred *pcr,
378 int ioflag)
379 {
380 PSSHFSAUTOVAR(pcc);
381 struct puffs_node *pn = opc;
382 struct psshfs_node *psn = pn->pn_data;
383 uint32_t readlen;
384
385 if (pn->pn_va.va_type == VDIR) {
386 rv = EISDIR;
387 goto farout;
388 }
389
390 readlen = *resid;
391 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len);
392 psbuf_put_8(pb, offset);
393 psbuf_put_4(pb, readlen);
394
395 /*
396 * Do this *after* accessing the file, the handle might not
397 * exist after blocking.
398 */
399 if (max_reads && ++psn->readcount > max_reads) {
400 struct psshfs_wait pw;
401
402 pw.pw_cc = pcc;
403 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
404 puffs_cc_yield(pcc);
405 }
406
407 GETRESPONSE(pb);
408
409 rv = psbuf_do_data(pb, buf, &readlen);
410 if (rv == 0)
411 *resid -= readlen;
412
413 out:
414 if (max_reads && --psn->readcount >= max_reads) {
415 struct psshfs_wait *pw;
416
417 pw = TAILQ_FIRST(&psn->pw);
418 assert(pw != NULL);
419 TAILQ_REMOVE(&psn->pw, pw, pw_entries);
420 puffs_cc_schedule(pw->pw_cc);
421 }
422
423 farout:
424 PSSHFSRETURN(rv);
425 }
426
427 /* XXX: we should getattr for size */
428 int
429 psshfs_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
430 off_t offset, size_t *resid, const struct puffs_cred *cred,
431 int ioflag)
432 {
433 PSSHFSAUTOVAR(pcc);
434 struct puffs_node *pn = opc;
435 struct psshfs_node *psn = pn->pn_data;
436 uint32_t writelen;
437
438 if (pn->pn_va.va_type == VDIR) {
439 rv = EISDIR;
440 goto out;
441 }
442
443 writelen = *resid;
444 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len);
445 psbuf_put_8(pb, offset);
446 psbuf_put_data(pb, buf, writelen);
447 GETRESPONSE(pb);
448
449 rv = psbuf_expect_status(pb);
450 if (rv == 0)
451 *resid = 0;
452
453 if (pn->pn_va.va_size < offset + writelen)
454 pn->pn_va.va_size = offset + writelen;
455
456 out:
457 PSSHFSRETURN(rv);
458 }
459
460 int
461 psshfs_node_readlink(struct puffs_cc *pcc, void *opc,
462 const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
463 {
464 PSSHFSAUTOVAR(pcc);
465 struct puffs_node *pn = opc;
466 struct psshfs_node *psn = pn->pn_data;
467 uint32_t count;
468
469 if (pctx->protover < 3) {
470 rv = EOPNOTSUPP;
471 goto out;
472 }
473
474 /*
475 * check if we can use a cached version
476 *
477 * XXX: we might end up reading the same link multiple times
478 * from the server if we get many requests at once, but that's
479 * quite harmless as this routine is reentrant.
480 */
481 if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread))
482 goto copy;
483
484 if (psn->symlink) {
485 free(psn->symlink);
486 psn->symlink = NULL;
487 psn->slread = 0;
488 }
489
490 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn));
491 GETRESPONSE(pb);
492
493 rv = psbuf_expect_name(pb, &count);
494 if (rv)
495 goto out;
496 if (count != 1) {
497 rv = EPROTO;
498 goto out;
499 }
500
501 rv = psbuf_get_str(pb, &psn->symlink, NULL);
502 if (rv)
503 goto out;
504 psn->slread = time(NULL);
505
506 copy:
507 *linklen = strlen(psn->symlink);
508 (void) memcpy(linkvalue, psn->symlink, *linklen);
509
510 out:
511 PSSHFSRETURN(rv);
512 }
513
514 static int
515 doremove(struct puffs_cc *pcc, struct puffs_node *pn_dir,
516 struct puffs_node *pn, const char *name)
517 {
518 PSSHFSAUTOVAR(pcc);
519 int op;
520
521 if (pn->pn_va.va_type == VDIR)
522 op = SSH_FXP_RMDIR;
523 else
524 op = SSH_FXP_REMOVE;
525
526 psbuf_req_str(pb, op, reqid, PNPATH(pn));
527 GETRESPONSE(pb);
528
529 rv = psbuf_expect_status(pb);
530 if (rv == 0)
531 nukenode(pn, name, 0);
532
533 out:
534 PSSHFSRETURN(rv);
535 }
536
537 int
538 psshfs_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
539 const struct puffs_cn *pcn)
540 {
541 struct puffs_node *pn_targ = targ;
542 int rv;
543
544 assert(pn_targ->pn_va.va_type != VDIR);
545
546 rv = doremove(pcc, opc, targ, pcn->pcn_name);
547 if (rv == 0)
548 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
549
550 return rv;
551 }
552
553 int
554 psshfs_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
555 const struct puffs_cn *pcn)
556 {
557 struct puffs_node *pn_targ = targ;
558 int rv;
559
560 assert(pn_targ->pn_va.va_type == VDIR);
561
562 rv = doremove(pcc, opc, targ, pcn->pcn_name);
563 if (rv == 0)
564 puffs_setback(pcc, PUFFS_SETBACK_NOREF_N2);
565
566 return rv;
567 }
568
569 int
570 psshfs_node_mkdir(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni,
571 const struct puffs_cn *pcn, const struct vattr *va)
572 {
573 PSSHFSAUTOVAR(pcc);
574 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
575 struct puffs_node *pn = opc;
576 struct puffs_node *pn_new;
577
578 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
579 if (!pn_new) {
580 rv = ENOMEM;
581 goto out;
582 }
583
584 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn));
585 psbuf_put_vattr(pb, va);
586 GETRESPONSE(pb);
587
588 rv = psbuf_expect_status(pb);
589
590 if (rv == 0)
591 puffs_newinfo_setcookie(pni, pn_new);
592 else
593 nukenode(pn_new, pcn->pcn_name, 1);
594
595 out:
596 PSSHFSRETURN(rv);
597 }
598
599 int
600 psshfs_node_symlink(struct puffs_cc *pcc, void *opc, struct puffs_newinfo *pni,
601 const struct puffs_cn *pcn, const struct vattr *va,
602 const char *link_target)
603 {
604 PSSHFSAUTOVAR(pcc);
605 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
606 struct puffs_node *pn = opc;
607 struct puffs_node *pn_new;
608
609 if (pctx->protover < 3) {
610 rv = EOPNOTSUPP;
611 goto out;
612 }
613
614 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
615 if (!pn_new) {
616 rv = ENOMEM;
617 goto out;
618 }
619
620 /*
621 * XXX: ietf says: source, target. openssh says: ietf who?
622 * Let's go with openssh and build quirk tables later if we care
623 */
624 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target);
625 psbuf_put_str(pb, PCNPATH(pcn));
626 GETRESPONSE(pb);
627
628 rv = psbuf_expect_status(pb);
629 if (rv == 0)
630 puffs_newinfo_setcookie(pni, pn_new);
631 else
632 nukenode(pn_new, pcn->pcn_name, 1);
633
634 out:
635 PSSHFSRETURN(rv);
636 }
637
638 int
639 psshfs_node_rename(struct puffs_cc *pcc, void *opc, void *src,
640 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
641 const struct puffs_cn *pcn_targ)
642 {
643 PSSHFSAUTOVAR(pcc);
644 struct puffs_node *pn_sf = src;
645 struct puffs_node *pn_td = targ_dir, *pn_tf = targ;
646 struct psshfs_node *psn_targdir = pn_td->pn_data;
647
648 if (pctx->protover < 2) {
649 rv = EOPNOTSUPP;
650 goto out;
651 }
652
653 if (pn_tf) {
654 rv = doremove(pcc, targ_dir, pn_tf, pcn_targ->pcn_name);
655 if (rv)
656 goto out;
657 }
658
659 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src));
660 psbuf_put_str(pb, PCNPATH(pcn_targ));
661 GETRESPONSE(pb);
662
663 rv = psbuf_expect_status(pb);
664 if (rv == 0) {
665 struct psshfs_dir *pd;
666
667 /*
668 * XXX: interfaces didn't quite work with rename..
669 * the song remains the same. go figure .. ;)
670 */
671 nukenode(pn_sf, pcn_src->pcn_name, 0);
672 pd = direnter(pn_td, pcn_targ->pcn_name);
673 pd->entry = pn_sf;
674 puffs_setvattr(&pd->va, &pn_sf->pn_va);
675
676 if (opc != targ_dir) {
677 psn_targdir->childcount++;
678 if (pn_sf->pn_va.va_type == VDIR)
679 pn_td->pn_va.va_nlink++;
680 }
681 }
682
683 out:
684 PSSHFSRETURN(rv);
685 }
686
687 /*
688 * So this file system happened to be written in such a way that
689 * lookup for ".." is hard if we lose the in-memory node. We'd
690 * need to recreate the entire directory structure from the root
691 * node up to the ".." node we're looking up.
692 *
693 * And since our entire fs structure is purely fictional (i.e. it's
694 * only in-memory, not fetchable from the server), the easiest way
695 * to deal with it is to not allow nodes with children to be
696 * reclaimed.
697 *
698 * If a node with children is being attempted to be reclaimed, we
699 * just mark it "reclaimed" but leave it as is until all its children
700 * have been reclaimed. If a lookup for that node is done meanwhile,
701 * it will be found by lookup() and we just remove the "reclaimed"
702 * bit.
703 */
704 int
705 psshfs_node_reclaim(struct puffs_cc *pcc, void *opc)
706 {
707 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
708 struct puffs_node *pn = opc, *pn_next, *pn_root;
709 struct psshfs_node *psn = pn->pn_data;
710
711 /*
712 * don't reclaim if we have file handle issued, otherwise
713 * we can't do fhtonode
714 */
715 if (psn->stat & PSN_HASFH)
716 return 0;
717
718 psn->stat |= PSN_RECLAIMED;
719 pn_root = puffs_getroot(pu);
720 for (; pn != pn_root; pn = pn_next) {
721 psn = pn->pn_data;
722 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0)
723 break;
724
725 pn_next = psn->parent;
726 doreclaim(pn);
727 }
728
729 return 0;
730 }
731