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