1 /* $NetBSD: subr.c,v 1.47 2009/11/05 13:28:20 pooka Exp $ */
4 * Copyright (c) 2006 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: subr.c,v 1.47 2009/11/05 13:28:20 pooka Exp $");
41 #include "sftp_proto.h"
44 freedircache(struct psshfs_dir
*base
, size_t count
)
48 for (i
= 0; i
< count
; i
++) {
49 free(base
[i
].entryname
);
50 base
[i
].entryname
= NULL
;
58 allocdirs(struct psshfs_node
*psn
)
60 size_t oldtot
= psn
->denttot
;
62 psn
->denttot
+= ENTRYCHUNK
;
63 psn
->dir
= erealloc(psn
->dir
,
64 psn
->denttot
* sizeof(struct psshfs_dir
));
65 memset(psn
->dir
+ oldtot
, 0, ENTRYCHUNK
* sizeof(struct psshfs_dir
));
69 setpnva(struct puffs_usermount
*pu
, struct puffs_node
*pn
,
70 const struct vattr
*vap
)
72 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
73 struct psshfs_node
*psn
= pn
->pn_data
;
77 * Check if the file was modified from below us.
78 * If so, invalidate page cache. This is the only
79 * sensible place we can do this in.
81 if (pn
->pn_va
.va_mtime
.tv_sec
!= PUFFS_VNOVAL
)
82 if (pn
->pn_va
.va_mtime
.tv_sec
!= vap
->va_mtime
.tv_sec
83 && pn
->pn_va
.va_type
== VREG
)
84 puffs_inval_pagecache_node(pu
, pn
);
87 if (pctx
->domangleuid
&& modva
.va_uid
== pctx
->mangleuid
)
88 modva
.va_uid
= pctx
->myuid
;
89 if (pctx
->domanglegid
&& modva
.va_gid
== pctx
->manglegid
)
90 modva
.va_gid
= pctx
->mygid
;
92 puffs_setvattr(&pn
->pn_va
, &modva
);
93 psn
->attrread
= time(NULL
);
97 lookup(struct psshfs_dir
*bdir
, size_t ndir
, const char *name
)
99 struct psshfs_dir
*test
;
102 for (i
= 0; i
< ndir
; i
++) {
104 if (test
->valid
!= 1)
106 if (strcmp(test
->entryname
, name
) == 0)
113 static struct psshfs_dir
*
114 lookup_by_entry(struct psshfs_dir
*bdir
, size_t ndir
, struct puffs_node
*entry
)
116 struct psshfs_dir
*test
;
119 for (i
= 0; i
< ndir
; i
++) {
121 if (test
->valid
!= 1)
123 if (test
->entry
== entry
)
132 closehandles(struct puffs_usermount
*pu
, struct psshfs_node
*psn
, int which
)
134 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
135 struct puffs_framebuf
*pb1
, *pb2
;
138 if (psn
->fhand_r
&& (which
& HANDLE_READ
)) {
139 assert(psn
->lazyopen_r
== NULL
);
141 pb1
= psbuf_makeout();
142 reqid
= NEXTREQ(pctx
);
143 psbuf_req_data(pb1
, SSH_FXP_CLOSE
, reqid
,
144 psn
->fhand_r
, psn
->fhand_r_len
);
145 puffs_framev_enqueue_justsend(pu
, pctx
->sshfd_data
, pb1
, 1, 0);
150 if (psn
->fhand_w
&& (which
& HANDLE_WRITE
)) {
151 assert(psn
->lazyopen_w
== NULL
);
153 pb2
= psbuf_makeout();
154 reqid
= NEXTREQ(pctx
);
155 psbuf_req_data(pb2
, SSH_FXP_CLOSE
, reqid
,
156 psn
->fhand_w
, psn
->fhand_w_len
);
157 puffs_framev_enqueue_justsend(pu
, pctx
->sshfd_data
, pb2
, 1, 0);
162 psn
->stat
|= PSN_HANDLECLOSE
;
166 lazyopen_rresp(struct puffs_usermount
*pu
, struct puffs_framebuf
*pb
,
167 void *arg
, int error
)
169 struct psshfs_node
*psn
= arg
;
171 /* XXX: this is not enough */
172 if (psn
->stat
& PSN_RECLAIMED
) {
179 error
= psbuf_expect_handle(pb
, &psn
->fhand_r
, &psn
->fhand_r_len
);
182 psn
->lazyopen_err_r
= error
;
183 psn
->lazyopen_r
= NULL
;
185 psn
->stat
&= ~PSN_DOLAZY_R
;
186 if (psn
->stat
& PSN_HANDLECLOSE
&& (psn
->stat
& PSN_LAZYWAIT_R
) == 0)
187 closehandles(pu
, psn
, HANDLE_READ
);
189 puffs_framebuf_destroy(pb
);
193 lazyopen_wresp(struct puffs_usermount
*pu
, struct puffs_framebuf
*pb
,
194 void *arg
, int error
)
196 struct psshfs_node
*psn
= arg
;
198 /* XXX: this is not enough */
199 if (psn
->stat
& PSN_RECLAIMED
) {
206 error
= psbuf_expect_handle(pb
, &psn
->fhand_w
, &psn
->fhand_w_len
);
209 psn
->lazyopen_err_w
= error
;
210 psn
->lazyopen_w
= NULL
;
212 psn
->stat
&= ~PSN_DOLAZY_W
;
213 if (psn
->stat
& PSN_HANDLECLOSE
&& (psn
->stat
& PSN_LAZYWAIT_W
) == 0)
214 closehandles(pu
, psn
, HANDLE_WRITE
);
216 puffs_framebuf_destroy(pb
);
220 struct psshfs_node
*psn
;
222 char entryname
[MAXPATHLEN
+1];
226 getpathattr(struct puffs_usermount
*pu
, const char *path
, struct vattr
*vap
)
230 psbuf_req_str(pb
, SSH_FXP_LSTAT
, reqid
, path
);
231 GETRESPONSE(pb
, pctx
->sshfd
);
233 rv
= psbuf_expect_attrs(pb
, vap
);
240 getnodeattr(struct puffs_usermount
*pu
, struct puffs_node
*pn
)
242 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
243 struct psshfs_node
*psn
= pn
->pn_data
;
247 if (!psn
->attrread
|| REFRESHTIMEOUT(pctx
, time(NULL
)-psn
->attrread
)) {
248 rv
= getpathattr(pu
, PNPATH(pn
), &va
);
252 setpnva(pu
, pn
, &va
);
259 sftp_readdir(struct puffs_usermount
*pu
, struct psshfs_ctx
*pctx
,
260 struct puffs_node
*pn
)
262 struct puffs_cc
*pcc
= puffs_cc_getcc(pu
);
263 struct psshfs_node
*psn
= pn
->pn_data
;
264 struct psshfs_dir
*olddir
, *testd
;
265 struct puffs_framebuf
*pb
;
266 uint32_t reqid
= NEXTREQ(pctx
);
267 uint32_t count
, dhandlen
;
270 char *longname
= NULL
;
274 assert(pn
->pn_va
.va_type
== VDIR
);
277 nent
= psn
->dentnext
;
279 if (psn
->dir
&& psn
->dentread
280 && !REFRESHTIMEOUT(pctx
, time(NULL
) - psn
->dentread
))
284 if ((rv
= puffs_inval_namecache_dir(pu
, pn
)))
285 warn("readdir: dcache inval fail %p", pn
);
288 pb
= psbuf_makeout();
289 psbuf_req_str(pb
, SSH_FXP_OPENDIR
, reqid
, PNPATH(pn
));
290 if (puffs_framev_enqueue_cc(pcc
, pctx
->sshfd
, pb
, 0) == -1) {
294 rv
= psbuf_expect_handle(pb
, &dhand
, &dhandlen
);
299 * Well, the following is O(n^2), so feel free to improve if it
300 * gets too taxing on your system.
304 * note: for the "getattr in batch" to work, this must be before
305 * the attribute-getting. Otherwise times for first entries in
306 * large directories might expire before the directory itself and
307 * result in one-by-one attribute fetching.
309 psn
->dentread
= time(NULL
);
316 reqid
= NEXTREQ(pctx
);
317 psbuf_recycleout(pb
);
318 psbuf_req_data(pb
, SSH_FXP_READDIR
, reqid
, dhand
, dhandlen
);
319 GETRESPONSE(pb
, pctx
->sshfd
);
322 if (psbuf_get_type(pb
) == SSH_FXP_STATUS
) {
323 rv
= psbuf_expect_status(pb
);
326 rv
= psbuf_expect_name(pb
, &count
);
330 for (; count
--; idx
++) {
331 if (idx
== psn
->denttot
)
333 if ((rv
= psbuf_get_str(pb
,
334 &psn
->dir
[idx
].entryname
, NULL
)))
336 if ((rv
= psbuf_get_str(pb
, &longname
, NULL
)) != 0)
338 if ((rv
= psbuf_get_vattr(pb
, &psn
->dir
[idx
].va
)) != 0)
340 if (sscanf(longname
, "%*s%d",
341 &psn
->dir
[idx
].va
.va_nlink
) != 1) {
349 * Check if we already have a psshfs_dir for the
350 * name we are processing. If so, use the old one.
351 * If not, create a new one
353 testd
= lookup(olddir
, nent
, psn
->dir
[idx
].entryname
);
355 psn
->dir
[idx
].entry
= testd
->entry
;
357 * Has entry. Update attributes to what
358 * we just got from the server.
361 setpnva(pu
, testd
->entry
,
363 psn
->dir
[idx
].va
.va_fileid
364 = testd
->entry
->pn_va
.va_fileid
;
367 * No entry. This can happen in two cases:
368 * 1) the file was created "behind our back"
370 * 2) we do two readdirs before we instantiate
371 * the node (or run with -t 0).
373 * Cache attributes from the server in
374 * case we want to instantiate this node
375 * soon. Also preserve the old inode number
376 * which was given when the dirent was created.
379 psn
->dir
[idx
].va
.va_fileid
380 = testd
->va
.va_fileid
;
381 testd
->va
= psn
->dir
[idx
].va
;
384 /* No previous entry? Initialize this one. */
386 psn
->dir
[idx
].entry
= NULL
;
387 psn
->dir
[idx
].va
.va_fileid
= pctx
->nextino
++;
389 psn
->dir
[idx
].attrread
= psn
->dentread
;
390 psn
->dir
[idx
].valid
= 1;
397 freedircache(olddir
, nent
);
399 reqid
= NEXTREQ(pctx
);
400 psbuf_recycleout(pb
);
401 psbuf_req_data(pb
, SSH_FXP_CLOSE
, reqid
, dhand
, dhandlen
);
402 puffs_framev_enqueue_justsend(pu
, pctx
->sshfd
, pb
, 1, 0);
414 makenode(struct puffs_usermount
*pu
, struct puffs_node
*parent
,
415 struct psshfs_dir
*pd
, const struct vattr
*vap
)
417 struct psshfs_node
*psn_parent
= parent
->pn_data
;
418 struct psshfs_node
*psn
;
419 struct puffs_node
*pn
;
421 psn
= emalloc(sizeof(struct psshfs_node
));
422 memset(psn
, 0, sizeof(struct psshfs_node
));
424 pn
= puffs_pn_new(pu
, psn
);
429 setpnva(pu
, pn
, &pd
->va
);
430 setpnva(pu
, pn
, vap
);
431 psn
->attrread
= pd
->attrread
;
434 psn
->parent
= parent
;
435 psn_parent
->childcount
++;
437 TAILQ_INIT(&psn
->pw
);
443 allocnode(struct puffs_usermount
*pu
, struct puffs_node
*parent
,
444 const char *entryname
, const struct vattr
*vap
)
446 struct psshfs_ctx
*pctx
= puffs_getspecific(pu
);
447 struct psshfs_dir
*pd
;
448 struct puffs_node
*pn
;
450 pd
= direnter(parent
, entryname
);
452 pd
->va
.va_fileid
= pctx
->nextino
++;
453 if (vap
->va_type
== VDIR
) {
455 parent
->pn_va
.va_nlink
++;
460 pn
= makenode(pu
, parent
, pd
, vap
);
462 pd
->va
.va_fileid
= pn
->pn_va
.va_fileid
;
468 direnter(struct puffs_node
*parent
, const char *entryname
)
470 struct psshfs_node
*psn_parent
= parent
->pn_data
;
471 struct psshfs_dir
*pd
;
474 /* create directory entry */
475 if (psn_parent
->denttot
== psn_parent
->dentnext
)
476 allocdirs(psn_parent
);
478 i
= psn_parent
->dentnext
;
479 pd
= &psn_parent
->dir
[i
];
480 pd
->entryname
= estrdup(entryname
);
483 puffs_vattr_null(&pd
->va
);
484 psn_parent
->dentnext
++;
490 doreclaim(struct puffs_node
*pn
)
492 struct psshfs_node
*psn
= pn
->pn_data
;
493 struct psshfs_node
*psn_parent
;
494 struct psshfs_dir
*dent
;
496 psn_parent
= psn
->parent
->pn_data
;
497 psn_parent
->childcount
--;
500 * Null out entry from directory. Do not treat a missing entry
501 * as an invariant error, since the node might be removed from
502 * under us, and we might do a readdir before the reclaim resulting
503 * in no directory entry in the parent directory.
505 dent
= lookup_by_entry(psn_parent
->dir
, psn_parent
->dentnext
, pn
);
509 if (pn
->pn_va
.va_type
== VDIR
) {
510 freedircache(psn
->dir
, psn
->dentnext
);
511 psn
->denttot
= psn
->dentnext
= 0;
520 nukenode(struct puffs_node
*node
, const char *entryname
, int reclaim
)
522 struct psshfs_node
*psn
, *psn_parent
;
523 struct psshfs_dir
*pd
;
526 psn_parent
= psn
->parent
->pn_data
;
527 pd
= lookup(psn_parent
->dir
, psn_parent
->dentnext
, entryname
);
531 pd
->entryname
= NULL
;
533 if (node
->pn_va
.va_type
== VDIR
)
534 psn
->parent
->pn_va
.va_nlink
--;