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