1 /* $NetBSD: dtfs_vnops.c,v 1.10 2013/10/19 17:45:00 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2006 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/types.h> 29 #include <sys/poll.h> 30 31 #include <assert.h> 32 #include <errno.h> 33 #include <puffs.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 #include <util.h> 39 40 #include "dtfs.h" 41 42 int 43 dtfs_node_lookup(struct puffs_usermount *pu, void *opc, 44 struct puffs_newinfo *pni, const struct puffs_cn *pcn) 45 { 46 struct puffs_node *pn_dir = opc; 47 struct dtfs_file *df = DTFS_CTOF(opc); 48 struct dtfs_dirent *dfd; 49 extern int straightflush; 50 int rv; 51 52 /* parent dir? */ 53 if (PCNISDOTDOT(pcn)) { 54 if (df->df_dotdot == NULL) 55 return ENOENT; 56 57 assert(df->df_dotdot->pn_va.va_type == VDIR); 58 puffs_newinfo_setcookie(pni, df->df_dotdot); 59 puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type); 60 61 return 0; 62 } 63 64 dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen); 65 if (dfd) { 66 if ((pcn->pcn_flags & NAMEI_ISLASTCN) && 67 (pcn->pcn_nameiop == NAMEI_DELETE)) { 68 rv = puffs_access(VDIR, pn_dir->pn_va.va_mode, 69 pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid, 70 PUFFS_VWRITE, pcn->pcn_cred); 71 if (rv) 72 return rv; 73 } 74 puffs_newinfo_setcookie(pni, dfd->dfd_node); 75 puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type); 76 puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size); 77 puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev); 78 79 if (straightflush) 80 puffs_flush_pagecache_node(pu, dfd->dfd_node); 81 82 return 0; 83 } 84 85 if ((pcn->pcn_flags & NAMEI_ISLASTCN) 86 && (pcn->pcn_nameiop == NAMEI_CREATE || 87 pcn->pcn_nameiop == NAMEI_RENAME)) { 88 rv = puffs_access(VDIR, pn_dir->pn_va.va_mode, 89 pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid, 90 PUFFS_VWRITE, pcn->pcn_cred); 91 if (rv) 92 return rv; 93 } 94 95 return ENOENT; 96 } 97 98 int 99 dtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode, 100 const struct puffs_cred *pcr) 101 { 102 struct puffs_node *pn = opc; 103 104 return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode, 105 pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr); 106 } 107 108 int 109 dtfs_node_setattr(struct puffs_usermount *pu, void *opc, 110 const struct vattr *va, const struct puffs_cred *pcr) 111 { 112 struct puffs_node *pn = opc; 113 int rv; 114 115 /* check permissions */ 116 if (va->va_flags != PUFFS_VNOVAL) 117 return EOPNOTSUPP; 118 119 if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) { 120 rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid, 121 va->va_uid, va->va_gid, pcr); 122 if (rv) 123 return rv; 124 } 125 126 if (va->va_mode != PUFFS_VNOVAL) { 127 rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid, 128 pn->pn_va.va_type, va->va_mode, pcr); 129 if (rv) 130 return rv; 131 } 132 133 if ((va->va_atime.tv_sec != PUFFS_VNOVAL 134 && va->va_atime.tv_nsec != PUFFS_VNOVAL) 135 || (va->va_mtime.tv_sec != PUFFS_VNOVAL 136 && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) { 137 rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid, 138 pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr); 139 if (rv) 140 return rv; 141 } 142 143 if (va->va_size != PUFFS_VNOVAL) { 144 switch (pn->pn_va.va_type) { 145 case VREG: 146 dtfs_setsize(pn, va->va_size); 147 pn->pn_va.va_bytes = va->va_size; 148 break; 149 case VBLK: 150 case VCHR: 151 case VFIFO: 152 break; 153 case VDIR: 154 return EISDIR; 155 default: 156 return EOPNOTSUPP; 157 } 158 } 159 160 puffs_setvattr(&pn->pn_va, va); 161 162 return 0; 163 } 164 165 /* create a new node in the parent directory specified by opc */ 166 int 167 dtfs_node_create(struct puffs_usermount *pu, void *opc, 168 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 169 const struct vattr *va) 170 { 171 struct puffs_node *pn_parent = opc; 172 struct puffs_node *pn_new; 173 174 if (!(va->va_type == VREG || va->va_type == VSOCK)) 175 return ENODEV; 176 177 pn_new = dtfs_genfile(pn_parent, pcn, va->va_type); 178 puffs_setvattr(&pn_new->pn_va, va); 179 180 puffs_newinfo_setcookie(pni, pn_new); 181 182 return 0; 183 } 184 185 int 186 dtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ, 187 const struct puffs_cn *pcn) 188 { 189 struct puffs_node *pn_parent = opc; 190 struct puffs_node *pn = targ; 191 192 if (pn->pn_va.va_type == VDIR) 193 return EPERM; 194 195 dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen); 196 197 if (pn->pn_va.va_nlink == 0) 198 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 199 200 return 0; 201 } 202 203 int 204 dtfs_node_mkdir(struct puffs_usermount *pu, void *opc, 205 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 206 const struct vattr *va) 207 { 208 struct puffs_node *pn_parent = opc; 209 struct puffs_node *pn_new; 210 211 pn_new = dtfs_genfile(pn_parent, pcn, VDIR); 212 puffs_setvattr(&pn_new->pn_va, va); 213 214 puffs_newinfo_setcookie(pni, pn_new); 215 216 return 0; 217 } 218 219 int 220 dtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ, 221 const struct puffs_cn *pcn) 222 { 223 struct puffs_node *pn_parent = opc; 224 struct dtfs_file *df = DTFS_CTOF(targ); 225 226 if (!LIST_EMPTY(&df->df_dirents)) 227 return ENOTEMPTY; 228 229 dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen); 230 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); 231 232 return 0; 233 } 234 235 int 236 dtfs_node_readdir(struct puffs_usermount *pu, void *opc, 237 struct dirent *dent, off_t *readoff, size_t *reslen, 238 const struct puffs_cred *pcr, 239 int *eofflag, off_t *cookies, size_t *ncookies) 240 { 241 struct puffs_node *pn = opc; 242 struct puffs_node *pn_nth; 243 struct dtfs_dirent *dfd_nth; 244 245 if (pn->pn_va.va_type != VDIR) 246 return ENOTDIR; 247 248 dtfs_updatetimes(pn, 1, 0, 0); 249 250 *ncookies = 0; 251 again: 252 if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) { 253 puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen); 254 (*readoff)++; 255 PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); 256 goto again; 257 } 258 259 for (;;) { 260 dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff)); 261 if (!dfd_nth) { 262 *eofflag = 1; 263 break; 264 } 265 pn_nth = dfd_nth->dfd_node; 266 267 if (!puffs_nextdent(&dent, dfd_nth->dfd_name, 268 pn_nth->pn_va.va_fileid, 269 puffs_vtype2dt(pn_nth->pn_va.va_type), 270 reslen)) 271 break; 272 273 (*readoff)++; 274 PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); 275 } 276 277 return 0; 278 } 279 280 int 281 dtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events) 282 { 283 struct dtfs_mount *dtm = puffs_getspecific(pu); 284 struct dtfs_poll dp; 285 struct itimerval it; 286 287 memset(&it, 0, sizeof(struct itimerval)); 288 it.it_value.tv_sec = 4; 289 if (setitimer(ITIMER_REAL, &it, NULL) == -1) 290 return errno; 291 292 dp.dp_pcc = puffs_cc_getcc(pu); 293 LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries); 294 puffs_cc_yield(dp.dp_pcc); 295 296 *events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); 297 return 0; 298 } 299 300 int 301 dtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot, 302 const struct puffs_cred *pcr) 303 { 304 struct dtfs_mount *dtm = puffs_getspecific(pu); 305 306 if ((dtm->dtm_allowprot & prot) != prot) 307 return EACCES; 308 309 return 0; 310 } 311 312 int 313 dtfs_node_rename(struct puffs_usermount *pu, void *opc, void *src, 314 const struct puffs_cn *pcn_src, void *targ_dir, void *targ, 315 const struct puffs_cn *pcn_targ) 316 { 317 struct dtfs_dirent *dfd_src; 318 struct dtfs_file *df_targ; 319 struct puffs_node *pn_sdir = opc; 320 struct puffs_node *pn_sfile = src; 321 struct puffs_node *pn_tdir = targ_dir; 322 struct puffs_node *pn_tfile = targ; 323 324 /* check that we don't do the old amigados trick */ 325 if (pn_sfile->pn_va.va_type == VDIR) { 326 if (dtfs_isunder(pn_tdir, pn_sfile)) 327 return EINVAL; 328 329 if ((pcn_src->pcn_namelen == 1 && pcn_src->pcn_name[0]=='.') || 330 opc == src || 331 PCNISDOTDOT(pcn_src) || 332 PCNISDOTDOT(pcn_targ)) { 333 return EINVAL; 334 } 335 } 336 337 dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir), 338 pcn_src->pcn_name, pcn_src->pcn_namelen); 339 340 /* does it still exist, or did someone race us here? */ 341 if (dfd_src == NULL) { 342 return ENOENT; 343 } 344 345 /* if there's a target file, nuke it for atomic replacement */ 346 if (pn_tfile) { 347 if (pn_tfile->pn_va.va_type == VDIR) { 348 df_targ = DTFS_CTOF(pn_tfile); 349 if (!LIST_EMPTY(&df_targ->df_dirents)) 350 return ENOTEMPTY; 351 } 352 dtfs_nukenode(pn_tfile, pn_tdir, 353 pcn_targ->pcn_name, pcn_targ->pcn_namelen); 354 } 355 356 /* out with the old */ 357 dtfs_removedent(pn_sdir, dfd_src); 358 /* and in with the new */ 359 dtfs_adddent(pn_tdir, dfd_src); 360 361 /* update name */ 362 free(dfd_src->dfd_name); 363 dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen); 364 dfd_src->dfd_namelen = strlen(dfd_src->dfd_name); 365 366 dtfs_updatetimes(src, 0, 1, 0); 367 368 return 0; 369 } 370 371 int 372 dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ, 373 const struct puffs_cn *pcn) 374 { 375 struct puffs_node *pn_dir = opc; 376 struct dtfs_dirent *dfd; 377 378 dfd = emalloc(sizeof(struct dtfs_dirent)); 379 dfd->dfd_node = targ; 380 dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen); 381 dfd->dfd_namelen = strlen(dfd->dfd_name); 382 dtfs_adddent(pn_dir, dfd); 383 384 dtfs_updatetimes(targ, 0, 1, 0); 385 386 return 0; 387 } 388 389 int 390 dtfs_node_symlink(struct puffs_usermount *pu, void *opc, 391 struct puffs_newinfo *pni, const struct puffs_cn *pcn_src, 392 const struct vattr *va, const char *link_target) 393 { 394 struct puffs_node *pn_parent = opc; 395 struct puffs_node *pn_new; 396 struct dtfs_file *df_new; 397 398 if (va->va_type != VLNK) 399 return ENODEV; 400 401 pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK); 402 puffs_setvattr(&pn_new->pn_va, va); 403 df_new = DTFS_PTOF(pn_new); 404 df_new->df_linktarget = estrdup(link_target); 405 pn_new->pn_va.va_size = strlen(df_new->df_linktarget); 406 407 puffs_newinfo_setcookie(pni, pn_new); 408 409 return 0; 410 } 411 412 int 413 dtfs_node_readlink(struct puffs_usermount *pu, void *opc, 414 const struct puffs_cred *cred, char *linkname, size_t *linklen) 415 { 416 struct dtfs_file *df = DTFS_CTOF(opc); 417 struct puffs_node *pn = opc; 418 419 assert(pn->pn_va.va_type == VLNK); 420 strlcpy(linkname, df->df_linktarget, *linklen); 421 *linklen = strlen(linkname); 422 423 return 0; 424 } 425 426 int 427 dtfs_node_mknod(struct puffs_usermount *pu, void *opc, 428 struct puffs_newinfo *pni, const struct puffs_cn *pcn, 429 const struct vattr *va) 430 { 431 struct puffs_node *pn_parent = opc; 432 struct puffs_node *pn_new; 433 434 if (!(va->va_type == VBLK || va->va_type == VCHR 435 || va->va_type == VFIFO)) 436 return EINVAL; 437 438 pn_new = dtfs_genfile(pn_parent, pcn, va->va_type); 439 puffs_setvattr(&pn_new->pn_va, va); 440 441 puffs_newinfo_setcookie(pni, pn_new); 442 443 return 0; 444 } 445 446 #define BLOCKOFF(a,b) ((a) & ((b)-1)) 447 #define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b)) 448 449 /* 450 * Read operation, used both for VOP_READ and VOP_GETPAGES 451 */ 452 int 453 dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf, 454 off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag) 455 { 456 struct puffs_node *pn = opc; 457 struct dtfs_file *df = DTFS_CTOF(opc); 458 quad_t xfer, origxfer; 459 uint8_t *src, *dest; 460 size_t copylen; 461 462 if (pn->pn_va.va_type != VREG) 463 return EISDIR; 464 465 xfer = MIN(*resid, df->df_datalen - offset); 466 if (xfer < 0) 467 return EINVAL; 468 469 dest = buf; 470 origxfer = xfer; 471 while (xfer > 0) { 472 copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE)); 473 src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)] 474 + BLOCKOFF(offset, DTFS_BLOCKSIZE); 475 memcpy(dest, src, copylen); 476 offset += copylen; 477 dest += copylen; 478 xfer -= copylen; 479 } 480 *resid -= origxfer; 481 482 dtfs_updatetimes(pn, 1, 0, 0); 483 484 return 0; 485 } 486 487 /* 488 * write operation on the wing 489 */ 490 int 491 dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, 492 off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag) 493 { 494 struct puffs_node *pn = opc; 495 struct dtfs_file *df = DTFS_CTOF(opc); 496 uint8_t *src, *dest; 497 size_t copylen; 498 499 if (pn->pn_va.va_type != VREG) 500 return EISDIR; 501 502 if (ioflag & PUFFS_IO_APPEND) 503 offset = pn->pn_va.va_size; 504 505 if (*resid + offset > pn->pn_va.va_size) 506 dtfs_setsize(pn, *resid + offset); 507 508 src = buf; 509 while (*resid > 0) { 510 int i; 511 copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE)); 512 i = BLOCKNUM(offset, DTFS_BLOCKSHIFT); 513 dest = df->df_blocks[i] 514 + BLOCKOFF(offset, DTFS_BLOCKSIZE); 515 memcpy(dest, src, copylen); 516 offset += copylen; 517 dest += copylen; 518 *resid -= copylen; 519 } 520 521 dtfs_updatetimes(pn, 0, 1, 1); 522 523 return 0; 524 } 525 526 int 527 dtfs_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc, 528 int name, register_t *retval) 529 { 530 531 switch (name) { 532 case _PC_LINK_MAX: 533 *retval = LINK_MAX; 534 return 0; 535 case _PC_NAME_MAX: 536 *retval = NAME_MAX; 537 return 0; 538 case _PC_PATH_MAX: 539 *retval = PATH_MAX; 540 return 0; 541 case _PC_PIPE_BUF: 542 *retval = PIPE_BUF; 543 return 0; 544 case _PC_CHOWN_RESTRICTED: 545 *retval = 1; 546 return 0; 547 case _PC_NO_TRUNC: 548 *retval = 1; 549 return 0; 550 case _PC_SYNC_IO: 551 *retval = 1; 552 return 0; 553 case _PC_FILESIZEBITS: 554 *retval = 43; /* this one goes to 11 */ 555 return 0; 556 case _PC_SYMLINK_MAX: 557 *retval = MAXPATHLEN; 558 return 0; 559 case _PC_2_SYMLINKS: 560 *retval = 1; 561 return 0; 562 default: 563 return EINVAL; 564 } 565 } 566 567 int 568 dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc) 569 { 570 struct puffs_node *pn = opc; 571 572 if (pn->pn_va.va_nlink == 0) 573 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1); 574 return 0; 575 } 576 577 int 578 dtfs_node_reclaim(struct puffs_usermount *pu, void *opc) 579 { 580 struct puffs_node *pn = opc; 581 582 if (pn->pn_va.va_nlink == 0) 583 dtfs_freenode(pn); 584 585 return 0; 586 } 587