Annotation of prex/usr/server/fs/vfs/vfs_syscalls.c, Revision 1.1
1.1 ! nbrk 1: /*
! 2: * Copyright (c) 2005-2007, Kohsuke Ohtani
! 3: * All rights reserved.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. Neither the name of the author nor the names of any co-contributors
! 14: * may be used to endorse or promote products derived from this software
! 15: * without specific prior written permission.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: */
! 29:
! 30: /*
! 31: * vfs_syscalls.c - everything in this file is a routine implementing
! 32: * a VFS system call.
! 33: */
! 34:
! 35: #include <prex/prex.h>
! 36: #include <sys/stat.h>
! 37: #include <sys/vnode.h>
! 38: #include <sys/file.h>
! 39: #include <sys/mount.h>
! 40: #include <sys/dirent.h>
! 41: #include <sys/list.h>
! 42: #include <sys/buf.h>
! 43:
! 44: #include <limits.h>
! 45: #include <unistd.h>
! 46: #include <stdlib.h>
! 47: #include <string.h>
! 48: #include <stdio.h>
! 49: #include <errno.h>
! 50: #include <fcntl.h>
! 51:
! 52: #include "vfs.h"
! 53:
! 54: int
! 55: sys_open(char *path, int flags, mode_t mode, file_t *pfp)
! 56: {
! 57: vnode_t vp, dvp;
! 58: file_t fp;
! 59: char *filename;
! 60: int err;
! 61:
! 62: DPRINTF(VFSDB_SYSCALL, ("sys_open: path=%s flags=%x mode=%x\n",
! 63: path, flags, mode));
! 64:
! 65: flags = FFLAGS(flags);
! 66: if ((flags & (FREAD | FWRITE)) == 0)
! 67: return EINVAL;
! 68: if (flags & O_CREAT) {
! 69: err = namei(path, &vp);
! 70: if (err == ENOENT) {
! 71: /* Create new file. */
! 72: if ((err = lookup(path, &dvp, &filename)) != 0)
! 73: return err;
! 74: if (dvp->v_mount->m_flags & MNT_RDONLY) {
! 75: vput(dvp);
! 76: return EROFS;
! 77: }
! 78: mode &= ~S_IFMT;
! 79: mode |= S_IFREG;
! 80: err = VOP_CREATE(dvp, filename, mode);
! 81: vput(dvp);
! 82: if (err)
! 83: return err;
! 84: if ((err = namei(path, &vp)) != 0)
! 85: return err;
! 86: flags &= ~O_TRUNC;
! 87: } else if (err) {
! 88: return err;
! 89: } else {
! 90: /* File already exits */
! 91: if (flags & O_EXCL) {
! 92: vput(vp);
! 93: return EEXIST;
! 94: }
! 95: flags &= ~O_CREAT;
! 96: }
! 97: } else {
! 98: /* Open */
! 99: if ((err = namei(path, &vp)) != 0)
! 100: return err;
! 101: }
! 102: if ((flags & O_CREAT) == 0) {
! 103: if (flags & FWRITE || flags & O_TRUNC) {
! 104: if (vp->v_mount->m_flags & MNT_RDONLY) {
! 105: vput(vp);
! 106: return EROFS;
! 107: }
! 108: if (vp->v_type == VDIR) {
! 109: /* Openning directory with writable. */
! 110: vput(vp);
! 111: return EISDIR;
! 112: }
! 113: }
! 114: }
! 115: if (flags & O_TRUNC) {
! 116: if (!(flags & FWRITE) || (vp->v_type != VREG)) {
! 117: vput(vp);
! 118: return EINVAL;
! 119: }
! 120: }
! 121: /* Process truncate request */
! 122: if (flags & O_TRUNC) {
! 123: if ((err = VOP_TRUNCATE(vp)) != 0) {
! 124: vput(vp);
! 125: return err;
! 126: }
! 127: }
! 128: /* Setup file structure */
! 129: if (!(fp = malloc(sizeof(struct file)))) {
! 130: vput(vp);
! 131: return ENOMEM;
! 132: }
! 133: /* Request to file system */
! 134: if ((err = VOP_OPEN(vp, flags)) != 0) {
! 135: free(fp);
! 136: vput(vp);
! 137: return err;
! 138: }
! 139: memset(fp, 0, sizeof(struct file));
! 140: fp->f_vnode = vp;
! 141: fp->f_flags = flags;
! 142: fp->f_offset = 0;
! 143: fp->f_count = 1;
! 144: *pfp = fp;
! 145: vn_unlock(vp);
! 146: return 0;
! 147: }
! 148:
! 149: int
! 150: sys_close(file_t fp)
! 151: {
! 152: vnode_t vp;
! 153: int err;
! 154:
! 155: DPRINTF(VFSDB_SYSCALL, ("sys_close: fp=%x\n", (u_int)fp));
! 156:
! 157: vp = fp->f_vnode;
! 158: if (--fp->f_count > 0) {
! 159: vrele(vp);
! 160: return 0;
! 161: }
! 162: vn_lock(vp);
! 163: if ((err = VOP_CLOSE(vp, fp)) != 0) {
! 164: vn_unlock(vp);
! 165: return err;
! 166: }
! 167: vput(vp);
! 168: free(fp);
! 169: return 0;
! 170: }
! 171:
! 172: int
! 173: sys_read(file_t fp, void *buf, size_t size, size_t *count)
! 174: {
! 175: vnode_t vp;
! 176: int err;
! 177:
! 178: DPRINTF(VFSDB_SYSCALL, ("sys_read: fp=%x buf=%x size=%d\n",
! 179: (u_int)fp, (u_int)buf, size));
! 180:
! 181: if ((fp->f_flags & FREAD) == 0)
! 182: return EPERM;
! 183: if (size == 0) {
! 184: *count = 0;
! 185: return 0;
! 186: }
! 187: vp = fp->f_vnode;
! 188: vn_lock(vp);
! 189: err = VOP_READ(vp, fp, buf, size, count);
! 190: vn_unlock(vp);
! 191: return err;
! 192: }
! 193:
! 194: int
! 195: sys_write(file_t fp, void *buf, size_t size, size_t *count)
! 196: {
! 197: vnode_t vp;
! 198: int err;
! 199:
! 200: DPRINTF(VFSDB_SYSCALL, ("sys_write: fp=%x buf=%x size=%d\n",
! 201: (u_int)fp, (u_int)buf, size));
! 202:
! 203: if (size == 0) {
! 204: *count = 0;
! 205: return 0;
! 206: }
! 207: vp = fp->f_vnode;
! 208: vn_lock(vp);
! 209: err = VOP_WRITE(vp, fp, buf, size, count);
! 210: vn_unlock(vp);
! 211: return err;
! 212: }
! 213:
! 214: int
! 215: sys_lseek(file_t fp, off_t off, int type, off_t *origin)
! 216: {
! 217: vnode_t vp;
! 218:
! 219: DPRINTF(VFSDB_SYSCALL, ("sys_seek: fp=%x off=%d type=%d\n",
! 220: (u_int)fp, (u_int)off, type));
! 221:
! 222: vp = fp->f_vnode;
! 223: vn_lock(vp);
! 224: switch (type) {
! 225: case SEEK_SET:
! 226: if (off < 0)
! 227: off = 0;
! 228: if (off > (off_t)vp->v_size)
! 229: off = vp->v_size;
! 230: break;
! 231: case SEEK_CUR:
! 232: if (fp->f_offset + off > (off_t)vp->v_size)
! 233: off = vp->v_size;
! 234: else if (fp->f_offset + off < 0)
! 235: off = 0;
! 236: else
! 237: off = fp->f_offset + off;
! 238: break;
! 239: case SEEK_END:
! 240: if (off > 0)
! 241: off = vp->v_size;
! 242: else if ((int)vp->v_size + off < 0)
! 243: off = 0;
! 244: else
! 245: off = vp->v_size + off;
! 246: break;
! 247: default:
! 248: vn_unlock(vp);
! 249: return EINVAL;
! 250: }
! 251: /* Request to check the file offset */
! 252: if (VOP_SEEK(vp, fp, fp->f_offset, off) != 0) {
! 253: vn_unlock(vp);
! 254: return EINVAL;
! 255: }
! 256: *origin = off;
! 257: fp->f_offset = off;
! 258: vn_unlock(vp);
! 259: return 0;
! 260: }
! 261:
! 262: int
! 263: sys_ioctl(file_t fp, u_long request, void *buf)
! 264: {
! 265: vnode_t vp;
! 266: int err;
! 267:
! 268: DPRINTF(VFSDB_SYSCALL, ("sys_ioctl: fp=%x request=%x\n", fp, request));
! 269:
! 270: if ((fp->f_flags & (FREAD | FWRITE)) == 0)
! 271: return EBADF;
! 272:
! 273: vp = fp->f_vnode;
! 274: vn_lock(vp);
! 275: err = VOP_IOCTL(vp, fp, request, buf);
! 276: vn_unlock(vp);
! 277: return err;
! 278: }
! 279:
! 280: int
! 281: sys_fsync(file_t fp)
! 282: {
! 283: vnode_t vp;
! 284: int err;
! 285:
! 286: DPRINTF(VFSDB_SYSCALL, ("sys_fsync: fp=%x\n", fp));
! 287:
! 288: if ((fp->f_flags & FREAD) == 0)
! 289: return EBADF;
! 290:
! 291: vp = fp->f_vnode;
! 292: vn_lock(vp);
! 293: err = VOP_FSYNC(vp, fp);
! 294: vn_unlock(vp);
! 295: return err;
! 296: }
! 297:
! 298: int
! 299: sys_fstat(file_t fp, struct stat *st)
! 300: {
! 301: vnode_t vp;
! 302: int err = 0;
! 303:
! 304: DPRINTF(VFSDB_SYSCALL, ("sys_fstat: fp=%x\n", fp));
! 305:
! 306: vp = fp->f_vnode;
! 307: vn_lock(vp);
! 308: err = vn_stat(vp, st);
! 309: vn_unlock(vp);
! 310: return err;
! 311: }
! 312:
! 313: /*
! 314: * Return 0 if directory is empty
! 315: */
! 316: static int
! 317: check_dir_empty(char *path)
! 318: {
! 319: int err;
! 320: file_t fp;
! 321: struct dirent dir;
! 322:
! 323: if ((err = sys_opendir(path, &fp)) != 0)
! 324: return err;
! 325: do {
! 326: err = sys_readdir(fp, &dir);
! 327: if (err)
! 328: break;
! 329: } while (!strcmp(dir.d_name, ".") || !strcmp(dir.d_name, ".."));
! 330:
! 331: sys_closedir(fp);
! 332:
! 333: if (err == ENOENT)
! 334: return 0;
! 335: else if (err == 0)
! 336: return EEXIST;
! 337: return err;
! 338: }
! 339:
! 340: int
! 341: sys_opendir(char *path, file_t *file)
! 342: {
! 343: vnode_t dvp;
! 344: file_t fp;
! 345: int err;
! 346:
! 347: DPRINTF(VFSDB_SYSCALL, ("sys_opendir: path=%s\n", path));
! 348:
! 349: if ((err = sys_open(path, O_RDONLY, 0, &fp)) != 0)
! 350: return err;
! 351:
! 352: dvp = fp->f_vnode;
! 353: vn_lock(dvp);
! 354: if (dvp->v_type != VDIR) {
! 355: vn_unlock(dvp);
! 356: sys_close(fp);
! 357: return ENOTDIR;
! 358: }
! 359: vn_unlock(dvp);
! 360:
! 361: *file = fp;
! 362: return 0;
! 363: }
! 364:
! 365: int
! 366: sys_closedir(file_t fp)
! 367: {
! 368: vnode_t dvp;
! 369: int err;
! 370:
! 371: DPRINTF(VFSDB_SYSCALL, ("sys_closedir: fp=%x\n", fp));
! 372:
! 373: dvp = fp->f_vnode;
! 374: vn_lock(dvp);
! 375: if (dvp->v_type != VDIR) {
! 376: vn_unlock(dvp);
! 377: return EBADF;
! 378: }
! 379: vn_unlock(dvp);
! 380: err = sys_close(fp);
! 381: return err;
! 382: }
! 383:
! 384: int
! 385: sys_readdir(file_t fp, struct dirent *dir)
! 386: {
! 387: vnode_t dvp;
! 388: int err;
! 389:
! 390: DPRINTF(VFSDB_SYSCALL, ("sys_readdir: fp=%x\n", fp));
! 391:
! 392: dvp = fp->f_vnode;
! 393: vn_lock(dvp);
! 394: if (dvp->v_type != VDIR) {
! 395: vn_unlock(dvp);
! 396: return ENOTDIR;
! 397: }
! 398: err = VOP_READDIR(dvp, fp, dir);
! 399: vn_unlock(dvp);
! 400: return err;
! 401: }
! 402:
! 403: int
! 404: sys_rewinddir(file_t fp)
! 405: {
! 406: vnode_t dvp;
! 407:
! 408: dvp = fp->f_vnode;
! 409: vn_lock(dvp);
! 410: if (dvp->v_type != VDIR) {
! 411: vn_unlock(dvp);
! 412: return EINVAL;
! 413: }
! 414: fp->f_offset = 0;
! 415: vn_unlock(dvp);
! 416: return 0;
! 417: }
! 418:
! 419: int
! 420: sys_seekdir(file_t fp, long loc)
! 421: {
! 422: vnode_t dvp;
! 423:
! 424: dvp = fp->f_vnode;
! 425: vn_lock(dvp);
! 426: if (dvp->v_type != VDIR) {
! 427: vn_unlock(dvp);
! 428: return EINVAL;
! 429: }
! 430: fp->f_offset = (off_t)loc;
! 431: vn_unlock(dvp);
! 432: return 0;
! 433: }
! 434:
! 435: int
! 436: sys_telldir(file_t fp, long *loc)
! 437: {
! 438: vnode_t dvp;
! 439:
! 440: dvp = fp->f_vnode;
! 441: vn_lock(dvp);
! 442: if (dvp->v_type != VDIR) {
! 443: vn_unlock(dvp);
! 444: return EINVAL;
! 445: }
! 446: *loc = (long)fp->f_offset;
! 447: vn_unlock(dvp);
! 448: return 0;
! 449: }
! 450:
! 451: int
! 452: sys_mkdir(char *path, mode_t mode)
! 453: {
! 454: char *name;
! 455: vnode_t vp, dvp;
! 456: int err;
! 457:
! 458: DPRINTF(VFSDB_SYSCALL, ("sys_mkdir: path=%s mode=%d\n", path, mode));
! 459:
! 460: if ((err = namei(path, &vp)) == 0) {
! 461: /* File already exists */
! 462: vput(vp);
! 463: return EEXIST;
! 464: }
! 465: /* Notice: vp is invalid here! */
! 466:
! 467: if ((err = lookup(path, &dvp, &name)) != 0) {
! 468: /* Directory already exists */
! 469: return err;
! 470: }
! 471: if (dvp->v_mount->m_flags & MNT_RDONLY) {
! 472: err = EROFS;
! 473: goto out;
! 474: }
! 475: mode &= ~S_IFMT;
! 476: mode |= S_IFDIR;
! 477:
! 478: err = VOP_MKDIR(dvp, name, mode);
! 479: out:
! 480: vput(dvp);
! 481: return err;
! 482: }
! 483:
! 484: int
! 485: sys_rmdir(char *path)
! 486: {
! 487: vnode_t vp, dvp;
! 488: int err;
! 489: char *name;
! 490:
! 491: DPRINTF(VFSDB_SYSCALL, ("sys_rmdir: path=%s\n", path));
! 492:
! 493: if ((err = check_dir_empty(path)) != 0)
! 494: return err;
! 495: if ((err = namei(path, &vp)) != 0)
! 496: return err;
! 497:
! 498: if (vp->v_mount->m_flags & MNT_RDONLY) {
! 499: err = EROFS;
! 500: goto out;
! 501: }
! 502: if (vp->v_type != VDIR) {
! 503: err = ENOTDIR;
! 504: goto out;
! 505: }
! 506: if (vp->v_flags & VROOT || vcount(vp) >= 2) {
! 507: err = EBUSY;
! 508: goto out;
! 509: }
! 510: if ((err = lookup(path, &dvp, &name)) != 0)
! 511: goto out;
! 512:
! 513: err = VOP_RMDIR(dvp, vp, name);
! 514: vn_unlock(vp);
! 515: vgone(vp);
! 516: vput(dvp);
! 517: return err;
! 518:
! 519: out:
! 520: vput(vp);
! 521: return err;
! 522: }
! 523:
! 524: int
! 525: sys_mknod(char *path, mode_t mode)
! 526: {
! 527: char *name;
! 528: vnode_t vp, dvp;
! 529: int err;
! 530:
! 531: DPRINTF(VFSDB_SYSCALL, ("sys_mknod: path=%s mode=%d\n", path, mode));
! 532:
! 533: switch (mode & S_IFMT) {
! 534: case S_IFREG:
! 535: case S_IFDIR:
! 536: case S_IFIFO:
! 537: case S_IFSOCK:
! 538: /* OK */
! 539: break;
! 540: default:
! 541: return EINVAL;
! 542: }
! 543:
! 544: if ((err = namei(path, &vp)) == 0) {
! 545: vput(vp);
! 546: return EEXIST;
! 547: }
! 548:
! 549: if ((err = lookup(path, &dvp, &name)) != 0)
! 550: return err;
! 551:
! 552: if (dvp->v_mount->m_flags & MNT_RDONLY) {
! 553: err = EROFS;
! 554: goto out;
! 555: }
! 556: if (S_ISDIR(mode))
! 557: err = VOP_MKDIR(dvp, name, mode);
! 558: else
! 559: err = VOP_CREATE(dvp, name, mode);
! 560: out:
! 561: vput(dvp);
! 562: return err;
! 563: }
! 564:
! 565: int
! 566: sys_rename(char *src, char *dest)
! 567: {
! 568: vnode_t vp1, vp2 = 0, dvp1, dvp2;
! 569: char *sname, *dname;
! 570: int err;
! 571: size_t len;
! 572: char root[] = "/";
! 573:
! 574: DPRINTF(VFSDB_SYSCALL, ("sys_rename: src=%s dest=%s\n", src, dest));
! 575:
! 576: if ((err = namei(src, &vp1)) != 0)
! 577: return err;
! 578: if (vp1->v_mount->m_flags & MNT_RDONLY) {
! 579: err = EROFS;
! 580: goto err1;
! 581: }
! 582: /* If source and dest are the same, do nothing */
! 583: if (!strncmp(src, dest, PATH_MAX))
! 584: goto err1;
! 585:
! 586: /* Check if target is directory of source */
! 587: len = strlen(dest);
! 588: if (!strncmp(src, dest, len)) {
! 589: err = EINVAL;
! 590: goto err1;
! 591: }
! 592: /* Is the source busy ? */
! 593: if (vcount(vp1) >= 2) {
! 594: err = EBUSY;
! 595: goto err1;
! 596: }
! 597: /* Check type of source & target */
! 598: err = namei(dest, &vp2);
! 599: if (err == 0) {
! 600: /* target exists */
! 601: if (vp1->v_type == VDIR && vp2->v_type != VDIR) {
! 602: err = ENOTDIR;
! 603: goto err2;
! 604: } else if (vp1->v_type != VDIR && vp2->v_type == VDIR) {
! 605: err = EISDIR;
! 606: goto err2;
! 607: }
! 608: if (vp2->v_type == VDIR && check_dir_empty(dest)) {
! 609: err = EEXIST;
! 610: goto err2;
! 611: }
! 612:
! 613: if (vcount(vp2) >= 2) {
! 614: err = EBUSY;
! 615: goto err2;
! 616: }
! 617: }
! 618:
! 619: dname = strrchr(dest, '/');
! 620: if (dname == NULL) {
! 621: err = ENOTDIR;
! 622: goto err2;
! 623: }
! 624: if (dname == dest)
! 625: dest = root;
! 626:
! 627: *dname = 0;
! 628: dname++;
! 629:
! 630: if ((err = lookup(src, &dvp1, &sname)) != 0)
! 631: goto err2;
! 632:
! 633: if ((err = namei(dest, &dvp2)) != 0)
! 634: goto err3;
! 635:
! 636: /* The source and dest must be same file system */
! 637: if (dvp1->v_mount != dvp2->v_mount) {
! 638: err = EXDEV;
! 639: goto err4;
! 640: }
! 641: err = VOP_RENAME(dvp1, vp1, sname, dvp2, vp2, dname);
! 642: err4:
! 643: vput(dvp2);
! 644: err3:
! 645: vput(dvp1);
! 646: err2:
! 647: if (vp2)
! 648: vput(vp2);
! 649: err1:
! 650: vput(vp1);
! 651: return err;
! 652: }
! 653:
! 654: int
! 655: sys_unlink(char *path)
! 656: {
! 657: char *name;
! 658: vnode_t vp, dvp;
! 659: int err;
! 660:
! 661: DPRINTF(VFSDB_SYSCALL, ("sys_unlink: path=%s\n", path));
! 662:
! 663: if ((err = namei(path, &vp)) != 0)
! 664: return err;
! 665:
! 666: if (vp->v_mount->m_flags & MNT_RDONLY) {
! 667: err = EROFS;
! 668: goto out;
! 669: }
! 670: if (vp->v_type == VDIR) {
! 671: err = EPERM;
! 672: goto out;
! 673: }
! 674: if (vp->v_flags & VROOT || vcount(vp) >= 2) {
! 675: err = EBUSY;
! 676: goto out;
! 677: }
! 678: if ((err = lookup(path, &dvp, &name)) != 0)
! 679: goto out;
! 680:
! 681: err = VOP_REMOVE(dvp, vp, name);
! 682:
! 683: vn_unlock(vp);
! 684: vgone(vp);
! 685: vput(dvp);
! 686: return 0;
! 687: out:
! 688: vput(vp);
! 689: return err;
! 690: }
! 691:
! 692: int
! 693: sys_access(char *path, int mode)
! 694: {
! 695: vnode_t vp;
! 696: int err;
! 697:
! 698: DPRINTF(VFSDB_SYSCALL, ("sys_access: path=%s\n", path));
! 699:
! 700: if ((err = namei(path, &vp)) != 0)
! 701: return err;
! 702:
! 703: err = EACCES;
! 704: if ((mode & X_OK) && (vp->v_mode & 0111) == 0)
! 705: goto out;
! 706: if ((mode & W_OK) && (vp->v_mode & 0222) == 0)
! 707: goto out;
! 708: if ((mode & R_OK) && (vp->v_mode & 0444) == 0)
! 709: goto out;
! 710: err = 0;
! 711: out:
! 712: vput(vp);
! 713: return err;
! 714: }
! 715:
! 716: int
! 717: sys_stat(char *path, struct stat *st)
! 718: {
! 719: vnode_t vp;
! 720: int err;
! 721:
! 722: if ((err = namei(path, &vp)) != 0)
! 723: return err;
! 724: err = vn_stat(vp, st);
! 725: vput(vp);
! 726: return err;
! 727: }
CVSweb