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