1 /* $NetBSD: pgfs_puffs.c,v 1.5 2014/10/18 07:11:07 snj Exp $ */
2
3 /*-
4 * Copyright (c)2010,2011 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * puffs node ops and fs ops.
31 */
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: pgfs_puffs.c,v 1.5 2014/10/18 07:11:07 snj Exp $");
36 #endif /* not lint */
37
38 #include <assert.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <puffs.h>
42 #include <inttypes.h>
43 #include <stdarg.h>
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #include <util.h>
49
50 #include <libpq-fe.h>
51 #include <libpq/libpq-fs.h> /* INV_* */
52
53 #include "pgfs.h"
54 #include "pgfs_db.h"
55 #include "pgfs_subs.h"
56 #include "pgfs_debug.h"
57
58 static fileid_t
cookie_to_fileid(puffs_cookie_t cookie)59 cookie_to_fileid(puffs_cookie_t cookie)
60 {
61
62 return (fileid_t)(uintptr_t)cookie;
63 }
64
65 static puffs_cookie_t
fileid_to_cookie(fileid_t id)66 fileid_to_cookie(fileid_t id)
67 {
68 puffs_cookie_t cookie = (puffs_cookie_t)(uintptr_t)id;
69
70 /* XXX not true for 32-bit ports */
71 assert(cookie_to_fileid(cookie) == id);
72 return cookie;
73 }
74
75 puffs_cookie_t
pgfs_root_cookie(void)76 pgfs_root_cookie(void)
77 {
78
79 return fileid_to_cookie(PGFS_ROOT_FILEID);
80 }
81
82 int
pgfs_node_getattr(struct puffs_usermount * pu,puffs_cookie_t opc,struct vattr * va,const struct puffs_cred * pcr)83 pgfs_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
84 struct vattr *va, const struct puffs_cred *pcr)
85 {
86 struct Xconn *xc;
87 struct fileid_lock_handle *lock;
88 fileid_t fileid = cookie_to_fileid(opc);
89 int error;
90
91 DPRINTF("%llu\n", fileid);
92 lock = fileid_lock(fileid, puffs_cc_getcc(pu));
93 retry:
94 xc = begin_readonly(pu, "getattr");
95 error = getattr(xc, fileid, va, GETATTR_ALL);
96 if (error != 0) {
97 goto got_error;
98 }
99 error = commit(xc);
100 if (error != 0) {
101 goto got_error;
102 }
103 goto done;
104 got_error:
105 rollback(xc);
106 if (error == EAGAIN) {
107 goto retry;
108 }
109 done:
110 fileid_unlock(lock);
111 return error;
112 }
113
114 #define PGFS_DIRCOOKIE_DOT 0 /* . entry */
115 #define PGFS_DIRCOOKIE_DOTDOT 1 /* .. entry */
116 #define PGFS_DIRCOOKIE_EOD 2 /* end of directory */
117
118 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)119 pgfs_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
120 struct dirent *dent, off_t *readoff, size_t *reslen,
121 const struct puffs_cred *pcr, int *eofflag, off_t *cookies,
122 size_t *ncookies)
123 {
124 fileid_t parent_fileid;
125 fileid_t child_fileid;
126 uint64_t cookie;
127 uint64_t nextcookie;
128 uint64_t offset;
129 struct Xconn *xc = NULL;
130 static const Oid types[] = {
131 TEXTOID, /* name */
132 INT8OID, /* cookie */
133 INT8OID, /* nextcookie */
134 INT8OID, /* child_fileid */
135 };
136 const char *name;
137 char *nametofree = NULL;
138 struct fetchstatus s;
139 int error;
140 bool fetching;
141 bool bufferfull;
142
143 parent_fileid = cookie_to_fileid(opc);
144 offset = *readoff;
145 DPRINTF("%llu %" PRIu64 "\n", parent_fileid, offset);
146 *ncookies = 0;
147 fetching = false;
148 next:
149 if (offset == PGFS_DIRCOOKIE_DOT) {
150 name = ".";
151 child_fileid = parent_fileid;
152 cookie = offset;
153 nextcookie = PGFS_DIRCOOKIE_DOTDOT;
154 goto store_and_next;
155 }
156 if (offset == PGFS_DIRCOOKIE_DOTDOT) {
157 if (parent_fileid != PGFS_ROOT_FILEID) {
158 if (xc == NULL) {
159 xc = begin(pu, "readdir1");
160 }
161 error = lookupp(xc, parent_fileid, &child_fileid);
162 if (error != 0) {
163 rollback(xc);
164 return error;
165 }
166 } else {
167 child_fileid = parent_fileid;
168 }
169 name = "..";
170 cookie = offset;
171 nextcookie = PGFS_DIRCOOKIE_EOD + 1;
172 goto store_and_next;
173 }
174 if (offset == PGFS_DIRCOOKIE_EOD) {
175 *eofflag = 1;
176 goto done;
177 }
178 /* offset > PGFS_DIRCOOKIE_EOD; normal entries */
179 if (xc == NULL) {
180 xc = begin(pu, "readdir2");
181 }
182 if (!fetching) {
183 static struct cmd *c;
184
185 /*
186 * a simpler query like "ORDER BY name OFFSET :offset - 3"
187 * would work well for most of cases. however, it doesn't for
188 * applications which expect readdir cookies are kept valid
189 * even after unlink of other entries in the directory.
190 * eg. cvs, bonnie++
191 *
192 * 2::int8 == PGFS_DIRCOOKIE_EOD
193 */
194 CREATECMD(c,
195 "SELECT name, cookie, "
196 "lead(cookie, 1, 2::int8) OVER (ORDER BY cookie), "
197 "child_fileid "
198 "FROM dirent "
199 "WHERE parent_fileid = $1 "
200 "AND cookie >= $2 "
201 "ORDER BY cookie", INT8OID, INT8OID);
202 error = sendcmd(xc, c, parent_fileid, offset);
203 if (error != 0) {
204 rollback(xc);
205 return error;
206 }
207 fetching = true;
208 fetchinit(&s, xc);
209 }
210 /*
211 * fetch and process an entry
212 */
213 error = FETCHNEXT(&s, types, &nametofree, &cookie, &nextcookie,
214 &child_fileid);
215 if (error == ENOENT) {
216 DPRINTF("ENOENT\n");
217 if (offset == PGFS_DIRCOOKIE_EOD + 1) {
218 DPRINTF("empty directory\n");
219 *eofflag = 1;
220 goto done;
221 }
222 fetchdone(&s);
223 rollback(xc);
224 return EINVAL;
225 }
226 if (error != 0) {
227 DPRINTF("error %d\n", error);
228 fetchdone(&s);
229 rollback(xc);
230 return error;
231 }
232 if (offset != cookie && offset != PGFS_DIRCOOKIE_EOD + 1) {
233 free(nametofree);
234 fetchdone(&s);
235 rollback(xc);
236 return EINVAL;
237 }
238 name = nametofree;
239 store_and_next:
240 /*
241 * store an entry and continue processing unless the result buffer
242 * is full.
243 */
244 bufferfull = !puffs_nextdent(&dent, name, child_fileid, DT_UNKNOWN,
245 reslen);
246 free(nametofree);
247 nametofree = NULL;
248 if (bufferfull) {
249 *eofflag = 0;
250 goto done;
251 }
252 PUFFS_STORE_DCOOKIE(cookies, ncookies, cookie);
253 offset = nextcookie;
254 *readoff = offset;
255 goto next;
256 done:
257 /*
258 * cleanup and update atime of the directory.
259 */
260 assert(nametofree == NULL);
261 if (fetching) {
262 fetchdone(&s);
263 fetching = false;
264 }
265 if (xc == NULL) {
266 retry:
267 xc = begin(pu, "readdir3");
268 }
269 error = update_atime(xc, parent_fileid);
270 if (error != 0) {
271 goto got_error;
272 }
273 error = commit(xc);
274 if (error != 0) {
275 goto got_error;
276 }
277 return 0;
278 got_error:
279 rollback(xc);
280 if (error == EAGAIN) {
281 goto retry;
282 }
283 return error;
284 }
285
286 int
pgfs_node_lookup(struct puffs_usermount * pu,puffs_cookie_t opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)287 pgfs_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
288 struct puffs_newinfo *pni, const struct puffs_cn *pcn)
289 {
290 struct vattr dva;
291 struct vattr cva;
292 struct puffs_cred * const pcr = pcn->pcn_cred;
293 fileid_t parent_fileid;
294 const char *name;
295 fileid_t child_fileid;
296 struct Xconn *xc;
297 mode_t access_mode;
298 int error;
299 int saved_error;
300
301 parent_fileid = cookie_to_fileid(opc);
302 name = pcn->pcn_name;
303 DPRINTF("%llu %s\n", parent_fileid, name);
304 assert(strcmp(name, ".")); /* . is handled by framework */
305 retry:
306 xc = begin_readonly(pu, "lookup");
307 error = getattr(xc, parent_fileid, &dva,
308 GETATTR_TYPE|GETATTR_MODE|GETATTR_UID|GETATTR_GID);
309 if (error != 0) {
310 goto got_error;
311 }
312 access_mode = PUFFS_VEXEC;
313 if ((pcn->pcn_flags & NAMEI_ISLASTCN) != 0 &&
314 pcn->pcn_nameiop != NAMEI_LOOKUP) {
315 access_mode |= PUFFS_VWRITE;
316 }
317 error = puffs_access(dva.va_type, dva.va_mode, dva.va_uid, dva.va_gid,
318 access_mode, pcr);
319 if (error != 0) {
320 goto commit_and_return;
321 }
322 if (!strcmp(name, "..")) {
323 error = lookupp(xc, parent_fileid, &child_fileid);
324 if (error != 0) {
325 goto got_error;
326 }
327 } else {
328 static struct cmd *c;
329 static const Oid types[] = { INT8OID, };
330 struct fetchstatus s;
331
332 CREATECMD(c, "SELECT child_fileid "
333 "FROM dirent "
334 "WHERE parent_fileid = $1 AND name = $2",
335 INT8OID, TEXTOID);
336 error = sendcmd(xc, c, parent_fileid, name);
337 if (error != 0) {
338 DPRINTF("sendcmd %d\n", error);
339 goto got_error;
340 }
341 fetchinit(&s, xc);
342 error = FETCHNEXT(&s, types, &child_fileid);
343 fetchdone(&s);
344 if (error == ENOENT) {
345 goto commit_and_return;
346 }
347 if (error != 0) {
348 goto got_error;
349 }
350 }
351 error = getattr(xc, child_fileid, &cva, GETATTR_TYPE|GETATTR_SIZE);
352 if (error != 0) {
353 goto got_error;
354 }
355 error = commit(xc);
356 if (error != 0) {
357 goto got_error;
358 }
359 puffs_newinfo_setcookie(pni, fileid_to_cookie(child_fileid));
360 puffs_newinfo_setvtype(pni, cva.va_type);
361 puffs_newinfo_setsize(pni, cva.va_size);
362 return 0;
363 got_error:
364 rollback(xc);
365 if (error == EAGAIN) {
366 goto retry;
367 }
368 return error;
369 commit_and_return:
370 saved_error = error;
371 error = commit(xc);
372 if (error != 0) {
373 goto got_error;
374 }
375 return saved_error;
376 }
377
378 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)379 pgfs_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
380 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
381 const struct vattr *va)
382 {
383 struct Xconn *xc;
384 fileid_t parent_fileid = cookie_to_fileid(opc);
385 fileid_t new_fileid;
386 struct puffs_cred * const pcr = pcn->pcn_cred;
387 uid_t uid;
388 gid_t gid;
389 int error;
390
391 DPRINTF("%llu %s\n", parent_fileid, pcn->pcn_name);
392 if (puffs_cred_getuid(pcr, &uid) == -1 ||
393 puffs_cred_getgid(pcr, &gid) == -1) {
394 return errno;
395 }
396 retry:
397 xc = begin(pu, "mkdir");
398 error = mklinkfile(xc, parent_fileid, pcn->pcn_name, VDIR,
399 va->va_mode, uid, gid, &new_fileid);
400 if (error == 0) {
401 error = update_nlink(xc, parent_fileid, 1);
402 }
403 if (error != 0) {
404 goto got_error;
405 }
406 error = commit(xc);
407 if (error != 0) {
408 goto got_error;
409 }
410 puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
411 return 0;
412 got_error:
413 rollback(xc);
414 if (error == EAGAIN) {
415 goto retry;
416 }
417 return error;
418 }
419
420 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)421 pgfs_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
422 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
423 const struct vattr *va)
424 {
425 struct Xconn *xc;
426 fileid_t parent_fileid = cookie_to_fileid(opc);
427 fileid_t new_fileid;
428 struct puffs_cred * const pcr = pcn->pcn_cred;
429 uid_t uid;
430 gid_t gid;
431 int error;
432
433 DPRINTF("%llu %s\n", parent_fileid, pcn->pcn_name);
434 if (puffs_cred_getuid(pcr, &uid) == -1 ||
435 puffs_cred_getgid(pcr, &gid) == -1) {
436 return errno;
437 }
438 retry:
439 xc = begin(pu, "create");
440 error = mklinkfile_lo(xc, parent_fileid, pcn->pcn_name, VREG,
441 va->va_mode,
442 uid, gid, &new_fileid, NULL);
443 if (error != 0) {
444 goto got_error;
445 }
446 error = commit(xc);
447 if (error != 0) {
448 goto got_error;
449 }
450 puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
451 return 0;
452 got_error:
453 rollback(xc);
454 if (error == EAGAIN) {
455 goto retry;
456 }
457 return error;
458 }
459
460 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)461 pgfs_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
462 uint8_t *buf, off_t offset, size_t *resid,
463 const struct puffs_cred *pcr, int ioflags)
464 {
465 struct Xconn *xc;
466 struct fileid_lock_handle *lock;
467 fileid_t fileid = cookie_to_fileid(opc);
468 size_t resultlen;
469 int fd;
470 int error;
471
472 if ((ioflags & PUFFS_IO_APPEND) != 0) {
473 DPRINTF("%llu append sz %zu\n", fileid, *resid);
474 } else {
475 DPRINTF("%llu off %" PRIu64 " sz %zu\n", fileid,
476 (uint64_t)offset, *resid);
477 }
478 lock = fileid_lock(fileid, puffs_cc_getcc(pu));
479 retry:
480 xc = begin(pu, "write");
481 error = update_mctime(xc, fileid);
482 if (error != 0) {
483 goto got_error;
484 }
485 error = lo_open_by_fileid(xc, fileid, INV_WRITE, &fd);
486 if (error != 0) {
487 goto got_error;
488 }
489 if ((ioflags & PUFFS_IO_APPEND) != 0) {
490 int32_t off;
491
492 error = my_lo_lseek(xc, fd, 0, SEEK_END, &off);
493 if (error != 0) {
494 goto got_error;
495 }
496 offset = off;
497 }
498 if (offset < 0) { /* negative offset */
499 error = EINVAL;
500 goto got_error;
501 }
502 if ((uint64_t)(INT64_MAX - offset) < *resid || /* int64 overflow */
503 INT_MAX < offset + *resid) { /* our max filesize */
504 error = EFBIG;
505 goto got_error;
506 }
507 if ((ioflags & PUFFS_IO_APPEND) == 0) {
508 error = my_lo_lseek(xc, fd, offset, SEEK_SET, NULL);
509 if (error != 0) {
510 goto got_error;
511 }
512 }
513 error = my_lo_write(xc, fd, (const char *)buf, *resid, &resultlen);
514 if (error != 0) {
515 goto got_error;
516 }
517 assert(*resid >= resultlen);
518 error = commit(xc);
519 if (error != 0) {
520 goto got_error;
521 }
522 *resid -= resultlen;
523 DPRINTF("resid %zu\n", *resid);
524 goto done;
525 got_error:
526 rollback(xc);
527 if (error == EAGAIN) {
528 goto retry;
529 }
530 done:
531 fileid_unlock(lock);
532 return error;
533 }
534
535 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)536 pgfs_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
537 uint8_t *buf, off_t offset, size_t *resid,
538 const struct puffs_cred *pcr, int ioflags)
539 {
540 struct Xconn *xc;
541 fileid_t fileid = cookie_to_fileid(opc);
542 size_t resultlen;
543 int fd;
544 int error;
545
546 DPRINTF("%llu off %" PRIu64 " sz %zu\n",
547 fileid, (uint64_t)offset, *resid);
548 retry:
549 xc = begin(pu, "read");
550 /*
551 * try to update atime first as it's prone to conflict with other
552 * transactions. eg. read-ahead requests can conflict each other.
553 * we don't want to retry my_lo_read as it's expensive.
554 *
555 * XXX probably worth to implement noatime mount option.
556 */
557 error = update_atime(xc, fileid);
558 if (error != 0) {
559 goto got_error;
560 }
561 error = lo_open_by_fileid(xc, fileid, INV_READ, &fd);
562 if (error != 0) {
563 goto got_error;
564 }
565 error = my_lo_lseek(xc, fd, offset, SEEK_SET, NULL);
566 if (error != 0) {
567 goto got_error;
568 }
569 error = my_lo_read(xc, fd, buf, *resid, &resultlen);
570 if (error != 0) {
571 goto got_error;
572 }
573 assert(*resid >= resultlen);
574 error = commit(xc);
575 if (error != 0) {
576 goto got_error;
577 }
578 *resid -= resultlen;
579 return 0;
580 got_error:
581 rollback(xc);
582 if (error == EAGAIN) {
583 goto retry;
584 }
585 return error;
586 }
587
588 int
pgfs_node_link(struct puffs_usermount * pu,puffs_cookie_t dir_opc,puffs_cookie_t targ_opc,const struct puffs_cn * pcn)589 pgfs_node_link(struct puffs_usermount *pu, puffs_cookie_t dir_opc,
590 puffs_cookie_t targ_opc, const struct puffs_cn *pcn)
591 {
592 struct Xconn *xc;
593 fileid_t dir_fileid = cookie_to_fileid(dir_opc);
594 fileid_t targ_fileid = cookie_to_fileid(targ_opc);
595 struct vattr va;
596 int error;
597
598 DPRINTF("%llu %llu %s\n", dir_fileid, targ_fileid, pcn->pcn_name);
599 retry:
600 xc = begin(pu, "link");
601 error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
602 if (error != 0) {
603 goto got_error;
604 }
605 if (va.va_type == VDIR) {
606 error = EPERM;
607 goto got_error;
608 }
609 error = linkfile(xc, dir_fileid, pcn->pcn_name, targ_fileid);
610 if (error != 0) {
611 goto got_error;
612 }
613 error = update_ctime(xc, targ_fileid);
614 if (error != 0) {
615 goto got_error;
616 }
617 error = commit(xc);
618 if (error != 0) {
619 goto got_error;
620 }
621 return 0;
622 got_error:
623 rollback(xc);
624 if (error == EAGAIN) {
625 goto retry;
626 }
627 return error;
628 }
629
630 int
pgfs_node_remove(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)631 pgfs_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
632 puffs_cookie_t targ, const struct puffs_cn *pcn)
633 {
634 struct Xconn *xc;
635 fileid_t fileid = cookie_to_fileid(opc);
636 fileid_t targ_fileid = cookie_to_fileid(targ);
637 struct vattr va;
638 int error;
639
640 retry:
641 xc = begin(pu, "remove");
642 error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
643 if (error != 0) {
644 goto got_error;
645 }
646 if (va.va_type == VDIR) {
647 error = EPERM;
648 goto got_error;
649 }
650 error = unlinkfile(xc, fileid, pcn->pcn_name, targ_fileid);
651 if (error != 0) {
652 goto got_error;
653 }
654 error = commit(xc);
655 if (error != 0) {
656 goto got_error;
657 }
658 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_INACT_N2);
659 return 0;
660 got_error:
661 rollback(xc);
662 if (error == EAGAIN) {
663 goto retry;
664 }
665 return error;
666 }
667
668 int
pgfs_node_rmdir(struct puffs_usermount * pu,puffs_cookie_t opc,puffs_cookie_t targ,const struct puffs_cn * pcn)669 pgfs_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
670 puffs_cookie_t targ, const struct puffs_cn *pcn)
671 {
672 struct Xconn *xc;
673 fileid_t parent_fileid = cookie_to_fileid(opc);
674 fileid_t targ_fileid = cookie_to_fileid(targ);
675 struct vattr va;
676 bool empty;
677 int error;
678
679 retry:
680 xc = begin(pu, "rmdir");
681 error = getattr(xc, targ_fileid, &va, GETATTR_TYPE);
682 if (error != 0) {
683 goto got_error;
684 }
685 if (va.va_type != VDIR) {
686 error = ENOTDIR;
687 goto got_error;
688 }
689 error = isempty(xc, targ_fileid, &empty);
690 if (error != 0) {
691 goto got_error;
692 }
693 if (!empty) {
694 error = ENOTEMPTY;
695 goto got_error;
696 }
697 error = unlinkfile(xc, parent_fileid, pcn->pcn_name, targ_fileid);
698 if (error == 0) {
699 error = update_nlink(xc, parent_fileid, -1);
700 }
701 if (error != 0) {
702 goto got_error;
703 }
704 error = commit(xc);
705 if (error != 0) {
706 goto got_error;
707 }
708 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_INACT_N2);
709 return 0;
710 got_error:
711 rollback(xc);
712 if (error == EAGAIN) {
713 goto retry;
714 }
715 return error;
716 }
717
718 int
pgfs_node_inactive(struct puffs_usermount * pu,puffs_cookie_t opc)719 pgfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
720 {
721 struct Xconn *xc;
722 fileid_t fileid = cookie_to_fileid(opc);
723 int error;
724
725 /*
726 * XXX
727 * probably this should be handed to the separate "reaper" context
728 * because lo_unlink() can be too expensive to execute synchronously.
729 * however, the puffs_cc API doesn't provide a way to create a worker
730 * context.
731 */
732
733 DPRINTF("%llu\n", fileid);
734 retry:
735 xc = begin(pu, "inactive");
736 error = cleanupfile(xc, fileid);
737 if (error != 0) {
738 goto got_error;
739 }
740 error = commit(xc);
741 if (error != 0) {
742 goto got_error;
743 }
744 return 0;
745 got_error:
746 rollback(xc);
747 if (error == EAGAIN) {
748 goto retry;
749 }
750 return error;
751 }
752
753 int
pgfs_node_setattr(struct puffs_usermount * pu,puffs_cookie_t opc,const struct vattr * va,const struct puffs_cred * pcr)754 pgfs_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
755 const struct vattr *va, const struct puffs_cred *pcr)
756 {
757 struct Xconn *xc;
758 struct fileid_lock_handle *lock;
759 fileid_t fileid = cookie_to_fileid(opc);
760 struct vattr ova;
761 unsigned int attrs;
762 int error;
763
764 DPRINTF("%llu\n", fileid);
765 if (va->va_flags != (u_long)PUFFS_VNOVAL) {
766 return EOPNOTSUPP;
767 }
768 attrs = 0;
769 if (va->va_uid != (uid_t)PUFFS_VNOVAL ||
770 va->va_gid != (gid_t)PUFFS_VNOVAL) {
771 attrs |= GETATTR_UID|GETATTR_GID|GETATTR_MODE;
772 }
773 if (va->va_mode != (mode_t)PUFFS_VNOVAL) {
774 attrs |= GETATTR_TYPE|GETATTR_UID|GETATTR_GID;
775 }
776 if (va->va_atime.tv_sec != PUFFS_VNOVAL ||
777 va->va_mtime.tv_sec != PUFFS_VNOVAL ||
778 va->va_ctime.tv_sec != PUFFS_VNOVAL) {
779 attrs |= GETATTR_UID|GETATTR_GID|GETATTR_MODE;
780 }
781 lock = fileid_lock(fileid, puffs_cc_getcc(pu));
782 retry:
783 xc = begin(pu, "setattr");
784 error = getattr(xc, fileid, &ova, attrs);
785 if (error != 0) {
786 goto got_error;
787 }
788 if (va->va_uid != (uid_t)PUFFS_VNOVAL ||
789 va->va_gid != (gid_t)PUFFS_VNOVAL) {
790 static struct cmd *c;
791 uint64_t newuid =
792 va->va_uid != (uid_t)PUFFS_VNOVAL ? va->va_uid : ova.va_uid;
793 uint64_t newgid =
794 va->va_gid != (gid_t)PUFFS_VNOVAL ? va->va_gid : ova.va_gid;
795
796 error = puffs_access_chown(ova.va_uid, ova.va_gid,
797 newuid, newgid, pcr);
798 if (error != 0) {
799 goto got_error;
800 }
801 CREATECMD(c,
802 "UPDATE file "
803 "SET uid = $1, gid = $2 "
804 "WHERE fileid = $3", INT8OID, INT8OID, INT8OID);
805 error = simplecmd(xc, c, newuid, newgid, fileid);
806 if (error != 0) {
807 goto got_error;
808 }
809 ova.va_uid = newuid;
810 ova.va_gid = newgid;
811 }
812 if (va->va_mode != (mode_t)PUFFS_VNOVAL) {
813 static struct cmd *c;
814 uint64_t newmode = va->va_mode;
815
816 error = puffs_access_chmod(ova.va_uid, ova.va_gid, ova.va_type,
817 newmode, pcr);
818 if (error != 0) {
819 goto got_error;
820 }
821 CREATECMD(c,
822 "UPDATE file "
823 "SET mode = $1 "
824 "WHERE fileid = $2", INT8OID, INT8OID);
825 error = simplecmd(xc, c, newmode, fileid);
826 if (error != 0) {
827 goto got_error;
828 }
829 ova.va_mode = newmode;
830 }
831 if (va->va_atime.tv_sec != PUFFS_VNOVAL ||
832 va->va_mtime.tv_sec != PUFFS_VNOVAL ||
833 va->va_ctime.tv_sec != PUFFS_VNOVAL ||
834 va->va_birthtime.tv_sec != PUFFS_VNOVAL) {
835 error = puffs_access_times(ova.va_uid, ova.va_gid, ova.va_mode,
836 (va->va_vaflags & VA_UTIMES_NULL) != 0, pcr);
837 if (error != 0) {
838 goto got_error;
839 }
840 if (va->va_atime.tv_sec != PUFFS_VNOVAL) {
841 static struct cmd *c;
842 char *ts;
843
844 error = timespec_to_pgtimestamp(&va->va_atime, &ts);
845 if (error != 0) {
846 goto got_error;
847 }
848 CREATECMD(c,
849 "UPDATE file "
850 "SET atime = $1 "
851 "WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
852 error = simplecmd(xc, c, ts, fileid);
853 free(ts);
854 if (error != 0) {
855 goto got_error;
856 }
857 }
858 if (va->va_mtime.tv_sec != PUFFS_VNOVAL) {
859 static struct cmd *c;
860 char *ts;
861
862 error = timespec_to_pgtimestamp(&va->va_mtime, &ts);
863 if (error != 0) {
864 goto got_error;
865 }
866 CREATECMD(c,
867 "UPDATE file "
868 "SET mtime = $1 "
869 "WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
870 error = simplecmd(xc, c, ts, fileid);
871 free(ts);
872 if (error != 0) {
873 goto got_error;
874 }
875 }
876 if (va->va_ctime.tv_sec != PUFFS_VNOVAL) {
877 static struct cmd *c;
878 char *ts;
879
880 error = timespec_to_pgtimestamp(&va->va_ctime, &ts);
881 if (error != 0) {
882 goto got_error;
883 }
884 CREATECMD(c,
885 "UPDATE file "
886 "SET ctime = $1 "
887 "WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
888 error = simplecmd(xc, c, ts, fileid);
889 free(ts);
890 if (error != 0) {
891 goto got_error;
892 }
893 }
894 if (va->va_birthtime.tv_sec != PUFFS_VNOVAL) {
895 static struct cmd *c;
896 char *ts;
897
898 error = timespec_to_pgtimestamp(&va->va_birthtime, &ts);
899 if (error != 0) {
900 goto got_error;
901 }
902 CREATECMD(c,
903 "UPDATE file "
904 "SET btime = $1 "
905 "WHERE fileid = $2", TIMESTAMPTZOID, INT8OID);
906 error = simplecmd(xc, c, ts, fileid);
907 free(ts);
908 if (error != 0) {
909 goto got_error;
910 }
911 }
912 }
913 if (va->va_size != (uint64_t)PUFFS_VNOVAL) {
914 int fd;
915
916 if (va->va_size > INT_MAX) {
917 error = EFBIG;
918 goto got_error;
919 }
920 error = lo_open_by_fileid(xc, fileid, INV_READ|INV_WRITE, &fd);
921 if (error != 0) {
922 goto got_error;
923 }
924 error = my_lo_truncate(xc, fd, va->va_size);
925 if (error != 0) {
926 goto got_error;
927 }
928 error = my_lo_close(xc, fd);
929 if (error != 0) {
930 goto got_error;
931 }
932 }
933 error = commit(xc);
934 if (error != 0) {
935 goto got_error;
936 }
937 goto done;
938 got_error:
939 rollback(xc);
940 if (error == EAGAIN) {
941 goto retry;
942 }
943 done:
944 fileid_unlock(lock);
945 return error;
946 }
947
948 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)949 pgfs_node_rename(struct puffs_usermount *pu, puffs_cookie_t src_dir,
950 puffs_cookie_t src, const struct puffs_cn *pcn_src,
951 puffs_cookie_t targ_dir, puffs_cookie_t targ,
952 const struct puffs_cn *pcn_targ)
953 {
954 struct Xconn *xc;
955 fileid_t fileid_src_dir = cookie_to_fileid(src_dir);
956 fileid_t fileid_src = cookie_to_fileid(src);
957 fileid_t fileid_targ_dir = cookie_to_fileid(targ_dir);
958 fileid_t fileid_targ = cookie_to_fileid(targ);
959 struct vattr va_src;
960 struct vattr va_targ;
961 int error;
962
963 DPRINTF("%llu %llu %llu %llu\n", fileid_src_dir, fileid_src,
964 fileid_targ_dir, fileid_targ);
965 retry:
966 xc = begin(pu, "rename");
967 error = getattr(xc, fileid_src, &va_src, GETATTR_TYPE);
968 if (error != 0) {
969 goto got_error;
970 }
971 if (va_src.va_type == VDIR) {
972 error = check_path(xc, fileid_src, fileid_targ_dir);
973 if (error != 0) {
974 goto got_error;
975 }
976 }
977 if (fileid_targ != 0) {
978 error = getattr(xc, fileid_targ, &va_targ,
979 GETATTR_TYPE|GETATTR_NLINK);
980 if (error != 0) {
981 goto got_error;
982 }
983 if (va_src.va_type == VDIR) {
984 if (va_targ.va_type != VDIR) {
985 error = ENOTDIR;
986 goto got_error;
987 }
988 if (va_targ.va_nlink != 2) {
989 error = ENOTEMPTY;
990 goto got_error;
991 }
992 } else if (va_targ.va_type == VDIR) {
993 error = EISDIR;
994 goto got_error;
995 }
996 error = unlinkfile(xc, fileid_targ_dir, pcn_targ->pcn_name,
997 fileid_targ);
998 if (error == 0 && va_targ.va_type == VDIR) {
999 error = update_nlink(xc, fileid_targ_dir, -1);
1000 }
1001 if (error != 0) {
1002 goto got_error;
1003 }
1004 }
1005 error = linkfile(xc, fileid_targ_dir, pcn_targ->pcn_name, fileid_src);
1006 if (error == 0 && va_src.va_type == VDIR) {
1007 error = update_nlink(xc, fileid_targ_dir, 1);
1008 }
1009 if (error != 0) {
1010 goto got_error;
1011 }
1012 /* XXX ctime? */
1013 error = unlinkfile(xc, fileid_src_dir, pcn_src->pcn_name, fileid_src);
1014 if (error == 0 && va_src.va_type == VDIR) {
1015 error = update_nlink(xc, fileid_src_dir, -1);
1016 }
1017 if (error != 0) {
1018 goto got_error;
1019 }
1020 error = commit(xc);
1021 if (error != 0) {
1022 goto got_error;
1023 }
1024 return 0;
1025 got_error:
1026 rollback(xc);
1027 if (error == EAGAIN) {
1028 goto retry;
1029 }
1030 return error;
1031 }
1032
1033 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)1034 pgfs_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
1035 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
1036 const struct vattr *va, const char *target)
1037 {
1038 struct Xconn *xc;
1039 struct puffs_cred *pcr = pcn->pcn_cred;
1040 fileid_t parent_fileid = cookie_to_fileid(opc);
1041 fileid_t new_fileid;
1042 size_t resultlen;
1043 size_t targetlen;
1044 uid_t uid;
1045 gid_t gid;
1046 int loid;
1047 int fd;
1048 int error;
1049
1050 DPRINTF("%llu %s %s\n", parent_fileid, pcn->pcn_name, target);
1051 if (puffs_cred_getuid(pcr, &uid) == -1 ||
1052 puffs_cred_getgid(pcr, &gid) == -1) {
1053 return errno;
1054 }
1055 retry:
1056 xc = begin(pu, "symlink");
1057 error = mklinkfile_lo(xc, parent_fileid, pcn->pcn_name, VLNK,
1058 va->va_mode, uid, gid, &new_fileid, &loid);
1059 if (error != 0) {
1060 goto got_error;
1061 }
1062 error = my_lo_open(xc, loid, INV_WRITE, &fd);
1063 if (error != 0) {
1064 goto got_error;
1065 }
1066 targetlen = strlen(target);
1067 error = my_lo_write(xc, fd, target, targetlen, &resultlen);
1068 if (error != 0) {
1069 goto got_error;
1070 }
1071 if (resultlen != targetlen) {
1072 error = ENOSPC; /* XXX */
1073 goto got_error;
1074 }
1075 error = commit(xc);
1076 if (error != 0) {
1077 goto got_error;
1078 }
1079 puffs_newinfo_setcookie(pni, fileid_to_cookie(new_fileid));
1080 return 0;
1081 got_error:
1082 rollback(xc);
1083 if (error == EAGAIN) {
1084 goto retry;
1085 }
1086 return error;
1087 }
1088
1089 int
pgfs_node_readlink(struct puffs_usermount * pu,puffs_cookie_t opc,const struct puffs_cred * pcr,char * buf,size_t * buflenp)1090 pgfs_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
1091 const struct puffs_cred *pcr, char *buf, size_t *buflenp)
1092 {
1093 fileid_t fileid = cookie_to_fileid(opc);
1094 struct Xconn *xc;
1095 size_t resultlen;
1096 int fd;
1097 int error;
1098
1099 DPRINTF("%llu\n", fileid);
1100 xc = begin_readonly(pu, "readlink");
1101 error = lo_open_by_fileid(xc, fileid, INV_READ, &fd);
1102 if (error != 0) {
1103 rollback(xc);
1104 return error;
1105 }
1106 error = my_lo_read(xc, fd, buf, *buflenp, &resultlen);
1107 if (error != 0) {
1108 rollback(xc);
1109 return error;
1110 }
1111 assert(resultlen <= *buflenp);
1112 error = commit(xc);
1113 if (error != 0) {
1114 return error;
1115 }
1116 *buflenp = resultlen;
1117 return 0;
1118 }
1119
1120 int
pgfs_node_access(struct puffs_usermount * pu,puffs_cookie_t opc,int mode,const struct puffs_cred * pcr)1121 pgfs_node_access(struct puffs_usermount *pu, puffs_cookie_t opc,
1122 int mode, const struct puffs_cred *pcr)
1123 {
1124 struct Xconn *xc;
1125 fileid_t fileid = cookie_to_fileid(opc);
1126 struct vattr va;
1127 int error;
1128
1129 DPRINTF("%llu\n", fileid);
1130 retry:
1131 xc = begin_readonly(pu, "access");
1132 error = getattr(xc, fileid, &va,
1133 GETATTR_TYPE|GETATTR_MODE|GETATTR_UID|GETATTR_GID);
1134 if (error != 0) {
1135 goto got_error;
1136 }
1137 error = commit(xc);
1138 if (error != 0) {
1139 goto got_error;
1140 }
1141 return puffs_access(va.va_type, va.va_mode, va.va_uid, va.va_gid, mode,
1142 pcr);
1143 got_error:
1144 rollback(xc);
1145 if (error == EAGAIN) {
1146 goto retry;
1147 }
1148 return error;
1149 }
1150
1151 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)1152 pgfs_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
1153 const struct puffs_cred *pcr, int flags, off_t offlo, off_t offhi)
1154 {
1155 fileid_t fileid = cookie_to_fileid(opc);
1156
1157 DPRINTF("%llu\n", fileid);
1158 return flush_xacts(pu);
1159 }
1160
1161 int
pgfs_fs_statvfs(struct puffs_usermount * pu,struct statvfs * sbp)1162 pgfs_fs_statvfs(struct puffs_usermount *pu, struct statvfs *sbp)
1163 {
1164 struct Xconn *xc;
1165 uint64_t nfiles;
1166 uint64_t bytes;
1167 uint64_t lo_bytes;
1168 static struct cmd *c_nfiles;
1169 static struct cmd *c_bytes;
1170 static struct cmd *c_lobytes;
1171 static const Oid types[] = { INT8OID, };
1172 struct fetchstatus s;
1173 int error;
1174
1175 retry:
1176 xc = begin_readonly(pu, "statvfs");
1177 /*
1178 * use an estimate which we can retrieve quickly, instead of
1179 * "SELECT count(*) from file".
1180 */
1181 CREATECMD_NOPARAM(c_nfiles,
1182 "SELECT reltuples::int8 "
1183 "FROM pg_class c LEFT JOIN pg_namespace n "
1184 "ON (n.oid=c.relnamespace) "
1185 "WHERE n.nspname = 'pgfs' AND c.relname = 'file'");
1186 CREATECMD_NOPARAM(c_bytes,
1187 "SELECT sum(pg_total_relation_size(c.oid))::int8 "
1188 "FROM pg_class c LEFT JOIN pg_namespace n "
1189 "ON (n.oid=c.relnamespace) "
1190 "WHERE n.nspname = 'pgfs'");
1191 /*
1192 * the following is not correct if someone else is using large objects
1193 * in the same database. we don't bother to join with datafork it as
1194 * it's too expensive for the little benefit.
1195 */
1196 CREATECMD_NOPARAM(c_lobytes,
1197 "SELECT pg_total_relation_size('pg_largeobject')::int8");
1198 error = sendcmd(xc, c_nfiles);
1199 if (error != 0) {
1200 goto got_error;
1201 }
1202 fetchinit(&s, xc);
1203 error = FETCHNEXT(&s, types, &nfiles);
1204 fetchdone(&s);
1205 if (error != 0) {
1206 goto got_error;
1207 }
1208 error = sendcmd(xc, c_bytes);
1209 if (error != 0) {
1210 goto got_error;
1211 }
1212 fetchinit(&s, xc);
1213 error = FETCHNEXT(&s, types, &bytes);
1214 fetchdone(&s);
1215 if (error != 0) {
1216 goto got_error;
1217 }
1218 error = sendcmd(xc, c_lobytes);
1219 if (error != 0) {
1220 goto got_error;
1221 }
1222 fetchinit(&s, xc);
1223 error = FETCHNEXT(&s, types, &lo_bytes);
1224 fetchdone(&s);
1225 if (error != 0) {
1226 goto got_error;
1227 }
1228 error = commit(xc);
1229 if (error != 0) {
1230 goto got_error;
1231 }
1232 /*
1233 * XXX fill f_blocks and f_files with meaningless large values.
1234 * there are no easy way to provide meaningful values for them
1235 * esp. with tablespaces.
1236 */
1237 sbp->f_bsize = LOBLKSIZE;
1238 sbp->f_frsize = LOBLKSIZE;
1239 sbp->f_blocks = INT64_MAX / 100 / sbp->f_frsize;
1240 sbp->f_bfree = sbp->f_blocks - howmany(bytes + lo_bytes, sbp->f_frsize);
1241 sbp->f_bavail = sbp->f_bfree;
1242 sbp->f_bresvd = 0;
1243 sbp->f_files = INT_MAX;
1244 sbp->f_ffree = sbp->f_files - nfiles;
1245 sbp->f_favail = sbp->f_ffree;
1246 sbp->f_fresvd = 0;
1247 return 0;
1248 got_error:
1249 rollback(xc);
1250 if (error == EAGAIN) {
1251 goto retry;
1252 }
1253 return error;
1254 }
1255