1 /* $NetBSD: null.c,v 1.27 2009/01/08 02:19:48 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 #if !defined(lint) 30 __RCSID("$NetBSD: null.c,v 1.27 2009/01/08 02:19:48 lukem Exp $"); 31 #endif /* !lint */ 32 33 /* 34 * A "nullfs" using puffs, i.e. maps one location in the hierarchy 35 * to another using standard system calls. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/time.h> 40 41 #include <assert.h> 42 #include <dirent.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <puffs.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 50 PUFFSOP_PROTOS(puffs_null) 51 52 /* 53 * set attributes to what is specified. XXX: no rollback in case of failure 54 */ 55 static int 56 processvattr(const char *path, const struct vattr *va, int regular) 57 { 58 struct timeval tv[2]; 59 60 /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */ 61 if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1) 62 if (lchown(path, va->va_uid, va->va_gid) == -1) 63 return errno; 64 65 if (va->va_mode != (unsigned)PUFFS_VNOVAL) 66 if (lchmod(path, va->va_mode) == -1) 67 return errno; 68 69 /* sloppy */ 70 if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL 71 || va->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL) { 72 TIMESPEC_TO_TIMEVAL(&tv[0], &va->va_atime); 73 TIMESPEC_TO_TIMEVAL(&tv[1], &va->va_mtime); 74 75 if (lutimes(path, tv) == -1) 76 return errno; 77 } 78 79 if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL) 80 if (truncate(path, (off_t)va->va_size) == -1) 81 return errno; 82 83 return 0; 84 } 85 86 /* 87 * Kludge to open files which aren't writable *any longer*. This kinda 88 * works because the vfs layer does validation checks based on the file's 89 * permissions to allow writable opening before opening them. However, 90 * the problem arises if we want to create a file, write to it (cache), 91 * adjust permissions and then flush the file. 92 */ 93 static int 94 writeableopen(const char *path) 95 { 96 struct stat sb; 97 mode_t origmode; 98 int sverr = 0; 99 int fd; 100 101 fd = open(path, O_WRONLY); 102 if (fd == -1) { 103 if (errno == EACCES) { 104 if (stat(path, &sb) == -1) 105 return -1; 106 origmode = sb.st_mode & ALLPERMS; 107 108 if (chmod(path, 0200) == -1) 109 return -1; 110 111 fd = open(path, O_WRONLY); 112 if (fd == -1) 113 sverr = errno; 114 115 chmod(path, origmode); 116 if (sverr) 117 errno = sverr; 118 } else 119 return -1; 120 } 121 122 return fd; 123 } 124 125 /*ARGSUSED*/ 126 static void * 127 inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) 128 { 129 ino_t *cmpino = arg; 130 131 if (pn->pn_va.va_fileid == *cmpino) 132 return pn; 133 return NULL; 134 } 135 136 static int 137 makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni, 138 const struct puffs_cn *pcn, const struct vattr *va, int regular) 139 { 140 struct puffs_node *pn; 141 struct stat sb; 142 int rv; 143 144 if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0) 145 return rv; 146 147 pn = puffs_pn_new(pu, NULL); 148 if (!pn) 149 return ENOMEM; 150 puffs_setvattr(&pn->pn_va, va); 151 152 if (lstat(PCNPATH(pcn), &sb) == -1) 153 return errno; 154 puffs_stat2vattr(&pn->pn_va, &sb); 155 156 puffs_newinfo_setcookie(pni, pn); 157 return 0; 158 } 159 160 /* This should be called first and overriden from the file system */ 161 void 162 puffs_null_setops(struct puffs_ops *pops) 163 { 164 165 PUFFSOP_SET(pops, puffs_null, fs, statvfs); 166 PUFFSOP_SETFSNOP(pops, unmount); 167 PUFFSOP_SETFSNOP(pops, sync); 168 PUFFSOP_SET(pops, puffs_null, fs, fhtonode); 169 PUFFSOP_SET(pops, puffs_null, fs, nodetofh); 170 171 PUFFSOP_SET(pops, puffs_null, node, lookup); 172 PUFFSOP_SET(pops, puffs_null, node, create); 173 PUFFSOP_SET(pops, puffs_null, node, mknod); 174 PUFFSOP_SET(pops, puffs_null, node, getattr); 175 PUFFSOP_SET(pops, puffs_null, node, setattr); 176 PUFFSOP_SET(pops, puffs_null, node, fsync); 177 PUFFSOP_SET(pops, puffs_null, node, remove); 178 PUFFSOP_SET(pops, puffs_null, node, link); 179 PUFFSOP_SET(pops, puffs_null, node, rename); 180 PUFFSOP_SET(pops, puffs_null, node, mkdir); 181 PUFFSOP_SET(pops, puffs_null, node, rmdir); 182 PUFFSOP_SET(pops, puffs_null, node, symlink); 183 PUFFSOP_SET(pops, puffs_null, node, readlink); 184 PUFFSOP_SET(pops, puffs_null, node, readdir); 185 PUFFSOP_SET(pops, puffs_null, node, read); 186 PUFFSOP_SET(pops, puffs_null, node, write); 187 PUFFSOP_SET(pops, puffs_genfs, node, reclaim); 188 } 189 190 /*ARGSUSED*/ 191 int 192 puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb) 193 { 194 195 if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1) 196 return errno; 197 198 return 0; 199 } 200 201 /* 202 * XXX: this is the stupidest crap ever, but: 203 * getfh() returns the fhandle type, when we are expected to deliver 204 * the fid type. Just adjust it a bit and stop whining. 205 * 206 * Yes, this really really needs fixing. Yes, *REALLY*. 207 */ 208 #define FHANDLE_HEADERLEN 8 209 struct kernfid { 210 unsigned short fid_len; /* length of data in bytes */ 211 unsigned short fid_reserved; /* compat: historic align */ 212 char fid_data[0]; /* data (variable length) */ 213 }; 214 215 /*ARGSUSED*/ 216 static void * 217 fhcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) 218 { 219 struct kernfid *kf1, *kf2; 220 221 if ((kf1 = pn->pn_data) == NULL) 222 return NULL; 223 kf2 = arg; 224 225 if (kf1->fid_len != kf2->fid_len) 226 return NULL; 227 228 /*LINTED*/ 229 if (memcmp(kf1, kf2, kf1->fid_len) == 0) 230 return pn; 231 return NULL; 232 } 233 234 /* 235 * This routine only supports file handles which have been issued while 236 * the server was alive. Not really stable ones, that is. 237 */ 238 /*ARGSUSED*/ 239 int 240 puffs_null_fs_fhtonode(struct puffs_usermount *pu, void *fid, size_t fidsize, 241 struct puffs_newinfo *pni) 242 { 243 struct puffs_node *pn_res; 244 245 pn_res = puffs_pn_nodewalk(pu, fhcmp, fid); 246 if (pn_res == NULL) 247 return ENOENT; 248 249 puffs_newinfo_setcookie(pni, pn_res); 250 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); 251 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); 252 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); 253 return 0; 254 } 255 256 /*ARGSUSED*/ 257 int 258 puffs_null_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t opc, 259 void *fid, size_t *fidsize) 260 { 261 struct puffs_node *pn = opc; 262 struct kernfid *kfid; 263 void *bounce; 264 int rv; 265 266 rv = 0; 267 bounce = NULL; 268 if (*fidsize) { 269 bounce = malloc(*fidsize + FHANDLE_HEADERLEN); 270 if (!bounce) 271 return ENOMEM; 272 *fidsize += FHANDLE_HEADERLEN; 273 } 274 if (getfh(PNPATH(pn), bounce, fidsize) == -1) 275 rv = errno; 276 else 277 memcpy(fid, (uint8_t *)bounce + FHANDLE_HEADERLEN, 278 *fidsize - FHANDLE_HEADERLEN); 279 kfid = fid; 280 if (rv == 0) { 281 *fidsize = kfid->fid_len; 282 pn->pn_data = malloc(*fidsize); 283 if (pn->pn_data == NULL) 284 abort(); /* lazy */ 285 memcpy(pn->pn_data, fid, *fidsize); 286 } else { 287 *fidsize -= FHANDLE_HEADERLEN; 288 } 289 free(bounce); 290 291 return rv; 292 } 293 294 int 295 puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, 296 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 297 { 298 struct puffs_node *pn = opc, *pn_res; 299 struct stat sb; 300 int rv; 301 302 assert(pn->pn_va.va_type == VDIR); 303 304 /* 305 * Note to whoever is copypasting this: you must first check 306 * if the node is there and only then do nodewalk. Alternatively 307 * you could make sure that you don't return unlinked/rmdir'd 308 * nodes in some other fashion 309 */ 310 rv = lstat(PCNPATH(pcn), &sb); 311 if (rv) 312 return errno; 313 314 /* XXX2: nodewalk is a bit too slow here */ 315 pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino); 316 317 if (pn_res == NULL) { 318 pn_res = puffs_pn_new(pu, NULL); 319 if (pn_res == NULL) 320 return ENOMEM; 321 puffs_stat2vattr(&pn_res->pn_va, &sb); 322 } 323 324 puffs_newinfo_setcookie(pni, pn_res); 325 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); 326 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); 327 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); 328 329 return 0; 330 } 331 332 /*ARGSUSED*/ 333 int 334 puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, 335 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 336 const struct vattr *va) 337 { 338 int fd, rv; 339 340 fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC); 341 if (fd == -1) 342 return errno; 343 close(fd); 344 345 rv = makenode(pu, pni, pcn, va, 1); 346 if (rv) 347 unlink(PCNPATH(pcn)); 348 return rv; 349 } 350 351 /*ARGSUSED*/ 352 int 353 puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc, 354 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 355 const struct vattr *va) 356 { 357 mode_t mode; 358 int rv; 359 360 mode = puffs_addvtype2mode(va->va_mode, va->va_type); 361 if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1) 362 return errno; 363 364 rv = makenode(pu, pni, pcn, va, 0); 365 if (rv) 366 unlink(PCNPATH(pcn)); 367 return rv; 368 } 369 370 /*ARGSUSED*/ 371 int 372 puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, 373 struct vattr *va, const struct puffs_cred *pcred) 374 { 375 struct puffs_node *pn = opc; 376 struct stat sb; 377 378 if (lstat(PNPATH(pn), &sb) == -1) 379 return errno; 380 puffs_stat2vattr(va, &sb); 381 382 return 0; 383 } 384 385 /*ARGSUSED*/ 386 int 387 puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, 388 const struct vattr *va, const struct puffs_cred *pcred) 389 { 390 struct puffs_node *pn = opc; 391 int rv; 392 393 rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG); 394 if (rv) 395 return rv; 396 397 puffs_setvattr(&pn->pn_va, va); 398 399 return 0; 400 } 401 402 /*ARGSUSED*/ 403 int 404 puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, 405 const struct puffs_cred *pcred, int how, 406 off_t offlo, off_t offhi) 407 { 408 struct puffs_node *pn = opc; 409 int fd, rv; 410 int fflags; 411 412 rv = 0; 413 fd = writeableopen(PNPATH(pn)); 414 if (fd == -1) 415 return errno; 416 417 if (how & PUFFS_FSYNC_DATAONLY) 418 fflags = FDATASYNC; 419 else 420 fflags = FFILESYNC; 421 if (how & PUFFS_FSYNC_CACHE) 422 fflags |= FDISKSYNC; 423 424 if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1) 425 rv = errno; 426 427 close(fd); 428 429 return rv; 430 } 431 432 /*ARGSUSED*/ 433 int 434 puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 435 puffs_cookie_t targ, const struct puffs_cn *pcn) 436 { 437 struct puffs_node *pn_targ = targ; 438 439 if (unlink(PNPATH(pn_targ)) == -1) 440 return errno; 441 puffs_pn_remove(pn_targ); 442 443 return 0; 444 } 445 446 /*ARGSUSED*/ 447 int 448 puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, 449 puffs_cookie_t targ, const struct puffs_cn *pcn) 450 { 451 struct puffs_node *pn_targ = targ; 452 453 if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1) 454 return errno; 455 456 return 0; 457 } 458 459 /*ARGSUSED*/ 460 int 461 puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, 462 puffs_cookie_t src, const struct puffs_cn *pcn_src, 463 puffs_cookie_t targ_dir, puffs_cookie_t targ, 464 const struct puffs_cn *pcn_targ) 465 { 466 467 if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1) 468 return errno; 469 470 return 0; 471 } 472 473 /*ARGSUSED*/ 474 int 475 puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 476 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 477 const struct vattr *va) 478 { 479 int rv; 480 481 if (mkdir(PCNPATH(pcn), va->va_mode) == -1) 482 return errno; 483 484 rv = makenode(pu, pni, pcn, va, 0); 485 if (rv) 486 rmdir(PCNPATH(pcn)); 487 return rv; 488 } 489 490 /*ARGSUSED*/ 491 int 492 puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 493 puffs_cookie_t targ, const struct puffs_cn *pcn) 494 { 495 struct puffs_node *pn_targ = targ; 496 497 if (rmdir(PNPATH(pn_targ)) == -1) 498 return errno; 499 puffs_pn_remove(pn_targ); 500 501 return 0; 502 } 503 504 /*ARGSUSED*/ 505 int 506 puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 507 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 508 const struct vattr *va, const char *linkname) 509 { 510 int rv; 511 512 if (symlink(linkname, PCNPATH(pcn)) == -1) 513 return errno; 514 515 rv = makenode(pu, pni, pcn, va, 0); 516 if (rv) 517 unlink(PCNPATH(pcn)); 518 return rv; 519 } 520 521 /*ARGSUSED*/ 522 int 523 puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 524 const struct puffs_cred *pcred, char *linkname, size_t *linklen) 525 { 526 struct puffs_node *pn = opc; 527 ssize_t rv; 528 529 rv = readlink(PNPATH(pn), linkname, *linklen); 530 if (rv == -1) 531 return errno; 532 533 *linklen = rv; 534 return 0; 535 } 536 537 /*ARGSUSED*/ 538 int 539 puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, 540 struct dirent *de, off_t *off, size_t *reslen, 541 const struct puffs_cred *pcred, int *eofflag, off_t *cookies, 542 size_t *ncookies) 543 { 544 struct puffs_node *pn = opc; 545 struct dirent entry, *result; 546 DIR *dp; 547 off_t i; 548 int rv; 549 550 *ncookies = 0; 551 dp = opendir(PNPATH(pn)); 552 if (dp == NULL) 553 return errno; 554 555 rv = 0; 556 i = *off; 557 558 /* 559 * XXX: need to do trickery here, telldir/seekdir would be nice, but 560 * then we'd need to keep state, which I'm too lazy to keep 561 */ 562 while (i--) { 563 rv = readdir_r(dp, &entry, &result); 564 if (rv || !result) 565 goto out; 566 } 567 568 for (;;) { 569 rv = readdir_r(dp, &entry, &result); 570 if (rv != 0) 571 goto out; 572 573 if (!result) { 574 *eofflag = 1; 575 goto out; 576 } 577 578 if (_DIRENT_SIZE(result) > *reslen) 579 goto out; 580 581 *de = *result; 582 *reslen -= _DIRENT_SIZE(result); 583 de = _DIRENT_NEXT(de); 584 585 (*off)++; 586 PUFFS_STORE_DCOOKIE(cookies, ncookies, *off); 587 } 588 589 out: 590 closedir(dp); 591 return 0; 592 } 593 594 /*ARGSUSED*/ 595 int 596 puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, 597 uint8_t *buf, off_t offset, size_t *buflen, 598 const struct puffs_cred *pcred, int ioflag) 599 { 600 struct puffs_node *pn = opc; 601 ssize_t n; 602 off_t off; 603 int fd, rv; 604 605 rv = 0; 606 fd = open(PNPATH(pn), O_RDONLY); 607 if (fd == -1) 608 return errno; 609 off = lseek(fd, offset, SEEK_SET); 610 if (off == -1) { 611 rv = errno; 612 goto out; 613 } 614 615 n = read(fd, buf, *buflen); 616 if (n == -1) 617 rv = errno; 618 else 619 *buflen -= n; 620 621 out: 622 close(fd); 623 return rv; 624 } 625 626 /*ARGSUSED*/ 627 int 628 puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, 629 uint8_t *buf, off_t offset, size_t *buflen, 630 const struct puffs_cred *pcred, int ioflag) 631 { 632 struct puffs_node *pn = opc; 633 ssize_t n; 634 off_t off; 635 int fd, rv; 636 637 rv = 0; 638 fd = writeableopen(PNPATH(pn)); 639 if (fd == -1) 640 return errno; 641 642 off = lseek(fd, offset, SEEK_SET); 643 if (off == -1) { 644 rv = errno; 645 goto out; 646 } 647 648 n = write(fd, buf, *buflen); 649 if (n == -1) 650 rv = errno; 651 else 652 *buflen -= n; 653 654 out: 655 close(fd); 656 return rv; 657 } 658