1 /* $NetBSD: subr.c,v 1.50 2010/04/01 02:34:09 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
36 #include "sftp_proto.h"
37 #include "util_compat.h"
40 freedircache(struct psshfs_dir *base, size_t count)
44 for (i = 0; i < count; i++) {
45 free(base[i].entryname);
46 base[i].entryname = NULL;
54 allocdirs(struct psshfs_node *psn)
56 size_t oldtot = psn->denttot;
58 psn->denttot += ENTRYCHUNK;
59 psn->dir = erealloc(psn->dir,
60 psn->denttot * sizeof(struct psshfs_dir));
61 memset(psn->dir + oldtot, 0, ENTRYCHUNK * sizeof(struct psshfs_dir));
65 setpnva(struct puffs_usermount *pu, struct puffs_node *pn,
66 const struct vattr *vap)
68 struct psshfs_ctx *pctx = puffs_getspecific(pu);
69 struct psshfs_node *psn = pn->pn_data;
73 * Check if the file was modified from below us.
74 * If so, invalidate page cache. This is the only
75 * sensible place we can do this in.
77 if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL)
78 if (pn->pn_va.va_mtime.tv_sec != vap->va_mtime.tv_sec
79 && pn->pn_va.va_type == VREG)
80 puffs_inval_pagecache_node(pu, pn);
83 if (pctx->domangleuid && modva.va_uid == pctx->mangleuid)
84 modva.va_uid = pctx->myuid;
85 if (pctx->domanglegid && modva.va_gid == pctx->manglegid)
86 modva.va_gid = pctx->mygid;
88 puffs_setvattr(&pn->pn_va, &modva);
89 psn->attrread = time(NULL);
93 lookup(struct psshfs_dir *bdir, size_t ndir, const char *name)
95 struct psshfs_dir *test;
98 for (i = 0; i < ndir; i++) {
100 if (test->valid != 1)
102 if (strcmp(test->entryname, name) == 0)
109 static struct psshfs_dir *
110 lookup_by_entry(struct psshfs_dir *bdir, size_t ndir, struct puffs_node *entry)
112 struct psshfs_dir *test;
115 for (i = 0; i < ndir; i++) {
117 if (test->valid != 1)
119 if (test->entry == entry)
128 closehandles(struct puffs_usermount *pu, struct psshfs_node *psn, int which)
130 struct psshfs_ctx *pctx = puffs_getspecific(pu);
131 struct puffs_framebuf *pb1, *pb2;
134 if (psn->fhand_r && (which & HANDLE_READ)) {
135 assert(psn->lazyopen_r == NULL);
137 pb1 = psbuf_makeout();
138 reqid = NEXTREQ(pctx);
139 psbuf_req_data(pb1, SSH_FXP_CLOSE, reqid,
140 psn->fhand_r, psn->fhand_r_len);
141 puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb1, 1, 0);
146 if (psn->fhand_w && (which & HANDLE_WRITE)) {
147 assert(psn->lazyopen_w == NULL);
149 pb2 = psbuf_makeout();
150 reqid = NEXTREQ(pctx);
151 psbuf_req_data(pb2, SSH_FXP_CLOSE, reqid,
152 psn->fhand_w, psn->fhand_w_len);
153 puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb2, 1, 0);
158 psn->stat |= PSN_HANDLECLOSE;
162 lazyopen_rresp(struct puffs_usermount *pu, struct puffs_framebuf *pb,
163 void *arg, int error)
165 struct psshfs_node *psn = arg;
167 /* XXX: this is not enough */
168 if (psn->stat & PSN_RECLAIMED) {
175 error = psbuf_expect_handle(pb, &psn->fhand_r, &psn->fhand_r_len);
178 psn->lazyopen_err_r = error;
179 psn->lazyopen_r = NULL;
181 psn->stat &= ~PSN_DOLAZY_R;
182 if (psn->stat & PSN_HANDLECLOSE && (psn->stat & PSN_LAZYWAIT_R) == 0)
183 closehandles(pu, psn, HANDLE_READ);
185 puffs_framebuf_destroy(pb);
189 lazyopen_wresp(struct puffs_usermount *pu, struct puffs_framebuf *pb,
190 void *arg, int error)
192 struct psshfs_node *psn = arg;
194 /* XXX: this is not enough */
195 if (psn->stat & PSN_RECLAIMED) {
202 error = psbuf_expect_handle(pb, &psn->fhand_w, &psn->fhand_w_len);
205 psn->lazyopen_err_w = error;
206 psn->lazyopen_w = NULL;
208 psn->stat &= ~PSN_DOLAZY_W;
209 if (psn->stat & PSN_HANDLECLOSE && (psn->stat & PSN_LAZYWAIT_W) == 0)
210 closehandles(pu, psn, HANDLE_WRITE);
212 puffs_framebuf_destroy(pb);
216 struct psshfs_node *psn;
218 char entryname[MAXPATHLEN+1];
222 getpathattr(struct puffs_usermount *pu, const char *path, struct vattr *vap)
226 psbuf_req_str(pb, SSH_FXP_LSTAT, reqid, path);
227 GETRESPONSE(pb, pctx->sshfd);
229 rv = psbuf_expect_attrs(pb, vap);
236 getnodeattr(struct puffs_usermount *pu, struct puffs_node *pn, const char *path)
238 struct psshfs_ctx *pctx = puffs_getspecific(pu);
239 struct psshfs_node *psn = pn->pn_data;
243 if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) {
244 rv = getpathattr(pu, path ? path : PNPATH(pn), &va);
248 setpnva(pu, pn, &va);
255 sftp_readdir(struct puffs_usermount *pu, struct psshfs_ctx *pctx,
256 struct puffs_node *pn)
258 struct puffs_cc *pcc = puffs_cc_getcc(pu);
259 struct psshfs_node *psn = pn->pn_data;
260 struct psshfs_dir *olddir, *testd;
261 struct puffs_framebuf *pb;
262 uint32_t reqid = NEXTREQ(pctx);
263 uint32_t count, dhandlen;
267 char *longname = NULL;
271 assert(pn->pn_va.va_type == VDIR);
274 nent = psn->dentnext;
276 if (psn->dir && psn->dentread
277 && !REFRESHTIMEOUT(pctx, time(NULL) - psn->dentread))
281 if ((rv = puffs_inval_namecache_dir(pu, pn)))
282 warn("readdir: dcache inval fail %p", pn);
285 pb = psbuf_makeout();
286 psbuf_req_str(pb, SSH_FXP_OPENDIR, reqid, PNPATH(pn));
287 if (puffs_framev_enqueue_cc(pcc, pctx->sshfd, pb, 0) == -1) {
291 rv = psbuf_expect_handle(pb, &dhand, &dhandlen);
296 * Well, the following is O(n^2), so feel free to improve if it
297 * gets too taxing on your system.
301 * note: for the "getattr in batch" to work, this must be before
302 * the attribute-getting. Otherwise times for first entries in
303 * large directories might expire before the directory itself and
304 * result in one-by-one attribute fetching.
306 psn->dentread = time(NULL);
313 reqid = NEXTREQ(pctx);
314 psbuf_recycleout(pb);
315 psbuf_req_data(pb, SSH_FXP_READDIR, reqid, dhand, dhandlen);
316 GETRESPONSE(pb, pctx->sshfd);
319 if (psbuf_get_type(pb) == SSH_FXP_STATUS) {
320 rv = psbuf_expect_status(pb);
323 rv = psbuf_expect_name(pb, &count);
327 for (; count--; idx++) {
328 if (idx == psn->denttot)
330 if ((rv = psbuf_get_str(pb,
331 &psn->dir[idx].entryname, NULL)))
333 if ((rv = psbuf_get_str(pb, &longname, NULL)) != 0)
335 if ((rv = psbuf_get_vattr(pb, &psn->dir[idx].va)) != 0)
337 if (sscanf(longname, "%*s%d",
342 psn->dir[idx].va.va_nlink = tmpval;
347 * In case of DOT, copy the attributes (mostly
348 * because we want the link count for the root dir).
350 if (strcmp(psn->dir[idx].entryname, ".") == 0) {
351 setpnva(pu, pn, &psn->dir[idx].va);
355 * Check if we already have a psshfs_dir for the
356 * name we are processing. If so, use the old one.
357 * If not, create a new one
359 testd = lookup(olddir, nent, psn->dir[idx].entryname);
361 psn->dir[idx].entry = testd->entry;
363 * Has entry. Update attributes to what
364 * we just got from the server.
367 setpnva(pu, testd->entry,
369 psn->dir[idx].va.va_fileid
370 = testd->entry->pn_va.va_fileid;
373 * No entry. This can happen in two cases:
374 * 1) the file was created "behind our back"
376 * 2) we do two readdirs before we instantiate
377 * the node (or run with -t 0).
379 * Cache attributes from the server in
380 * case we want to instantiate this node
381 * soon. Also preserve the old inode number
382 * which was given when the dirent was created.
385 psn->dir[idx].va.va_fileid
386 = testd->va.va_fileid;
387 testd->va = psn->dir[idx].va;
390 /* No previous entry? Initialize this one. */
392 psn->dir[idx].entry = NULL;
393 psn->dir[idx].va.va_fileid = pctx->nextino++;
395 psn->dir[idx].attrread = psn->dentread;
396 psn->dir[idx].valid = 1;
403 freedircache(olddir, nent);
405 reqid = NEXTREQ(pctx);
406 psbuf_recycleout(pb);
407 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, dhand, dhandlen);
408 puffs_framev_enqueue_justsend(pu, pctx->sshfd, pb, 1, 0);
420 makenode(struct puffs_usermount *pu, struct puffs_node *parent,
421 const struct psshfs_dir *pd, const struct vattr *vap)
423 struct psshfs_node *psn_parent = parent->pn_data;
424 struct psshfs_node *psn;
425 struct puffs_node *pn;
427 psn = emalloc(sizeof(struct psshfs_node));
428 memset(psn, 0, sizeof(struct psshfs_node));
430 pn = puffs_pn_new(pu, psn);
435 setpnva(pu, pn, &pd->va);
436 setpnva(pu, pn, vap);
437 psn->attrread = pd->attrread;
439 psn->parent = parent;
440 psn_parent->childcount++;
442 TAILQ_INIT(&psn->pw);
448 allocnode(struct puffs_usermount *pu, struct puffs_node *parent,
449 const char *entryname, const struct vattr *vap)
451 struct psshfs_ctx *pctx = puffs_getspecific(pu);
452 struct psshfs_dir *pd;
453 struct puffs_node *pn;
455 pd = direnter(parent, entryname);
457 pd->va.va_fileid = pctx->nextino++;
458 if (vap->va_type == VDIR) {
460 parent->pn_va.va_nlink++;
465 pn = makenode(pu, parent, pd, vap);
467 pd->va.va_fileid = pn->pn_va.va_fileid;
475 direnter(struct puffs_node *parent, const char *entryname)
477 struct psshfs_node *psn_parent = parent->pn_data;
478 struct psshfs_dir *pd;
481 /* create directory entry */
482 if (psn_parent->denttot == psn_parent->dentnext)
483 allocdirs(psn_parent);
485 i = psn_parent->dentnext;
486 pd = &psn_parent->dir[i];
487 pd->entryname = estrdup(entryname);
490 puffs_vattr_null(&pd->va);
491 psn_parent->dentnext++;
497 doreclaim(struct puffs_node *pn)
499 struct psshfs_node *psn = pn->pn_data;
500 struct psshfs_node *psn_parent;
501 struct psshfs_dir *dent;
503 psn_parent = psn->parent->pn_data;
504 psn_parent->childcount--;
507 * Null out entry from directory. Do not treat a missing entry
508 * as an invariant error, since the node might be removed from
509 * under us, and we might do a readdir before the reclaim resulting
510 * in no directory entry in the parent directory.
512 dent = lookup_by_entry(psn_parent->dir, psn_parent->dentnext, pn);
516 if (pn->pn_va.va_type == VDIR) {
517 freedircache(psn->dir, psn->dentnext);
518 psn->denttot = psn->dentnext = 0;
527 nukenode(struct puffs_node *node, const char *entryname, int reclaim)
529 struct psshfs_node *psn, *psn_parent;
530 struct psshfs_dir *pd;
533 psn_parent = psn->parent->pn_data;
534 pd = lookup(psn_parent->dir, psn_parent->dentnext, entryname);
538 pd->entryname = NULL;
540 if (node->pn_va.va_type == VDIR)
541 psn->parent->pn_va.va_nlink--;