1 /* $NetBSD: node.c,v 1.59 2009/11/05 13:28:18 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
28 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: node.c,v 1.59 2009/11/05 13:28:18 pooka Exp $");
39 #include "sftp_proto.h"
42 psshfs_node_lookup(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
43 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
)
45 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
46 struct puffs_node
*pn_dir
= opc
;
47 struct psshfs_node
*psn
, *psn_dir
= pn_dir
->pn_data
;
48 struct puffs_node
*pn
;
49 struct psshfs_dir
*pd
;
53 if (PCNISDOTDOT(pcn
)) {
54 psn
= psn_dir
->parent
->pn_data
;
55 psn
->stat
&= ~PSN_RECLAIMED
;
57 puffs_newinfo_setcookie(pni
, psn_dir
->parent
);
58 puffs_newinfo_setvtype(pni
, VDIR
);
62 rv
= sftp_readdir(pu
, pctx
, pn_dir
);
68 * Can't read the directory. We still might be
69 * able to find the node with getattr in -r+x dirs
71 rv
= getpathattr(pu
, PCNPATH(pcn
), &va
);
76 if (va
.va_type
== VDIR
)
81 pn
= allocnode(pu
, pn_dir
, pcn
->pcn_name
, &va
);
83 psn
->attrread
= time(NULL
);
85 pd
= lookup(psn_dir
->dir
, psn_dir
->dentnext
, pcn
->pcn_name
);
93 pn
= makenode(pu
, pn_dir
, pd
, &pd
->va
);
97 psn
->stat
&= ~PSN_RECLAIMED
;
99 puffs_newinfo_setcookie(pni
, pn
);
100 puffs_newinfo_setvtype(pni
, pn
->pn_va
.va_type
);
101 puffs_newinfo_setsize(pni
, pn
->pn_va
.va_size
);
107 psshfs_node_getattr(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
108 struct vattr
*vap
, const struct puffs_cred
*pcr
)
110 struct puffs_node
*pn
= opc
;
113 rv
= getnodeattr(pu
, pn
);
117 memcpy(vap
, &pn
->pn_va
, sizeof(struct vattr
));
123 psshfs_node_setattr(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
124 const struct vattr
*va
, const struct puffs_cred
*pcr
)
127 struct vattr kludgeva
;
128 struct puffs_node
*pn
= opc
;
130 psbuf_req_str(pb
, SSH_FXP_SETSTAT
, reqid
, PNPATH(pn
));
132 memcpy(&kludgeva
, va
, sizeof(struct vattr
));
134 /* XXX: kludge due to openssh server implementation */
135 if (va
->va_atime
.tv_sec
!= PUFFS_VNOVAL
136 && va
->va_mtime
.tv_sec
== PUFFS_VNOVAL
) {
137 if (pn
->pn_va
.va_mtime
.tv_sec
!= PUFFS_VNOVAL
)
138 kludgeva
.va_mtime
.tv_sec
= pn
->pn_va
.va_mtime
.tv_sec
;
140 kludgeva
.va_mtime
.tv_sec
= va
->va_atime
.tv_sec
;
142 if (va
->va_mtime
.tv_sec
!= PUFFS_VNOVAL
143 && va
->va_atime
.tv_sec
== PUFFS_VNOVAL
) {
144 if (pn
->pn_va
.va_atime
.tv_sec
!= PUFFS_VNOVAL
)
145 kludgeva
.va_atime
.tv_sec
= pn
->pn_va
.va_atime
.tv_sec
;
147 kludgeva
.va_atime
.tv_sec
= va
->va_mtime
.tv_sec
;
150 psbuf_put_vattr(pb
, &kludgeva
, pctx
);
151 GETRESPONSE(pb
, pctx
->sshfd
);
153 rv
= psbuf_expect_status(pb
);
155 puffs_setvattr(&pn
->pn_va
, &kludgeva
);
162 psshfs_node_create(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
163 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
164 const struct vattr
*va
)
167 struct puffs_node
*pn
= opc
;
168 struct puffs_node
*pn_new
;
172 /* Create node on server first */
173 psbuf_req_str(pb
, SSH_FXP_OPEN
, reqid
, PCNPATH(pcn
));
174 psbuf_put_4(pb
, SSH_FXF_WRITE
| SSH_FXF_CREAT
| SSH_FXF_TRUNC
);
175 psbuf_put_vattr(pb
, va
, pctx
);
176 GETRESPONSE(pb
, pctx
->sshfd
);
177 rv
= psbuf_expect_handle(pb
, &fhand
, &fhandlen
);
182 * Do *not* create the local node before getting a response
183 * from the server. Otherwise we might screw up consistency,
184 * namely that the node can be looked up before create has
185 * returned (mind you, the kernel will unlock the directory
186 * before the create call from userspace returns).
188 pn_new
= allocnode(pu
, pn
, pcn
->pcn_name
, va
);
190 struct puffs_framebuf
*pb2
= psbuf_makeout();
191 reqid
= NEXTREQ(pctx
);
192 psbuf_req_str(pb2
, SSH_FXP_REMOVE
, reqid
, PCNPATH(pcn
));
193 JUSTSEND(pb2
, pctx
->sshfd
);
198 puffs_newinfo_setcookie(pni
, pn_new
);
200 reqid
= NEXTREQ(pctx
);
201 psbuf_recycleout(pb
);
202 psbuf_req_data(pb
, SSH_FXP_CLOSE
, reqid
, fhand
, fhandlen
);
203 JUSTSEND(pb
, pctx
->sshfd
);
213 * Open a file handle. This is used for read and write. We do not
214 * wait here for the success or failure of this operation. This is
215 * because otherwise opening and closing file handles would block
216 * reading potentially cached information. Rather, we defer the wait
217 * to read/write and therefore allow cached access without a wait.
219 * If we have not yet succesfully opened a type of handle, we do wait
220 * here. Also, if a lazy open fails, we revert back to the same
224 psshfs_node_open(struct puffs_usermount
*pu
, puffs_cookie_t opc
, int mode
,
225 const struct puffs_cred
*pcr
)
227 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
228 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
229 struct puffs_framebuf
*pb
, *pb2
;
231 struct puffs_node
*pn
= opc
;
232 struct psshfs_node
*psn
= pn
->pn_data
;
234 int didread
, didwrite
;
237 if (pn
->pn_va
.va_type
== VDIR
)
240 puffs_setback(pcc
, PUFFS_SETBACK_INACT_N1
);
241 puffs_vattr_null(&va
);
242 didread
= didwrite
= 0;
243 if (mode
& FREAD
&& psn
->fhand_r
== NULL
&& psn
->lazyopen_r
== NULL
) {
244 pb
= psbuf_makeout();
246 reqid
= NEXTREQ(pctx
);
247 psbuf_req_str(pb
, SSH_FXP_OPEN
, reqid
, PNPATH(pn
));
248 psbuf_put_4(pb
, SSH_FXF_READ
);
249 psbuf_put_vattr(pb
, &va
, pctx
);
251 if (puffs_framev_enqueue_cb(pu
, pctx
->sshfd_data
, pb
,
252 lazyopen_rresp
, psn
, 0) == -1) {
254 puffs_framebuf_destroy(pb
);
258 psn
->lazyopen_r
= pb
;
261 if (mode
& FWRITE
&& psn
->fhand_w
== NULL
&& psn
->lazyopen_w
== NULL
) {
262 pb2
= psbuf_makeout();
264 reqid
= NEXTREQ(pctx
);
265 psbuf_req_str(pb2
, SSH_FXP_OPEN
, reqid
, PNPATH(pn
));
266 psbuf_put_4(pb2
, SSH_FXF_WRITE
);
267 psbuf_put_vattr(pb2
, &va
, pctx
);
269 if (puffs_framev_enqueue_cb(pu
, pctx
->sshfd_data
, pb2
,
270 lazyopen_wresp
, psn
, 0) == -1) {
272 puffs_framebuf_destroy(pb2
);
276 psn
->lazyopen_w
= pb2
;
279 psn
->stat
&= ~PSN_HANDLECLOSE
;
283 if (didread
&& (psn
->stat
& PSN_DOLAZY_R
) == 0) {
284 assert(psn
->lazyopen_r
);
286 rv
= puffs_framev_framebuf_ccpromote(psn
->lazyopen_r
, pcc
);
287 lazyopen_rresp(pu
, psn
->lazyopen_r
, psn
, rv
);
289 psn
->stat
|= PSN_DOLAZY_R
;
291 if (psn
->lazyopen_err_r
)
292 return psn
->lazyopen_err_r
;
298 if (didwrite
&& (psn
->stat
& PSN_DOLAZY_W
) == 0) {
299 assert(psn
->lazyopen_w
);
301 rv
= puffs_framev_framebuf_ccpromote(psn
->lazyopen_w
, pcc
);
302 lazyopen_wresp(pu
, psn
->lazyopen_w
, psn
, rv
);
304 psn
->stat
|= PSN_DOLAZY_W
;
306 if (psn
->lazyopen_err_w
)
307 return psn
->lazyopen_err_w
;
316 psshfs_node_inactive(struct puffs_usermount
*pu
, puffs_cookie_t opc
)
318 struct puffs_node
*pn
= opc
;
320 closehandles(pu
, pn
->pn_data
, HANDLE_READ
| HANDLE_WRITE
);
325 psshfs_node_readdir(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
326 struct dirent
*dent
, off_t
*readoff
, size_t *reslen
,
327 const struct puffs_cred
*pcr
, int *eofflag
,
328 off_t
*cookies
, size_t *ncookies
)
330 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
331 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
332 struct puffs_node
*pn
= opc
;
333 struct psshfs_node
*psn
= pn
->pn_data
;
334 struct psshfs_dir
*pd
;
339 if (psn
->stat
& PSN_READDIR
) {
340 struct psshfs_wait pw
;
344 pw
.pw_type
= PWTYPE_READDIR
;
345 TAILQ_INSERT_TAIL(&psn
->pw
, &pw
, pw_entries
);
349 psn
->stat
|= PSN_READDIR
;
354 rv
= sftp_readdir(pu
, pctx
, pn
);
358 /* find next dirent */
359 for (i
= *readoff
;;i
++) {
360 if (i
>= psn
->dentnext
)
369 if (!puffs_nextdent(&dent
, pd
->entryname
,
370 pd
->va
.va_fileid
, puffs_vtype2dt(pd
->va
.va_type
), reslen
)) {
375 /* find next entry, store possible nfs key */
377 if (++i
>= psn
->dentnext
)
380 } while (pd
->valid
== 0);
381 PUFFS_STORE_DCOOKIE(cookies
, ncookies
, (off_t
)i
);
386 if (i
>= psn
->dentnext
)
393 struct psshfs_wait
*pw
;
395 /* all will likely run to completion because of cache */
396 TAILQ_FOREACH(pw
, &psn
->pw
, pw_entries
) {
397 assert(pw
->pw_type
== PWTYPE_READDIR
);
398 puffs_cc_schedule(pw
->pw_cc
);
399 TAILQ_REMOVE(&psn
->pw
, pw
, pw_entries
);
402 psn
->stat
&= ~PSN_READDIR
;
409 psshfs_node_read(struct puffs_usermount
*pu
, puffs_cookie_t opc
, uint8_t *buf
,
410 off_t offset
, size_t *resid
, const struct puffs_cred
*pcr
,
414 struct puffs_node
*pn
= opc
;
415 struct psshfs_node
*psn
= pn
->pn_data
;
416 struct psshfs_wait
*pwp
;
419 if (pn
->pn_va
.va_type
== VDIR
) {
424 /* check that a lazyopen didn't fail */
425 if (!psn
->fhand_r
&& !psn
->lazyopen_r
) {
426 rv
= psn
->lazyopen_err_r
;
430 /* if someone is already waiting for the lazyopen, "just" wait */
431 if (psn
->stat
& PSN_LAZYWAIT_R
) {
432 struct psshfs_wait pw
;
434 assert(psn
->lazyopen_r
);
437 pw
.pw_type
= PWTYPE_READ1
;
438 TAILQ_INSERT_TAIL(&psn
->pw
, &pw
, pw_entries
);
442 /* if lazyopening, wait for the result */
443 if (psn
->lazyopen_r
) {
444 psn
->stat
|= PSN_LAZYWAIT_R
;
445 rv
= puffs_framev_framebuf_ccpromote(psn
->lazyopen_r
, pcc
);
446 lazyopen_rresp(pu
, psn
->lazyopen_r
, psn
, rv
);
448 /* schedule extra waiters */
449 TAILQ_FOREACH(pwp
, &psn
->pw
, pw_entries
)
450 if (pwp
->pw_type
== PWTYPE_READ1
) {
451 puffs_cc_schedule(pwp
->pw_cc
);
452 TAILQ_REMOVE(&psn
->pw
, pwp
, pw_entries
);
454 psn
->stat
&= ~PSN_LAZYWAIT_R
;
456 if ((rv
= psn
->lazyopen_err_r
) != 0)
460 /* if there is still no handle, just refuse to live with this */
467 psbuf_req_data(pb
, SSH_FXP_READ
, reqid
, psn
->fhand_r
, psn
->fhand_r_len
);
468 psbuf_put_8(pb
, offset
);
469 psbuf_put_4(pb
, readlen
);
472 * Do this *after* accessing the file, the handle might not
473 * exist after blocking.
475 if (max_reads
&& ++psn
->readcount
> max_reads
) {
476 struct psshfs_wait pw
;
479 pw
.pw_type
= PWTYPE_READ2
;
480 TAILQ_INSERT_TAIL(&psn
->pw
, &pw
, pw_entries
);
484 GETRESPONSE(pb
, pctx
->sshfd_data
);
486 rv
= psbuf_do_data(pb
, buf
, &readlen
);
491 if (max_reads
&& --psn
->readcount
>= max_reads
) {
492 TAILQ_FOREACH(pwp
, &psn
->pw
, pw_entries
)
493 if (pwp
->pw_type
== PWTYPE_READ2
)
496 puffs_cc_schedule(pwp
->pw_cc
);
497 TAILQ_REMOVE(&psn
->pw
, pwp
, pw_entries
);
501 /* check if we need a lazyclose */
502 if (psn
->stat
& PSN_HANDLECLOSE
&& psn
->fhand_r
) {
503 TAILQ_FOREACH(pwp
, &psn
->pw
, pw_entries
)
504 if (pwp
->pw_type
== PWTYPE_READ1
)
507 closehandles(pu
, psn
, HANDLE_READ
);
512 /* XXX: we should getattr for size */
514 psshfs_node_write(struct puffs_usermount
*pu
, puffs_cookie_t opc
, uint8_t *buf
,
515 off_t offset
, size_t *resid
, const struct puffs_cred
*cred
,
519 struct psshfs_wait
*pwp
;
520 struct puffs_node
*pn
= opc
;
521 struct psshfs_node
*psn
= pn
->pn_data
;
524 if (pn
->pn_va
.va_type
== VDIR
) {
529 /* check that a lazyopen didn't fail */
530 if (!psn
->fhand_w
&& !psn
->lazyopen_w
) {
531 rv
= psn
->lazyopen_err_w
;
535 if (psn
->stat
& PSN_LAZYWAIT_W
) {
536 struct psshfs_wait pw
;
538 assert(psn
->lazyopen_w
);
541 pw
.pw_type
= PWTYPE_WRITE
;
542 TAILQ_INSERT_TAIL(&psn
->pw
, &pw
, pw_entries
);
547 * If lazyopening, wait for the result.
548 * There can still be more than oen writer at a time in case
549 * the kernel issues write FAFs.
551 if (psn
->lazyopen_w
) {
552 psn
->stat
|= PSN_LAZYWAIT_W
;
553 rv
= puffs_framev_framebuf_ccpromote(psn
->lazyopen_w
, pcc
);
554 lazyopen_wresp(pu
, psn
->lazyopen_w
, psn
, rv
);
556 /* schedule extra waiters */
557 TAILQ_FOREACH(pwp
, &psn
->pw
, pw_entries
)
558 if (pwp
->pw_type
== PWTYPE_WRITE
) {
559 puffs_cc_schedule(pwp
->pw_cc
);
560 TAILQ_REMOVE(&psn
->pw
, pwp
, pw_entries
);
562 psn
->stat
&= ~PSN_LAZYWAIT_W
;
564 if ((rv
= psn
->lazyopen_err_w
) != 0)
575 psbuf_req_data(pb
, SSH_FXP_WRITE
, reqid
, psn
->fhand_w
,psn
->fhand_w_len
);
576 psbuf_put_8(pb
, offset
);
577 psbuf_put_data(pb
, buf
, writelen
);
578 GETRESPONSE(pb
, pctx
->sshfd_data
);
580 rv
= psbuf_expect_status(pb
);
584 if (pn
->pn_va
.va_size
< (uint64_t)offset
+ writelen
)
585 pn
->pn_va
.va_size
= offset
+ writelen
;
588 /* check if we need a lazyclose */
589 if (psn
->stat
& PSN_HANDLECLOSE
&& psn
->fhand_w
) {
590 TAILQ_FOREACH(pwp
, &psn
->pw
, pw_entries
)
591 if (pwp
->pw_type
== PWTYPE_WRITE
)
594 closehandles(pu
, psn
, HANDLE_WRITE
);
600 psshfs_node_readlink(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
601 const struct puffs_cred
*cred
, char *linkvalue
, size_t *linklen
)
604 struct puffs_node
*pn
= opc
;
605 struct psshfs_node
*psn
= pn
->pn_data
;
608 if (pctx
->protover
< 3) {
614 * check if we can use a cached version
616 * XXX: we might end up reading the same link multiple times
617 * from the server if we get many requests at once, but that's
618 * quite harmless as this routine is reentrant.
620 if (psn
->symlink
&& !REFRESHTIMEOUT(pctx
, time(NULL
) - psn
->slread
))
629 psbuf_req_str(pb
, SSH_FXP_READLINK
, reqid
, PNPATH(pn
));
630 GETRESPONSE(pb
, pctx
->sshfd
);
632 rv
= psbuf_expect_name(pb
, &count
);
640 rv
= psbuf_get_str(pb
, &psn
->symlink
, NULL
);
643 psn
->slread
= time(NULL
);
646 *linklen
= strlen(psn
->symlink
);
647 (void) memcpy(linkvalue
, psn
->symlink
, *linklen
);
654 doremove(struct puffs_usermount
*pu
, struct puffs_node
*pn_dir
,
655 struct puffs_node
*pn
, const char *name
)
660 if (pn
->pn_va
.va_type
== VDIR
)
665 psbuf_req_str(pb
, op
, reqid
, PNPATH(pn
));
666 GETRESPONSE(pb
, pctx
->sshfd
);
668 rv
= psbuf_expect_status(pb
);
670 nukenode(pn
, name
, 0);
677 psshfs_node_remove(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
678 puffs_cookie_t targ
, const struct puffs_cn
*pcn
)
680 struct puffs_node
*pn_targ
= targ
;
683 assert(pn_targ
->pn_va
.va_type
!= VDIR
);
685 rv
= doremove(pu
, opc
, targ
, pcn
->pcn_name
);
687 puffs_setback(puffs_cc_getcc(pu
), PUFFS_SETBACK_NOREF_N2
);
693 psshfs_node_rmdir(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
694 puffs_cookie_t targ
, const struct puffs_cn
*pcn
)
696 struct puffs_node
*pn_targ
= targ
;
699 assert(pn_targ
->pn_va
.va_type
== VDIR
);
701 rv
= doremove(pu
, opc
, targ
, pcn
->pcn_name
);
703 puffs_setback(puffs_cc_getcc(pu
), PUFFS_SETBACK_NOREF_N2
);
709 psshfs_node_mkdir(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
710 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
711 const struct vattr
*va
)
714 struct puffs_node
*pn
= opc
;
715 struct puffs_node
*pn_new
;
717 psbuf_req_str(pb
, SSH_FXP_MKDIR
, reqid
, PCNPATH(pcn
));
718 psbuf_put_vattr(pb
, va
, pctx
);
719 GETRESPONSE(pb
, pctx
->sshfd
);
721 rv
= psbuf_expect_status(pb
);
725 pn_new
= allocnode(pu
, pn
, pcn
->pcn_name
, va
);
727 puffs_newinfo_setcookie(pni
, pn_new
);
729 struct puffs_framebuf
*pb2
= psbuf_makeout();
730 reqid
= NEXTREQ(pctx
);
731 psbuf_recycleout(pb2
);
732 psbuf_req_str(pb2
, SSH_FXP_RMDIR
, reqid
, PCNPATH(pcn
));
733 JUSTSEND(pb2
, pctx
->sshfd
);
742 psshfs_node_symlink(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
743 struct puffs_newinfo
*pni
, const struct puffs_cn
*pcn
,
744 const struct vattr
*va
, const char *link_target
)
747 struct puffs_node
*pn
= opc
;
748 struct puffs_node
*pn_new
;
750 if (pctx
->protover
< 3) {
756 * XXX: ietf says: source, target. openssh says: ietf who?
757 * Let's go with openssh and build quirk tables later if we care
759 psbuf_req_str(pb
, SSH_FXP_SYMLINK
, reqid
, link_target
);
760 psbuf_put_str(pb
, PCNPATH(pcn
));
761 GETRESPONSE(pb
, pctx
->sshfd
);
763 rv
= psbuf_expect_status(pb
);
767 pn_new
= allocnode(pu
, pn
, pcn
->pcn_name
, va
);
769 puffs_newinfo_setcookie(pni
, pn_new
);
771 struct puffs_framebuf
*pb2
= psbuf_makeout();
772 reqid
= NEXTREQ(pctx
);
773 psbuf_recycleout(pb2
);
774 psbuf_req_str(pb2
, SSH_FXP_REMOVE
, reqid
, PCNPATH(pcn
));
775 JUSTSEND(pb2
, pctx
->sshfd
);
784 psshfs_node_rename(struct puffs_usermount
*pu
, puffs_cookie_t opc
,
785 puffs_cookie_t src
, const struct puffs_cn
*pcn_src
,
786 puffs_cookie_t targ_dir
, puffs_cookie_t targ
,
787 const struct puffs_cn
*pcn_targ
)
790 struct puffs_node
*pn_sf
= src
;
791 struct puffs_node
*pn_td
= targ_dir
, *pn_tf
= targ
;
792 struct psshfs_node
*psn_src
= pn_sf
->pn_data
;
793 struct psshfs_node
*psn_targdir
= pn_td
->pn_data
;
795 if (pctx
->protover
< 2) {
801 rv
= doremove(pu
, targ_dir
, pn_tf
, pcn_targ
->pcn_name
);
806 psbuf_req_str(pb
, SSH_FXP_RENAME
, reqid
, PCNPATH(pcn_src
));
807 psbuf_put_str(pb
, PCNPATH(pcn_targ
));
808 GETRESPONSE(pb
, pctx
->sshfd
);
810 rv
= psbuf_expect_status(pb
);
812 struct psshfs_dir
*pd
;
815 * XXX: interfaces didn't quite work with rename..
816 * the song remains the same. go figure .. ;)
818 nukenode(pn_sf
, pcn_src
->pcn_name
, 0);
819 pd
= direnter(pn_td
, pcn_targ
->pcn_name
);
821 puffs_setvattr(&pd
->va
, &pn_sf
->pn_va
);
823 if (opc
!= targ_dir
) {
824 psn_targdir
->childcount
++;
825 psn_src
->parent
= pn_td
;
826 if (pn_sf
->pn_va
.va_type
== VDIR
)
827 pn_td
->pn_va
.va_nlink
++;
836 * So this file system happened to be written in such a way that
837 * lookup for ".." is hard if we lose the in-memory node. We'd
838 * need to recreate the entire directory structure from the root
839 * node up to the ".." node we're looking up.
841 * And since our entire fs structure is purely fictional (i.e. it's
842 * only in-memory, not fetchable from the server), the easiest way
843 * to deal with it is to not allow nodes with children to be
846 * If a node with children is being attempted to be reclaimed, we
847 * just mark it "reclaimed" but leave it as is until all its children
848 * have been reclaimed. If a lookup for that node is done meanwhile,
849 * it will be found by lookup() and we just remove the "reclaimed"
853 psshfs_node_reclaim(struct puffs_usermount
*pu
, puffs_cookie_t opc
)
855 struct puffs_node
*pn
= opc
, *pn_next
, *pn_root
;
856 struct psshfs_node
*psn
= pn
->pn_data
;
859 * don't reclaim if we have file handle issued, otherwise
860 * we can't do fhtonode
862 if (psn
->stat
& PSN_HASFH
)
865 psn
->stat
|= PSN_RECLAIMED
;
866 pn_root
= puffs_getroot(pu
);
867 for (; pn
!= pn_root
; pn
= pn_next
) {
869 if ((psn
->stat
& PSN_RECLAIMED
) == 0 || psn
->childcount
!= 0)
872 pn_next
= psn
->parent
;