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