1 1.66 andvar /* $NetBSD: node.c,v 1.66 2021/09/16 21:29:42 andvar Exp $ */ 2 1.1 pooka 3 1.1 pooka /* 4 1.58 pooka * Copyright (c) 2006-2009 Antti Kantee. All Rights Reserved. 5 1.1 pooka * 6 1.1 pooka * Redistribution and use in source and binary forms, with or without 7 1.1 pooka * modification, are permitted provided that the following conditions 8 1.1 pooka * are met: 9 1.1 pooka * 1. Redistributions of source code must retain the above copyright 10 1.1 pooka * notice, this list of conditions and the following disclaimer. 11 1.1 pooka * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 pooka * notice, this list of conditions and the following disclaimer in the 13 1.1 pooka * documentation and/or other materials provided with the distribution. 14 1.1 pooka * 15 1.1 pooka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 1.1 pooka * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 1.1 pooka * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 1.1 pooka * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 1.1 pooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 1.1 pooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 1.1 pooka * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 1.1 pooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 1.1 pooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 1.1 pooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 1.1 pooka * SUCH DAMAGE. 26 1.1 pooka */ 27 1.1 pooka 28 1.1 pooka #include <sys/cdefs.h> 29 1.1 pooka #ifndef lint 30 1.66 andvar __RCSID("$NetBSD: node.c,v 1.66 2021/09/16 21:29:42 andvar Exp $"); 31 1.1 pooka #endif /* !lint */ 32 1.1 pooka 33 1.1 pooka #include <assert.h> 34 1.1 pooka #include <errno.h> 35 1.1 pooka #include <stdio.h> 36 1.1 pooka #include <stdlib.h> 37 1.1 pooka 38 1.1 pooka #include "psshfs.h" 39 1.1 pooka #include "sftp_proto.h" 40 1.1 pooka 41 1.1 pooka int 42 1.54 pooka psshfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, 43 1.48 pooka struct puffs_newinfo *pni, const struct puffs_cn *pcn) 44 1.1 pooka { 45 1.16 pooka struct psshfs_ctx *pctx = puffs_getspecific(pu); 46 1.1 pooka struct puffs_node *pn_dir = opc; 47 1.8 pooka struct psshfs_node *psn, *psn_dir = pn_dir->pn_data; 48 1.1 pooka struct puffs_node *pn; 49 1.1 pooka struct psshfs_dir *pd; 50 1.11 pooka struct vattr va; 51 1.1 pooka int rv; 52 1.1 pooka 53 1.8 pooka if (PCNISDOTDOT(pcn)) { 54 1.8 pooka psn = psn_dir->parent->pn_data; 55 1.28 pooka psn->stat &= ~PSN_RECLAIMED; 56 1.8 pooka 57 1.32 pooka puffs_newinfo_setcookie(pni, psn_dir->parent); 58 1.32 pooka puffs_newinfo_setvtype(pni, VDIR); 59 1.1 pooka return 0; 60 1.1 pooka } 61 1.1 pooka 62 1.52 pooka rv = sftp_readdir(pu, pctx, pn_dir); 63 1.1 pooka if (rv) { 64 1.11 pooka if (rv != EPERM) 65 1.11 pooka return rv; 66 1.11 pooka 67 1.11 pooka /* 68 1.11 pooka * Can't read the directory. We still might be 69 1.11 pooka * able to find the node with getattr in -r+x dirs 70 1.11 pooka */ 71 1.48 pooka rv = getpathattr(pu, PCNPATH(pcn), &va); 72 1.11 pooka if (rv) 73 1.11 pooka return rv; 74 1.11 pooka 75 1.11 pooka /* guess */ 76 1.11 pooka if (va.va_type == VDIR) 77 1.11 pooka va.va_nlink = 2; 78 1.11 pooka else 79 1.11 pooka va.va_nlink = 1; 80 1.11 pooka 81 1.11 pooka pn = allocnode(pu, pn_dir, pcn->pcn_name, &va); 82 1.12 pooka psn = pn->pn_data; 83 1.12 pooka psn->attrread = time(NULL); 84 1.11 pooka } else { 85 1.11 pooka pd = lookup(psn_dir->dir, psn_dir->dentnext, pcn->pcn_name); 86 1.11 pooka if (!pd) { 87 1.11 pooka return ENOENT; 88 1.11 pooka } 89 1.1 pooka 90 1.11 pooka if (pd->entry) 91 1.11 pooka pn = pd->entry; 92 1.11 pooka else 93 1.61 pooka pd->entry = pn = makenode(pu, pn_dir, pd, &pd->va); 94 1.61 pooka 95 1.61 pooka /* 96 1.61 pooka * sure sure we have fresh attributes. most likely we will 97 1.61 pooka * have them cached. we might not if we go through: 98 1.61 pooka * create - reclaim - lookup (this). 99 1.61 pooka */ 100 1.61 pooka rv = getnodeattr(pu, pn, PCNPATH(pcn)); 101 1.61 pooka if (rv) 102 1.61 pooka return rv; 103 1.61 pooka 104 1.12 pooka psn = pn->pn_data; 105 1.6 pooka } 106 1.1 pooka 107 1.28 pooka psn->stat &= ~PSN_RECLAIMED; 108 1.8 pooka 109 1.32 pooka puffs_newinfo_setcookie(pni, pn); 110 1.32 pooka puffs_newinfo_setvtype(pni, pn->pn_va.va_type); 111 1.32 pooka puffs_newinfo_setsize(pni, pn->pn_va.va_size); 112 1.1 pooka 113 1.1 pooka return 0; 114 1.1 pooka } 115 1.1 pooka 116 1.1 pooka int 117 1.54 pooka psshfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, 118 1.54 pooka struct vattr *vap, const struct puffs_cred *pcr) 119 1.1 pooka { 120 1.1 pooka struct puffs_node *pn = opc; 121 1.11 pooka int rv; 122 1.1 pooka 123 1.61 pooka rv = getnodeattr(pu, pn, NULL); 124 1.11 pooka if (rv) 125 1.11 pooka return rv; 126 1.1 pooka 127 1.1 pooka memcpy(vap, &pn->pn_va, sizeof(struct vattr)); 128 1.1 pooka 129 1.11 pooka return 0; 130 1.1 pooka } 131 1.1 pooka 132 1.1 pooka int 133 1.54 pooka psshfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, 134 1.46 pooka const struct vattr *va, const struct puffs_cred *pcr) 135 1.1 pooka { 136 1.63 riastrad struct puffs_cc *pcc = puffs_cc_getcc(pu); 137 1.63 riastrad struct psshfs_ctx *pctx = puffs_getspecific(pu); 138 1.63 riastrad uint32_t reqid; 139 1.63 riastrad struct puffs_framebuf *pb; 140 1.3 pooka struct vattr kludgeva; 141 1.1 pooka struct puffs_node *pn = opc; 142 1.63 riastrad struct psshfs_node *psn = pn->pn_data; 143 1.63 riastrad int rv; 144 1.63 riastrad 145 1.63 riastrad /* 146 1.63 riastrad * If we cached the remote attributes recently enough, and this 147 1.63 riastrad * setattr operation would change nothing that sftp actually 148 1.63 riastrad * records, then we can skip the sftp request. So first check 149 1.63 riastrad * whether we have the attributes cached, and then compare 150 1.63 riastrad * every field that we might send to the sftp server. 151 1.63 riastrad */ 152 1.63 riastrad 153 1.63 riastrad if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) 154 1.63 riastrad goto setattr; 155 1.63 riastrad 156 1.63 riastrad #define CHECK(FIELD, TYPE) do { \ 157 1.63 riastrad if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ 158 1.63 riastrad (va->FIELD != pn->pn_va.FIELD)) \ 159 1.63 riastrad goto setattr; \ 160 1.63 riastrad } while (0) 161 1.63 riastrad 162 1.63 riastrad #define CHECKID(FIELD, TYPE, DOMANGLE, MINE, MANGLED) do { \ 163 1.63 riastrad if ((va->FIELD != (TYPE)PUFFS_VNOVAL) && \ 164 1.63 riastrad (pn->pn_va.FIELD != \ 165 1.63 riastrad ((pctx->DOMANGLE && (va->FIELD == pctx->MINE)) \ 166 1.63 riastrad ? pctx->MANGLED \ 167 1.63 riastrad : va->FIELD))) \ 168 1.63 riastrad goto setattr; \ 169 1.63 riastrad } while (0) 170 1.63 riastrad 171 1.63 riastrad CHECK(va_size, uint64_t); 172 1.63 riastrad CHECKID(va_uid, uid_t, domangleuid, myuid, mangleuid); 173 1.63 riastrad CHECKID(va_gid, gid_t, domanglegid, mygid, manglegid); 174 1.63 riastrad CHECK(va_mode, mode_t); 175 1.63 riastrad CHECK(va_atime.tv_sec, time_t); 176 1.63 riastrad CHECK(va_mtime.tv_sec, time_t); 177 1.63 riastrad 178 1.63 riastrad /* Nothing to change. */ 179 1.63 riastrad return 0; 180 1.63 riastrad 181 1.63 riastrad #undef CHECK 182 1.63 riastrad #undef CHECKID 183 1.63 riastrad 184 1.63 riastrad setattr: 185 1.63 riastrad reqid = NEXTREQ(pctx); 186 1.63 riastrad pb = psbuf_makeout(); 187 1.1 pooka 188 1.5 pooka psbuf_req_str(pb, SSH_FXP_SETSTAT, reqid, PNPATH(pn)); 189 1.3 pooka 190 1.3 pooka memcpy(&kludgeva, va, sizeof(struct vattr)); 191 1.3 pooka 192 1.3 pooka /* XXX: kludge due to openssh server implementation */ 193 1.3 pooka if (va->va_atime.tv_sec != PUFFS_VNOVAL 194 1.3 pooka && va->va_mtime.tv_sec == PUFFS_VNOVAL) { 195 1.3 pooka if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL) 196 1.3 pooka kludgeva.va_mtime.tv_sec = pn->pn_va.va_mtime.tv_sec; 197 1.3 pooka else 198 1.3 pooka kludgeva.va_mtime.tv_sec = va->va_atime.tv_sec; 199 1.3 pooka } 200 1.3 pooka if (va->va_mtime.tv_sec != PUFFS_VNOVAL 201 1.3 pooka && va->va_atime.tv_sec == PUFFS_VNOVAL) { 202 1.3 pooka if (pn->pn_va.va_atime.tv_sec != PUFFS_VNOVAL) 203 1.3 pooka kludgeva.va_atime.tv_sec = pn->pn_va.va_atime.tv_sec; 204 1.3 pooka else 205 1.3 pooka kludgeva.va_atime.tv_sec = va->va_mtime.tv_sec; 206 1.3 pooka } 207 1.63 riastrad 208 1.60 pooka psbuf_put_vattr(pb, &kludgeva, pctx); 209 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 210 1.1 pooka 211 1.1 pooka rv = psbuf_expect_status(pb); 212 1.63 riastrad if (rv == 0) { 213 1.3 pooka puffs_setvattr(&pn->pn_va, &kludgeva); 214 1.63 riastrad psn->attrread = time(NULL); 215 1.63 riastrad } 216 1.1 pooka 217 1.27 pooka out: 218 1.63 riastrad puffs_framebuf_destroy(pb); 219 1.63 riastrad return rv; 220 1.1 pooka } 221 1.1 pooka 222 1.1 pooka int 223 1.54 pooka psshfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, 224 1.53 pooka struct puffs_newinfo *pni, const struct puffs_cn *pcn, 225 1.53 pooka const struct vattr *va) 226 1.1 pooka { 227 1.48 pooka PSSHFSAUTOVAR(pu); 228 1.1 pooka struct puffs_node *pn = opc; 229 1.1 pooka struct puffs_node *pn_new; 230 1.1 pooka char *fhand = NULL; 231 1.9 pooka uint32_t fhandlen; 232 1.1 pooka 233 1.53 pooka /* Create node on server first */ 234 1.53 pooka psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PCNPATH(pcn)); 235 1.53 pooka psbuf_put_4(pb, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); 236 1.60 pooka psbuf_put_vattr(pb, va, pctx); 237 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 238 1.53 pooka rv = psbuf_expect_handle(pb, &fhand, &fhandlen); 239 1.53 pooka if (rv) 240 1.53 pooka goto out; 241 1.53 pooka 242 1.53 pooka /* 243 1.53 pooka * Do *not* create the local node before getting a response 244 1.53 pooka * from the server. Otherwise we might screw up consistency, 245 1.53 pooka * namely that the node can be looked up before create has 246 1.53 pooka * returned (mind you, the kernel will unlock the directory 247 1.53 pooka * before the create call from userspace returns). 248 1.53 pooka */ 249 1.1 pooka pn_new = allocnode(pu, pn, pcn->pcn_name, va); 250 1.13 pooka if (!pn_new) { 251 1.53 pooka struct puffs_framebuf *pb2 = psbuf_makeout(); 252 1.53 pooka reqid = NEXTREQ(pctx); 253 1.53 pooka psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 254 1.57 pooka JUSTSEND(pb2, pctx->sshfd); 255 1.1 pooka rv = ENOMEM; 256 1.1 pooka } 257 1.1 pooka 258 1.53 pooka if (pn_new) 259 1.32 pooka puffs_newinfo_setcookie(pni, pn_new); 260 1.1 pooka 261 1.1 pooka reqid = NEXTREQ(pctx); 262 1.22 pooka psbuf_recycleout(pb); 263 1.1 pooka psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, fhand, fhandlen); 264 1.57 pooka JUSTSEND(pb, pctx->sshfd); 265 1.23 pooka free(fhand); 266 1.53 pooka return rv; 267 1.1 pooka 268 1.1 pooka out: 269 1.1 pooka free(fhand); 270 1.1 pooka PSSHFSRETURN(rv); 271 1.1 pooka } 272 1.1 pooka 273 1.47 pooka /* 274 1.47 pooka * Open a file handle. This is used for read and write. We do not 275 1.47 pooka * wait here for the success or failure of this operation. This is 276 1.47 pooka * because otherwise opening and closing file handles would block 277 1.47 pooka * reading potentially cached information. Rather, we defer the wait 278 1.47 pooka * to read/write and therefore allow cached access without a wait. 279 1.47 pooka * 280 1.66 andvar * If we have not yet successfully opened a type of handle, we do wait 281 1.47 pooka * here. Also, if a lazy open fails, we revert back to the same 282 1.47 pooka * state of waiting. 283 1.47 pooka */ 284 1.36 pooka int 285 1.54 pooka psshfs_node_open(struct puffs_usermount *pu, puffs_cookie_t opc, int mode, 286 1.46 pooka const struct puffs_cred *pcr) 287 1.24 pooka { 288 1.48 pooka struct puffs_cc *pcc = puffs_cc_getcc(pu); 289 1.48 pooka struct psshfs_ctx *pctx = puffs_getspecific(pu); 290 1.47 pooka struct puffs_framebuf *pb, *pb2; 291 1.24 pooka struct vattr va; 292 1.24 pooka struct puffs_node *pn = opc; 293 1.24 pooka struct psshfs_node *psn = pn->pn_data; 294 1.47 pooka uint32_t reqid; 295 1.47 pooka int didread, didwrite; 296 1.47 pooka int rv = 0; 297 1.24 pooka 298 1.24 pooka if (pn->pn_va.va_type == VDIR) 299 1.47 pooka return 0; 300 1.24 pooka 301 1.25 pooka puffs_setback(pcc, PUFFS_SETBACK_INACT_N1); 302 1.24 pooka puffs_vattr_null(&va); 303 1.47 pooka didread = didwrite = 0; 304 1.47 pooka if (mode & FREAD && psn->fhand_r == NULL && psn->lazyopen_r == NULL) { 305 1.47 pooka pb = psbuf_makeout(); 306 1.47 pooka 307 1.47 pooka reqid = NEXTREQ(pctx); 308 1.24 pooka psbuf_req_str(pb, SSH_FXP_OPEN, reqid, PNPATH(pn)); 309 1.24 pooka psbuf_put_4(pb, SSH_FXF_READ); 310 1.60 pooka psbuf_put_vattr(pb, &va, pctx); 311 1.24 pooka 312 1.57 pooka if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb, 313 1.47 pooka lazyopen_rresp, psn, 0) == -1) { 314 1.56 pooka rv = errno; 315 1.47 pooka puffs_framebuf_destroy(pb); 316 1.24 pooka goto out; 317 1.47 pooka } 318 1.47 pooka 319 1.47 pooka psn->lazyopen_r = pb; 320 1.47 pooka didread = 1; 321 1.24 pooka } 322 1.47 pooka if (mode & FWRITE && psn->fhand_w == NULL && psn->lazyopen_w == NULL) { 323 1.47 pooka pb2 = psbuf_makeout(); 324 1.24 pooka 325 1.47 pooka reqid = NEXTREQ(pctx); 326 1.47 pooka psbuf_req_str(pb2, SSH_FXP_OPEN, reqid, PNPATH(pn)); 327 1.47 pooka psbuf_put_4(pb2, SSH_FXF_WRITE); 328 1.60 pooka psbuf_put_vattr(pb2, &va, pctx); 329 1.47 pooka 330 1.57 pooka if (puffs_framev_enqueue_cb(pu, pctx->sshfd_data, pb2, 331 1.47 pooka lazyopen_wresp, psn, 0) == -1) { 332 1.56 pooka rv = errno; 333 1.47 pooka puffs_framebuf_destroy(pb2); 334 1.24 pooka goto out; 335 1.47 pooka } 336 1.47 pooka 337 1.47 pooka psn->lazyopen_w = pb2; 338 1.47 pooka didwrite = 1; 339 1.24 pooka } 340 1.47 pooka psn->stat &= ~PSN_HANDLECLOSE; 341 1.24 pooka 342 1.24 pooka out: 343 1.47 pooka /* wait? */ 344 1.47 pooka if (didread && (psn->stat & PSN_DOLAZY_R) == 0) { 345 1.47 pooka assert(psn->lazyopen_r); 346 1.47 pooka 347 1.47 pooka rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); 348 1.47 pooka lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); 349 1.47 pooka if (psn->fhand_r) { 350 1.47 pooka psn->stat |= PSN_DOLAZY_R; 351 1.47 pooka } else { 352 1.47 pooka if (psn->lazyopen_err_r) 353 1.47 pooka return psn->lazyopen_err_r; 354 1.47 pooka return EINVAL; 355 1.47 pooka } 356 1.47 pooka } 357 1.24 pooka 358 1.47 pooka /* wait? */ 359 1.47 pooka if (didwrite && (psn->stat & PSN_DOLAZY_W) == 0) { 360 1.47 pooka assert(psn->lazyopen_w); 361 1.47 pooka 362 1.47 pooka rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 363 1.47 pooka lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 364 1.47 pooka if (psn->fhand_w) { 365 1.47 pooka psn->stat |= PSN_DOLAZY_W; 366 1.47 pooka } else { 367 1.47 pooka if (psn->lazyopen_err_w) 368 1.47 pooka return psn->lazyopen_err_w; 369 1.47 pooka return EINVAL; 370 1.47 pooka } 371 1.24 pooka } 372 1.24 pooka 373 1.47 pooka return rv; 374 1.36 pooka } 375 1.36 pooka 376 1.36 pooka int 377 1.54 pooka psshfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) 378 1.36 pooka { 379 1.36 pooka struct puffs_node *pn = opc; 380 1.36 pooka 381 1.48 pooka closehandles(pu, pn->pn_data, HANDLE_READ | HANDLE_WRITE); 382 1.24 pooka return 0; 383 1.24 pooka } 384 1.24 pooka 385 1.24 pooka int 386 1.54 pooka psshfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, 387 1.54 pooka struct dirent *dent, off_t *readoff, size_t *reslen, 388 1.54 pooka const struct puffs_cred *pcr, int *eofflag, 389 1.54 pooka off_t *cookies, size_t *ncookies) 390 1.1 pooka { 391 1.48 pooka struct puffs_cc *pcc = puffs_cc_getcc(pu); 392 1.48 pooka struct psshfs_ctx *pctx = puffs_getspecific(pu); 393 1.1 pooka struct puffs_node *pn = opc; 394 1.1 pooka struct psshfs_node *psn = pn->pn_data; 395 1.1 pooka struct psshfs_dir *pd; 396 1.59 pooka size_t i; 397 1.59 pooka int rv, set_readdir; 398 1.38 pooka 399 1.47 pooka restart: 400 1.38 pooka if (psn->stat & PSN_READDIR) { 401 1.41 pooka struct psshfs_wait pw; 402 1.38 pooka 403 1.38 pooka set_readdir = 0; 404 1.41 pooka pw.pw_cc = pcc; 405 1.47 pooka pw.pw_type = PWTYPE_READDIR; 406 1.41 pooka TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 407 1.38 pooka puffs_cc_yield(pcc); 408 1.47 pooka goto restart; 409 1.38 pooka } else { 410 1.38 pooka psn->stat |= PSN_READDIR; 411 1.38 pooka set_readdir = 1; 412 1.38 pooka } 413 1.1 pooka 414 1.17 pooka *ncookies = 0; 415 1.52 pooka rv = sftp_readdir(pu, pctx, pn); 416 1.62 pooka if (rv) { 417 1.38 pooka goto out; 418 1.62 pooka } 419 1.1 pooka 420 1.35 pooka /* find next dirent */ 421 1.35 pooka for (i = *readoff;;i++) { 422 1.38 pooka if (i >= psn->dentnext) 423 1.35 pooka goto out; 424 1.1 pooka pd = &psn->dir[i]; 425 1.35 pooka if (pd->valid) 426 1.35 pooka break; 427 1.35 pooka } 428 1.35 pooka 429 1.35 pooka for (;;) { 430 1.35 pooka *readoff = i; 431 1.1 pooka if (!puffs_nextdent(&dent, pd->entryname, 432 1.38 pooka pd->va.va_fileid, puffs_vtype2dt(pd->va.va_type), reslen)) { 433 1.38 pooka rv = 0; 434 1.38 pooka goto out; 435 1.38 pooka } 436 1.35 pooka 437 1.35 pooka /* find next entry, store possible nfs key */ 438 1.35 pooka do { 439 1.38 pooka if (++i >= psn->dentnext) 440 1.35 pooka goto out; 441 1.35 pooka pd = &psn->dir[i]; 442 1.35 pooka } while (pd->valid == 0); 443 1.19 pooka PUFFS_STORE_DCOOKIE(cookies, ncookies, (off_t)i); 444 1.1 pooka } 445 1.35 pooka 446 1.35 pooka out: 447 1.38 pooka if (rv == 0) { 448 1.38 pooka if (i >= psn->dentnext) 449 1.38 pooka *eofflag = 1; 450 1.38 pooka 451 1.38 pooka *readoff = i; 452 1.38 pooka } 453 1.38 pooka 454 1.38 pooka if (set_readdir) { 455 1.41 pooka struct psshfs_wait *pw; 456 1.1 pooka 457 1.42 pooka /* all will likely run to completion because of cache */ 458 1.47 pooka TAILQ_FOREACH(pw, &psn->pw, pw_entries) { 459 1.47 pooka assert(pw->pw_type == PWTYPE_READDIR); 460 1.42 pooka puffs_cc_schedule(pw->pw_cc); 461 1.49 pooka TAILQ_REMOVE(&psn->pw, pw, pw_entries); 462 1.47 pooka } 463 1.38 pooka 464 1.38 pooka psn->stat &= ~PSN_READDIR; 465 1.38 pooka } 466 1.38 pooka 467 1.38 pooka return rv; 468 1.1 pooka } 469 1.1 pooka 470 1.1 pooka int 471 1.54 pooka psshfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, 472 1.1 pooka off_t offset, size_t *resid, const struct puffs_cred *pcr, 473 1.1 pooka int ioflag) 474 1.1 pooka { 475 1.48 pooka PSSHFSAUTOVAR(pu); 476 1.1 pooka struct puffs_node *pn = opc; 477 1.24 pooka struct psshfs_node *psn = pn->pn_data; 478 1.47 pooka struct psshfs_wait *pwp; 479 1.24 pooka uint32_t readlen; 480 1.1 pooka 481 1.42 pooka if (pn->pn_va.va_type == VDIR) { 482 1.42 pooka rv = EISDIR; 483 1.42 pooka goto farout; 484 1.42 pooka } 485 1.42 pooka 486 1.47 pooka /* check that a lazyopen didn't fail */ 487 1.47 pooka if (!psn->fhand_r && !psn->lazyopen_r) { 488 1.47 pooka rv = psn->lazyopen_err_r; 489 1.47 pooka goto farout; 490 1.47 pooka } 491 1.47 pooka 492 1.47 pooka /* if someone is already waiting for the lazyopen, "just" wait */ 493 1.47 pooka if (psn->stat & PSN_LAZYWAIT_R) { 494 1.47 pooka struct psshfs_wait pw; 495 1.47 pooka 496 1.47 pooka assert(psn->lazyopen_r); 497 1.47 pooka 498 1.47 pooka pw.pw_cc = pcc; 499 1.47 pooka pw.pw_type = PWTYPE_READ1; 500 1.47 pooka TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 501 1.47 pooka puffs_cc_yield(pcc); 502 1.47 pooka } 503 1.47 pooka 504 1.47 pooka /* if lazyopening, wait for the result */ 505 1.47 pooka if (psn->lazyopen_r) { 506 1.47 pooka psn->stat |= PSN_LAZYWAIT_R; 507 1.47 pooka rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_r, pcc); 508 1.47 pooka lazyopen_rresp(pu, psn->lazyopen_r, psn, rv); 509 1.47 pooka 510 1.47 pooka /* schedule extra waiters */ 511 1.47 pooka TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 512 1.49 pooka if (pwp->pw_type == PWTYPE_READ1) { 513 1.47 pooka puffs_cc_schedule(pwp->pw_cc); 514 1.49 pooka TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 515 1.49 pooka } 516 1.47 pooka psn->stat &= ~PSN_LAZYWAIT_R; 517 1.47 pooka 518 1.47 pooka if ((rv = psn->lazyopen_err_r) != 0) 519 1.47 pooka goto farout; 520 1.47 pooka } 521 1.47 pooka 522 1.47 pooka /* if there is still no handle, just refuse to live with this */ 523 1.47 pooka if (!psn->fhand_r) { 524 1.47 pooka rv = EINVAL; 525 1.47 pooka goto farout; 526 1.47 pooka } 527 1.47 pooka 528 1.64 jakllsch again: 529 1.41 pooka readlen = *resid; 530 1.41 pooka psbuf_req_data(pb, SSH_FXP_READ, reqid, psn->fhand_r, psn->fhand_r_len); 531 1.41 pooka psbuf_put_8(pb, offset); 532 1.41 pooka psbuf_put_4(pb, readlen); 533 1.41 pooka 534 1.41 pooka /* 535 1.41 pooka * Do this *after* accessing the file, the handle might not 536 1.41 pooka * exist after blocking. 537 1.41 pooka */ 538 1.41 pooka if (max_reads && ++psn->readcount > max_reads) { 539 1.41 pooka struct psshfs_wait pw; 540 1.41 pooka 541 1.41 pooka pw.pw_cc = pcc; 542 1.47 pooka pw.pw_type = PWTYPE_READ2; 543 1.41 pooka TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 544 1.41 pooka puffs_cc_yield(pcc); 545 1.1 pooka } 546 1.1 pooka 547 1.57 pooka GETRESPONSE(pb, pctx->sshfd_data); 548 1.1 pooka 549 1.1 pooka rv = psbuf_do_data(pb, buf, &readlen); 550 1.64 jakllsch if (rv == 0) { 551 1.1 pooka *resid -= readlen; 552 1.64 jakllsch buf += readlen; 553 1.64 jakllsch offset += readlen; 554 1.64 jakllsch } 555 1.1 pooka 556 1.27 pooka out: 557 1.41 pooka if (max_reads && --psn->readcount >= max_reads) { 558 1.47 pooka TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 559 1.47 pooka if (pwp->pw_type == PWTYPE_READ2) 560 1.47 pooka break; 561 1.47 pooka assert(pwp != NULL); 562 1.49 pooka puffs_cc_schedule(pwp->pw_cc); 563 1.47 pooka TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 564 1.41 pooka } 565 1.41 pooka 566 1.65 christos if (rv == 0 && *resid > 0 && readlen > 0) { 567 1.64 jakllsch reqid = NEXTREQ(pctx); 568 1.64 jakllsch psbuf_recycleout(pb); 569 1.64 jakllsch goto again; 570 1.64 jakllsch } 571 1.64 jakllsch 572 1.42 pooka farout: 573 1.47 pooka /* check if we need a lazyclose */ 574 1.47 pooka if (psn->stat & PSN_HANDLECLOSE && psn->fhand_r) { 575 1.47 pooka TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 576 1.47 pooka if (pwp->pw_type == PWTYPE_READ1) 577 1.47 pooka break; 578 1.47 pooka if (pwp == NULL) 579 1.47 pooka closehandles(pu, psn, HANDLE_READ); 580 1.47 pooka } 581 1.1 pooka PSSHFSRETURN(rv); 582 1.1 pooka } 583 1.1 pooka 584 1.1 pooka /* XXX: we should getattr for size */ 585 1.1 pooka int 586 1.54 pooka psshfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, 587 1.1 pooka off_t offset, size_t *resid, const struct puffs_cred *cred, 588 1.1 pooka int ioflag) 589 1.1 pooka { 590 1.48 pooka PSSHFSAUTOVAR(pu); 591 1.47 pooka struct psshfs_wait *pwp; 592 1.1 pooka struct puffs_node *pn = opc; 593 1.24 pooka struct psshfs_node *psn = pn->pn_data; 594 1.24 pooka uint32_t writelen; 595 1.1 pooka 596 1.1 pooka if (pn->pn_va.va_type == VDIR) { 597 1.1 pooka rv = EISDIR; 598 1.27 pooka goto out; 599 1.1 pooka } 600 1.1 pooka 601 1.47 pooka /* check that a lazyopen didn't fail */ 602 1.47 pooka if (!psn->fhand_w && !psn->lazyopen_w) { 603 1.47 pooka rv = psn->lazyopen_err_w; 604 1.47 pooka goto out; 605 1.47 pooka } 606 1.47 pooka 607 1.47 pooka if (psn->stat & PSN_LAZYWAIT_W) { 608 1.47 pooka struct psshfs_wait pw; 609 1.47 pooka 610 1.47 pooka assert(psn->lazyopen_w); 611 1.47 pooka 612 1.47 pooka pw.pw_cc = pcc; 613 1.47 pooka pw.pw_type = PWTYPE_WRITE; 614 1.47 pooka TAILQ_INSERT_TAIL(&psn->pw, &pw, pw_entries); 615 1.47 pooka puffs_cc_yield(pcc); 616 1.47 pooka } 617 1.47 pooka 618 1.47 pooka /* 619 1.47 pooka * If lazyopening, wait for the result. 620 1.47 pooka * There can still be more than oen writer at a time in case 621 1.47 pooka * the kernel issues write FAFs. 622 1.47 pooka */ 623 1.47 pooka if (psn->lazyopen_w) { 624 1.47 pooka psn->stat |= PSN_LAZYWAIT_W; 625 1.47 pooka rv = puffs_framev_framebuf_ccpromote(psn->lazyopen_w, pcc); 626 1.47 pooka lazyopen_wresp(pu, psn->lazyopen_w, psn, rv); 627 1.47 pooka 628 1.47 pooka /* schedule extra waiters */ 629 1.47 pooka TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 630 1.49 pooka if (pwp->pw_type == PWTYPE_WRITE) { 631 1.47 pooka puffs_cc_schedule(pwp->pw_cc); 632 1.49 pooka TAILQ_REMOVE(&psn->pw, pwp, pw_entries); 633 1.49 pooka } 634 1.47 pooka psn->stat &= ~PSN_LAZYWAIT_W; 635 1.47 pooka 636 1.47 pooka if ((rv = psn->lazyopen_err_w) != 0) 637 1.47 pooka goto out; 638 1.47 pooka } 639 1.47 pooka 640 1.47 pooka if (!psn->fhand_w) { 641 1.47 pooka abort(); 642 1.47 pooka rv = EINVAL; 643 1.47 pooka goto out; 644 1.47 pooka } 645 1.47 pooka 646 1.1 pooka writelen = *resid; 647 1.24 pooka psbuf_req_data(pb, SSH_FXP_WRITE, reqid, psn->fhand_w,psn->fhand_w_len); 648 1.1 pooka psbuf_put_8(pb, offset); 649 1.1 pooka psbuf_put_data(pb, buf, writelen); 650 1.57 pooka GETRESPONSE(pb, pctx->sshfd_data); 651 1.1 pooka 652 1.1 pooka rv = psbuf_expect_status(pb); 653 1.1 pooka if (rv == 0) 654 1.1 pooka *resid = 0; 655 1.1 pooka 656 1.59 pooka if (pn->pn_va.va_size < (uint64_t)offset + writelen) 657 1.1 pooka pn->pn_va.va_size = offset + writelen; 658 1.1 pooka 659 1.27 pooka out: 660 1.47 pooka /* check if we need a lazyclose */ 661 1.47 pooka if (psn->stat & PSN_HANDLECLOSE && psn->fhand_w) { 662 1.47 pooka TAILQ_FOREACH(pwp, &psn->pw, pw_entries) 663 1.47 pooka if (pwp->pw_type == PWTYPE_WRITE) 664 1.47 pooka break; 665 1.47 pooka if (pwp == NULL) 666 1.47 pooka closehandles(pu, psn, HANDLE_WRITE); 667 1.47 pooka } 668 1.1 pooka PSSHFSRETURN(rv); 669 1.1 pooka } 670 1.1 pooka 671 1.1 pooka int 672 1.54 pooka psshfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 673 1.1 pooka const struct puffs_cred *cred, char *linkvalue, size_t *linklen) 674 1.1 pooka { 675 1.48 pooka PSSHFSAUTOVAR(pu); 676 1.1 pooka struct puffs_node *pn = opc; 677 1.43 pooka struct psshfs_node *psn = pn->pn_data; 678 1.1 pooka uint32_t count; 679 1.1 pooka 680 1.1 pooka if (pctx->protover < 3) { 681 1.1 pooka rv = EOPNOTSUPP; 682 1.1 pooka goto out; 683 1.1 pooka } 684 1.1 pooka 685 1.44 pooka /* 686 1.44 pooka * check if we can use a cached version 687 1.44 pooka * 688 1.44 pooka * XXX: we might end up reading the same link multiple times 689 1.44 pooka * from the server if we get many requests at once, but that's 690 1.44 pooka * quite harmless as this routine is reentrant. 691 1.44 pooka */ 692 1.44 pooka if (psn->symlink && !REFRESHTIMEOUT(pctx, time(NULL) - psn->slread)) 693 1.43 pooka goto copy; 694 1.43 pooka 695 1.44 pooka if (psn->symlink) { 696 1.44 pooka free(psn->symlink); 697 1.44 pooka psn->symlink = NULL; 698 1.44 pooka psn->slread = 0; 699 1.44 pooka } 700 1.44 pooka 701 1.5 pooka psbuf_req_str(pb, SSH_FXP_READLINK, reqid, PNPATH(pn)); 702 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 703 1.1 pooka 704 1.1 pooka rv = psbuf_expect_name(pb, &count); 705 1.22 pooka if (rv) 706 1.22 pooka goto out; 707 1.1 pooka if (count != 1) { 708 1.1 pooka rv = EPROTO; 709 1.1 pooka goto out; 710 1.1 pooka } 711 1.22 pooka 712 1.43 pooka rv = psbuf_get_str(pb, &psn->symlink, NULL); 713 1.1 pooka if (rv) 714 1.1 pooka goto out; 715 1.44 pooka psn->slread = time(NULL); 716 1.43 pooka 717 1.43 pooka copy: 718 1.43 pooka *linklen = strlen(psn->symlink); 719 1.43 pooka (void) memcpy(linkvalue, psn->symlink, *linklen); 720 1.1 pooka 721 1.1 pooka out: 722 1.1 pooka PSSHFSRETURN(rv); 723 1.1 pooka } 724 1.1 pooka 725 1.34 pooka static int 726 1.48 pooka doremove(struct puffs_usermount *pu, struct puffs_node *pn_dir, 727 1.34 pooka struct puffs_node *pn, const char *name) 728 1.34 pooka { 729 1.48 pooka PSSHFSAUTOVAR(pu); 730 1.34 pooka int op; 731 1.34 pooka 732 1.34 pooka if (pn->pn_va.va_type == VDIR) 733 1.34 pooka op = SSH_FXP_RMDIR; 734 1.34 pooka else 735 1.34 pooka op = SSH_FXP_REMOVE; 736 1.34 pooka 737 1.34 pooka psbuf_req_str(pb, op, reqid, PNPATH(pn)); 738 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 739 1.34 pooka 740 1.34 pooka rv = psbuf_expect_status(pb); 741 1.34 pooka if (rv == 0) 742 1.34 pooka nukenode(pn, name, 0); 743 1.34 pooka 744 1.34 pooka out: 745 1.34 pooka PSSHFSRETURN(rv); 746 1.34 pooka } 747 1.34 pooka 748 1.1 pooka int 749 1.54 pooka psshfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 750 1.54 pooka puffs_cookie_t targ, const struct puffs_cn *pcn) 751 1.1 pooka { 752 1.1 pooka struct puffs_node *pn_targ = targ; 753 1.34 pooka int rv; 754 1.34 pooka 755 1.34 pooka assert(pn_targ->pn_va.va_type != VDIR); 756 1.34 pooka 757 1.48 pooka rv = doremove(pu, opc, targ, pcn->pcn_name); 758 1.34 pooka if (rv == 0) 759 1.48 pooka puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 760 1.1 pooka 761 1.34 pooka return rv; 762 1.34 pooka } 763 1.1 pooka 764 1.34 pooka int 765 1.54 pooka psshfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 766 1.54 pooka puffs_cookie_t targ, const struct puffs_cn *pcn) 767 1.34 pooka { 768 1.34 pooka struct puffs_node *pn_targ = targ; 769 1.34 pooka int rv; 770 1.1 pooka 771 1.34 pooka assert(pn_targ->pn_va.va_type == VDIR); 772 1.1 pooka 773 1.48 pooka rv = doremove(pu, opc, targ, pcn->pcn_name); 774 1.34 pooka if (rv == 0) 775 1.48 pooka puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 776 1.1 pooka 777 1.34 pooka return rv; 778 1.1 pooka } 779 1.1 pooka 780 1.1 pooka int 781 1.54 pooka psshfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 782 1.53 pooka struct puffs_newinfo *pni, const struct puffs_cn *pcn, 783 1.53 pooka const struct vattr *va) 784 1.1 pooka { 785 1.48 pooka PSSHFSAUTOVAR(pu); 786 1.1 pooka struct puffs_node *pn = opc; 787 1.1 pooka struct puffs_node *pn_new; 788 1.1 pooka 789 1.5 pooka psbuf_req_str(pb, SSH_FXP_MKDIR, reqid, PCNPATH(pcn)); 790 1.60 pooka psbuf_put_vattr(pb, va, pctx); 791 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 792 1.1 pooka 793 1.1 pooka rv = psbuf_expect_status(pb); 794 1.53 pooka if (rv) 795 1.53 pooka goto out; 796 1.1 pooka 797 1.53 pooka pn_new = allocnode(pu, pn, pcn->pcn_name, va); 798 1.53 pooka if (pn_new) { 799 1.32 pooka puffs_newinfo_setcookie(pni, pn_new); 800 1.53 pooka } else { 801 1.53 pooka struct puffs_framebuf *pb2 = psbuf_makeout(); 802 1.53 pooka reqid = NEXTREQ(pctx); 803 1.53 pooka psbuf_recycleout(pb2); 804 1.53 pooka psbuf_req_str(pb2, SSH_FXP_RMDIR, reqid, PCNPATH(pcn)); 805 1.57 pooka JUSTSEND(pb2, pctx->sshfd); 806 1.53 pooka rv = ENOMEM; 807 1.53 pooka } 808 1.1 pooka 809 1.1 pooka out: 810 1.1 pooka PSSHFSRETURN(rv); 811 1.1 pooka } 812 1.1 pooka 813 1.1 pooka int 814 1.54 pooka psshfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 815 1.53 pooka struct puffs_newinfo *pni, const struct puffs_cn *pcn, 816 1.53 pooka const struct vattr *va, const char *link_target) 817 1.1 pooka { 818 1.48 pooka PSSHFSAUTOVAR(pu); 819 1.1 pooka struct puffs_node *pn = opc; 820 1.1 pooka struct puffs_node *pn_new; 821 1.1 pooka 822 1.1 pooka if (pctx->protover < 3) { 823 1.1 pooka rv = EOPNOTSUPP; 824 1.1 pooka goto out; 825 1.1 pooka } 826 1.1 pooka 827 1.1 pooka /* 828 1.1 pooka * XXX: ietf says: source, target. openssh says: ietf who? 829 1.1 pooka * Let's go with openssh and build quirk tables later if we care 830 1.1 pooka */ 831 1.1 pooka psbuf_req_str(pb, SSH_FXP_SYMLINK, reqid, link_target); 832 1.5 pooka psbuf_put_str(pb, PCNPATH(pcn)); 833 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 834 1.1 pooka 835 1.1 pooka rv = psbuf_expect_status(pb); 836 1.53 pooka if (rv) 837 1.53 pooka goto out; 838 1.53 pooka 839 1.53 pooka pn_new = allocnode(pu, pn, pcn->pcn_name, va); 840 1.53 pooka if (pn_new) { 841 1.32 pooka puffs_newinfo_setcookie(pni, pn_new); 842 1.53 pooka } else { 843 1.53 pooka struct puffs_framebuf *pb2 = psbuf_makeout(); 844 1.53 pooka reqid = NEXTREQ(pctx); 845 1.53 pooka psbuf_recycleout(pb2); 846 1.53 pooka psbuf_req_str(pb2, SSH_FXP_REMOVE, reqid, PCNPATH(pcn)); 847 1.57 pooka JUSTSEND(pb2, pctx->sshfd); 848 1.53 pooka rv = ENOMEM; 849 1.53 pooka } 850 1.1 pooka 851 1.1 pooka out: 852 1.1 pooka PSSHFSRETURN(rv); 853 1.1 pooka } 854 1.1 pooka 855 1.1 pooka int 856 1.54 pooka psshfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, 857 1.54 pooka puffs_cookie_t src, const struct puffs_cn *pcn_src, 858 1.54 pooka puffs_cookie_t targ_dir, puffs_cookie_t targ, 859 1.1 pooka const struct puffs_cn *pcn_targ) 860 1.1 pooka { 861 1.48 pooka PSSHFSAUTOVAR(pu); 862 1.1 pooka struct puffs_node *pn_sf = src; 863 1.1 pooka struct puffs_node *pn_td = targ_dir, *pn_tf = targ; 864 1.55 pooka struct psshfs_node *psn_src = pn_sf->pn_data; 865 1.1 pooka struct psshfs_node *psn_targdir = pn_td->pn_data; 866 1.1 pooka 867 1.1 pooka if (pctx->protover < 2) { 868 1.1 pooka rv = EOPNOTSUPP; 869 1.1 pooka goto out; 870 1.1 pooka } 871 1.1 pooka 872 1.1 pooka if (pn_tf) { 873 1.48 pooka rv = doremove(pu, targ_dir, pn_tf, pcn_targ->pcn_name); 874 1.1 pooka if (rv) 875 1.1 pooka goto out; 876 1.1 pooka } 877 1.1 pooka 878 1.5 pooka psbuf_req_str(pb, SSH_FXP_RENAME, reqid, PCNPATH(pcn_src)); 879 1.5 pooka psbuf_put_str(pb, PCNPATH(pcn_targ)); 880 1.57 pooka GETRESPONSE(pb, pctx->sshfd); 881 1.1 pooka 882 1.1 pooka rv = psbuf_expect_status(pb); 883 1.1 pooka if (rv == 0) { 884 1.1 pooka struct psshfs_dir *pd; 885 1.1 pooka 886 1.1 pooka /* 887 1.1 pooka * XXX: interfaces didn't quite work with rename.. 888 1.1 pooka * the song remains the same. go figure .. ;) 889 1.1 pooka */ 890 1.4 pooka nukenode(pn_sf, pcn_src->pcn_name, 0); 891 1.1 pooka pd = direnter(pn_td, pcn_targ->pcn_name); 892 1.1 pooka pd->entry = pn_sf; 893 1.1 pooka puffs_setvattr(&pd->va, &pn_sf->pn_va); 894 1.1 pooka 895 1.1 pooka if (opc != targ_dir) { 896 1.1 pooka psn_targdir->childcount++; 897 1.55 pooka psn_src->parent = pn_td; 898 1.1 pooka if (pn_sf->pn_va.va_type == VDIR) 899 1.1 pooka pn_td->pn_va.va_nlink++; 900 1.1 pooka } 901 1.1 pooka } 902 1.1 pooka 903 1.1 pooka out: 904 1.1 pooka PSSHFSRETURN(rv); 905 1.1 pooka } 906 1.8 pooka 907 1.8 pooka /* 908 1.8 pooka * So this file system happened to be written in such a way that 909 1.8 pooka * lookup for ".." is hard if we lose the in-memory node. We'd 910 1.8 pooka * need to recreate the entire directory structure from the root 911 1.8 pooka * node up to the ".." node we're looking up. 912 1.8 pooka * 913 1.8 pooka * And since our entire fs structure is purely fictional (i.e. it's 914 1.8 pooka * only in-memory, not fetchable from the server), the easiest way 915 1.8 pooka * to deal with it is to not allow nodes with children to be 916 1.8 pooka * reclaimed. 917 1.8 pooka * 918 1.8 pooka * If a node with children is being attempted to be reclaimed, we 919 1.8 pooka * just mark it "reclaimed" but leave it as is until all its children 920 1.8 pooka * have been reclaimed. If a lookup for that node is done meanwhile, 921 1.8 pooka * it will be found by lookup() and we just remove the "reclaimed" 922 1.8 pooka * bit. 923 1.8 pooka */ 924 1.8 pooka int 925 1.54 pooka psshfs_node_reclaim(struct puffs_usermount *pu, puffs_cookie_t opc) 926 1.8 pooka { 927 1.16 pooka struct puffs_node *pn = opc, *pn_next, *pn_root; 928 1.8 pooka struct psshfs_node *psn = pn->pn_data; 929 1.8 pooka 930 1.17 pooka /* 931 1.17 pooka * don't reclaim if we have file handle issued, otherwise 932 1.17 pooka * we can't do fhtonode 933 1.17 pooka */ 934 1.28 pooka if (psn->stat & PSN_HASFH) 935 1.17 pooka return 0; 936 1.17 pooka 937 1.28 pooka psn->stat |= PSN_RECLAIMED; 938 1.16 pooka pn_root = puffs_getroot(pu); 939 1.16 pooka for (; pn != pn_root; pn = pn_next) { 940 1.8 pooka psn = pn->pn_data; 941 1.28 pooka if ((psn->stat & PSN_RECLAIMED) == 0 || psn->childcount != 0) 942 1.8 pooka break; 943 1.8 pooka 944 1.8 pooka pn_next = psn->parent; 945 1.8 pooka doreclaim(pn); 946 1.8 pooka } 947 1.8 pooka 948 1.8 pooka return 0; 949 1.8 pooka } 950