subr.c revision 1.3
1/* $NetBSD: subr.c,v 1.3 2007/01/07 21:59:27 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 * 3. The name of the company nor the name of the author may be used to 15 * endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32#ifndef lint 33__RCSID("$NetBSD: subr.c,v 1.3 2007/01/07 21:59:27 pooka Exp $"); 34#endif /* !lint */ 35 36#include <assert.h> 37#include <errno.h> 38#include <puffs.h> 39#include <stdlib.h> 40#include <util.h> 41 42#include "psshfs.h" 43#include "sftp_proto.h" 44 45static void 46freedircache(struct psshfs_dir *base, size_t count) 47{ 48 int i; 49 50 for (i = 0; i < count; i++) { 51 free(base[i].entryname); 52 base[i].entryname = NULL; 53 } 54 55 free(base); 56} 57 58#define ENTRYCHUNK 16 59static void 60allocdirs(struct psshfs_node *psn) 61{ 62 size_t oldtot = psn->denttot; 63 64 psn->denttot += ENTRYCHUNK; 65 psn->dir = erealloc(psn->dir, 66 psn->denttot * sizeof(struct psshfs_dir)); 67 memset(psn->dir + oldtot, 0, ENTRYCHUNK * sizeof(struct psshfs_dir)); 68} 69 70struct psshfs_dir * 71lookup(struct psshfs_dir *basedir, size_t ndir, const char *name) 72{ 73 struct psshfs_dir *ent; 74 int i; 75 76 for (i = 0; i < ndir; i++) { 77 ent = &basedir[i]; 78 if (ent->valid != 1) 79 continue; 80 if (strcmp(ent->entryname, name) == 0) 81 return ent; 82 } 83 84 return NULL; 85} 86 87int 88sftp_readdir(struct puffs_cc *pcc, struct psshfs_ctx *pctx, 89 struct puffs_node *pn) 90{ 91 struct psshfs_node *psn = pn->pn_data; 92 struct psshfs_dir *olddir, *testd; 93 struct psbuf *pb; 94 uint32_t reqid = NEXTREQ(pctx); 95 uint32_t count; 96 char *dhand = NULL; 97 size_t dhandlen, nent; 98 char *longname; 99 int idx, rv; 100 101 assert(pn->pn_va.va_type == VDIR); 102 103 if (psn->dir && (time(NULL) - psn->dentread) < PSSHFS_REFRESHIVAL) 104 return 0; 105 106 pb = psbuf_make(PSB_OUT); 107 psbuf_req_str(pb, SSH_FXP_OPENDIR, reqid, pn->pn_path); 108 pssh_outbuf_enqueue(pctx, pb, pcc, reqid); 109 110 puffs_cc_yield(pcc); 111 112 rv = psbuf_expect_handle(pb, &dhand, &dhandlen); 113 if (rv) 114 goto wayout; 115 116 /* 117 * Well, the following is O(n^2), so feel free to improve if it 118 * gets too taxing on your system. 119 */ 120 olddir = psn->dir; 121 nent = psn->dentnext; 122 123 psn->dentnext = 0; 124 psn->denttot = 0; 125 psn->dir = NULL; 126 idx = 0; 127 128 for (;;) { 129 reqid = NEXTREQ(pctx); 130 psbuf_recycle(pb, PSB_OUT); 131 psbuf_req_data(pb, SSH_FXP_READDIR, reqid, dhand, dhandlen); 132 pssh_outbuf_enqueue(pctx, pb, pcc, reqid); 133 134 puffs_cc_yield(pcc); 135 136 /* check for EOF */ 137 if (pb->type == SSH_FXP_STATUS) { 138 rv = psbuf_expect_status(pb); 139 goto out; 140 } 141 rv = psbuf_expect_name(pb, &count); 142 if (rv) 143 goto out; 144 145 for (; count--; idx++) { 146 if (idx == psn->denttot) 147 allocdirs(psn); 148 if (!psbuf_get_str(pb, &psn->dir[idx].entryname, 149 NULL)) { 150 rv = EPROTO; 151 goto out; 152 } 153 if (!psbuf_get_str(pb, &longname, NULL)) { 154 rv = EPROTO; 155 goto out; 156 } 157 if (!psbuf_get_vattr(pb, &psn->dir[idx].va)) { 158 rv = EPROTO; 159 goto out; 160 } 161 if (sscanf(longname, "%*s%d", 162 &psn->dir[idx].va.va_nlink) != 1) { 163 rv = EPROTO; 164 goto out; 165 } 166 free(longname); 167 168 testd = lookup(olddir, nent, psn->dir[idx].entryname); 169 if (testd) { 170 psn->dir[idx].entry = testd->entry; 171 psn->dir[idx].va.va_fileid 172 = testd->va.va_fileid; 173 } else { 174 psn->dir[idx].entry = NULL; 175 psn->dir[idx].va.va_fileid = pctx->nextino++; 176 } 177 psn->dir[idx].valid = 1; 178 } 179 } 180 181 out: 182 /* XXX: rv */ 183 psn->dentnext = idx; 184 psn->dentread = time(NULL); 185 freedircache(olddir, nent); 186 187 reqid = NEXTREQ(pctx); 188 psbuf_recycle(pb, PSB_OUT); 189 psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, dhand, dhandlen); 190 pssh_outbuf_enqueue(pctx, pb, pcc, reqid); 191 192 puffs_cc_yield(pcc); 193 194 /* EDONTCARE for the response */ 195 196 wayout: 197 free(dhand); 198 psbuf_destroy(pb); 199 return rv; 200} 201 202struct puffs_node * 203makenode(struct puffs_usermount *pu, struct puffs_node *parent, 204 struct psshfs_dir *pd, const struct vattr *vap) 205{ 206 struct psshfs_node *psn_parent = parent->pn_data; 207 struct psshfs_node *psn; 208 struct puffs_node *pn; 209 210 psn = emalloc(sizeof(struct psshfs_node)); 211 memset(psn, 0, sizeof(struct psshfs_node)); 212 213 pn = puffs_pn_new(pu, psn); 214 if (!pn) { 215 free(psn); 216 return NULL; 217 } 218 puffs_setvattr(&pn->pn_va, &pd->va); 219 puffs_setvattr(&pn->pn_va, vap); 220 221 pd->entry = pn; 222 psn->parent = parent; 223 psn_parent->childcount++; 224 225 return pn; 226} 227 228struct puffs_node * 229allocnode(struct puffs_usermount *pu, struct puffs_node *parent, 230 const char *entryname, const struct vattr *vap) 231{ 232 struct psshfs_ctx *pctx = pu->pu_privdata; 233 struct psshfs_dir *pd; 234 struct puffs_node *pn; 235 236 pd = direnter(parent, entryname); 237 238 pd->va.va_fileid = pctx->nextino++; 239 if (vap->va_type == VDIR) { 240 pd->va.va_nlink = 2; 241 parent->pn_va.va_nlink++; 242 } else { 243 pd->va.va_nlink = 1; 244 } 245 246 pn = makenode(pu, parent, pd, vap); 247 if (pn) 248 pd->va.va_fileid = pn->pn_va.va_fileid; 249 250 return pn; 251} 252 253struct psshfs_dir * 254direnter(struct puffs_node *parent, const char *entryname) 255{ 256 struct psshfs_node *psn_parent = parent->pn_data; 257 struct psshfs_dir *pd; 258 int i; 259 260 /* create directory entry */ 261 if (psn_parent->denttot == psn_parent->dentnext) 262 allocdirs(psn_parent); 263 264 i = psn_parent->dentnext; 265 pd = &psn_parent->dir[i]; 266 pd->entryname = estrdup(entryname); 267 pd->valid = 1; 268 psn_parent->dentnext++; 269 270 return pd; 271} 272 273void 274nukenode(struct puffs_node *node, const char *entryname) 275{ 276 struct psshfs_node *psn, *psn_parent; 277 struct psshfs_dir *pd; 278 279 psn = node->pn_data; 280 psn_parent = psn->parent->pn_data; 281 psn_parent->childcount--; 282 pd = lookup(psn_parent->dir, psn_parent->dentnext, entryname); 283 assert(pd != NULL); 284 pd->valid = 0; 285 free(pd->entryname); 286 pd->entryname = NULL; 287 288 if (node->pn_va.va_type == VDIR) { 289 psn->parent->pn_va.va_nlink--; 290 freedircache(psn->dir, psn->dentnext); 291 } 292} 293