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