1 /* $NetBSD: node.c,v 1.62 2010/10/29 16:13:51 pooka Exp $ */
4 * Copyright (c) 2006-2009 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
34 #include "sftp_proto.h"
37 psshfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
38 struct puffs_newinfo *pni, const struct puffs_cn *pcn)
40 struct psshfs_ctx *pctx = puffs_getspecific(pu);
41 struct puffs_node *pn_dir = opc;
42 struct psshfs_node *psn, *psn_dir = pn_dir->pn_data;
43 struct puffs_node *pn;
44 struct psshfs_dir *pd;
48 rv = sftp_readdir(pu, pctx, pn_dir);
54 * Can't read the directory. We still might be
55 * able to find the node with getattr in -r+x dirs
57 rv = getpathattr(pu, PCNPATH(pcn), &va);
62 if (va.va_type == VDIR)
67 pn = allocnode(pu, pn_dir, pcn->pcn_name, &va);
69 psn->attrread = time(NULL);
71 pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name);
79 pd->entry = pn = makenode(pu, pn_dir, pd, &pd->va);
82 * sure sure we have fresh attributes. most likely we will
83 * have them cached. we might not if we go through:
84 * create - reclaim - lookup (this).
86 rv = getnodeattr(pu, pn, PCNPATH(pcn));
93 psn->stat &= ~PSN_RECLAIMED;
95 puffs_newinfo_setcookie(pni, pn);
96 puffs_newinfo_setvtype(pni, pn->pn_va.va_type);
97 puffs_newinfo_setsize(pni, pn->pn_va.va_size);
103 psshfs_node_lookupdotdot(struct puffs_usermount *pu, puffs_cookie_t opc,
104 struct puffs_newinfo *pni, const struct puffs_cn *pcn)
106 struct puffs_node *pn_dir = opc;
107 struct psshfs_node *psn, *psn_dir = pn_dir->pn_data;
109 psn = psn_dir->parent->pn_data;
110 psn->stat &= ~PSN_RECLAIMED;
112 puffs_newinfo_setcookie(pni, psn_dir->parent);
113 puffs_newinfo_setvtype(pni, VDIR);
118 psshfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
119 struct vattr *vap, const struct puffs_cred *pcr)
121 struct puffs_node *pn = opc;
124 rv = getnodeattr(pu, pn, NULL);
128 memcpy(vap, &pn->pn_va, sizeof(struct vattr));
134 psshfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
135 const struct vattr *va, const struct puffs_cred *pcr)
138 struct vattr kludgeva;
139 struct puffs_node *pn = opc;
141 psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn));
143 memcpy(&kludgeva, va, sizeof(struct vattr));
145 /* XXX: kludge due to openssh server implementation */
146 if (va->va_atime.tv_sec != PUFFS_VNOVAL
147 && va->va_mtime.tv_sec == PUFFS_VNOVAL) {
148 if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL)
149 kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec;
151 kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec;
153 if (va->va_mtime.tv_sec != PUFFS_VNOVAL
154 && va->va_atime.tv_sec == PUFFS_VNOVAL) {
155 if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL)
156 kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec;
158 kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec;
161 psbuf_put_vattr(pb, &kludgeva, pctx);
162 GETRESPONSE(pb, pctx->sshfd);
164 rv = psbuf_expect_status(pb);
166 puffs_setvattr(&pn->pn_va, &kludgeva);
173 psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
174 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
175 const struct vattr *va)
178 struct puffs_node *pn = opc;
179 struct puffs_node *pn_new;
183 /* Create node on server first */
184 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn));
185 psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
186 psbuf_put_vattr(pb, va, pctx);
187 GETRESPONSE(pb, pctx->sshfd);
188 rv = psbuf_expect_handle(pb, &fhand, &fhandlen);
193 * Do *not* create the local node before getting a response
194 * from the server. Otherwise we might screw up consistency,
195 * namely that the node can be looked up before create has
196 * returned (mind you, the kernel will unlock the directory
197 * before the create call from userspace returns).
199 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
201 struct puffs_framebuf *pb2 = psbuf_makeout();
202 reqid = NEXTREQ(pctx);
203 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn));
204 JUSTSEND(pb2, pctx->sshfd);
209 puffs_newinfo_setcookie(pni, pn_new);
211 reqid = NEXTREQ(pctx);
212 psbuf_recycleout(pb);
213 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen);
214 JUSTSEND(pb, pctx->sshfd);
224 * Open a file handle. This is used for read and write. We do not
225 * wait here for the success or failure of this operation. This is
226 * because otherwise opening and closing file handles would block
227 * reading potentially cached information. Rather, we defer the wait
228 * to read/write and therefore allow cached access without a wait.
230 * If we have not yet succesfully opened a type of handle, we do wait
231 * here. Also, if a lazy open fails, we revert back to the same
235 psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode,
236 const struct puffs_cred *pcr)
238 struct puffs_cc *pcc = puffs_cc_getcc(pu);
239 struct psshfs_ctx *pctx = puffs_getspecific(pu);
240 struct puffs_framebuf *pb, *pb2;
242 struct puffs_node *pn = opc;
243 struct psshfs_node *psn = pn->pn_data;
245 int didread, didwrite;
248 if (pn->pn_va.va_type == VDIR)
251 puffs_setback(pcc, PUFFS_SETBACK_INACT_N1);
252 puffs_vattr_null(&va);
253 didread = didwrite = 0;
254 if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) {
255 pb = psbuf_makeout();
257 reqid = NEXTREQ(pctx);
258 psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn));
259 psbuf_put_4(pb, SSH_FXF_READ);
260 psbuf_put_vattr(pb, &va, pctx);
262 if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb,
263 lazyopen_rresp, psn, 0) == -1) {
265 puffs_framebuf_destroy(pb);
269 psn->lazyopen_r = pb;
272 if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) {
273 pb2 = psbuf_makeout();
275 reqid = NEXTREQ(pctx);
276 psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn));
277 psbuf_put_4(pb2, SSH_FXF_WRITE);
278 psbuf_put_vattr(pb2, &va, pctx);
280 if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2,
281 lazyopen_wresp, psn, 0) == -1) {
283 puffs_framebuf_destroy(pb2);
287 psn->lazyopen_w = pb2;
290 psn->stat &= ~PSN_HANDLECLOSE;
294 if (didread && (psn->stat & PSN_DOLAZY_R) == 0) {
295 assert(psn->lazyopen_r);
297 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc);
298 lazyopen_rresp(pu, psn->lazyopen_r, psn, rv);
300 psn->stat |= PSN_DOLAZY_R;
302 if (psn->lazyopen_err_r)
303 return psn->lazyopen_err_r;
309 if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) {
310 assert(psn->lazyopen_w);
312 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc);
313 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv);
315 psn->stat |= PSN_DOLAZY_W;
317 if (psn->lazyopen_err_w)
318 return psn->lazyopen_err_w;
327 psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
329 struct puffs_node *pn = opc;
331 closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE);
336 psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
337 struct dirent *dent, off_t *readoff, size_t *reslen,
338 const struct puffs_cred *pcr, int *eofflag,
339 off_t *cookies, size_t *ncookies)
341 struct puffs_cc *pcc = puffs_cc_getcc(pu);
342 struct psshfs_ctx *pctx = puffs_getspecific(pu);
343 struct puffs_node *pn = opc;
344 struct psshfs_node *psn = pn->pn_data;
345 struct psshfs_dir *pd;
350 if (psn->stat & PSN_READDIR) {
351 struct psshfs_wait pw;
355 pw.pw_type = PWTYPE_READDIR;
356 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
360 psn->stat |= PSN_READDIR;
365 rv = sftp_readdir(pu, pctx, pn);
370 /* find next dirent */
371 for (i = *readoff;;i++) {
372 if (i >= psn->dentnext)
381 if (!puffs_nextdent(&dent, pd->entryname,
382 pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) {
387 /* find next entry, store possible nfs key */
389 if (++i >= psn->dentnext)
392 } while (pd->valid == 0);
393 PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i);
398 if (i >= psn->dentnext)
405 struct psshfs_wait *pw;
407 /* all will likely run to completion because of cache */
408 TAILQ_FOREACH(pw, &psn->pw, pw_entries) {
409 assert(pw->pw_type == PWTYPE_READDIR);
410 puffs_cc_schedule(pw->pw_cc);
411 TAILQ_REMOVE(&psn->pw, pw, pw_entries);
414 psn->stat &= ~PSN_READDIR;
421 psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf,
422 off_t offset, size_t *resid, const struct puffs_cred *pcr,
426 struct puffs_node *pn = opc;
427 struct psshfs_node *psn = pn->pn_data;
428 struct psshfs_wait *pwp;
431 if (pn->pn_va.va_type == VDIR) {
436 /* check that a lazyopen didn't fail */
437 if (!psn->fhand_r && !psn->lazyopen_r) {
438 rv = psn->lazyopen_err_r;
442 /* if someone is already waiting for the lazyopen, "just" wait */
443 if (psn->stat & PSN_LAZYWAIT_R) {
444 struct psshfs_wait pw;
446 assert(psn->lazyopen_r);
449 pw.pw_type = PWTYPE_READ1;
450 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
454 /* if lazyopening, wait for the result */
455 if (psn->lazyopen_r) {
456 psn->stat |= PSN_LAZYWAIT_R;
457 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc);
458 lazyopen_rresp(pu, psn->lazyopen_r, psn, rv);
460 /* schedule extra waiters */
461 TAILQ_FOREACH(pwp, &psn->pw, pw_entries)
462 if (pwp->pw_type == PWTYPE_READ1) {
463 puffs_cc_schedule(pwp->pw_cc);
464 TAILQ_REMOVE(&psn->pw, pwp, pw_entries);
466 psn->stat &= ~PSN_LAZYWAIT_R;
468 if ((rv = psn->lazyopen_err_r) != 0)
472 /* if there is still no handle, just refuse to live with this */
479 psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len);
480 psbuf_put_8(pb, offset);
481 psbuf_put_4(pb, readlen);
484 * Do this *after* accessing the file, the handle might not
485 * exist after blocking.
487 if (max_reads && ++psn->readcount > max_reads) {
488 struct psshfs_wait pw;
491 pw.pw_type = PWTYPE_READ2;
492 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
496 GETRESPONSE(pb, pctx->sshfd_data);
498 rv = psbuf_do_data(pb, buf, &readlen);
503 if (max_reads && --psn->readcount >= max_reads) {
504 TAILQ_FOREACH(pwp, &psn->pw, pw_entries)
505 if (pwp->pw_type == PWTYPE_READ2)
508 puffs_cc_schedule(pwp->pw_cc);
509 TAILQ_REMOVE(&psn->pw, pwp, pw_entries);
513 /* check if we need a lazyclose */
514 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) {
515 TAILQ_FOREACH(pwp, &psn->pw, pw_entries)
516 if (pwp->pw_type == PWTYPE_READ1)
519 closehandles(pu, psn, HANDLE_READ);
524 /* XXX: we should getattr for size */
526 psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf,
527 off_t offset, size_t *resid, const struct puffs_cred *cred,
531 struct psshfs_wait *pwp;
532 struct puffs_node *pn = opc;
533 struct psshfs_node *psn = pn->pn_data;
536 if (pn->pn_va.va_type == VDIR) {
541 /* check that a lazyopen didn't fail */
542 if (!psn->fhand_w && !psn->lazyopen_w) {
543 rv = psn->lazyopen_err_w;
547 if (psn->stat & PSN_LAZYWAIT_W) {
548 struct psshfs_wait pw;
550 assert(psn->lazyopen_w);
553 pw.pw_type = PWTYPE_WRITE;
554 TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries);
559 * If lazyopening, wait for the result.
560 * There can still be more than oen writer at a time in case
561 * the kernel issues write FAFs.
563 if (psn->lazyopen_w) {
564 psn->stat |= PSN_LAZYWAIT_W;
565 rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc);
566 lazyopen_wresp(pu, psn->lazyopen_w, psn, rv);
568 /* schedule extra waiters */
569 TAILQ_FOREACH(pwp, &psn->pw, pw_entries)
570 if (pwp->pw_type == PWTYPE_WRITE) {
571 puffs_cc_schedule(pwp->pw_cc);
572 TAILQ_REMOVE(&psn->pw, pwp, pw_entries);
574 psn->stat &= ~PSN_LAZYWAIT_W;
576 if ((rv = psn->lazyopen_err_w) != 0)
587 psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len);
588 psbuf_put_8(pb, offset);
589 psbuf_put_data(pb, buf, writelen);
590 GETRESPONSE(pb, pctx->sshfd_data);
592 rv = psbuf_expect_status(pb);
596 if (pn->pn_va.va_size < (uint64_t)offset + writelen)
597 pn->pn_va.va_size = offset + writelen;
600 /* check if we need a lazyclose */
601 if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) {
602 TAILQ_FOREACH(pwp, &psn->pw, pw_entries)
603 if (pwp->pw_type == PWTYPE_WRITE)
606 closehandles(pu, psn, HANDLE_WRITE);
612 psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
613 const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
616 struct puffs_node *pn = opc;
617 struct psshfs_node *psn = pn->pn_data;
620 if (pctx->protover < 3) {
626 * check if we can use a cached version
628 * XXX: we might end up reading the same link multiple times
629 * from the server if we get many requests at once, but that's
630 * quite harmless as this routine is reentrant.
632 if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread))
641 psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn));
642 GETRESPONSE(pb, pctx->sshfd);
644 rv = psbuf_expect_name(pb, &count);
652 rv = psbuf_get_str(pb, &psn->symlink, NULL);
655 psn->slread = time(NULL);
658 *linklen = strlen(psn->symlink);
659 (void) memcpy(linkvalue, psn->symlink, *linklen);
666 doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir,
667 struct puffs_node *pn, const char *name)
672 if (pn->pn_va.va_type == VDIR)
677 psbuf_req_str(pb, op, reqid, PNPATH(pn));
678 GETRESPONSE(pb, pctx->sshfd);
680 rv = psbuf_expect_status(pb);
682 nukenode(pn, name, 0);
689 psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
690 puffs_cookie_t targ, const struct puffs_cn *pcn)
692 struct puffs_node *pn_targ = targ;
695 assert(pn_targ->pn_va.va_type != VDIR);
697 rv = doremove(pu, opc, targ, pcn->pcn_name);
699 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
705 psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
706 puffs_cookie_t targ, const struct puffs_cn *pcn)
708 struct puffs_node *pn_targ = targ;
711 assert(pn_targ->pn_va.va_type == VDIR);
713 rv = doremove(pu, opc, targ, pcn->pcn_name);
715 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
721 psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
722 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
723 const struct vattr *va)
726 struct puffs_node *pn = opc;
727 struct puffs_node *pn_new;
729 psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn));
730 psbuf_put_vattr(pb, va, pctx);
731 GETRESPONSE(pb, pctx->sshfd);
733 rv = psbuf_expect_status(pb);
737 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
739 puffs_newinfo_setcookie(pni, pn_new);
741 struct puffs_framebuf *pb2 = psbuf_makeout();
742 reqid = NEXTREQ(pctx);
743 psbuf_recycleout(pb2);
744 psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn));
745 JUSTSEND(pb2, pctx->sshfd);
754 psshfs_node_link(struct puffs_usermount *pu, puffs_cookie_t opc,
755 puffs_cookie_t targ, const struct puffs_cn *pcn)
758 struct puffs_node *pn = opc;
759 struct puffs_node *pn_targ = targ;
761 if (pctx->protover < 3) {
766 psbuf_req_str(pb, SSH_FXP_EXTENDED, reqid, "hardlink@openssh.com");
767 psbuf_put_str(pb, PNPATH(pn_targ));
768 psbuf_put_str(pb, PCNPATH(pcn));
770 GETRESPONSE(pb, pctx->sshfd);
771 rv = psbuf_expect_status(pb);
774 struct psshfs_node *psn;
776 /* Force refresh of attributes */
777 psn = pn_targ->pn_data;
780 /* Force refresh of dirents */
791 psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
792 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
793 const struct vattr *va, const char *link_target)
796 struct puffs_node *pn = opc;
797 struct puffs_node *pn_new;
799 if (pctx->protover < 3) {
805 * XXX: ietf says: source, target. openssh says: ietf who?
806 * Let's go with openssh and build quirk tables later if we care
808 psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target);
809 psbuf_put_str(pb, PCNPATH(pcn));
810 GETRESPONSE(pb, pctx->sshfd);
812 rv = psbuf_expect_status(pb);
816 pn_new = allocnode(pu, pn, pcn->pcn_name, va);
818 puffs_newinfo_setcookie(pni, pn_new);
820 struct puffs_framebuf *pb2 = psbuf_makeout();
821 reqid = NEXTREQ(pctx);
822 psbuf_recycleout(pb2);
823 psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn));
824 JUSTSEND(pb2, pctx->sshfd);
833 psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc,
834 puffs_cookie_t src, const struct puffs_cn *pcn_src,
835 puffs_cookie_t targ_dir, puffs_cookie_t targ,
836 const struct puffs_cn *pcn_targ)
839 struct puffs_node *pn_sf = src;
840 struct puffs_node *pn_td = targ_dir, *pn_tf = targ;
841 struct psshfs_node *psn_src = pn_sf->pn_data;
842 struct psshfs_node *psn_targdir = pn_td->pn_data;
844 if (pctx->protover < 2) {
850 rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name);
855 psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src));
856 psbuf_put_str(pb, PCNPATH(pcn_targ));
857 GETRESPONSE(pb, pctx->sshfd);
859 rv = psbuf_expect_status(pb);
861 struct psshfs_dir *pd;
864 * XXX: interfaces didn't quite work with rename..
865 * the song remains the same. go figure .. ;)
867 nukenode(pn_sf, pcn_src->pcn_name, 0);
868 pd = direnter(pn_td, pcn_targ->pcn_name);
870 puffs_setvattr(&pd->va, &pn_sf->pn_va);
872 if (opc != targ_dir) {
873 psn_targdir->childcount++;
874 psn_src->parent = pn_td;
875 if (pn_sf->pn_va.va_type == VDIR)
876 pn_td->pn_va.va_nlink++;
885 * So this file system happened to be written in such a way that
886 * lookup for ".." is hard if we lose the in-memory node. We'd
887 * need to recreate the entire directory structure from the root
888 * node up to the ".." node we're looking up.
890 * And since our entire fs structure is purely fictional (i.e. it's
891 * only in-memory, not fetchable from the server), the easiest way
892 * to deal with it is to not allow nodes with children to be
895 * If a node with children is being attempted to be reclaimed, we
896 * just mark it "reclaimed" but leave it as is until all its children
897 * have been reclaimed. If a lookup for that node is done meanwhile,
898 * it will be found by lookup() and we just remove the "reclaimed"
902 psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc)
904 struct puffs_node *pn = opc, *pn_next, *pn_root;
905 struct psshfs_node *psn = pn->pn_data;
908 * don't reclaim if we have file handle issued, otherwise
909 * we can't do fhtonode
911 if (psn->stat & PSN_HASFH)
914 psn->stat |= PSN_RECLAIMED;
915 pn_root = puffs_getroot(pu);
916 for (; pn != pn_root; pn = pn_next) {
918 if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0)
921 pn_next = psn->parent;