UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. 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 are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #define _POSIX_PTHREAD_SEMANTIS 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <sys/types.h> 35 #include <aio.h> 36 #include <ucx/map.h> 37 38 #include "../util/pool.h" 39 #include "netsite.h" 40 #include "acl.h" 41 #include "vfs.h" 42 #include "threadpools.h" 43 #include "event.h" 44 45 #define VFS_MALLOC(pool, size) pool ? pool_malloc(pool, size) : malloc(size) 46 #define VFS_FREE(pool, ptr) pool ? pool_free(pool, ptr) : free(ptr) 47 48 static UcxMap *vfs_map; 49 50 static VFS sys_vfs = { 51 sys_vfs_open, 52 sys_vfs_stat, 53 sys_vfs_fstat, 54 sys_vfs_opendir, 55 sys_vfs_mkdir, 56 sys_vfs_unlink, 57 VFS_CHECKS_ACL 58 }; 59 60 static VFS_IO sys_file_io = { 61 sys_file_read, 62 sys_file_write, 63 sys_file_pread, 64 sys_file_pwrite, 65 sys_file_seek, 66 sys_file_close, 67 //sys_file_aioread, 68 //sys_file_aiowrite, 69 NULL, 70 NULL 71 }; 72 73 static VFS_DIRIO sys_dir_io = { 74 sys_dir_read, 75 sys_dir_close 76 }; 77 78 int vfs_init() { 79 vfs_map = ucx_map_new(16); 80 if(!vfs_map) { 81 return -1; 82 } 83 return 0; 84 } 85 86 void vfs_add(char *name, VFS *vfs) { 87 WS_ASSERT(name); 88 89 if(!vfs_map) { 90 vfs_init(); 91 } 92 ucx_map_cstr_put(vfs_map, name, vfs); 93 } 94 95 VFSContext* vfs_request_context(Session *sn, Request *rq) { 96 WS_ASSERT(sn); 97 WS_ASSERT(rq); 98 99 VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext)); 100 ctx->sn = sn; 101 ctx->rq = rq; 102 ctx->vfs = rq->vfs ? rq->vfs : &sys_vfs; 103 ctx->user = acllist_getuser(sn, rq, rq->acllist); 104 ctx->acllist = rq->acllist; 105 ctx->aclreqaccess = rq->aclreqaccess; 106 ctx->pool = sn->pool; 107 ctx->vfs_errno = 0; 108 return ctx; 109 } 110 111 SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) { 112 WS_ASSERT(ctx); 113 WS_ASSERT(path); 114 115 uint32_t access_mask = ctx->aclreqaccess | acl_oflag2mask(oflags); 116 117 // ctx->aclreqaccess should be the complete access mask 118 uint32_t m = ctx->aclreqaccess; // save original access mask 119 ctx->aclreqaccess = access_mask; // set mask for vfs->open call 120 if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) { 121 // VFS does not evaluates the ACL itself, so we have to do it here 122 SysACL sysacl; 123 if(sys_acl_check(ctx, access_mask, &sysacl)) { 124 return NULL; 125 } 126 } 127 SYS_FILE file = ctx->vfs->open(ctx, path, oflags); 128 ctx->aclreqaccess = m; // restore original access mask 129 return file; 130 } 131 132 SYS_FILE vfs_openRO(VFSContext *ctx, char *path) { 133 return vfs_open(ctx, path, O_RDONLY); 134 } 135 136 SYS_FILE vfs_openWO(VFSContext *ctx, char *path) { 137 return vfs_open(ctx, path, O_WRONLY | O_CREAT); 138 } 139 140 SYS_FILE vfs_openRW(VFSContext *ctx, char *path) { 141 return vfs_open(ctx, path, O_RDONLY | O_WRONLY | O_CREAT); 142 } 143 144 int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { 145 WS_ASSERT(ctx); 146 WS_ASSERT(path); 147 148 uint32_t access_mask = ctx->aclreqaccess | ACL_READ_ATTRIBUTES; 149 150 // ctx->aclreqaccess should be the complete access mask 151 uint32_t m = ctx->aclreqaccess; // save original access mask 152 ctx->aclreqaccess = access_mask; // set mask for vfs->open call 153 if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) { 154 // VFS does not evaluates the ACL itself, so we have to do it here 155 SysACL sysacl; 156 if(sys_acl_check(ctx, access_mask, &sysacl)) { 157 return -1; 158 } 159 } 160 int ret = ctx->vfs->stat(ctx, path, buf); 161 ctx->aclreqaccess = m; // restore original access mask 162 return ret; 163 } 164 165 int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { 166 WS_ASSERT(ctx); 167 WS_ASSERT(fd); 168 WS_ASSERT(buf); 169 170 return ctx->vfs->fstat(ctx, fd, buf); 171 } 172 173 void vfs_close(SYS_FILE fd) { 174 WS_ASSERT(fd); 175 176 fd->io->close(fd); 177 if(fd->ctx) { 178 pool_free(fd->ctx->pool, fd); 179 } else { 180 free(fd); 181 } 182 } 183 184 VFS_DIR vfs_opendir(VFSContext *ctx, char *path) { 185 WS_ASSERT(ctx); 186 WS_ASSERT(path); 187 188 uint32_t access_mask = ctx->aclreqaccess | ACL_LIST; 189 190 // ctx->aclreqaccess should be the complete access mask 191 uint32_t m = ctx->aclreqaccess; // save original access mask 192 ctx->aclreqaccess = access_mask; // set mask for vfs->open call 193 if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) { 194 // VFS does not evaluates the ACL itself, so we have to do it here 195 SysACL sysacl; 196 if(sys_acl_check(ctx, access_mask, &sysacl)) { 197 return NULL; 198 } 199 } 200 VFS_DIR dir = ctx->vfs->opendir(ctx, path); 201 ctx->aclreqaccess = m; // restore original access mask 202 return dir; 203 } 204 205 int vfs_readdir(VFS_DIR dir, VFS_ENTRY *entry) { 206 WS_ASSERT(dir); 207 WS_ASSERT(entry); 208 209 return dir->io->readdir(dir, entry, 0); 210 } 211 212 int vfs_readdir_stat(VFS_DIR dir, VFS_ENTRY *entry) { 213 WS_ASSERT(dir); 214 WS_ASSERT(entry); 215 216 return dir->io->readdir(dir, entry, 1); 217 } 218 219 void vfs_closedir(VFS_DIR dir) { 220 WS_ASSERT(dir); 221 222 dir->io->close(dir); 223 if(dir->ctx) { 224 VFS_FREE(dir->ctx->pool, dir); 225 } else { 226 free(dir); 227 } 228 } 229 230 int vfs_mkdir(VFSContext *ctx, char *path) { 231 WS_ASSERT(ctx); 232 WS_ASSERT(path); 233 234 return vfs_path_op(ctx, path, ctx->vfs->mkdir, ACL_ADD_FILE); 235 } 236 237 int vfs_unlink(VFSContext *ctx, char *path) { 238 WS_ASSERT(ctx); 239 WS_ASSERT(path); 240 241 return vfs_path_op(ctx, path, ctx->vfs->unlink, ACL_DELETE); 242 } 243 244 245 // private 246 int vfs_path_op(VFSContext *ctx, char *path, vfs_op_f op, uint32_t access) { 247 uint32_t access_mask = ctx->aclreqaccess; 248 access_mask |= access; 249 250 // ctx->aclreqaccess should be the complete access mask 251 uint32_t m = ctx->aclreqaccess; // save original access mask 252 ctx->aclreqaccess = access_mask; // set mask for vfs function call 253 if((ctx->vfs->flags & VFS_CHECKS_ACL) != VFS_CHECKS_ACL) { 254 // VFS does not evaluates the ACL itself, so we have to do it here 255 SysACL sysacl; 256 if(sys_acl_check(ctx, access_mask, &sysacl)) { 257 return -1; 258 } 259 } 260 int ret = op(ctx, path); 261 ctx->aclreqaccess = m; // restore original access mask 262 return ret; 263 } 264 265 /* system vfs implementation */ 266 267 SYS_FILE sys_vfs_open(VFSContext *ctx, char *path, int oflags) { 268 uint32_t access_mask = ctx->aclreqaccess; 269 pool_handle_t *pool = ctx->pool; 270 271 // check ACLs 272 SysACL sysacl; 273 if(sys_acl_check(ctx, access_mask, &sysacl)) { 274 return NULL; 275 } 276 277 if(sysacl.acl) { 278 if(!fs_acl_check(&sysacl, ctx->user, path, access_mask)) { 279 acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); 280 return NULL; 281 } 282 } 283 284 // open file 285 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 286 int fd = open(path, oflags, mode); 287 if(fd == -1) { 288 if(ctx) { 289 ctx->vfs_errno = errno; 290 sys_set_error_status(ctx); 291 } 292 return NULL; 293 } 294 295 // if a file system acl is active, we set the owner for newly created files 296 if(((oflags & O_CREAT) == O_CREAT) && sysacl.user_uid != -1) { 297 if(fchown(fd, sysacl.user_uid, sysacl.user_gid)) { 298 perror("vfs_open: fchown"); 299 system_close(fd); 300 return NULL; 301 } 302 } 303 304 VFSFile *file = VFS_MALLOC(pool, sizeof(VFSFile)); 305 if(!file) { 306 system_close(fd); 307 return NULL; 308 } 309 file->ctx = ctx; 310 file->data = NULL; 311 file->fd = fd; 312 file->io = &sys_file_io; 313 return file; 314 } 315 316 int sys_vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { 317 uint32_t access_mask = ctx->aclreqaccess; 318 319 // check ACLs 320 SysACL sysacl; 321 if(sys_acl_check(ctx, access_mask, &sysacl)) { 322 return -1; 323 } 324 325 if(sysacl.acl) { 326 if(!fs_acl_check(&sysacl, ctx->user, path, access_mask)) { 327 acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); 328 return -1; 329 } 330 } 331 332 // stat 333 if(stat(path, buf)) { 334 if(ctx) { 335 ctx->vfs_errno = errno; 336 sys_set_error_status(ctx); 337 } 338 return -1; 339 } 340 341 return 0; 342 } 343 344 int sys_vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { 345 // stat 346 if(fstat(fd->fd, buf)) { 347 if(ctx) { 348 ctx->vfs_errno = errno; 349 } 350 return -1; 351 } 352 353 return 0; 354 } 355 356 VFS_DIR sys_vfs_opendir(VFSContext *ctx, char *path) { 357 uint32_t access_mask = ctx->aclreqaccess; 358 pool_handle_t *pool = ctx->pool; 359 360 // check ACLs 361 SysACL sysacl; 362 if(sys_acl_check(ctx, access_mask, &sysacl)) { 363 return NULL; 364 } 365 366 if(sysacl.acl) { 367 if(!fs_acl_check(&sysacl, ctx->user, path, access_mask)) { 368 acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); 369 return NULL; 370 } 371 } 372 373 // open directory 374 #ifdef BSD 375 DIR *sys_dir = opendir(path); 376 int dir_fd = sys_dir ? dirfd(sys_dir) : 0; 377 #else 378 int dir_fd = open(path, O_RDONLY); 379 if(dir_fd == -1) { 380 if(ctx) { 381 ctx->vfs_errno = errno; 382 sys_set_error_status(ctx); 383 } 384 return NULL; 385 } 386 DIR *sys_dir = fdopendir(dir_fd); 387 #endif 388 if(!sys_dir) { 389 if(ctx) { 390 ctx->vfs_errno = errno; 391 sys_set_error_status(ctx); 392 } 393 return NULL; 394 } 395 396 SysVFSDir *dir_data = VFS_MALLOC(pool, sizeof(SysVFSDir)); 397 if(!dir_data) { 398 closedir(sys_dir); 399 return NULL; 400 } 401 long maxfilelen = fpathconf(dir_fd, _PC_NAME_MAX); 402 size_t entry_len = offsetof(struct dirent, d_name) + maxfilelen + 1; 403 dir_data->cur = VFS_MALLOC(pool, entry_len); 404 if(!dir_data->cur) { 405 closedir(sys_dir); 406 VFS_FREE(pool, dir_data); 407 return NULL; 408 } 409 dir_data->dir = sys_dir; 410 411 VFSDir *dir = VFS_MALLOC(pool, sizeof(VFSDir)); 412 if(!dir) { 413 closedir(sys_dir); 414 VFS_FREE(pool, dir_data->cur); 415 VFS_FREE(pool, dir_data); 416 return NULL; 417 } 418 dir->ctx = ctx; 419 dir->data = dir_data; 420 dir->fd = dir_fd; 421 dir->io = &sys_dir_io; 422 return dir; 423 } 424 425 int sys_vfs_mkdir(VFSContext *ctx, char *path) { 426 return sys_path_op(ctx, path, sys_mkdir); 427 } 428 429 int sys_vfs_unlink(VFSContext *ctx, char *path) { 430 return sys_path_op(ctx, path, sys_unlink); 431 } 432 433 434 int sys_path_op(VFSContext *ctx, char *path, sys_op_f op) { 435 uint32_t access_mask = ctx->aclreqaccess; 436 437 // check ACLs 438 SysACL sysacl; 439 if(sys_acl_check(ctx, access_mask, &sysacl)) { 440 return -1; 441 } 442 443 if(sysacl.acl) { 444 if(!fs_acl_check(&sysacl, ctx->user, path, access_mask)) { 445 acl_set_error_status(ctx->sn, ctx->rq, sysacl.acl, ctx->user); 446 return -1; 447 } 448 } 449 450 // do path operation 451 if(op(ctx, path, &sysacl)) { 452 // error 453 ctx->vfs_errno = errno; 454 sys_set_error_status(ctx); 455 return -1; 456 } 457 458 return 0; 459 } 460 461 int sys_acl_check(VFSContext *ctx, uint32_t access_mask, SysACL *sysacl) { 462 if(sysacl) { 463 sysacl->acl = NULL; 464 } 465 if(!ctx) { 466 return 0; 467 } 468 469 ACLListHandle *acllist = ctx->acllist; 470 if(acllist) { 471 ACLList *acl = acl_evallist( 472 acllist, 473 ctx->user, 474 access_mask, 475 &sysacl->acl); 476 477 if(acl) { 478 acl_set_error_status(ctx->sn, ctx->rq, acl, ctx->user); 479 return 1; 480 } 481 } 482 483 return 0; 484 } 485 486 void sys_set_error_status(VFSContext *ctx) { 487 if(ctx->sn && ctx->rq) { 488 int status = util_errno2status(ctx->vfs_errno); 489 protocol_status(ctx->sn, ctx->rq, status, NULL); 490 } 491 } 492 493 ssize_t sys_file_read(SYS_FILE fd, void *buf, size_t nbyte) { 494 return read(fd->fd, buf, nbyte); 495 } 496 497 ssize_t sys_file_write(SYS_FILE fd, const void *buf, size_t nbyte) { 498 return write(fd->fd, buf, nbyte); 499 } 500 501 ssize_t sys_file_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) { 502 return pread(fd->fd, buf, nbyte, offset); 503 } 504 505 ssize_t sys_file_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) { 506 return pwrite(fd->fd, buf, nbyte, offset); 507 } 508 509 off_t sys_file_seek(SYS_FILE fd, off_t offset, int whence) { 510 return lseek(fd->fd, offset, whence); 511 } 512 513 void sys_file_close(SYS_FILE fd) { 514 system_close(fd->fd); 515 } 516 517 int sys_file_aioread(aiocb_s *aiocb) { 518 WS_ASSERT(aiocb->buf); 519 WS_ASSERT(aiocb->nbytes > 0); 520 return ev_aioread(aiocb->filedes->fd, aiocb); 521 } 522 523 int sys_file_aiowrite(aiocb_s *aiocb) { 524 WS_ASSERT(aiocb->buf); 525 WS_ASSERT(aiocb->nbytes > 0); 526 return ev_aiowrite(aiocb->filedes->fd, aiocb); 527 } 528 529 530 int sys_dir_read(VFS_DIR dir, VFS_ENTRY *entry, int getstat) { 531 SysVFSDir *dirdata = dir->data; 532 struct dirent *result = NULL; 533 int s = readdir_r(dirdata->dir, dirdata->cur, &result); 534 if(!s && result) { 535 char *name = result->d_name; 536 if(!strcmp(name, ".") || !strcmp(name, "..")) { 537 return sys_dir_read(dir, entry, getstat); 538 } else { 539 entry->name = name; 540 if(getstat) { 541 // TODO: check ACLs again for new path 542 if(fstatat(dir->fd, result->d_name, &entry->stat, 0)) { 543 entry->stat_errno = errno; 544 } 545 entry->stat_extra = NULL; 546 } 547 return 1; 548 } 549 } else { 550 return 0; 551 } 552 } 553 554 void sys_dir_close(VFS_DIR dir) { 555 SysVFSDir *dirdata = dir->data; 556 closedir(dirdata->dir); 557 558 pool_handle_t *pool = dir->ctx->pool; 559 if(pool) { 560 pool_free(pool, dirdata->cur); 561 pool_free(pool, dirdata); 562 pool_free(pool, dir); 563 } else { 564 free(dirdata->cur); 565 free(dirdata); 566 free(dir); 567 } 568 } 569 570 int sys_mkdir(VFSContext *ctx, char *path, SysACL *sysacl) { 571 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 572 int ret = mkdir(path, mode); 573 if(ret == 0) { 574 if(sysacl->user_uid != -1) { 575 if(chown(path, sysacl->user_uid, sysacl->user_gid)) { 576 // TODO: error 577 } 578 } 579 } 580 return ret; 581 } 582 583 int sys_unlink(VFSContext *ctx, char *path, SysACL *sysacl) { 584 return unlink(path); 585 } 586 587 /* public file api */ 588 589 NSAPI_PUBLIC int system_fread(SYS_FILE fd, void *buf, int nbyte) { 590 return fd->io->read(fd, buf, nbyte); 591 } 592 593 NSAPI_PUBLIC int system_fwrite(SYS_FILE fd, const void *buf, int nbyte) { 594 return fd->io->write(fd, buf, nbyte); 595 } 596 597 NSAPI_PUBLIC int system_pread(SYS_FILE fd, void *buf, int nbyte, off_t offset) { 598 return fd->io->pread(fd, buf, nbyte, offset); 599 } 600 601 NSAPI_PUBLIC int system_pwrite(SYS_FILE fd, const void *buf, int nbyte, off_t offset) { 602 return fd->io->pwrite(fd, buf, nbyte, offset); 603 } 604 605 NSAPI_PUBLIC off_t system_lseek(SYS_FILE fd, off_t offset, int whence) { 606 return fd->io->seek(fd, offset, whence); 607 } 608 609 NSAPI_PUBLIC int system_fclose(SYS_FILE fd) { 610 vfs_close(fd); 611 return 0; 612 } 613 614 // AIO API 615 616 NSAPI_PUBLIC int system_aio_read(aiocb_s *aiocb) { 617 if(!aiocb->event || !aiocb->evhandler) { 618 return -1; 619 } 620 621 SYS_FILE file = aiocb->filedes; 622 aiocb->event->object = (intptr_t)aiocb; 623 if(file->io->opt_aioread) { 624 return file->io->opt_aioread(aiocb); 625 } else { 626 vfs_queue_aio(aiocb, VFS_AIO_READ); 627 return 0; 628 } 629 } 630 631 NSAPI_PUBLIC int system_aio_write(aiocb_s *aiocb) { 632 if(!aiocb->event || !aiocb->evhandler) { 633 return -1; 634 } 635 636 SYS_FILE file = aiocb->filedes; 637 aiocb->event->object = (intptr_t)aiocb; 638 if(file->io->opt_aiowrite) { 639 return file->io->opt_aiowrite(aiocb); 640 } else { 641 vfs_queue_aio(aiocb, VFS_AIO_WRITE); 642 return 0; 643 } 644 } 645 646 static void* vfs_aio_read(aiocb_s *aiocb) { 647 int result = system_pread(aiocb->filedes, aiocb->buf, aiocb->nbytes, aiocb->offset); 648 aiocb->result = result; 649 if(result < 0) { 650 aiocb->result_errno = errno; 651 } 652 event_send(aiocb->evhandler, aiocb->event); 653 return NULL; 654 } 655 656 static void* vfs_aio_write(aiocb_s *aiocb) { 657 int result = system_pwrite(aiocb->filedes, aiocb->buf, aiocb->nbytes, aiocb->offset); 658 aiocb->result = result; 659 if(result < 0) { 660 aiocb->result_errno = errno; 661 } 662 event_send(aiocb->evhandler, aiocb->event); 663 return NULL; 664 } 665 666 void vfs_queue_aio(aiocb_s *aiocb, VFSAioOp op) { 667 threadpool_t *pool = get_default_iopool(); // TODO: use specific IOPool 668 if(op == VFS_AIO_READ) { 669 threadpool_run(pool, (job_callback_f)vfs_aio_read, aiocb); 670 } else if(VFS_AIO_WRITE) { 671 threadpool_run(pool, (job_callback_f)vfs_aio_write, aiocb); 672 } 673 } 674