1 /* $NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka 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.25 2008/08/12 19:44:39 pooka 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 <stdio.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 #include <utime.h> 49 50 #include "puffs.h" 51 52 53 PUFFSOP_PROTOS(puffs_null) 54 55 /* 56 * set attributes to what is specified. XXX: no rollback in case of failure 57 */ 58 static int 59 processvattr(const char *path, const struct vattr *va, int regular) 60 { 61 struct utimbuf tbuf; 62 63 /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */ 64 if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1) 65 /* FIXME: lchown */ 66 if (chown(path, va->va_uid, va->va_gid) == -1) 67 return errno; 68 69 #ifndef __minix 70 if (va->va_mode != (unsigned)PUFFS_VNOVAL) 71 #endif 72 /* FIXME: lchmod */ 73 if (chmod(path, va->va_mode) == -1) 74 return errno; 75 76 /* sloppy */ 77 if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL 78 || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) { 79 /* FIXME: nsec too */ 80 tbuf.actime = va->va_atime.tv_sec; 81 tbuf.modtime = va->va_mtime.tv_sec; 82 83 /* FIXME: lutimes */ 84 if (utime(path, &tbuf) == -1) 85 return errno; 86 } 87 88 if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL) 89 if (truncate(path, (off_t)va->va_size) == -1) 90 return errno; 91 92 return 0; 93 } 94 95 /* 96 * Kludge to open files which aren't writable *any longer*. This kinda 97 * works because the vfs layer does validation checks based on the file's 98 * permissions to allow writable opening before opening them. However, 99 * the problem arises if we want to create a file, write to it (cache), 100 * adjust permissions and then flush the file. 101 */ 102 static int 103 writeableopen(const char *path) 104 { 105 struct stat sb; 106 mode_t origmode; 107 int sverr = 0; 108 int fd; 109 110 fd = open(path, O_WRONLY); 111 if (fd == -1) { 112 if (errno == EACCES) { 113 if (stat(path, &sb) == -1) 114 return -1; 115 origmode = sb.st_mode & ALLPERMS; 116 117 if (chmod(path, 0200) == -1) 118 return -1; 119 120 fd = open(path, O_WRONLY); 121 if (fd == -1) 122 sverr = errno; 123 124 chmod(path, origmode); 125 if (sverr) 126 errno = sverr; 127 } else 128 return -1; 129 } 130 131 return fd; 132 } 133 134 /*ARGSUSED*/ 135 static void * 136 inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) 137 { 138 ino_t *cmpino = arg; 139 140 if (pn->pn_va.va_fileid == *cmpino) 141 return pn; 142 return NULL; 143 } 144 145 static int 146 makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni, 147 const struct puffs_cn *pcn, const struct vattr *va, int regular) 148 { 149 struct puffs_node *pn; 150 struct stat sb; 151 int rv; 152 153 if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0) 154 return rv; 155 156 pn = puffs_pn_new(pu, NULL); 157 if (!pn) 158 return ENOMEM; 159 puffs_setvattr(&pn->pn_va, va); 160 161 if (lstat(PCNPATH(pcn), &sb) == -1) 162 return errno; 163 puffs_stat2vattr(&pn->pn_va, &sb); 164 165 puffs_newinfo_setcookie(pni, pn); 166 return 0; 167 } 168 169 /* This should be called first and overriden from the file system */ 170 void 171 puffs_null_setops(struct puffs_ops *pops) 172 { 173 174 PUFFSOP_SET(pops, puffs_null, fs, statvfs); 175 PUFFSOP_SETFSNOP(pops, unmount); 176 PUFFSOP_SETFSNOP(pops, sync); 177 178 PUFFSOP_SET(pops, puffs_null, node, lookup); 179 PUFFSOP_SET(pops, puffs_null, node, create); 180 PUFFSOP_SET(pops, puffs_null, node, mknod); 181 PUFFSOP_SET(pops, puffs_null, node, getattr); 182 PUFFSOP_SET(pops, puffs_null, node, setattr); 183 PUFFSOP_SET(pops, puffs_null, node, fsync); 184 PUFFSOP_SET(pops, puffs_null, node, remove); 185 PUFFSOP_SET(pops, puffs_null, node, link); 186 PUFFSOP_SET(pops, puffs_null, node, rename); 187 PUFFSOP_SET(pops, puffs_null, node, mkdir); 188 PUFFSOP_SET(pops, puffs_null, node, rmdir); 189 PUFFSOP_SET(pops, puffs_null, node, symlink); 190 PUFFSOP_SET(pops, puffs_null, node, readlink); 191 PUFFSOP_SET(pops, puffs_null, node, readdir); 192 PUFFSOP_SET(pops, puffs_null, node, read); 193 PUFFSOP_SET(pops, puffs_null, node, write); 194 PUFFSOP_SET(pops, puffs_genfs, node, reclaim); 195 } 196 197 /*ARGSUSED*/ 198 int 199 puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb) 200 { 201 202 if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1) 203 return errno; 204 205 return 0; 206 } 207 208 int 209 puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc, 210 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 211 { 212 struct puffs_node *pn = opc, *pn_res; 213 struct stat sb; 214 int rv; 215 216 assert(pn->pn_va.va_type == VDIR); 217 218 /* 219 * Note to whoever is copypasting this: you must first check 220 * if the node is there and only then do nodewalk. Alternatively 221 * you could make sure that you don't return unlinked/rmdir'd 222 * nodes in some other fashion 223 */ 224 rv = lstat(PCNPATH(pcn), &sb); 225 if (rv) 226 return errno; 227 228 /* XXX2: nodewalk is a bit too slow here */ 229 pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino); 230 231 if (pn_res == NULL) { 232 pn_res = puffs_pn_new(pu, NULL); 233 if (pn_res == NULL) 234 return ENOMEM; 235 puffs_stat2vattr(&pn_res->pn_va, &sb); 236 } 237 238 puffs_newinfo_setcookie(pni, pn_res); 239 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); 240 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); 241 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); 242 243 return 0; 244 } 245 246 /*ARGSUSED*/ 247 int 248 puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc, 249 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 250 const struct vattr *va) 251 { 252 int fd, rv; 253 254 fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC); 255 if (fd == -1) 256 return errno; 257 close(fd); 258 259 rv = makenode(pu, pni, pcn, va, 1); 260 if (rv) 261 unlink(PCNPATH(pcn)); 262 return rv; 263 } 264 265 /*ARGSUSED*/ 266 int 267 puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc, 268 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 269 const struct vattr *va) 270 { 271 mode_t mode; 272 int rv; 273 274 mode = puffs_addvtype2mode(va->va_mode, va->va_type); 275 if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1) 276 return errno; 277 278 rv = makenode(pu, pni, pcn, va, 0); 279 if (rv) 280 unlink(PCNPATH(pcn)); 281 return rv; 282 } 283 284 /*ARGSUSED*/ 285 int 286 puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, 287 struct vattr *va, const struct puffs_cred *pcred) 288 { 289 struct puffs_node *pn = opc; 290 struct stat sb; 291 292 if (lstat(PNPATH(pn), &sb) == -1) 293 return errno; 294 puffs_stat2vattr(va, &sb); 295 296 return 0; 297 } 298 299 /*ARGSUSED*/ 300 int 301 puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, 302 const struct vattr *va, const struct puffs_cred *pcred) 303 { 304 struct puffs_node *pn = opc; 305 int rv; 306 307 rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG); 308 if (rv) 309 return rv; 310 311 puffs_setvattr(&pn->pn_va, va); 312 313 return 0; 314 } 315 316 /*ARGSUSED*/ 317 int 318 puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, 319 const struct puffs_cred *pcred, int how, 320 off_t offlo, off_t offhi) 321 { 322 /* FIXME: implement me. */ 323 #if 0 324 struct puffs_node *pn = opc; 325 int fd, rv; 326 int fflags; 327 328 rv = 0; 329 fd = writeableopen(PNPATH(pn)); 330 if (fd == -1) 331 return errno; 332 333 if (how & PUFFS_FSYNC_DATAONLY) 334 fflags = FDATASYNC; 335 else 336 fflags = FFILESYNC; 337 if (how & PUFFS_FSYNC_CACHE) 338 fflags |= FDISKSYNC; 339 340 if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1) 341 rv = errno; 342 343 close(fd); 344 345 return rv; 346 #endif 347 return 0; 348 } 349 350 /*ARGSUSED*/ 351 int 352 puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc, 353 puffs_cookie_t targ, const struct puffs_cn *pcn) 354 { 355 struct puffs_node *pn_targ = targ; 356 357 if (unlink(PCNPATH(pcn)) == -1) 358 return errno; 359 puffs_pn_remove(pn_targ); 360 361 return 0; 362 } 363 364 /*ARGSUSED*/ 365 int 366 puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, 367 puffs_cookie_t targ, const struct puffs_cn *pcn) 368 { 369 struct puffs_node *pn_targ = targ; 370 371 if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1) 372 return errno; 373 374 return 0; 375 } 376 377 /*ARGSUSED*/ 378 int 379 puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc, 380 puffs_cookie_t src, const struct puffs_cn *pcn_src, 381 puffs_cookie_t targ_dir, puffs_cookie_t targ, 382 const struct puffs_cn *pcn_targ) 383 { 384 struct puffs_node *pn_targ = targ; 385 386 if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1) 387 return errno; 388 389 if (pn_targ) 390 puffs_pn_remove(pn_targ); 391 392 return 0; 393 } 394 395 /*ARGSUSED*/ 396 int 397 puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc, 398 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 399 const struct vattr *va) 400 { 401 int rv; 402 403 if (mkdir(PCNPATH(pcn), va->va_mode) == -1) 404 return errno; 405 406 rv = makenode(pu, pni, pcn, va, 0); 407 if (rv) 408 rmdir(PCNPATH(pcn)); 409 return rv; 410 } 411 412 /*ARGSUSED*/ 413 int 414 puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, 415 puffs_cookie_t targ, const struct puffs_cn *pcn) 416 { 417 struct puffs_node *pn_targ = targ; 418 419 if (rmdir(PNPATH(pn_targ)) == -1) 420 return errno; 421 puffs_pn_remove(pn_targ); 422 423 return 0; 424 } 425 426 /*ARGSUSED*/ 427 int 428 puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc, 429 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 430 const struct vattr *va, const char *linkname) 431 { 432 int rv; 433 434 if (symlink(linkname, PCNPATH(pcn)) == -1) 435 return errno; 436 437 rv = makenode(pu, pni, pcn, va, 0); 438 if (rv) 439 unlink(PCNPATH(pcn)); 440 return rv; 441 } 442 443 /*ARGSUSED*/ 444 int 445 puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, 446 const struct puffs_cred *pcred, char *linkname, size_t *linklen) 447 { 448 struct puffs_node *pn = opc; 449 ssize_t rv; 450 451 rv = readlink(PNPATH(pn), linkname, *linklen); 452 if (rv == -1) 453 return errno; 454 455 *linklen = rv; 456 return 0; 457 } 458 459 /*ARGSUSED*/ 460 int 461 puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, 462 struct dirent *de, off_t *off, size_t *reslen, 463 const struct puffs_cred *pcred, int *eofflag, off_t *cookies, 464 size_t *ncookies) 465 { 466 /* TODO: use original code since we have libc from NetBSD */ 467 struct puffs_node *pn = opc; 468 struct dirent *entry; 469 DIR *dp; 470 off_t i; 471 int rv; 472 473 dp = opendir(PNPATH(pn)); 474 if (dp == NULL) 475 return errno; 476 477 rv = 0; 478 i = *off; 479 480 /* 481 * XXX: need to do trickery here, telldir/seekdir would be nice, but 482 * then we'd need to keep state, which I'm too lazy to keep 483 */ 484 while (i--) { 485 entry = readdir(dp); 486 if (!entry) { 487 *eofflag = 1; 488 goto out; 489 } 490 } 491 492 for (;;) { 493 /* FIXME: DIRENT_SIZE macro? For now do calculations here */ 494 int namelen; 495 char* cp; 496 int dirent_size; 497 498 entry = readdir(dp); 499 500 if (!entry) { 501 *eofflag = 1; 502 goto out; 503 } 504 505 cp = memchr(entry->d_name, '\0', NAME_MAX); 506 if (cp == NULL) 507 namelen = NAME_MAX; 508 else 509 namelen = cp - (entry->d_name); 510 dirent_size = _DIRENT_RECLEN(entry, namelen); 511 512 if (dirent_size > *reslen) 513 goto out; 514 515 *de = *entry; 516 strncpy(de->d_name, entry->d_name, namelen); 517 *reslen -= dirent_size; 518 519 de = _DIRENT_NEXT(de); 520 (*off)++; 521 } 522 523 out: 524 closedir(dp); 525 return 0; 526 } 527 528 /*ARGSUSED*/ 529 int 530 puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc, 531 uint8_t *buf, off_t offset, size_t *buflen, 532 const struct puffs_cred *pcred, int ioflag) 533 { 534 struct puffs_node *pn = opc; 535 ssize_t n; 536 off_t off; 537 int fd, rv; 538 539 rv = 0; 540 fd = open(PNPATH(pn), O_RDONLY); 541 if (fd == -1) 542 return errno; 543 off = lseek(fd, offset, SEEK_SET); 544 if (off == -1) { 545 rv = errno; 546 goto out; 547 } 548 549 n = read(fd, buf, *buflen); 550 if (n == -1) 551 rv = errno; 552 else 553 *buflen -= n; 554 555 out: 556 close(fd); 557 return rv; 558 } 559 560 /*ARGSUSED*/ 561 int 562 puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, 563 uint8_t *buf, off_t offset, size_t *buflen, 564 const struct puffs_cred *pcred, int ioflag) 565 { 566 struct puffs_node *pn = opc; 567 ssize_t n; 568 off_t off; 569 int fd, rv; 570 571 rv = 0; 572 fd = writeableopen(PNPATH(pn)); 573 if (fd == -1) 574 return errno; 575 576 off = lseek(fd, offset, SEEK_SET); 577 if (off == -1) { 578 rv = errno; 579 goto out; 580 } 581 582 n = write(fd, buf, *buflen); 583 if (n == -1) 584 rv = errno; 585 else 586 *buflen -= n; 587 588 out: 589 close(fd); 590 return rv; 591 } 592