1 /* $NetBSD: null.c,v 1.22 2007/11/05 16:53:00 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.22 2007/11/05 16:53:00 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 <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 != (unsigned)PUFFS_VNOVAL 71 || va->va_mtime.tv_sec != (unsigned)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 169 PUFFSOP_SET(pops, puffs_null, node, lookup); 170 PUFFSOP_SET(pops, puffs_null, node, create); 171 PUFFSOP_SET(pops, puffs_null, node, mknod); 172 PUFFSOP_SET(pops, puffs_null, node, getattr); 173 PUFFSOP_SET(pops, puffs_null, node, setattr); 174 PUFFSOP_SET(pops, puffs_null, node, fsync); 175 PUFFSOP_SET(pops, puffs_null, node, remove); 176 PUFFSOP_SET(pops, puffs_null, node, link); 177 PUFFSOP_SET(pops, puffs_null, node, rename); 178 PUFFSOP_SET(pops, puffs_null, node, mkdir); 179 PUFFSOP_SET(pops, puffs_null, node, rmdir); 180 PUFFSOP_SET(pops, puffs_null, node, symlink); 181 PUFFSOP_SET(pops, puffs_null, node, readlink); 182 PUFFSOP_SET(pops, puffs_null, node, readdir); 183 PUFFSOP_SET(pops, puffs_null, node, read); 184 PUFFSOP_SET(pops, puffs_null, node, write); 185 PUFFSOP_SET(pops, puffs_genfs, node, reclaim); 186 } 187 188 /*ARGSUSED*/ 189 int 190 puffs_null_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, 191 const struct puffs_cid *pcid) 192 { 193 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 194 195 if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1) 196 return errno; 197 198 return 0; 199 } 200 201 int 202 puffs_null_node_lookup(struct puffs_cc *pcc, void *opc, 203 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 204 { 205 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 206 struct puffs_node *pn = opc, *pn_res; 207 struct stat sb; 208 int rv; 209 210 assert(pn->pn_va.va_type == VDIR); 211 212 /* 213 * Note to whoever is copypasting this: you must first check 214 * if the node is there and only then do nodewalk. Alternatively 215 * you could make sure that you don't return unlinked/rmdir'd 216 * nodes in some other fashion 217 */ 218 rv = lstat(PCNPATH(pcn), &sb); 219 if (rv) 220 return errno; 221 222 /* XXX2: nodewalk is a bit too slow here */ 223 pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino); 224 225 if (pn_res == NULL) { 226 pn_res = puffs_pn_new(pu, NULL); 227 if (pn_res == NULL) 228 return ENOMEM; 229 puffs_stat2vattr(&pn_res->pn_va, &sb); 230 } 231 232 puffs_newinfo_setcookie(pni, pn_res); 233 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type); 234 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size); 235 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev); 236 237 return 0; 238 } 239 240 /*ARGSUSED*/ 241 int 242 puffs_null_node_create(struct puffs_cc *pcc, void *opc, 243 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 244 const struct vattr *va) 245 { 246 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 247 int fd, rv; 248 249 fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC); 250 if (fd == -1) 251 return errno; 252 close(fd); 253 254 rv = makenode(pu, pni, pcn, va, 1); 255 if (rv) 256 unlink(PCNPATH(pcn)); 257 return rv; 258 } 259 260 /*ARGSUSED*/ 261 int 262 puffs_null_node_mknod(struct puffs_cc *pcc, void *opc, 263 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 264 const struct vattr *va) 265 { 266 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 267 mode_t mode; 268 int rv; 269 270 mode = puffs_addvtype2mode(va->va_mode, va->va_type); 271 if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1) 272 return errno; 273 274 rv = makenode(pu, pni, pcn, va, 0); 275 if (rv) 276 unlink(PCNPATH(pcn)); 277 return rv; 278 } 279 280 /*ARGSUSED*/ 281 int 282 puffs_null_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va, 283 const struct puffs_cred *pcred, const struct puffs_cid *pcid) 284 { 285 struct puffs_node *pn = opc; 286 struct stat sb; 287 288 if (lstat(PNPATH(pn), &sb) == -1) 289 return errno; 290 puffs_stat2vattr(va, &sb); 291 292 return 0; 293 } 294 295 /*ARGSUSED*/ 296 int 297 puffs_null_node_setattr(struct puffs_cc *pcc, void *opc, 298 const struct vattr *va, const struct puffs_cred *pcred, 299 const struct puffs_cid *pcid) 300 { 301 struct puffs_node *pn = opc; 302 int rv; 303 304 rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG); 305 if (rv) 306 return rv; 307 308 puffs_setvattr(&pn->pn_va, va); 309 310 return 0; 311 } 312 313 /*ARGSUSED*/ 314 int 315 puffs_null_node_fsync(struct puffs_cc *pcc, void *opc, 316 const struct puffs_cred *pcred, int how, 317 off_t offlo, off_t offhi, const struct puffs_cid *pcid) 318 { 319 struct puffs_node *pn = opc; 320 int fd, rv; 321 int fflags; 322 323 rv = 0; 324 fd = writeableopen(PNPATH(pn)); 325 if (fd == -1) 326 return errno; 327 328 if (how & PUFFS_FSYNC_DATAONLY) 329 fflags = FDATASYNC; 330 else 331 fflags = FFILESYNC; 332 if (how & PUFFS_FSYNC_CACHE) 333 fflags |= FDISKSYNC; 334 335 if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1) 336 rv = errno; 337 338 close(fd); 339 340 return rv; 341 } 342 343 /*ARGSUSED*/ 344 int 345 puffs_null_node_remove(struct puffs_cc *pcc, void *opc, void *targ, 346 const struct puffs_cn *pcn) 347 { 348 struct puffs_node *pn_targ = targ; 349 350 if (unlink(PNPATH(pn_targ)) == -1) 351 return errno; 352 puffs_pn_remove(pn_targ); 353 354 return 0; 355 } 356 357 /*ARGSUSED*/ 358 int 359 puffs_null_node_link(struct puffs_cc *pcc, void *opc, void *targ, 360 const struct puffs_cn *pcn) 361 { 362 struct puffs_node *pn_targ = targ; 363 364 if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1) 365 return errno; 366 367 return 0; 368 } 369 370 /*ARGSUSED*/ 371 int 372 puffs_null_node_rename(struct puffs_cc *pcc, void *opc, void *src, 373 const struct puffs_cn *pcn_src, void *targ_dir, void *targ, 374 const struct puffs_cn *pcn_targ) 375 { 376 377 if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1) 378 return errno; 379 380 return 0; 381 } 382 383 /*ARGSUSED*/ 384 int 385 puffs_null_node_mkdir(struct puffs_cc *pcc, void *opc, 386 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 387 const struct vattr *va) 388 { 389 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 390 int rv; 391 392 if (mkdir(PCNPATH(pcn), va->va_mode) == -1) 393 return errno; 394 395 rv = makenode(pu, pni, pcn, va, 0); 396 if (rv) 397 rmdir(PCNPATH(pcn)); 398 return rv; 399 } 400 401 /*ARGSUSED*/ 402 int 403 puffs_null_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ, 404 const struct puffs_cn *pcn) 405 { 406 struct puffs_node *pn_targ = targ; 407 408 if (rmdir(PNPATH(pn_targ)) == -1) 409 return errno; 410 puffs_pn_remove(pn_targ); 411 412 return 0; 413 } 414 415 /*ARGSUSED*/ 416 int 417 puffs_null_node_symlink(struct puffs_cc *pcc, void *opc, 418 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 419 const struct vattr *va, const char *linkname) 420 { 421 struct puffs_usermount *pu = puffs_cc_getusermount(pcc); 422 int rv; 423 424 if (symlink(linkname, PCNPATH(pcn)) == -1) 425 return errno; 426 427 rv = makenode(pu, pni, pcn, va, 0); 428 if (rv) 429 unlink(PCNPATH(pcn)); 430 return rv; 431 } 432 433 /*ARGSUSED*/ 434 int 435 puffs_null_node_readlink(struct puffs_cc *pcc, void *opc, 436 const struct puffs_cred *pcred, char *linkname, size_t *linklen) 437 { 438 struct puffs_node *pn = opc; 439 ssize_t rv; 440 441 rv = readlink(PNPATH(pn), linkname, *linklen); 442 if (rv == -1) 443 return errno; 444 445 *linklen = rv; 446 return 0; 447 } 448 449 /*ARGSUSED*/ 450 int 451 puffs_null_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *de, 452 off_t *off, size_t *reslen, const struct puffs_cred *pcred, 453 int *eofflag, off_t *cookies, size_t *ncookies) 454 { 455 struct puffs_node *pn = opc; 456 struct dirent entry, *result; 457 DIR *dp; 458 off_t i; 459 int rv; 460 461 dp = opendir(PNPATH(pn)); 462 if (dp == NULL) 463 return errno; 464 465 rv = 0; 466 i = *off; 467 468 /* 469 * XXX: need to do trickery here, telldir/seekdir would be nice, but 470 * then we'd need to keep state, which I'm too lazy to keep 471 */ 472 while (i--) { 473 rv = readdir_r(dp, &entry, &result); 474 if (rv || !result) 475 goto out; 476 } 477 478 for (;;) { 479 rv = readdir_r(dp, &entry, &result); 480 if (rv != 0) 481 goto out; 482 483 if (!result) 484 goto out; 485 486 if (_DIRENT_SIZE(result) > *reslen) 487 goto out; 488 489 *de = *result; 490 *reslen -= _DIRENT_SIZE(result); 491 de = _DIRENT_NEXT(de); 492 493 (*off)++; 494 } 495 496 out: 497 closedir(dp); 498 return 0; 499 } 500 501 /*ARGSUSED*/ 502 int 503 puffs_null_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf, 504 off_t offset, size_t *buflen, const struct puffs_cred *pcred, 505 int ioflag) 506 { 507 struct puffs_node *pn = opc; 508 ssize_t n; 509 off_t off; 510 int fd, rv; 511 512 rv = 0; 513 fd = open(PNPATH(pn), O_RDONLY); 514 if (fd == -1) 515 return errno; 516 off = lseek(fd, offset, SEEK_SET); 517 if (off == -1) { 518 rv = errno; 519 goto out; 520 } 521 522 n = read(fd, buf, *buflen); 523 if (n == -1) 524 rv = errno; 525 else 526 *buflen -= n; 527 528 out: 529 close(fd); 530 return rv; 531 } 532 533 /*ARGSUSED*/ 534 int 535 puffs_null_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf, 536 off_t offset, size_t *buflen, const struct puffs_cred *pcred, 537 int ioflag) 538 { 539 struct puffs_node *pn = opc; 540 ssize_t n; 541 off_t off; 542 int fd, rv; 543 544 rv = 0; 545 fd = writeableopen(PNPATH(pn)); 546 if (fd == -1) 547 return errno; 548 549 off = lseek(fd, offset, SEEK_SET); 550 if (off == -1) { 551 rv = errno; 552 goto out; 553 } 554 555 n = write(fd, buf, *buflen); 556 if (n == -1) 557 rv = errno; 558 else 559 *buflen -= n; 560 561 out: 562 close(fd); 563 return rv; 564 } 565