xref: /netbsd-src/usr.sbin/puffs/mount_psshfs/subr.c (revision 4f048a363dd254f28a9a27b09373a95f70506bf1)
1*4f048a36Schristos /*      $NetBSD: subr.c,v 1.51 2012/11/04 22:46:08 christos Exp $        */
2c3ef8ea5Spooka 
3c3ef8ea5Spooka /*
4c3ef8ea5Spooka  * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
5c3ef8ea5Spooka  *
6c3ef8ea5Spooka  * Redistribution and use in source and binary forms, with or without
7c3ef8ea5Spooka  * modification, are permitted provided that the following conditions
8c3ef8ea5Spooka  * are met:
9c3ef8ea5Spooka  * 1. Redistributions of source code must retain the above copyright
10c3ef8ea5Spooka  *    notice, this list of conditions and the following disclaimer.
11c3ef8ea5Spooka  * 2. Redistributions in binary form must reproduce the above copyright
12c3ef8ea5Spooka  *    notice, this list of conditions and the following disclaimer in the
13c3ef8ea5Spooka  *    documentation and/or other materials provided with the distribution.
14c3ef8ea5Spooka  *
15c3ef8ea5Spooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16c3ef8ea5Spooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17c3ef8ea5Spooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18c3ef8ea5Spooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c3ef8ea5Spooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c3ef8ea5Spooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21c3ef8ea5Spooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c3ef8ea5Spooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c3ef8ea5Spooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c3ef8ea5Spooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c3ef8ea5Spooka  * SUCH DAMAGE.
26c3ef8ea5Spooka  */
27c3ef8ea5Spooka 
28c3ef8ea5Spooka #include <sys/cdefs.h>
29c3ef8ea5Spooka #ifndef lint
30*4f048a36Schristos __RCSID("$NetBSD: subr.c,v 1.51 2012/11/04 22:46:08 christos Exp $");
31c3ef8ea5Spooka #endif /* !lint */
32c3ef8ea5Spooka 
33*4f048a36Schristos #include <stdio.h>
34c3ef8ea5Spooka #include <assert.h>
3523c02977Spooka #include <err.h>
36c3ef8ea5Spooka #include <errno.h>
37c3ef8ea5Spooka #include <puffs.h>
38c3ef8ea5Spooka #include <stdlib.h>
39c3ef8ea5Spooka #include <util.h>
40c3ef8ea5Spooka 
41c3ef8ea5Spooka #include "psshfs.h"
42c3ef8ea5Spooka #include "sftp_proto.h"
43c3ef8ea5Spooka 
44c3ef8ea5Spooka static void
freedircache(struct psshfs_dir * base,size_t count)45c3ef8ea5Spooka freedircache(struct psshfs_dir *base, size_t count)
46c3ef8ea5Spooka {
475866121cSpooka 	size_t i;
48c3ef8ea5Spooka 
49c3ef8ea5Spooka 	for (i = 0; i < count; i++) {
50c3ef8ea5Spooka 		free(base[i].entryname);
51c3ef8ea5Spooka 		base[i].entryname = NULL;
52c3ef8ea5Spooka 	}
53c3ef8ea5Spooka 
54c3ef8ea5Spooka 	free(base);
55c3ef8ea5Spooka }
56c3ef8ea5Spooka 
57c3ef8ea5Spooka #define ENTRYCHUNK 16
58c3ef8ea5Spooka static void
allocdirs(struct psshfs_node * psn)59c3ef8ea5Spooka allocdirs(struct psshfs_node *psn)
60c3ef8ea5Spooka {
61c3ef8ea5Spooka 	size_t oldtot = psn->denttot;
62c3ef8ea5Spooka 
63c3ef8ea5Spooka 	psn->denttot += ENTRYCHUNK;
64c3ef8ea5Spooka 	psn->dir = erealloc(psn->dir,
65c3ef8ea5Spooka 	    psn->denttot * sizeof(struct psshfs_dir));
66c3ef8ea5Spooka 	memset(psn->dir + oldtot, 0, ENTRYCHUNK * sizeof(struct psshfs_dir));
67c3ef8ea5Spooka }
68c3ef8ea5Spooka 
69968196afSpooka static void
setpnva(struct puffs_usermount * pu,struct puffs_node * pn,const struct vattr * vap)70968196afSpooka setpnva(struct puffs_usermount *pu, struct puffs_node *pn,
71968196afSpooka 	const struct vattr *vap)
72968196afSpooka {
73c4291c19Spooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
74968196afSpooka 	struct psshfs_node *psn = pn->pn_data;
75c4291c19Spooka 	struct vattr modva;
76968196afSpooka 
77968196afSpooka 	/*
78968196afSpooka 	 * Check if the file was modified from below us.
79968196afSpooka 	 * If so, invalidate page cache.  This is the only
80968196afSpooka 	 * sensible place we can do this in.
81968196afSpooka 	 */
82968196afSpooka 	if (pn->pn_va.va_mtime.tv_sec != PUFFS_VNOVAL)
83968196afSpooka 		if (pn->pn_va.va_mtime.tv_sec != vap->va_mtime.tv_sec
84968196afSpooka 		    && pn->pn_va.va_type == VREG)
85968196afSpooka 			puffs_inval_pagecache_node(pu, pn);
86968196afSpooka 
87c4291c19Spooka 	modva = *vap;
88c4291c19Spooka 	if (pctx->domangleuid && modva.va_uid == pctx->mangleuid)
89c4291c19Spooka 		modva.va_uid = pctx->myuid;
90c4291c19Spooka 	if (pctx->domanglegid && modva.va_gid == pctx->manglegid)
91c4291c19Spooka 		modva.va_gid = pctx->mygid;
92c4291c19Spooka 
93c4291c19Spooka 	puffs_setvattr(&pn->pn_va, &modva);
94968196afSpooka 	psn->attrread = time(NULL);
95968196afSpooka }
96968196afSpooka 
97c3ef8ea5Spooka struct psshfs_dir *
lookup(struct psshfs_dir * bdir,size_t ndir,const char * name)98f5fed534Spooka lookup(struct psshfs_dir *bdir, size_t ndir, const char *name)
99c3ef8ea5Spooka {
100f5fed534Spooka 	struct psshfs_dir *test;
1015866121cSpooka 	size_t i;
102c3ef8ea5Spooka 
103c3ef8ea5Spooka 	for (i = 0; i < ndir; i++) {
104f5fed534Spooka 		test = &bdir[i];
105f5fed534Spooka 		if (test->valid != 1)
106c3ef8ea5Spooka 			continue;
107f5fed534Spooka 		if (strcmp(test->entryname, name) == 0)
108f5fed534Spooka 			return test;
109f5fed534Spooka 	}
110f5fed534Spooka 
111f5fed534Spooka 	return NULL;
112f5fed534Spooka }
113f5fed534Spooka 
114f5fed534Spooka static struct psshfs_dir *
lookup_by_entry(struct psshfs_dir * bdir,size_t ndir,struct puffs_node * entry)115f5fed534Spooka lookup_by_entry(struct psshfs_dir *bdir, size_t ndir, struct puffs_node *entry)
116f5fed534Spooka {
117f5fed534Spooka 	struct psshfs_dir *test;
1185866121cSpooka 	size_t i;
119f5fed534Spooka 
120f5fed534Spooka 	for (i = 0; i < ndir; i++) {
121f5fed534Spooka 		test = &bdir[i];
122f5fed534Spooka 		if (test->valid != 1)
123f5fed534Spooka 			continue;
124f5fed534Spooka 		if (test->entry == entry)
125f5fed534Spooka 			return test;
126c3ef8ea5Spooka 	}
127c3ef8ea5Spooka 
128c3ef8ea5Spooka 	return NULL;
129c3ef8ea5Spooka }
130c3ef8ea5Spooka 
1313fe3f654Spooka 
1323fe3f654Spooka void
closehandles(struct puffs_usermount * pu,struct psshfs_node * psn,int which)1333fe3f654Spooka closehandles(struct puffs_usermount *pu, struct psshfs_node *psn, int which)
1343fe3f654Spooka {
1353fe3f654Spooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
1363fe3f654Spooka 	struct puffs_framebuf *pb1, *pb2;
1373fe3f654Spooka 	uint32_t reqid;
1383fe3f654Spooka 
1393fe3f654Spooka 	if (psn->fhand_r && (which & HANDLE_READ)) {
1403fe3f654Spooka 		assert(psn->lazyopen_r == NULL);
1413fe3f654Spooka 
1423fe3f654Spooka 		pb1 = psbuf_makeout();
1433fe3f654Spooka 		reqid = NEXTREQ(pctx);
1443fe3f654Spooka 		psbuf_req_data(pb1, SSH_FXP_CLOSE, reqid,
1453fe3f654Spooka 		    psn->fhand_r, psn->fhand_r_len);
1468ff91531Spooka 		puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb1, 1, 0);
1473fe3f654Spooka 		free(psn->fhand_r);
1483fe3f654Spooka 		psn->fhand_r = NULL;
1493fe3f654Spooka 	}
1503fe3f654Spooka 
1513fe3f654Spooka 	if (psn->fhand_w && (which & HANDLE_WRITE)) {
1523fe3f654Spooka 		assert(psn->lazyopen_w == NULL);
1533fe3f654Spooka 
1543fe3f654Spooka 		pb2 = psbuf_makeout();
1553fe3f654Spooka 		reqid = NEXTREQ(pctx);
1563fe3f654Spooka 		psbuf_req_data(pb2, SSH_FXP_CLOSE, reqid,
1573fe3f654Spooka 		    psn->fhand_w, psn->fhand_w_len);
1588ff91531Spooka 		puffs_framev_enqueue_justsend(pu, pctx->sshfd_data, pb2, 1, 0);
1593fe3f654Spooka 		free(psn->fhand_w);
1603fe3f654Spooka 		psn->fhand_w = NULL;
1613fe3f654Spooka 	}
1623fe3f654Spooka 
1633fe3f654Spooka 	psn->stat |= PSN_HANDLECLOSE;
1643fe3f654Spooka }
1653fe3f654Spooka 
1663fe3f654Spooka void
lazyopen_rresp(struct puffs_usermount * pu,struct puffs_framebuf * pb,void * arg,int error)1673fe3f654Spooka lazyopen_rresp(struct puffs_usermount *pu, struct puffs_framebuf *pb,
1683fe3f654Spooka 	void *arg, int error)
1693fe3f654Spooka {
1703fe3f654Spooka 	struct psshfs_node *psn = arg;
1713fe3f654Spooka 
1723fe3f654Spooka 	/* XXX: this is not enough */
1733fe3f654Spooka 	if (psn->stat & PSN_RECLAIMED) {
1743fe3f654Spooka 		error = ENOENT;
1753fe3f654Spooka 		goto moreout;
1763fe3f654Spooka 	}
1773fe3f654Spooka 	if (error)
1783fe3f654Spooka 		goto out;
1793fe3f654Spooka 
1803fe3f654Spooka 	error = psbuf_expect_handle(pb, &psn->fhand_r, &psn->fhand_r_len);
1813fe3f654Spooka 
1823fe3f654Spooka  out:
1833fe3f654Spooka 	psn->lazyopen_err_r = error;
1843fe3f654Spooka 	psn->lazyopen_r = NULL;
1853fe3f654Spooka 	if (error)
1863fe3f654Spooka 		psn->stat &= ~PSN_DOLAZY_R;
1873fe3f654Spooka 	if (psn->stat & PSN_HANDLECLOSE && (psn->stat & PSN_LAZYWAIT_R) == 0)
1883fe3f654Spooka 		closehandles(pu, psn, HANDLE_READ);
1893fe3f654Spooka  moreout:
1903fe3f654Spooka 	puffs_framebuf_destroy(pb);
1913fe3f654Spooka }
1923fe3f654Spooka 
1933fe3f654Spooka void
lazyopen_wresp(struct puffs_usermount * pu,struct puffs_framebuf * pb,void * arg,int error)1943fe3f654Spooka lazyopen_wresp(struct puffs_usermount *pu, struct puffs_framebuf *pb,
1953fe3f654Spooka 	void *arg, int error)
1963fe3f654Spooka {
1973fe3f654Spooka 	struct psshfs_node *psn = arg;
1983fe3f654Spooka 
1993fe3f654Spooka 	/* XXX: this is not enough */
2003fe3f654Spooka 	if (psn->stat & PSN_RECLAIMED) {
2013fe3f654Spooka 		error = ENOENT;
2023fe3f654Spooka 		goto moreout;
2033fe3f654Spooka 	}
2043fe3f654Spooka 	if (error)
2053fe3f654Spooka 		goto out;
2063fe3f654Spooka 
2073fe3f654Spooka 	error = psbuf_expect_handle(pb, &psn->fhand_w, &psn->fhand_w_len);
2083fe3f654Spooka 
2093fe3f654Spooka  out:
2103fe3f654Spooka 	psn->lazyopen_err_w = error;
2113fe3f654Spooka 	psn->lazyopen_w = NULL;
2123fe3f654Spooka 	if (error)
2133fe3f654Spooka 		psn->stat &= ~PSN_DOLAZY_W;
2143fe3f654Spooka 	if (psn->stat & PSN_HANDLECLOSE && (psn->stat & PSN_LAZYWAIT_W) == 0)
2153fe3f654Spooka 		closehandles(pu, psn, HANDLE_WRITE);
2163fe3f654Spooka  moreout:
2173fe3f654Spooka 	puffs_framebuf_destroy(pb);
2183fe3f654Spooka }
2193fe3f654Spooka 
220306e0025Spooka struct readdirattr {
221306e0025Spooka 	struct psshfs_node *psn;
222306e0025Spooka 	int idx;
223306e0025Spooka 	char entryname[MAXPATHLEN+1];
224306e0025Spooka };
225306e0025Spooka 
226c3ef8ea5Spooka int
getpathattr(struct puffs_usermount * pu,const char * path,struct vattr * vap)22721913eabSpooka getpathattr(struct puffs_usermount *pu, const char *path, struct vattr *vap)
2287517e20cSpooka {
22921913eabSpooka 	PSSHFSAUTOVAR(pu);
2307517e20cSpooka 
2317517e20cSpooka 	psbuf_req_str(pb, SSH_FXP_LSTAT, reqid, path);
2328ff91531Spooka 	GETRESPONSE(pb, pctx->sshfd);
2337517e20cSpooka 
2347517e20cSpooka 	rv = psbuf_expect_attrs(pb, vap);
2357517e20cSpooka 
236e3468dbcSpooka  out:
2377517e20cSpooka 	PSSHFSRETURN(rv);
2387517e20cSpooka }
2397517e20cSpooka 
2407517e20cSpooka int
getnodeattr(struct puffs_usermount * pu,struct puffs_node * pn,const char * path)241326638b5Spooka getnodeattr(struct puffs_usermount *pu, struct puffs_node *pn, const char *path)
2427517e20cSpooka {
243a56f46a5Spooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
2447517e20cSpooka 	struct psshfs_node *psn = pn->pn_data;
2457517e20cSpooka 	struct vattr va;
246f845b0daSpooka 	int rv;
2477517e20cSpooka 
248a56f46a5Spooka 	if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) {
249326638b5Spooka 		rv = getpathattr(pu, path ? path : PNPATH(pn), &va);
2507517e20cSpooka 		if (rv)
2517517e20cSpooka 			return rv;
2527517e20cSpooka 
253968196afSpooka 		setpnva(pu, pn, &va);
2547517e20cSpooka 	}
2557517e20cSpooka 
2567517e20cSpooka 	return 0;
2577517e20cSpooka }
2587517e20cSpooka 
2597517e20cSpooka int
sftp_readdir(struct puffs_usermount * pu,struct psshfs_ctx * pctx,struct puffs_node * pn)260a86a0093Spooka sftp_readdir(struct puffs_usermount *pu, struct psshfs_ctx *pctx,
261c3ef8ea5Spooka 	struct puffs_node *pn)
262c3ef8ea5Spooka {
263a86a0093Spooka 	struct puffs_cc *pcc = puffs_cc_getcc(pu);
264c3ef8ea5Spooka 	struct psshfs_node *psn = pn->pn_data;
265c3ef8ea5Spooka 	struct psshfs_dir *olddir, *testd;
2660e7bdfc1Spooka 	struct puffs_framebuf *pb;
267c3ef8ea5Spooka 	uint32_t reqid = NEXTREQ(pctx);
268ad70c5abSpooka 	uint32_t count, dhandlen;
269c3ef8ea5Spooka 	char *dhand = NULL;
270ad70c5abSpooka 	size_t nent;
27138602220Spooka 	char *longname = NULL;
2725866121cSpooka 	size_t idx;
2735866121cSpooka 	int rv;
274c3ef8ea5Spooka 
275c3ef8ea5Spooka 	assert(pn->pn_va.va_type == VDIR);
276e3468dbcSpooka 	idx = 0;
277e3468dbcSpooka 	olddir = psn->dir;
278e3468dbcSpooka 	nent = psn->dentnext;
279c3ef8ea5Spooka 
280c5afd8dcSpooka 	if (psn->dir && psn->dentread
281c5afd8dcSpooka 	    && !REFRESHTIMEOUT(pctx, time(NULL) - psn->dentread))
282c3ef8ea5Spooka 		return 0;
283c3ef8ea5Spooka 
284375257e2Spooka 	if (psn->dentread) {
28523c02977Spooka 		if ((rv = puffs_inval_namecache_dir(pu, pn)))
28623c02977Spooka 			warn("readdir: dcache inval fail %p", pn);
287375257e2Spooka 	}
2889da6af0aSpooka 
2890e7bdfc1Spooka 	pb = psbuf_makeout();
2905d293715Spooka 	psbuf_req_str(pb, SSH_FXP_OPENDIR, reqid, PNPATH(pn));
291c64b357fSpooka 	if (puffs_framev_enqueue_cc(pcc, pctx->sshfd, pb, 0) == -1) {
292c64b357fSpooka 		rv = errno;
293c64b357fSpooka 		goto wayout;
294c64b357fSpooka 	}
295c3ef8ea5Spooka 	rv = psbuf_expect_handle(pb, &dhand, &dhandlen);
296c3ef8ea5Spooka 	if (rv)
297c3ef8ea5Spooka 		goto wayout;
298c3ef8ea5Spooka 
299c3ef8ea5Spooka 	/*
300c3ef8ea5Spooka 	 * Well, the following is O(n^2), so feel free to improve if it
301c3ef8ea5Spooka 	 * gets too taxing on your system.
302c3ef8ea5Spooka 	 */
303c3ef8ea5Spooka 
304306e0025Spooka 	/*
305306e0025Spooka 	 * note: for the "getattr in batch" to work, this must be before
306306e0025Spooka 	 * the attribute-getting.  Otherwise times for first entries in
307306e0025Spooka 	 * large directories might expire before the directory itself and
308306e0025Spooka 	 * result in one-by-one attribute fetching.
309306e0025Spooka 	 */
310306e0025Spooka 	psn->dentread = time(NULL);
311306e0025Spooka 
312c3ef8ea5Spooka 	psn->dentnext = 0;
313c3ef8ea5Spooka 	psn->denttot = 0;
314c3ef8ea5Spooka 	psn->dir = NULL;
315c3ef8ea5Spooka 
316c3ef8ea5Spooka 	for (;;) {
317c3ef8ea5Spooka 		reqid = NEXTREQ(pctx);
3180e7bdfc1Spooka 		psbuf_recycleout(pb);
319c3ef8ea5Spooka 		psbuf_req_data(pb, SSH_FXP_READDIR, reqid, dhand, dhandlen);
3208ff91531Spooka 		GETRESPONSE(pb, pctx->sshfd);
321c3ef8ea5Spooka 
322c3ef8ea5Spooka 		/* check for EOF */
3230e7bdfc1Spooka 		if (psbuf_get_type(pb) == SSH_FXP_STATUS) {
324c3ef8ea5Spooka 			rv = psbuf_expect_status(pb);
325c3ef8ea5Spooka 			goto out;
326c3ef8ea5Spooka 		}
327c3ef8ea5Spooka 		rv = psbuf_expect_name(pb, &count);
328c3ef8ea5Spooka 		if (rv)
329c3ef8ea5Spooka 			goto out;
330c3ef8ea5Spooka 
331c3ef8ea5Spooka 		for (; count--; idx++) {
332c3ef8ea5Spooka 			if (idx == psn->denttot)
333c3ef8ea5Spooka 				allocdirs(psn);
3340e7bdfc1Spooka 			if ((rv = psbuf_get_str(pb,
3350e7bdfc1Spooka 			    &psn->dir[idx].entryname, NULL)))
336c3ef8ea5Spooka 				goto out;
33738602220Spooka 			if ((rv = psbuf_get_str(pb, &longname, NULL)) != 0)
338c3ef8ea5Spooka 				goto out;
33938602220Spooka 			if ((rv = psbuf_get_vattr(pb, &psn->dir[idx].va)) != 0)
340c3ef8ea5Spooka 				goto out;
341c3ef8ea5Spooka 			if (sscanf(longname, "%*s%d",
342c3ef8ea5Spooka 			    &psn->dir[idx].va.va_nlink) != 1) {
343c3ef8ea5Spooka 				rv = EPROTO;
344c3ef8ea5Spooka 				goto out;
345c3ef8ea5Spooka 			}
346c3ef8ea5Spooka 			free(longname);
34738602220Spooka 			longname = NULL;
348c3ef8ea5Spooka 
34900b48f3eSpooka 			/*
35073655f9fSpooka 			 * In case of DOT, copy the attributes (mostly
35173655f9fSpooka 			 * because we want the link count for the root dir).
35273655f9fSpooka 			 */
35373655f9fSpooka 			if (strcmp(psn->dir[idx].entryname, ".") == 0) {
35473655f9fSpooka 				setpnva(pu, pn, &psn->dir[idx].va);
35573655f9fSpooka 			}
35673655f9fSpooka 
35773655f9fSpooka 			/*
35800b48f3eSpooka 			 * Check if we already have a psshfs_dir for the
35900b48f3eSpooka 			 * name we are processing.  If so, use the old one.
36000b48f3eSpooka 			 * If not, create a new one
36100b48f3eSpooka 			 */
362c3ef8ea5Spooka 			testd = lookup(olddir, nent, psn->dir[idx].entryname);
363c3ef8ea5Spooka 			if (testd) {
364c3ef8ea5Spooka 				psn->dir[idx].entry = testd->entry;
36500b48f3eSpooka 				/*
36600b48f3eSpooka 				 * Has entry.  Update attributes to what
36700b48f3eSpooka 				 * we just got from the server.
36800b48f3eSpooka 				 */
36900b48f3eSpooka 				if (testd->entry) {
370f845b0daSpooka 					setpnva(pu, testd->entry,
371f845b0daSpooka 					    &psn->dir[idx].va);
372074f023aSpooka 					psn->dir[idx].va.va_fileid
373074f023aSpooka 					    = testd->entry->pn_va.va_fileid;
37400b48f3eSpooka 
37500b48f3eSpooka 				/*
37600b48f3eSpooka 				 * No entry.  This can happen in two cases:
37700b48f3eSpooka 				 * 1) the file was created "behind our back"
37800b48f3eSpooka 				 *    on the server
37900b48f3eSpooka 				 * 2) we do two readdirs before we instantiate
38000b48f3eSpooka 				 *    the node (or run with -t 0).
38100b48f3eSpooka 				 *
38200b48f3eSpooka 				 * Cache attributes from the server in
38300b48f3eSpooka 				 * case we want to instantiate this node
384343d2779Spooka 				 * soon.  Also preserve the old inode number
385343d2779Spooka 				 * which was given when the dirent was created.
38600b48f3eSpooka 				 */
38700b48f3eSpooka 				} else {
38800b48f3eSpooka 					psn->dir[idx].va.va_fileid
389794adab5Spooka 					    = testd->va.va_fileid;
390794adab5Spooka 					testd->va = psn->dir[idx].va;
39100b48f3eSpooka 				}
39200b48f3eSpooka 
39300b48f3eSpooka 			/* No previous entry?  Initialize this one. */
394c3ef8ea5Spooka 			} else {
395c3ef8ea5Spooka 				psn->dir[idx].entry = NULL;
396c3ef8ea5Spooka 				psn->dir[idx].va.va_fileid = pctx->nextino++;
397c3ef8ea5Spooka 			}
398f845b0daSpooka 			psn->dir[idx].attrread = psn->dentread;
399c3ef8ea5Spooka 			psn->dir[idx].valid = 1;
400c3ef8ea5Spooka 		}
401c3ef8ea5Spooka 	}
402c3ef8ea5Spooka 
403c3ef8ea5Spooka  out:
404c3ef8ea5Spooka 	/* XXX: rv */
405c3ef8ea5Spooka 	psn->dentnext = idx;
406c3ef8ea5Spooka 	freedircache(olddir, nent);
407c3ef8ea5Spooka 
408c3ef8ea5Spooka 	reqid = NEXTREQ(pctx);
4090e7bdfc1Spooka 	psbuf_recycleout(pb);
410c3ef8ea5Spooka 	psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, dhand, dhandlen);
411485aeac1Spooka 	puffs_framev_enqueue_justsend(pu, pctx->sshfd, pb, 1, 0);
412f446ae6bSpooka 	free(dhand);
41338602220Spooka 	free(longname);
414a769baf0Spooka 
4157517e20cSpooka 	return rv;
416c3ef8ea5Spooka 
417c3ef8ea5Spooka  wayout:
418c3ef8ea5Spooka 	free(dhand);
419f446ae6bSpooka 	PSSHFSRETURN(rv);
420c3ef8ea5Spooka }
421c3ef8ea5Spooka 
422c3ef8ea5Spooka struct puffs_node *
makenode(struct puffs_usermount * pu,struct puffs_node * parent,const struct psshfs_dir * pd,const struct vattr * vap)423c3ef8ea5Spooka makenode(struct puffs_usermount *pu, struct puffs_node *parent,
424326638b5Spooka 	const struct psshfs_dir *pd, const struct vattr *vap)
425c3ef8ea5Spooka {
426c3ef8ea5Spooka 	struct psshfs_node *psn_parent = parent->pn_data;
427c3ef8ea5Spooka 	struct psshfs_node *psn;
428c3ef8ea5Spooka 	struct puffs_node *pn;
429c3ef8ea5Spooka 
430c3ef8ea5Spooka 	psn = emalloc(sizeof(struct psshfs_node));
431c3ef8ea5Spooka 	memset(psn, 0, sizeof(struct psshfs_node));
432c3ef8ea5Spooka 
433c3ef8ea5Spooka 	pn = puffs_pn_new(pu, psn);
434c3ef8ea5Spooka 	if (!pn) {
435c3ef8ea5Spooka 		free(psn);
436c3ef8ea5Spooka 		return NULL;
437c3ef8ea5Spooka 	}
438968196afSpooka 	setpnva(pu, pn, &pd->va);
439968196afSpooka 	setpnva(pu, pn, vap);
440f845b0daSpooka 	psn->attrread = pd->attrread;
441c3ef8ea5Spooka 
442c3ef8ea5Spooka 	psn->parent = parent;
443c3ef8ea5Spooka 	psn_parent->childcount++;
444c3ef8ea5Spooka 
4458543b6baSpooka 	TAILQ_INIT(&psn->pw);
4469b93e5eaSpooka 
447c3ef8ea5Spooka 	return pn;
448c3ef8ea5Spooka }
449c3ef8ea5Spooka 
450c3ef8ea5Spooka struct puffs_node *
allocnode(struct puffs_usermount * pu,struct puffs_node * parent,const char * entryname,const struct vattr * vap)451c3ef8ea5Spooka allocnode(struct puffs_usermount *pu, struct puffs_node *parent,
452c3ef8ea5Spooka 	const char *entryname, const struct vattr *vap)
453c3ef8ea5Spooka {
45495a18d20Spooka 	struct psshfs_ctx *pctx = puffs_getspecific(pu);
455c3ef8ea5Spooka 	struct psshfs_dir *pd;
45694f62e9bSpooka 	struct puffs_node *pn;
457c3ef8ea5Spooka 
458c3ef8ea5Spooka 	pd = direnter(parent, entryname);
459c3ef8ea5Spooka 
460c3ef8ea5Spooka 	pd->va.va_fileid = pctx->nextino++;
461c53a62f5Spooka 	if (vap->va_type == VDIR) {
462c3ef8ea5Spooka 		pd->va.va_nlink = 2;
463c53a62f5Spooka 		parent->pn_va.va_nlink++;
464c53a62f5Spooka 	} else {
465c3ef8ea5Spooka 		pd->va.va_nlink = 1;
466c53a62f5Spooka 	}
467c3ef8ea5Spooka 
46894f62e9bSpooka 	pn = makenode(pu, parent, pd, vap);
469326638b5Spooka 	if (pn) {
47094f62e9bSpooka 		pd->va.va_fileid = pn->pn_va.va_fileid;
471326638b5Spooka 		pd->entry = pn;
472326638b5Spooka 	}
47394f62e9bSpooka 
47494f62e9bSpooka 	return pn;
475c3ef8ea5Spooka }
476c3ef8ea5Spooka 
477c3ef8ea5Spooka struct psshfs_dir *
direnter(struct puffs_node * parent,const char * entryname)478c3ef8ea5Spooka direnter(struct puffs_node *parent, const char *entryname)
479c3ef8ea5Spooka {
480c3ef8ea5Spooka 	struct psshfs_node *psn_parent = parent->pn_data;
481c3ef8ea5Spooka 	struct psshfs_dir *pd;
482c3ef8ea5Spooka 	int i;
483c3ef8ea5Spooka 
484c3ef8ea5Spooka 	/* create directory entry */
485c3ef8ea5Spooka 	if (psn_parent->denttot == psn_parent->dentnext)
486c3ef8ea5Spooka 		allocdirs(psn_parent);
487c3ef8ea5Spooka 
488c3ef8ea5Spooka 	i = psn_parent->dentnext;
489c3ef8ea5Spooka 	pd = &psn_parent->dir[i];
490c3ef8ea5Spooka 	pd->entryname = estrdup(entryname);
491c3ef8ea5Spooka 	pd->valid = 1;
492306e0025Spooka 	pd->attrread = 0;
4936ee2e9c0Spooka 	puffs_vattr_null(&pd->va);
494c3ef8ea5Spooka 	psn_parent->dentnext++;
495c3ef8ea5Spooka 
496c3ef8ea5Spooka 	return pd;
497c3ef8ea5Spooka }
498c3ef8ea5Spooka 
499c3ef8ea5Spooka void
doreclaim(struct puffs_node * pn)500f5fed534Spooka doreclaim(struct puffs_node *pn)
501f5fed534Spooka {
502f5fed534Spooka 	struct psshfs_node *psn = pn->pn_data;
503f5fed534Spooka 	struct psshfs_node *psn_parent;
504f5fed534Spooka 	struct psshfs_dir *dent;
505f5fed534Spooka 
506f5fed534Spooka 	psn_parent = psn->parent->pn_data;
507f5fed534Spooka 	psn_parent->childcount--;
508f5fed534Spooka 
509f446ae6bSpooka 	/*
510f446ae6bSpooka 	 * Null out entry from directory.  Do not treat a missing entry
511f446ae6bSpooka 	 * as an invariant error, since the node might be removed from
512f446ae6bSpooka 	 * under us, and we might do a readdir before the reclaim resulting
513f446ae6bSpooka 	 * in no directory entry in the parent directory.
514f446ae6bSpooka 	 */
515f5fed534Spooka 	dent = lookup_by_entry(psn_parent->dir, psn_parent->dentnext, pn);
516f446ae6bSpooka 	if (dent)
517f5fed534Spooka 		dent->entry = NULL;
518f5fed534Spooka 
519a769baf0Spooka 	if (pn->pn_va.va_type == VDIR) {
520f5fed534Spooka 		freedircache(psn->dir, psn->dentnext);
521d32c8fa5Spooka 		psn->denttot = psn->dentnext = 0;
522a769baf0Spooka 	}
5236b4d0688Spooka 	if (psn->symlink)
5246b4d0688Spooka 		free(psn->symlink);
525f5fed534Spooka 
526f5fed534Spooka 	puffs_pn_put(pn);
527f5fed534Spooka }
528f5fed534Spooka 
529f5fed534Spooka void
nukenode(struct puffs_node * node,const char * entryname,int reclaim)530f5fed534Spooka nukenode(struct puffs_node *node, const char *entryname, int reclaim)
531c3ef8ea5Spooka {
532c3ef8ea5Spooka 	struct psshfs_node *psn, *psn_parent;
533c3ef8ea5Spooka 	struct psshfs_dir *pd;
534c3ef8ea5Spooka 
535c3ef8ea5Spooka 	psn = node->pn_data;
536c3ef8ea5Spooka 	psn_parent = psn->parent->pn_data;
537c3ef8ea5Spooka 	pd = lookup(psn_parent->dir, psn_parent->dentnext, entryname);
538c3ef8ea5Spooka 	assert(pd != NULL);
539c3ef8ea5Spooka 	pd->valid = 0;
540c3ef8ea5Spooka 	free(pd->entryname);
541c3ef8ea5Spooka 	pd->entryname = NULL;
542c3ef8ea5Spooka 
543f5fed534Spooka 	if (node->pn_va.va_type == VDIR)
544c3ef8ea5Spooka 		psn->parent->pn_va.va_nlink--;
5450ac91974Spooka 
546f5fed534Spooka 	if (reclaim)
547f5fed534Spooka 		doreclaim(node);
548c3ef8ea5Spooka }
549