xref: /netbsd-src/share/examples/puffs/pgfs/pgfs_puffs.c (revision 682be92a684b18aebce7601d6f3e24d42786cc2f)
1*682be92aSsnj /*	$NetBSD: pgfs_puffs.c,v 1.5 2014/10/18 07:11:07 snj Exp $	*/
249413e37Syamt 
349413e37Syamt /*-
449413e37Syamt  * Copyright (c)2010,2011 YAMAMOTO Takashi,
549413e37Syamt  * All rights reserved.
649413e37Syamt  *
749413e37Syamt  * Redistribution and use in source and binary forms, with or without
849413e37Syamt  * modification, are permitted provided that the following conditions
949413e37Syamt  * are met:
1049413e37Syamt  * 1. Redistributions of source code must retain the above copyright
1149413e37Syamt  *    notice, this list of conditions and the following disclaimer.
1249413e37Syamt  * 2. Redistributions in binary form must reproduce the above copyright
1349413e37Syamt  *    notice, this list of conditions and the following disclaimer in the
1449413e37Syamt  *    documentation and/or other materials provided with the distribution.
1549413e37Syamt  *
1649413e37Syamt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1749413e37Syamt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1849413e37Syamt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1949413e37Syamt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2049413e37Syamt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2149413e37Syamt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2249413e37Syamt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2349413e37Syamt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2449413e37Syamt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2549413e37Syamt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2649413e37Syamt  * SUCH DAMAGE.
2749413e37Syamt  */
2849413e37Syamt 
2949413e37Syamt /*
3049413e37Syamt  * puffs node ops and fs ops.
3149413e37Syamt  */
3249413e37Syamt 
3349413e37Syamt #include <sys/cdefs.h>
3449413e37Syamt #ifndef lint
35*682be92aSsnj __RCSID("$NetBSD: pgfs_puffs.c,v 1.5 2014/10/18 07:11:07 snj Exp $");
3649413e37Syamt #endif /* not lint */
3749413e37Syamt 
3849413e37Syamt #include <assert.h>
3949413e37Syamt #include <err.h>
4049413e37Syamt #include <errno.h>
4149413e37Syamt #include <puffs.h>
4249413e37Syamt #include <inttypes.h>
4349413e37Syamt #include <stdarg.h>
4449413e37Syamt #include <stdbool.h>
4549413e37Syamt #include <stdio.h>
4649413e37Syamt #include <stdlib.h>
4749413e37Syamt #include <time.h>
4849413e37Syamt #include <util.h>
4949413e37Syamt 
5049413e37Syamt #include <libpq-fe.h>
5149413e37Syamt #include <libpq/libpq-fs.h>	/* INV_* */
5249413e37Syamt 
5349413e37Syamt #include "pgfs.h"
5449413e37Syamt #include "pgfs_db.h"
5549413e37Syamt #include "pgfs_subs.h"
5649413e37Syamt #include "pgfs_debug.h"
5749413e37Syamt 
5849413e37Syamt static fileid_t
cookie_to_fileid(puffs_cookie_t cookie)5949413e37Syamt cookie_to_fileid(puffs_cookie_t cookie)
6049413e37Syamt {
6149413e37Syamt 
6249413e37Syamt 	return (fileid_t)(uintptr_t)cookie;
6349413e37Syamt }
6449413e37Syamt 
6549413e37Syamt static puffs_cookie_t
fileid_to_cookie(fileid_t id)6649413e37Syamt fileid_to_cookie(fileid_t id)
6749413e37Syamt {
6849413e37Syamt 	puffs_cookie_t cookie = (puffs_cookie_t)(uintptr_t)id;
6949413e37Syamt 
7049413e37Syamt 	/* XXX not true for 32-bit ports */
7149413e37Syamt 	assert(cookie_to_fileid(cookie) == id);
7249413e37Syamt 	return cookie;
7349413e37Syamt }
7449413e37Syamt 
7549413e37Syamt puffs_cookie_t
pgfs_root_cookie(void)7649413e37Syamt pgfs_root_cookie(void)
7749413e37Syamt {
7849413e37Syamt 
7949413e37Syamt 	return fileid_to_cookie(PGFS_ROOT_FILEID);
8049413e37Syamt }
8149413e37Syamt 
8249413e37Syamt int
pgfs_node_getattr(struct puffs_usermount * pu,puffs_cookie_t opc,struct vattr * va,const struct puffs_cred * pcr)8349413e37Syamt pgfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
8449413e37Syamt     struct vattr *va, const struct puffs_cred *pcr)
8549413e37Syamt {
8649413e37Syamt 	struct Xconn *xc;
8749413e37Syamt 	struct fileid_lock_handle *lock;
8849413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
8949413e37Syamt 	int error;
9049413e37Syamt 
9149413e37Syamt 	DPRINTF("%llu\n", fileid);
9249413e37Syamt 	lock = fileid_lock(fileid, puffs_cc_getcc(pu));
9349413e37Syamt retry:
94cb9c1205Syamt 	xc = begin_readonly(pu, "getattr");
9549413e37Syamt 	error = getattr(xc, fileid, va, GETATTR_ALL);
9649413e37Syamt 	if (error != 0) {
9749413e37Syamt 		goto got_error;
9849413e37Syamt 	}
9949413e37Syamt 	error = commit(xc);
10049413e37Syamt 	if (error != 0) {
10149413e37Syamt 		goto got_error;
10249413e37Syamt 	}
10349413e37Syamt 	goto done;
10449413e37Syamt got_error:
10549413e37Syamt 	rollback(xc);
10649413e37Syamt 	if (error == EAGAIN) {
10749413e37Syamt 		goto retry;
10849413e37Syamt 	}
10949413e37Syamt done:
11049413e37Syamt 	fileid_unlock(lock);
11149413e37Syamt 	return error;
11249413e37Syamt }
11349413e37Syamt 
11449413e37Syamt #define	PGFS_DIRCOOKIE_DOT	0	/* . entry */
11549413e37Syamt #define	PGFS_DIRCOOKIE_DOTDOT	1	/* .. entry */
11649413e37Syamt #define	PGFS_DIRCOOKIE_EOD	2	/* end of directory */
11749413e37Syamt 
11849413e37Syamt int
pgfs_node_readdir(struct puffs_usermount * pu,puffs_cookie_t opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)11949413e37Syamt pgfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
12049413e37Syamt     struct dirent *dent, off_t *readoff, size_t *reslen,
12149413e37Syamt     const struct puffs_cred *pcr, int *eofflag, off_t *cookies,
12249413e37Syamt     size_t *ncookies)
12349413e37Syamt {
12449413e37Syamt 	fileid_t parent_fileid;
12549413e37Syamt 	fileid_t child_fileid;
12649413e37Syamt 	uint64_t cookie;
12749413e37Syamt 	uint64_t nextcookie;
12849413e37Syamt 	uint64_t offset;
12949413e37Syamt 	struct Xconn *xc = NULL;
13049413e37Syamt 	static const Oid types[] = {
13149413e37Syamt 		TEXTOID,	/* name */
13249413e37Syamt 		INT8OID,	/* cookie */
13349413e37Syamt 		INT8OID,	/* nextcookie */
13449413e37Syamt 		INT8OID,	/* child_fileid */
13549413e37Syamt 	};
13649413e37Syamt 	const char *name;
13749413e37Syamt 	char *nametofree = NULL;
13849413e37Syamt 	struct fetchstatus s;
13949413e37Syamt 	int error;
14049413e37Syamt 	bool fetching;
14149413e37Syamt 	bool bufferfull;
14249413e37Syamt 
14349413e37Syamt 	parent_fileid = cookie_to_fileid(opc);
14449413e37Syamt 	offset = *readoff;
14549413e37Syamt 	DPRINTF("%llu %" PRIu64 "\n", parent_fileid, offset);
14649413e37Syamt 	*ncookies = 0;
14749413e37Syamt 	fetching = false;
14849413e37Syamt next:
14949413e37Syamt 	if (offset == PGFS_DIRCOOKIE_DOT) {
15049413e37Syamt 		name = ".";
15149413e37Syamt 		child_fileid = parent_fileid;
15249413e37Syamt 		cookie = offset;
15349413e37Syamt 		nextcookie = PGFS_DIRCOOKIE_DOTDOT;
15449413e37Syamt 		goto store_and_next;
15549413e37Syamt 	}
15649413e37Syamt 	if (offset == PGFS_DIRCOOKIE_DOTDOT) {
15749413e37Syamt 		if (parent_fileid != PGFS_ROOT_FILEID) {
15849413e37Syamt 			if (xc == NULL) {
159cb9c1205Syamt 				xc = begin(pu, "readdir1");
16049413e37Syamt 			}
16149413e37Syamt 			error = lookupp(xc, parent_fileid, &child_fileid);
16249413e37Syamt 			if (error != 0) {
16349413e37Syamt 				rollback(xc);
16449413e37Syamt 				return error;
16549413e37Syamt 			}
16649413e37Syamt 		} else {
16749413e37Syamt 			child_fileid = parent_fileid;
16849413e37Syamt 		}
16949413e37Syamt 		name = "..";
17049413e37Syamt 		cookie = offset;
17149413e37Syamt 		nextcookie = PGFS_DIRCOOKIE_EOD + 1;
17249413e37Syamt 		goto store_and_next;
17349413e37Syamt 	}
17449413e37Syamt 	if (offset == PGFS_DIRCOOKIE_EOD) {
17549413e37Syamt 		*eofflag = 1;
17649413e37Syamt 		goto done;
17749413e37Syamt 	}
17849413e37Syamt 	/* offset > PGFS_DIRCOOKIE_EOD; normal entries */
17949413e37Syamt 	if (xc == NULL) {
180cb9c1205Syamt 		xc = begin(pu, "readdir2");
18149413e37Syamt 	}
18249413e37Syamt 	if (!fetching) {
18349413e37Syamt 		static struct cmd *c;
18449413e37Syamt 
18549413e37Syamt 		/*
18649413e37Syamt 		 * a simpler query like "ORDER BY name OFFSET :offset - 3"
18749413e37Syamt 		 * would work well for most of cases.  however, it doesn't for
18849413e37Syamt 		 * applications which expect readdir cookies are kept valid
18949413e37Syamt 		 * even after unlink of other entries in the directory.
19049413e37Syamt 		 * eg. cvs, bonnie++
19149413e37Syamt 		 *
19249413e37Syamt 		 * 2::int8 == PGFS_DIRCOOKIE_EOD
19349413e37Syamt 		 */
19449413e37Syamt 		CREATECMD(c,
19549413e37Syamt 			"SELECT name, cookie, "
19649413e37Syamt 			"lead(cookie, 1, 2::int8) OVER (ORDER BY cookie), "
19749413e37Syamt 			"child_fileid "
19849413e37Syamt 			"FROM dirent "
19949413e37Syamt 			"WHERE parent_fileid = $1 "
20049413e37Syamt 			"AND cookie >= $2 "
20149413e37Syamt 			"ORDER BY cookie", INT8OID, INT8OID);
20249413e37Syamt 		error = sendcmd(xc, c, parent_fileid, offset);
20349413e37Syamt 		if (error != 0) {
20449413e37Syamt 			rollback(xc);
20549413e37Syamt 			return error;
20649413e37Syamt 		}
20749413e37Syamt 		fetching = true;
20849413e37Syamt 		fetchinit(&s, xc);
20949413e37Syamt 	}
21049413e37Syamt 	/*
21149413e37Syamt 	 * fetch and process an entry
21249413e37Syamt 	 */
21349413e37Syamt 	error = FETCHNEXT(&s, types, &nametofree, &cookie, &nextcookie,
21449413e37Syamt 	    &child_fileid);
21549413e37Syamt 	if (error == ENOENT) {
21649413e37Syamt 		DPRINTF("ENOENT\n");
21749413e37Syamt 		if (offset == PGFS_DIRCOOKIE_EOD + 1) {
21849413e37Syamt 			DPRINTF("empty directory\n");
21949413e37Syamt 			*eofflag = 1;
22049413e37Syamt 			goto done;
22149413e37Syamt 		}
22249413e37Syamt 		fetchdone(&s);
22349413e37Syamt 		rollback(xc);
22449413e37Syamt 		return EINVAL;
22549413e37Syamt 	}
22649413e37Syamt 	if (error != 0) {
22749413e37Syamt 		DPRINTF("error %d\n", error);
22849413e37Syamt 		fetchdone(&s);
22949413e37Syamt 		rollback(xc);
23049413e37Syamt 		return error;
23149413e37Syamt 	}
23249413e37Syamt 	if (offset != cookie && offset != PGFS_DIRCOOKIE_EOD + 1) {
23349413e37Syamt 		free(nametofree);
23449413e37Syamt 		fetchdone(&s);
23549413e37Syamt 		rollback(xc);
23649413e37Syamt 		return EINVAL;
23749413e37Syamt 	}
23849413e37Syamt 	name = nametofree;
23949413e37Syamt store_and_next:
24049413e37Syamt 	/*
24149413e37Syamt 	 * store an entry and continue processing unless the result buffer
24249413e37Syamt 	 * is full.
24349413e37Syamt 	 */
24449413e37Syamt 	bufferfull = !puffs_nextdent(&dent, name, child_fileid, DT_UNKNOWN,
24549413e37Syamt 	    reslen);
24649413e37Syamt 	free(nametofree);
24749413e37Syamt 	nametofree = NULL;
24849413e37Syamt 	if (bufferfull) {
24949413e37Syamt 		*eofflag = 0;
25049413e37Syamt 		goto done;
25149413e37Syamt 	}
25249413e37Syamt 	PUFFS_STORE_DCOOKIE(cookies, ncookies, cookie);
25349413e37Syamt 	offset = nextcookie;
25449413e37Syamt 	*readoff = offset;
25549413e37Syamt 	goto next;
25649413e37Syamt done:
25749413e37Syamt 	/*
25849413e37Syamt 	 * cleanup and update atime of the directory.
25949413e37Syamt 	 */
26049413e37Syamt 	assert(nametofree == NULL);
26149413e37Syamt 	if (fetching) {
26249413e37Syamt 		fetchdone(&s);
26349413e37Syamt 		fetching = false;
26449413e37Syamt 	}
26549413e37Syamt 	if (xc == NULL) {
26649413e37Syamt retry:
267cb9c1205Syamt 		xc = begin(pu, "readdir3");
26849413e37Syamt 	}
26949413e37Syamt 	error = update_atime(xc, parent_fileid);
27049413e37Syamt 	if (error != 0) {
27149413e37Syamt 		goto got_error;
27249413e37Syamt 	}
27349413e37Syamt 	error = commit(xc);
27449413e37Syamt 	if (error != 0) {
27549413e37Syamt 		goto got_error;
27649413e37Syamt 	}
27749413e37Syamt 	return 0;
27849413e37Syamt got_error:
27949413e37Syamt 	rollback(xc);
28049413e37Syamt 	if (error == EAGAIN) {
28149413e37Syamt 		goto retry;
28249413e37Syamt 	}
28349413e37Syamt 	return error;
28449413e37Syamt }
28549413e37Syamt 
28649413e37Syamt int
pgfs_node_lookup(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)28749413e37Syamt pgfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
28849413e37Syamt     struct puffs_newinfo *pni, const struct puffs_cn *pcn)
28949413e37Syamt {
29049413e37Syamt 	struct vattr dva;
29149413e37Syamt 	struct vattr cva;
29249413e37Syamt 	struct puffs_cred * const pcr = pcn->pcn_cred;
29349413e37Syamt 	fileid_t parent_fileid;
29449413e37Syamt 	const char *name;
29549413e37Syamt 	fileid_t child_fileid;
29649413e37Syamt 	struct Xconn *xc;
29749413e37Syamt 	mode_t access_mode;
29849413e37Syamt 	int error;
29949413e37Syamt 	int saved_error;
30049413e37Syamt 
30149413e37Syamt 	parent_fileid = cookie_to_fileid(opc);
30249413e37Syamt 	name = pcn->pcn_name;
30349413e37Syamt 	DPRINTF("%llu %s\n", parent_fileid, name);
30449413e37Syamt 	assert(strcmp(name, ".")); /* . is handled by framework */
30549413e37Syamt retry:
306cb9c1205Syamt 	xc = begin_readonly(pu, "lookup");
30749413e37Syamt 	error = getattr(xc, parent_fileid, &dva,
30849413e37Syamt 	    GETATTR_TYPE|GETATTR_MODE|GETATTR_UID|GETATTR_GID);
30949413e37Syamt 	if (error != 0) {
31049413e37Syamt 		goto got_error;
31149413e37Syamt 	}
31249413e37Syamt 	access_mode = PUFFS_VEXEC;
31349413e37Syamt 	if ((pcn->pcn_flags & NAMEI_ISLASTCN) != 0 &&
31449413e37Syamt 	    pcn->pcn_nameiop != NAMEI_LOOKUP) {
31549413e37Syamt 		access_mode |= PUFFS_VWRITE;
31649413e37Syamt 	}
31749413e37Syamt 	error = puffs_access(dva.va_type, dva.va_mode, dva.va_uid, dva.va_gid,
31849413e37Syamt 	    access_mode, pcr);
31949413e37Syamt 	if (error != 0) {
32049413e37Syamt 		goto commit_and_return;
32149413e37Syamt 	}
32249413e37Syamt 	if (!strcmp(name, "..")) {
32349413e37Syamt 		error = lookupp(xc, parent_fileid, &child_fileid);
32449413e37Syamt 		if (error != 0) {
32549413e37Syamt 			goto got_error;
32649413e37Syamt 		}
32749413e37Syamt 	} else {
32849413e37Syamt 		static struct cmd *c;
32949413e37Syamt 		static const Oid types[] = { INT8OID, };
33049413e37Syamt 		struct fetchstatus s;
33149413e37Syamt 
33249413e37Syamt 		CREATECMD(c, "SELECT child_fileid "
33349413e37Syamt 			"FROM dirent "
334cb9c1205Syamt 			"WHERE parent_fileid = $1 AND name = $2",
33549413e37Syamt 			INT8OID, TEXTOID);
33649413e37Syamt 		error = sendcmd(xc, c, parent_fileid, name);
33749413e37Syamt 		if (error != 0) {
33849413e37Syamt 			DPRINTF("sendcmd %d\n", error);
33949413e37Syamt 			goto got_error;
34049413e37Syamt 		}
34149413e37Syamt 		fetchinit(&s, xc);
34249413e37Syamt 		error = FETCHNEXT(&s, types, &child_fileid);
34349413e37Syamt 		fetchdone(&s);
34449413e37Syamt 		if (error == ENOENT) {
34549413e37Syamt 			goto commit_and_return;
34649413e37Syamt 		}
34749413e37Syamt 		if (error != 0) {
34849413e37Syamt 			goto got_error;
34949413e37Syamt 		}
35049413e37Syamt 	}
35149413e37Syamt 	error = getattr(xc, child_fileid, &cva, GETATTR_TYPE|GETATTR_SIZE);
35249413e37Syamt 	if (error != 0) {
35349413e37Syamt 		goto got_error;
35449413e37Syamt 	}
35549413e37Syamt 	error = commit(xc);
35649413e37Syamt 	if (error != 0) {
35749413e37Syamt 		goto got_error;
35849413e37Syamt 	}
35949413e37Syamt 	puffs_newinfo_setcookie(pni, fileid_to_cookie(child_fileid));
36049413e37Syamt 	puffs_newinfo_setvtype(pni, cva.va_type);
36149413e37Syamt 	puffs_newinfo_setsize(pni, cva.va_size);
36249413e37Syamt 	return 0;
36349413e37Syamt got_error:
36449413e37Syamt 	rollback(xc);
36549413e37Syamt 	if (error == EAGAIN) {
36649413e37Syamt 		goto retry;
36749413e37Syamt 	}
36849413e37Syamt 	return error;
36949413e37Syamt commit_and_return:
37049413e37Syamt 	saved_error = error;
37149413e37Syamt 	error = commit(xc);
37249413e37Syamt 	if (error != 0) {
37349413e37Syamt 		goto got_error;
37449413e37Syamt 	}
37549413e37Syamt 	return saved_error;
37649413e37Syamt }
37749413e37Syamt 
37849413e37Syamt int
pgfs_node_mkdir(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)37949413e37Syamt pgfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
38049413e37Syamt     struct puffs_newinfo *pni, const struct puffs_cn *pcn,
38149413e37Syamt     const struct vattr *va)
38249413e37Syamt {
38349413e37Syamt 	struct Xconn *xc;
38449413e37Syamt 	fileid_t parent_fileid = cookie_to_fileid(opc);
38549413e37Syamt 	fileid_t new_fileid;
38649413e37Syamt 	struct puffs_cred * const pcr = pcn->pcn_cred;
38749413e37Syamt 	uid_t uid;
38849413e37Syamt 	gid_t gid;
38949413e37Syamt 	int error;
39049413e37Syamt 
39149413e37Syamt 	DPRINTF("%llu %s\n", parent_fileid, pcn->pcn_name);
39249413e37Syamt 	if (puffs_cred_getuid(pcr, &uid) == -1 ||
39349413e37Syamt 	    puffs_cred_getgid(pcr, &gid) == -1) {
39449413e37Syamt 		return errno;
39549413e37Syamt 	}
39649413e37Syamt retry:
397cb9c1205Syamt 	xc = begin(pu, "mkdir");
39849413e37Syamt 	error = mklinkfile(xc, parent_fileid, pcn->pcn_name, VDIR,
39949413e37Syamt 	    va->va_mode, uid, gid, &new_fileid);
40049413e37Syamt 	if (error == 0) {
40149413e37Syamt 		error = update_nlink(xc, parent_fileid, 1);
40249413e37Syamt 	}
40349413e37Syamt 	if (error != 0) {
40449413e37Syamt 		goto got_error;
40549413e37Syamt 	}
40649413e37Syamt 	error = commit(xc);
40749413e37Syamt 	if (error != 0) {
40849413e37Syamt 		goto got_error;
40949413e37Syamt 	}
41049413e37Syamt 	puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
41149413e37Syamt 	return 0;
41249413e37Syamt got_error:
41349413e37Syamt 	rollback(xc);
41449413e37Syamt 	if (error == EAGAIN) {
41549413e37Syamt 		goto retry;
41649413e37Syamt 	}
41749413e37Syamt 	return error;
41849413e37Syamt }
41949413e37Syamt 
42049413e37Syamt int
pgfs_node_create(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)42149413e37Syamt pgfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
42249413e37Syamt     struct puffs_newinfo *pni, const struct puffs_cn *pcn,
42349413e37Syamt     const struct vattr *va)
42449413e37Syamt {
42549413e37Syamt 	struct Xconn *xc;
42649413e37Syamt 	fileid_t parent_fileid = cookie_to_fileid(opc);
42749413e37Syamt 	fileid_t new_fileid;
42849413e37Syamt 	struct puffs_cred * const pcr = pcn->pcn_cred;
42949413e37Syamt 	uid_t uid;
43049413e37Syamt 	gid_t gid;
43149413e37Syamt 	int error;
43249413e37Syamt 
43349413e37Syamt 	DPRINTF("%llu %s\n", parent_fileid, pcn->pcn_name);
43449413e37Syamt 	if (puffs_cred_getuid(pcr, &uid) == -1 ||
43549413e37Syamt 	    puffs_cred_getgid(pcr, &gid) == -1) {
43649413e37Syamt 		return errno;
43749413e37Syamt 	}
43849413e37Syamt retry:
439cb9c1205Syamt 	xc = begin(pu, "create");
44049413e37Syamt 	error = mklinkfile_lo(xc, parent_fileid, pcn->pcn_name, VREG,
44149413e37Syamt 	    va->va_mode,
44249413e37Syamt 	    uid, gid, &new_fileid, NULL);
44349413e37Syamt 	if (error != 0) {
44449413e37Syamt 		goto got_error;
44549413e37Syamt 	}
44649413e37Syamt 	error = commit(xc);
44749413e37Syamt 	if (error != 0) {
44849413e37Syamt 		goto got_error;
44949413e37Syamt 	}
45049413e37Syamt 	puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
45149413e37Syamt 	return 0;
45249413e37Syamt got_error:
45349413e37Syamt 	rollback(xc);
45449413e37Syamt 	if (error == EAGAIN) {
45549413e37Syamt 		goto retry;
45649413e37Syamt 	}
45749413e37Syamt 	return error;
45849413e37Syamt }
45949413e37Syamt 
46049413e37Syamt int
pgfs_node_write(struct puffs_usermount * pu,puffs_cookie_t opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflags)46149413e37Syamt pgfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
46249413e37Syamt     uint8_t *buf, off_t offset, size_t *resid,
46349413e37Syamt     const struct puffs_cred *pcr, int ioflags)
46449413e37Syamt {
46549413e37Syamt 	struct Xconn *xc;
46649413e37Syamt 	struct fileid_lock_handle *lock;
46749413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
46849413e37Syamt 	size_t resultlen;
46949413e37Syamt 	int fd;
47049413e37Syamt 	int error;
47149413e37Syamt 
47249413e37Syamt 	if ((ioflags & PUFFS_IO_APPEND) != 0) {
47349413e37Syamt 		DPRINTF("%llu append sz %zu\n", fileid, *resid);
47449413e37Syamt 	} else {
47549413e37Syamt 		DPRINTF("%llu off %" PRIu64 " sz %zu\n", fileid,
47649413e37Syamt 		    (uint64_t)offset, *resid);
47749413e37Syamt 	}
47849413e37Syamt 	lock = fileid_lock(fileid, puffs_cc_getcc(pu));
47949413e37Syamt retry:
480cb9c1205Syamt 	xc = begin(pu, "write");
48149413e37Syamt 	error = update_mctime(xc, fileid);
48249413e37Syamt 	if (error != 0) {
48349413e37Syamt 		goto got_error;
48449413e37Syamt 	}
48549413e37Syamt 	error = lo_open_by_fileid(xc, fileid, INV_WRITE, &fd);
48649413e37Syamt 	if (error != 0) {
48749413e37Syamt 		goto got_error;
48849413e37Syamt 	}
48949413e37Syamt 	if ((ioflags & PUFFS_IO_APPEND) != 0) {
49049413e37Syamt 		int32_t off;
49149413e37Syamt 
49249413e37Syamt 		error = my_lo_lseek(xc, fd, 0, SEEK_END, &off);
49349413e37Syamt 		if (error != 0) {
49449413e37Syamt 			goto got_error;
49549413e37Syamt 		}
49649413e37Syamt 		offset = off;
49749413e37Syamt 	}
49849413e37Syamt 	if (offset < 0) {			/* negative offset */
49949413e37Syamt 		error = EINVAL;
50049413e37Syamt 		goto got_error;
50149413e37Syamt 	}
50249413e37Syamt 	if ((uint64_t)(INT64_MAX - offset) < *resid ||	/* int64 overflow */
50349413e37Syamt 	    INT_MAX < offset + *resid) {	/* our max filesize */
50449413e37Syamt 		error = EFBIG;
50549413e37Syamt 		goto got_error;
50649413e37Syamt 	}
50749413e37Syamt 	if ((ioflags & PUFFS_IO_APPEND) == 0) {
50849413e37Syamt 		error = my_lo_lseek(xc, fd, offset, SEEK_SET, NULL);
50949413e37Syamt 		if (error != 0) {
51049413e37Syamt 			goto got_error;
51149413e37Syamt 		}
51249413e37Syamt 	}
51349413e37Syamt 	error = my_lo_write(xc, fd, (const char *)buf, *resid, &resultlen);
51449413e37Syamt 	if (error != 0) {
51549413e37Syamt 		goto got_error;
51649413e37Syamt 	}
51749413e37Syamt 	assert(*resid >= resultlen);
51849413e37Syamt 	error = commit(xc);
51949413e37Syamt 	if (error != 0) {
52049413e37Syamt 		goto got_error;
52149413e37Syamt 	}
52249413e37Syamt 	*resid -= resultlen;
52349413e37Syamt 	DPRINTF("resid %zu\n", *resid);
52449413e37Syamt 	goto done;
52549413e37Syamt got_error:
52649413e37Syamt 	rollback(xc);
52749413e37Syamt 	if (error == EAGAIN) {
52849413e37Syamt 		goto retry;
52949413e37Syamt 	}
53049413e37Syamt done:
53149413e37Syamt 	fileid_unlock(lock);
53249413e37Syamt 	return error;
53349413e37Syamt }
53449413e37Syamt 
53549413e37Syamt int
pgfs_node_read(struct puffs_usermount * pu,puffs_cookie_t opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflags)53649413e37Syamt pgfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
53749413e37Syamt     uint8_t *buf, off_t offset, size_t *resid,
53849413e37Syamt     const struct puffs_cred *pcr, int ioflags)
53949413e37Syamt {
54049413e37Syamt 	struct Xconn *xc;
54149413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
54249413e37Syamt 	size_t resultlen;
54349413e37Syamt 	int fd;
54449413e37Syamt 	int error;
54549413e37Syamt 
54649413e37Syamt 	DPRINTF("%llu off %" PRIu64 " sz %zu\n",
54749413e37Syamt 	    fileid, (uint64_t)offset, *resid);
54849413e37Syamt retry:
549cb9c1205Syamt 	xc = begin(pu, "read");
55049413e37Syamt 	/*
551*682be92aSsnj 	 * try to update atime first as it's prone to conflict with other
55249413e37Syamt 	 * transactions.  eg. read-ahead requests can conflict each other.
55349413e37Syamt 	 * we don't want to retry my_lo_read as it's expensive.
55449413e37Syamt 	 *
55549413e37Syamt 	 * XXX probably worth to implement noatime mount option.
55649413e37Syamt 	 */
55749413e37Syamt 	error = update_atime(xc, fileid);
55849413e37Syamt 	if (error != 0) {
55949413e37Syamt 		goto got_error;
56049413e37Syamt 	}
56149413e37Syamt 	error = lo_open_by_fileid(xc, fileid, INV_READ, &fd);
56249413e37Syamt 	if (error != 0) {
56349413e37Syamt 		goto got_error;
56449413e37Syamt 	}
56549413e37Syamt 	error = my_lo_lseek(xc, fd, offset, SEEK_SET, NULL);
56649413e37Syamt 	if (error != 0) {
56749413e37Syamt 		goto got_error;
56849413e37Syamt 	}
56949413e37Syamt 	error = my_lo_read(xc, fd, buf, *resid, &resultlen);
57049413e37Syamt 	if (error != 0) {
57149413e37Syamt 		goto got_error;
57249413e37Syamt 	}
57349413e37Syamt 	assert(*resid >= resultlen);
57449413e37Syamt 	error = commit(xc);
57549413e37Syamt 	if (error != 0) {
57649413e37Syamt 		goto got_error;
57749413e37Syamt 	}
57849413e37Syamt 	*resid -= resultlen;
57949413e37Syamt 	return 0;
58049413e37Syamt got_error:
58149413e37Syamt 	rollback(xc);
58249413e37Syamt 	if (error == EAGAIN) {
58349413e37Syamt 		goto retry;
58449413e37Syamt 	}
58549413e37Syamt 	return error;
58649413e37Syamt }
58749413e37Syamt 
58849413e37Syamt int
pgfs_node_link(struct puffs_usermount * pu,puffs_cookie_t dir_opc,puffs_cookie_t targ_opc,const struct puffs_cn * pcn)58949413e37Syamt pgfs_node_link(struct puffs_usermount *pu, puffs_cookie_t dir_opc,
59049413e37Syamt     puffs_cookie_t targ_opc, const struct puffs_cn *pcn)
59149413e37Syamt {
59249413e37Syamt 	struct Xconn *xc;
59349413e37Syamt 	fileid_t dir_fileid = cookie_to_fileid(dir_opc);
59449413e37Syamt 	fileid_t targ_fileid = cookie_to_fileid(targ_opc);
59549413e37Syamt 	struct vattr va;
59649413e37Syamt 	int error;
59749413e37Syamt 
59849413e37Syamt 	DPRINTF("%llu %llu %s\n", dir_fileid, targ_fileid, pcn->pcn_name);
59949413e37Syamt retry:
600cb9c1205Syamt 	xc = begin(pu, "link");
60149413e37Syamt 	error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
60249413e37Syamt 	if (error != 0) {
60349413e37Syamt 		goto got_error;
60449413e37Syamt 	}
60549413e37Syamt 	if (va.va_type == VDIR) {
60649413e37Syamt 		error = EPERM;
60749413e37Syamt 		goto got_error;
60849413e37Syamt 	}
60949413e37Syamt 	error = linkfile(xc, dir_fileid, pcn->pcn_name, targ_fileid);
61049413e37Syamt 	if (error != 0) {
61149413e37Syamt 		goto got_error;
61249413e37Syamt 	}
61349413e37Syamt 	error = update_ctime(xc, targ_fileid);
61449413e37Syamt 	if (error != 0) {
61549413e37Syamt 		goto got_error;
61649413e37Syamt 	}
61749413e37Syamt 	error = commit(xc);
61849413e37Syamt 	if (error != 0) {
61949413e37Syamt 		goto got_error;
62049413e37Syamt 	}
62149413e37Syamt 	return 0;
62249413e37Syamt got_error:
62349413e37Syamt 	rollback(xc);
62449413e37Syamt 	if (error == EAGAIN) {
62549413e37Syamt 		goto retry;
62649413e37Syamt 	}
62749413e37Syamt 	return error;
62849413e37Syamt }
62949413e37Syamt 
63049413e37Syamt int
pgfs_node_remove(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)63149413e37Syamt pgfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
63249413e37Syamt     puffs_cookie_t targ, const struct puffs_cn *pcn)
63349413e37Syamt {
63449413e37Syamt 	struct Xconn *xc;
63549413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
63649413e37Syamt 	fileid_t targ_fileid = cookie_to_fileid(targ);
63749413e37Syamt 	struct vattr va;
63849413e37Syamt 	int error;
63949413e37Syamt 
64049413e37Syamt retry:
641cb9c1205Syamt 	xc = begin(pu, "remove");
64249413e37Syamt 	error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
64349413e37Syamt 	if (error != 0) {
64449413e37Syamt 		goto got_error;
64549413e37Syamt 	}
64649413e37Syamt 	if (va.va_type == VDIR) {
64749413e37Syamt 		error = EPERM;
64849413e37Syamt 		goto got_error;
64949413e37Syamt 	}
65049413e37Syamt 	error = unlinkfile(xc, fileid, pcn->pcn_name, targ_fileid);
65149413e37Syamt 	if (error != 0) {
65249413e37Syamt 		goto got_error;
65349413e37Syamt 	}
65449413e37Syamt 	error = commit(xc);
65549413e37Syamt 	if (error != 0) {
65649413e37Syamt 		goto got_error;
65749413e37Syamt 	}
658c47d2fd4Syamt 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_INACT_N2);
65949413e37Syamt 	return 0;
66049413e37Syamt got_error:
66149413e37Syamt 	rollback(xc);
66249413e37Syamt 	if (error == EAGAIN) {
66349413e37Syamt 		goto retry;
66449413e37Syamt 	}
66549413e37Syamt 	return error;
66649413e37Syamt }
66749413e37Syamt 
66849413e37Syamt int
pgfs_node_rmdir(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)66949413e37Syamt pgfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
67049413e37Syamt     puffs_cookie_t targ, const struct puffs_cn *pcn)
67149413e37Syamt {
67249413e37Syamt 	struct Xconn *xc;
67349413e37Syamt 	fileid_t parent_fileid = cookie_to_fileid(opc);
67449413e37Syamt 	fileid_t targ_fileid = cookie_to_fileid(targ);
67549413e37Syamt 	struct vattr va;
67649413e37Syamt 	bool empty;
67749413e37Syamt 	int error;
67849413e37Syamt 
67949413e37Syamt retry:
680cb9c1205Syamt 	xc = begin(pu, "rmdir");
68149413e37Syamt 	error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
68249413e37Syamt 	if (error != 0) {
68349413e37Syamt 		goto got_error;
68449413e37Syamt 	}
68549413e37Syamt 	if (va.va_type != VDIR) {
68649413e37Syamt 		error = ENOTDIR;
68749413e37Syamt 		goto got_error;
68849413e37Syamt 	}
68949413e37Syamt 	error = isempty(xc, targ_fileid, &empty);
69049413e37Syamt 	if (error != 0) {
69149413e37Syamt 		goto got_error;
69249413e37Syamt 	}
69349413e37Syamt 	if (!empty) {
69449413e37Syamt 		error = ENOTEMPTY;
69549413e37Syamt 		goto got_error;
69649413e37Syamt 	}
69749413e37Syamt 	error = unlinkfile(xc, parent_fileid, pcn->pcn_name, targ_fileid);
69849413e37Syamt 	if (error == 0) {
69949413e37Syamt 		error = update_nlink(xc, parent_fileid, -1);
70049413e37Syamt 	}
70149413e37Syamt 	if (error != 0) {
70249413e37Syamt 		goto got_error;
70349413e37Syamt 	}
70449413e37Syamt 	error = commit(xc);
70549413e37Syamt 	if (error != 0) {
70649413e37Syamt 		goto got_error;
70749413e37Syamt 	}
708c47d2fd4Syamt 	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_INACT_N2);
70949413e37Syamt 	return 0;
71049413e37Syamt got_error:
71149413e37Syamt 	rollback(xc);
71249413e37Syamt 	if (error == EAGAIN) {
71349413e37Syamt 		goto retry;
71449413e37Syamt 	}
71549413e37Syamt 	return error;
71649413e37Syamt }
71749413e37Syamt 
71849413e37Syamt int
pgfs_node_inactive(struct puffs_usermount * pu,puffs_cookie_t opc)71949413e37Syamt pgfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
72049413e37Syamt {
72149413e37Syamt 	struct Xconn *xc;
72249413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
72349413e37Syamt 	int error;
72449413e37Syamt 
72549413e37Syamt 	/*
72649413e37Syamt 	 * XXX
72749413e37Syamt 	 * probably this should be handed to the separate "reaper" context
72849413e37Syamt 	 * because lo_unlink() can be too expensive to execute synchronously.
72949413e37Syamt 	 * however, the puffs_cc API doesn't provide a way to create a worker
73049413e37Syamt 	 * context.
73149413e37Syamt 	 */
73249413e37Syamt 
73349413e37Syamt 	DPRINTF("%llu\n", fileid);
73449413e37Syamt retry:
735cb9c1205Syamt 	xc = begin(pu, "inactive");
736e5acf2f8Syamt 	error = cleanupfile(xc, fileid);
73749413e37Syamt 	if (error != 0) {
73849413e37Syamt 		goto got_error;
73949413e37Syamt 	}
74049413e37Syamt 	error = commit(xc);
74149413e37Syamt 	if (error != 0) {
74249413e37Syamt 		goto got_error;
74349413e37Syamt 	}
74449413e37Syamt 	return 0;
74549413e37Syamt got_error:
74649413e37Syamt 	rollback(xc);
74749413e37Syamt 	if (error == EAGAIN) {
74849413e37Syamt 		goto retry;
74949413e37Syamt 	}
75049413e37Syamt 	return error;
75149413e37Syamt }
75249413e37Syamt 
75349413e37Syamt int
pgfs_node_setattr(struct puffs_usermount * pu,puffs_cookie_t opc,const struct vattr * va,const struct puffs_cred * pcr)75449413e37Syamt pgfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
75549413e37Syamt     const struct vattr *va, const struct puffs_cred *pcr)
75649413e37Syamt {
75749413e37Syamt 	struct Xconn *xc;
75849413e37Syamt 	struct fileid_lock_handle *lock;
75949413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
76049413e37Syamt 	struct vattr ova;
76149413e37Syamt 	unsigned int attrs;
76249413e37Syamt 	int error;
76349413e37Syamt 
76449413e37Syamt 	DPRINTF("%llu\n", fileid);
76549413e37Syamt 	if (va->va_flags != (u_long)PUFFS_VNOVAL) {
76649413e37Syamt 		return EOPNOTSUPP;
76749413e37Syamt 	}
76849413e37Syamt 	attrs = 0;
76949413e37Syamt 	if (va->va_uid != (uid_t)PUFFS_VNOVAL ||
77049413e37Syamt 	    va->va_gid != (gid_t)PUFFS_VNOVAL) {
77149413e37Syamt 		attrs |= GETATTR_UID|GETATTR_GID|GETATTR_MODE;
77249413e37Syamt 	}
77349413e37Syamt 	if (va->va_mode != (mode_t)PUFFS_VNOVAL) {
77449413e37Syamt 		attrs |= GETATTR_TYPE|GETATTR_UID|GETATTR_GID;
77549413e37Syamt 	}
77649413e37Syamt 	if (va->va_atime.tv_sec != PUFFS_VNOVAL ||
77749413e37Syamt 	    va->va_mtime.tv_sec != PUFFS_VNOVAL ||
77849413e37Syamt 	    va->va_ctime.tv_sec != PUFFS_VNOVAL) {
77949413e37Syamt 		attrs |= GETATTR_UID|GETATTR_GID|GETATTR_MODE;
78049413e37Syamt 	}
78149413e37Syamt 	lock = fileid_lock(fileid, puffs_cc_getcc(pu));
78249413e37Syamt retry:
783cb9c1205Syamt 	xc = begin(pu, "setattr");
78449413e37Syamt 	error = getattr(xc, fileid, &ova, attrs);
78549413e37Syamt 	if (error != 0) {
78649413e37Syamt 		goto got_error;
78749413e37Syamt 	}
78849413e37Syamt 	if (va->va_uid != (uid_t)PUFFS_VNOVAL ||
78949413e37Syamt 	    va->va_gid != (gid_t)PUFFS_VNOVAL) {
79049413e37Syamt 		static struct cmd *c;
79149413e37Syamt 		uint64_t newuid =
79249413e37Syamt 		    va->va_uid != (uid_t)PUFFS_VNOVAL ? va->va_uid : ova.va_uid;
79349413e37Syamt 		uint64_t newgid =
79449413e37Syamt 		    va->va_gid != (gid_t)PUFFS_VNOVAL ? va->va_gid : ova.va_gid;
79549413e37Syamt 
79649413e37Syamt 		error = puffs_access_chown(ova.va_uid, ova.va_gid,
79749413e37Syamt 		    newuid, newgid, pcr);
79849413e37Syamt 		if (error != 0) {
79949413e37Syamt 			goto got_error;
80049413e37Syamt 		}
80149413e37Syamt 		CREATECMD(c,
80249413e37Syamt 			"UPDATE file "
80349413e37Syamt 			"SET uid = $1, gid = $2 "
80449413e37Syamt 			"WHERE fileid = $3", INT8OID, INT8OID, INT8OID);
80549413e37Syamt 		error = simplecmd(xc, c, newuid, newgid, fileid);
80649413e37Syamt 		if (error != 0) {
80749413e37Syamt 			goto got_error;
80849413e37Syamt 		}
80949413e37Syamt 		ova.va_uid = newuid;
81049413e37Syamt 		ova.va_gid = newgid;
81149413e37Syamt 	}
81249413e37Syamt 	if (va->va_mode != (mode_t)PUFFS_VNOVAL) {
81349413e37Syamt 		static struct cmd *c;
81449413e37Syamt 		uint64_t newmode = va->va_mode;
81549413e37Syamt 
81649413e37Syamt 		error = puffs_access_chmod(ova.va_uid, ova.va_gid, ova.va_type,
81749413e37Syamt 		    newmode, pcr);
81849413e37Syamt 		if (error != 0) {
81949413e37Syamt 			goto got_error;
82049413e37Syamt 		}
82149413e37Syamt 		CREATECMD(c,
82249413e37Syamt 			"UPDATE file "
82349413e37Syamt 			"SET mode = $1 "
82449413e37Syamt 			"WHERE fileid = $2", INT8OID, INT8OID);
82549413e37Syamt 		error = simplecmd(xc, c, newmode, fileid);
82649413e37Syamt 		if (error != 0) {
82749413e37Syamt 			goto got_error;
82849413e37Syamt 		}
82949413e37Syamt 		ova.va_mode = newmode;
83049413e37Syamt 	}
83149413e37Syamt 	if (va->va_atime.tv_sec != PUFFS_VNOVAL ||
83249413e37Syamt 	    va->va_mtime.tv_sec != PUFFS_VNOVAL ||
83349413e37Syamt 	    va->va_ctime.tv_sec != PUFFS_VNOVAL ||
83449413e37Syamt 	    va->va_birthtime.tv_sec != PUFFS_VNOVAL) {
83549413e37Syamt 		error = puffs_access_times(ova.va_uid, ova.va_gid, ova.va_mode,
83649413e37Syamt 		    (va->va_vaflags & VA_UTIMES_NULL) != 0, pcr);
83749413e37Syamt 		if (error != 0) {
83849413e37Syamt 			goto got_error;
83949413e37Syamt 		}
84049413e37Syamt 		if (va->va_atime.tv_sec != PUFFS_VNOVAL) {
84149413e37Syamt 			static struct cmd *c;
84249413e37Syamt 			char *ts;
84349413e37Syamt 
84449413e37Syamt 			error = timespec_to_pgtimestamp(&va->va_atime, &ts);
84549413e37Syamt 			if (error != 0) {
84649413e37Syamt 				goto got_error;
84749413e37Syamt 			}
84849413e37Syamt 			CREATECMD(c,
84949413e37Syamt 				"UPDATE file "
85049413e37Syamt 				"SET atime = $1 "
85149413e37Syamt 				"WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
85249413e37Syamt 			error = simplecmd(xc, c, ts, fileid);
85349413e37Syamt 			free(ts);
85449413e37Syamt 			if (error != 0) {
85549413e37Syamt 				goto got_error;
85649413e37Syamt 			}
85749413e37Syamt 		}
85849413e37Syamt 		if (va->va_mtime.tv_sec != PUFFS_VNOVAL) {
85949413e37Syamt 			static struct cmd *c;
86049413e37Syamt 			char *ts;
86149413e37Syamt 
86249413e37Syamt 			error = timespec_to_pgtimestamp(&va->va_mtime, &ts);
86349413e37Syamt 			if (error != 0) {
86449413e37Syamt 				goto got_error;
86549413e37Syamt 			}
86649413e37Syamt 			CREATECMD(c,
86749413e37Syamt 				"UPDATE file "
86849413e37Syamt 				"SET mtime = $1 "
86949413e37Syamt 				"WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
87049413e37Syamt 			error = simplecmd(xc, c, ts, fileid);
87149413e37Syamt 			free(ts);
87249413e37Syamt 			if (error != 0) {
87349413e37Syamt 				goto got_error;
87449413e37Syamt 			}
87549413e37Syamt 		}
87649413e37Syamt 		if (va->va_ctime.tv_sec != PUFFS_VNOVAL) {
87749413e37Syamt 			static struct cmd *c;
87849413e37Syamt 			char *ts;
87949413e37Syamt 
88049413e37Syamt 			error = timespec_to_pgtimestamp(&va->va_ctime, &ts);
88149413e37Syamt 			if (error != 0) {
88249413e37Syamt 				goto got_error;
88349413e37Syamt 			}
88449413e37Syamt 			CREATECMD(c,
88549413e37Syamt 				"UPDATE file "
88649413e37Syamt 				"SET ctime = $1 "
88749413e37Syamt 				"WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
88849413e37Syamt 			error = simplecmd(xc, c, ts, fileid);
88949413e37Syamt 			free(ts);
89049413e37Syamt 			if (error != 0) {
89149413e37Syamt 				goto got_error;
89249413e37Syamt 			}
89349413e37Syamt 		}
89449413e37Syamt 		if (va->va_birthtime.tv_sec != PUFFS_VNOVAL) {
89549413e37Syamt 			static struct cmd *c;
89649413e37Syamt 			char *ts;
89749413e37Syamt 
89849413e37Syamt 			error = timespec_to_pgtimestamp(&va->va_birthtime, &ts);
89949413e37Syamt 			if (error != 0) {
90049413e37Syamt 				goto got_error;
90149413e37Syamt 			}
90249413e37Syamt 			CREATECMD(c,
90349413e37Syamt 				"UPDATE file "
90449413e37Syamt 				"SET btime = $1 "
90549413e37Syamt 				"WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
90649413e37Syamt 			error = simplecmd(xc, c, ts, fileid);
90749413e37Syamt 			free(ts);
90849413e37Syamt 			if (error != 0) {
90949413e37Syamt 				goto got_error;
91049413e37Syamt 			}
91149413e37Syamt 		}
91249413e37Syamt 	}
91349413e37Syamt 	if (va->va_size != (uint64_t)PUFFS_VNOVAL) {
91449413e37Syamt 		int fd;
91549413e37Syamt 
91649413e37Syamt 		if (va->va_size > INT_MAX) {
91749413e37Syamt 			error = EFBIG;
91849413e37Syamt 			goto got_error;
91949413e37Syamt 		}
92049413e37Syamt 		error = lo_open_by_fileid(xc, fileid, INV_READ|INV_WRITE, &fd);
92149413e37Syamt 		if (error != 0) {
92249413e37Syamt 			goto got_error;
92349413e37Syamt 		}
92449413e37Syamt 		error = my_lo_truncate(xc, fd, va->va_size);
92549413e37Syamt 		if (error != 0) {
92649413e37Syamt 			goto got_error;
92749413e37Syamt 		}
92849413e37Syamt 		error = my_lo_close(xc, fd);
92949413e37Syamt 		if (error != 0) {
93049413e37Syamt 			goto got_error;
93149413e37Syamt 		}
93249413e37Syamt 	}
93349413e37Syamt 	error = commit(xc);
93449413e37Syamt 	if (error != 0) {
93549413e37Syamt 		goto got_error;
93649413e37Syamt 	}
93749413e37Syamt 	goto done;
93849413e37Syamt got_error:
93949413e37Syamt 	rollback(xc);
94049413e37Syamt 	if (error == EAGAIN) {
94149413e37Syamt 		goto retry;
94249413e37Syamt 	}
94349413e37Syamt done:
94449413e37Syamt 	fileid_unlock(lock);
94549413e37Syamt 	return error;
94649413e37Syamt }
94749413e37Syamt 
94849413e37Syamt int
pgfs_node_rename(struct puffs_usermount * pu,puffs_cookie_t src_dir,puffs_cookie_t src,const struct puffs_cn * pcn_src,puffs_cookie_t targ_dir,puffs_cookie_t targ,const struct puffs_cn * pcn_targ)94949413e37Syamt pgfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t src_dir,
95049413e37Syamt     puffs_cookie_t src, const struct puffs_cn *pcn_src,
95149413e37Syamt     puffs_cookie_t targ_dir, puffs_cookie_t targ,
95249413e37Syamt     const struct puffs_cn *pcn_targ)
95349413e37Syamt {
95449413e37Syamt 	struct Xconn *xc;
95549413e37Syamt 	fileid_t fileid_src_dir = cookie_to_fileid(src_dir);
95649413e37Syamt 	fileid_t fileid_src = cookie_to_fileid(src);
95749413e37Syamt 	fileid_t fileid_targ_dir = cookie_to_fileid(targ_dir);
95849413e37Syamt 	fileid_t fileid_targ = cookie_to_fileid(targ);
95949413e37Syamt 	struct vattr va_src;
96049413e37Syamt 	struct vattr va_targ;
96149413e37Syamt 	int error;
96249413e37Syamt 
96349413e37Syamt 	DPRINTF("%llu %llu %llu %llu\n", fileid_src_dir, fileid_src,
96449413e37Syamt 	    fileid_targ_dir, fileid_targ);
96549413e37Syamt retry:
966cb9c1205Syamt 	xc = begin(pu, "rename");
96749413e37Syamt 	error = getattr(xc, fileid_src, &va_src, GETATTR_TYPE);
96849413e37Syamt 	if (error != 0) {
96949413e37Syamt 		goto got_error;
97049413e37Syamt 	}
97149413e37Syamt 	if (va_src.va_type == VDIR) {
97249413e37Syamt 		error = check_path(xc, fileid_src, fileid_targ_dir);
97349413e37Syamt 		if (error != 0) {
97449413e37Syamt 			goto got_error;
97549413e37Syamt 		}
97649413e37Syamt 	}
97749413e37Syamt 	if (fileid_targ != 0) {
97849413e37Syamt 		error = getattr(xc, fileid_targ, &va_targ,
97949413e37Syamt 		    GETATTR_TYPE|GETATTR_NLINK);
98049413e37Syamt 		if (error != 0) {
98149413e37Syamt 			goto got_error;
98249413e37Syamt 		}
98349413e37Syamt 		if (va_src.va_type == VDIR) {
98449413e37Syamt 			if (va_targ.va_type != VDIR) {
98549413e37Syamt 				error = ENOTDIR;
98649413e37Syamt 				goto got_error;
98749413e37Syamt 			}
98849413e37Syamt 			if (va_targ.va_nlink != 2) {
98949413e37Syamt 				error = ENOTEMPTY;
99049413e37Syamt 				goto got_error;
99149413e37Syamt 			}
99249413e37Syamt 		} else if (va_targ.va_type == VDIR) {
99349413e37Syamt 			error = EISDIR;
99449413e37Syamt 			goto got_error;
99549413e37Syamt 		}
99649413e37Syamt 		error = unlinkfile(xc, fileid_targ_dir, pcn_targ->pcn_name,
99749413e37Syamt 		    fileid_targ);
99849413e37Syamt 		if (error == 0 && va_targ.va_type == VDIR) {
99949413e37Syamt 			error = update_nlink(xc, fileid_targ_dir, -1);
100049413e37Syamt 		}
100149413e37Syamt 		if (error != 0) {
100249413e37Syamt 			goto got_error;
100349413e37Syamt 		}
100449413e37Syamt 	}
100549413e37Syamt 	error = linkfile(xc, fileid_targ_dir, pcn_targ->pcn_name, fileid_src);
100649413e37Syamt 	if (error == 0 && va_src.va_type == VDIR) {
100749413e37Syamt 		error = update_nlink(xc, fileid_targ_dir, 1);
100849413e37Syamt 	}
100949413e37Syamt 	if (error != 0) {
101049413e37Syamt 		goto got_error;
101149413e37Syamt 	}
101249413e37Syamt 	/* XXX ctime? */
101349413e37Syamt 	error = unlinkfile(xc, fileid_src_dir, pcn_src->pcn_name, fileid_src);
101449413e37Syamt 	if (error == 0 && va_src.va_type == VDIR) {
101549413e37Syamt 		error = update_nlink(xc, fileid_src_dir, -1);
101649413e37Syamt 	}
101749413e37Syamt 	if (error != 0) {
101849413e37Syamt 		goto got_error;
101949413e37Syamt 	}
102049413e37Syamt 	error = commit(xc);
102149413e37Syamt 	if (error != 0) {
102249413e37Syamt 		goto got_error;
102349413e37Syamt 	}
102449413e37Syamt 	return 0;
102549413e37Syamt got_error:
102649413e37Syamt 	rollback(xc);
102749413e37Syamt 	if (error == EAGAIN) {
102849413e37Syamt 		goto retry;
102949413e37Syamt 	}
103049413e37Syamt 	return error;
103149413e37Syamt }
103249413e37Syamt 
103349413e37Syamt int
pgfs_node_symlink(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va,const char * target)103449413e37Syamt pgfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
103549413e37Syamt     struct puffs_newinfo *pni, const struct puffs_cn *pcn,
103649413e37Syamt     const struct vattr *va, const char *target)
103749413e37Syamt {
103849413e37Syamt 	struct Xconn *xc;
103949413e37Syamt 	struct puffs_cred *pcr = pcn->pcn_cred;
104049413e37Syamt 	fileid_t parent_fileid = cookie_to_fileid(opc);
104149413e37Syamt 	fileid_t new_fileid;
104249413e37Syamt 	size_t resultlen;
104349413e37Syamt 	size_t targetlen;
104449413e37Syamt 	uid_t uid;
104549413e37Syamt 	gid_t gid;
104649413e37Syamt 	int loid;
104749413e37Syamt 	int fd;
104849413e37Syamt 	int error;
104949413e37Syamt 
105049413e37Syamt 	DPRINTF("%llu %s %s\n", parent_fileid, pcn->pcn_name, target);
105149413e37Syamt 	if (puffs_cred_getuid(pcr, &uid) == -1 ||
105249413e37Syamt 	    puffs_cred_getgid(pcr, &gid) == -1) {
105349413e37Syamt 		return errno;
105449413e37Syamt 	}
105549413e37Syamt retry:
1056cb9c1205Syamt 	xc = begin(pu, "symlink");
105749413e37Syamt 	error = mklinkfile_lo(xc, parent_fileid, pcn->pcn_name, VLNK,
105849413e37Syamt 	    va->va_mode, uid, gid, &new_fileid, &loid);
105949413e37Syamt 	if (error != 0) {
106049413e37Syamt 		goto got_error;
106149413e37Syamt 	}
106249413e37Syamt 	error = my_lo_open(xc, loid, INV_WRITE, &fd);
106349413e37Syamt 	if (error != 0) {
106449413e37Syamt 		goto got_error;
106549413e37Syamt 	}
106649413e37Syamt 	targetlen = strlen(target);
106749413e37Syamt 	error = my_lo_write(xc, fd, target, targetlen, &resultlen);
106849413e37Syamt 	if (error != 0) {
106949413e37Syamt 		goto got_error;
107049413e37Syamt 	}
107149413e37Syamt 	if (resultlen != targetlen) {
107249413e37Syamt 		error = ENOSPC; /* XXX */
107349413e37Syamt 		goto got_error;
107449413e37Syamt 	}
107549413e37Syamt 	error = commit(xc);
107649413e37Syamt 	if (error != 0) {
107749413e37Syamt 		goto got_error;
107849413e37Syamt 	}
107949413e37Syamt 	puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
108049413e37Syamt 	return 0;
108149413e37Syamt got_error:
108249413e37Syamt 	rollback(xc);
108349413e37Syamt 	if (error == EAGAIN) {
108449413e37Syamt 		goto retry;
108549413e37Syamt 	}
108649413e37Syamt 	return error;
108749413e37Syamt }
108849413e37Syamt 
108949413e37Syamt int
pgfs_node_readlink(struct puffs_usermount * pu,puffs_cookie_t opc,const struct puffs_cred * pcr,char * buf,size_t * buflenp)109049413e37Syamt pgfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
109149413e37Syamt     const struct puffs_cred *pcr, char *buf, size_t *buflenp)
109249413e37Syamt {
109349413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
109449413e37Syamt 	struct Xconn *xc;
109549413e37Syamt 	size_t resultlen;
109649413e37Syamt 	int fd;
109749413e37Syamt 	int error;
109849413e37Syamt 
109949413e37Syamt 	DPRINTF("%llu\n", fileid);
1100cb9c1205Syamt 	xc = begin_readonly(pu, "readlink");
110149413e37Syamt 	error = lo_open_by_fileid(xc, fileid, INV_READ, &fd);
110249413e37Syamt 	if (error != 0) {
110349413e37Syamt 		rollback(xc);
110449413e37Syamt 		return error;
110549413e37Syamt 	}
110649413e37Syamt 	error = my_lo_read(xc, fd, buf, *buflenp, &resultlen);
110749413e37Syamt 	if (error != 0) {
110849413e37Syamt 		rollback(xc);
110949413e37Syamt 		return error;
111049413e37Syamt 	}
111149413e37Syamt 	assert(resultlen <= *buflenp);
111249413e37Syamt 	error = commit(xc);
111349413e37Syamt 	if (error != 0) {
111449413e37Syamt 		return error;
111549413e37Syamt 	}
111649413e37Syamt 	*buflenp = resultlen;
111749413e37Syamt 	return 0;
111849413e37Syamt }
111949413e37Syamt 
112049413e37Syamt int
pgfs_node_access(struct puffs_usermount * pu,puffs_cookie_t opc,int mode,const struct puffs_cred * pcr)112149413e37Syamt pgfs_node_access(struct puffs_usermount *pu, puffs_cookie_t opc,
112249413e37Syamt     int mode, const struct puffs_cred *pcr)
112349413e37Syamt {
112449413e37Syamt 	struct Xconn *xc;
112549413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
112649413e37Syamt 	struct vattr va;
112749413e37Syamt 	int error;
112849413e37Syamt 
112949413e37Syamt 	DPRINTF("%llu\n", fileid);
113049413e37Syamt retry:
1131cb9c1205Syamt 	xc = begin_readonly(pu, "access");
113249413e37Syamt 	error = getattr(xc, fileid, &va,
113349413e37Syamt 	    GETATTR_TYPE|GETATTR_MODE|GETATTR_UID|GETATTR_GID);
113449413e37Syamt 	if (error != 0) {
113549413e37Syamt 		goto got_error;
113649413e37Syamt 	}
113749413e37Syamt 	error = commit(xc);
113849413e37Syamt 	if (error != 0) {
113949413e37Syamt 		goto got_error;
114049413e37Syamt 	}
114149413e37Syamt 	return puffs_access(va.va_type, va.va_mode, va.va_uid, va.va_gid, mode,
114249413e37Syamt 	    pcr);
114349413e37Syamt got_error:
114449413e37Syamt 	rollback(xc);
114549413e37Syamt 	if (error == EAGAIN) {
114649413e37Syamt 		goto retry;
114749413e37Syamt 	}
114849413e37Syamt 	return error;
114949413e37Syamt }
115049413e37Syamt 
115149413e37Syamt int
pgfs_node_fsync(struct puffs_usermount * pu,puffs_cookie_t opc,const struct puffs_cred * pcr,int flags,off_t offlo,off_t offhi)115249413e37Syamt pgfs_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
115349413e37Syamt     const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi)
115449413e37Syamt {
115549413e37Syamt 	fileid_t fileid = cookie_to_fileid(opc);
115649413e37Syamt 
115749413e37Syamt 	DPRINTF("%llu\n", fileid);
115849413e37Syamt 	return flush_xacts(pu);
115949413e37Syamt }
116049413e37Syamt 
116149413e37Syamt int
pgfs_fs_statvfs(struct puffs_usermount * pu,struct statvfs * sbp)116249413e37Syamt pgfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
116349413e37Syamt {
116449413e37Syamt 	struct Xconn *xc;
116549413e37Syamt 	uint64_t nfiles;
116649413e37Syamt 	uint64_t bytes;
116749413e37Syamt 	uint64_t lo_bytes;
116849413e37Syamt 	static struct cmd *c_nfiles;
116949413e37Syamt 	static struct cmd *c_bytes;
117049413e37Syamt 	static struct cmd *c_lobytes;
117149413e37Syamt 	static const Oid types[] = { INT8OID, };
117249413e37Syamt 	struct fetchstatus s;
117349413e37Syamt 	int error;
117449413e37Syamt 
117549413e37Syamt retry:
1176cb9c1205Syamt 	xc = begin_readonly(pu, "statvfs");
117749413e37Syamt 	/*
117849413e37Syamt 	 * use an estimate which we can retrieve quickly, instead of
117949413e37Syamt 	 * "SELECT count(*) from file".
118049413e37Syamt 	 */
118149413e37Syamt 	CREATECMD_NOPARAM(c_nfiles,
118249413e37Syamt 		"SELECT reltuples::int8 "
118349413e37Syamt 		"FROM pg_class c LEFT JOIN pg_namespace n "
118449413e37Syamt 		"ON (n.oid=c.relnamespace) "
118549413e37Syamt 		"WHERE n.nspname = 'pgfs' AND c.relname = 'file'");
118649413e37Syamt 	CREATECMD_NOPARAM(c_bytes,
118749413e37Syamt 		"SELECT sum(pg_total_relation_size(c.oid))::int8 "
118849413e37Syamt 		"FROM pg_class c LEFT JOIN pg_namespace n "
118949413e37Syamt 		"ON (n.oid=c.relnamespace) "
119049413e37Syamt 		"WHERE n.nspname = 'pgfs'");
119149413e37Syamt 	/*
119249413e37Syamt 	 * the following is not correct if someone else is using large objects
119349413e37Syamt 	 * in the same database.  we don't bother to join with datafork it as
119449413e37Syamt 	 * it's too expensive for the little benefit.
119549413e37Syamt 	 */
119649413e37Syamt 	CREATECMD_NOPARAM(c_lobytes,
119749413e37Syamt 		"SELECT pg_total_relation_size('pg_largeobject')::int8");
119849413e37Syamt 	error = sendcmd(xc, c_nfiles);
119949413e37Syamt 	if (error != 0) {
120049413e37Syamt 		goto got_error;
120149413e37Syamt 	}
120249413e37Syamt 	fetchinit(&s, xc);
120349413e37Syamt 	error = FETCHNEXT(&s, types, &nfiles);
120449413e37Syamt 	fetchdone(&s);
120549413e37Syamt 	if (error != 0) {
120649413e37Syamt 		goto got_error;
120749413e37Syamt 	}
120849413e37Syamt 	error = sendcmd(xc, c_bytes);
120949413e37Syamt 	if (error != 0) {
121049413e37Syamt 		goto got_error;
121149413e37Syamt 	}
121249413e37Syamt 	fetchinit(&s, xc);
121349413e37Syamt 	error = FETCHNEXT(&s, types, &bytes);
121449413e37Syamt 	fetchdone(&s);
121549413e37Syamt 	if (error != 0) {
121649413e37Syamt 		goto got_error;
121749413e37Syamt 	}
121849413e37Syamt 	error = sendcmd(xc, c_lobytes);
121949413e37Syamt 	if (error != 0) {
122049413e37Syamt 		goto got_error;
122149413e37Syamt 	}
122249413e37Syamt 	fetchinit(&s, xc);
122349413e37Syamt 	error = FETCHNEXT(&s, types, &lo_bytes);
122449413e37Syamt 	fetchdone(&s);
122549413e37Syamt 	if (error != 0) {
122649413e37Syamt 		goto got_error;
122749413e37Syamt 	}
122849413e37Syamt 	error = commit(xc);
122949413e37Syamt 	if (error != 0) {
123049413e37Syamt 		goto got_error;
123149413e37Syamt 	}
123249413e37Syamt 	/*
123349413e37Syamt 	 * XXX fill f_blocks and f_files with meaningless large values.
123449413e37Syamt 	 * there are no easy way to provide meaningful values for them
123549413e37Syamt 	 * esp. with tablespaces.
123649413e37Syamt 	 */
123749413e37Syamt 	sbp->f_bsize = LOBLKSIZE;
123849413e37Syamt 	sbp->f_frsize = LOBLKSIZE;
123949413e37Syamt 	sbp->f_blocks = INT64_MAX / 100 / sbp->f_frsize;
124049413e37Syamt 	sbp->f_bfree = sbp->f_blocks - howmany(bytes + lo_bytes, sbp->f_frsize);
124149413e37Syamt 	sbp->f_bavail = sbp->f_bfree;
124249413e37Syamt 	sbp->f_bresvd = 0;
124349413e37Syamt 	sbp->f_files = INT_MAX;
124449413e37Syamt 	sbp->f_ffree = sbp->f_files - nfiles;
124549413e37Syamt 	sbp->f_favail = sbp->f_ffree;
124649413e37Syamt 	sbp->f_fresvd = 0;
124749413e37Syamt 	return 0;
124849413e37Syamt got_error:
124949413e37Syamt 	rollback(xc);
125049413e37Syamt 	if (error == EAGAIN) {
125149413e37Syamt 		goto retry;
125249413e37Syamt 	}
125349413e37Syamt 	return error;
125449413e37Syamt }
1255