|
|
1.1 ! root 1: /* $Header: /kernel/kersrc/coh.286/RCS/fs1.c,v 1.1 92/07/17 15:18:05 bin Exp Locker: bin $ */ ! 2: /* (lgl- ! 3: * The information contained herein is a trade secret of Mark Williams ! 4: * Company, and is confidential information. It is provided under a ! 5: * license agreement, and may be copied or disclosed only under the ! 6: * terms of that agreement. Any reproduction or disclosure of this ! 7: * material without the express written authorization of Mark Williams ! 8: * Company or persuant to the license agreement is unlawful. ! 9: * ! 10: * COHERENT Version 2.3.37 ! 11: * Copyright (c) 1982, 1983, 1984. ! 12: * An unpublished work by Mark Williams Company, Chicago. ! 13: * All rights reserved. ! 14: -lgl) */ ! 15: /* ! 16: * Coherent. ! 17: * Filesystem (mostly handling of in core inodes). ! 18: * ! 19: * $Log: fs1.c,v $ ! 20: * Revision 1.1 92/07/17 15:18:05 bin ! 21: * Initial revision ! 22: * ! 23: * Revision 1.1 88/03/24 16:13:47 src ! 24: * Initial revision ! 25: * ! 26: * 87/11/25 Allan Cornish /usr/src/sys/coh/fs1.c ! 27: * vaddr_t bp->b_vaddr --> faddr_t bp->b_faddr. ! 28: * ! 29: * 86/12/13 Allan Cornish /usr/src/sys/coh/fs1.c ! 30: * isync() no longer updates the disk image of a character device inode. ! 31: * ! 32: * 86/11/19 Allan Cornish /usr/src/sys/coh/fs1.c ! 33: * idirent() initializes the (new) (IO).io_flag field to 0. ! 34: */ ! 35: #include <sys/coherent.h> ! 36: #include <sys/buf.h> ! 37: #include <canon.h> ! 38: #include <sys/dir.h> ! 39: #include <errno.h> ! 40: #include <sys/filsys.h> ! 41: #include <sys/ino.h> ! 42: #include <sys/inode.h> ! 43: #include <sys/io.h> ! 44: #include <sys/mount.h>> ! 45: #include <sys/stat.h> ! 46: ! 47: /* ! 48: * Get character for `ftoi' depending on what space the characters are ! 49: * coming from. ! 50: */ ! 51: #define ftoic(p) (u.u_io.io_seg==IOSYS ? *p : getubd(p)) ! 52: ! 53: /* ! 54: * Map the given filename to an inode. If an error is encountered, ! 55: * `u.u_error' is set. `u.u_error' is always returned. As this routine ! 56: * needs to set several things, depending on the type of access, `t', ! 57: * there are places in the processes' user area reserved for this routine ! 58: * to set. These are defined in the user process structure. The seek ! 59: * position is always set to the position of the directory entry of the ! 60: * child if the child exists or the first free position if it doesn't. ! 61: * 'r' => Reference. A pointer to the child's inode is returned locked. ! 62: * 'c' => Create. If the child exists, a pointer to the inode is returned ! 63: * locked. Otherwise if the parent directory exists, a pointer to ! 64: * the parent directory is returned locked. Otherwise, an error. ! 65: * 'u' => Unlink. The parent directory is returned unlocked. The child's ! 66: * inode number is returned. The seek position is also set. ! 67: */ ! 68: ftoi(np, t) ! 69: char *np; ! 70: { ! 71: register INODE *cip; ! 72: register char *cp; ! 73: register int c; ! 74: register struct direct *dp; ! 75: register BUF *bp; ! 76: fsize_t cseek, fseek, s; ! 77: int fflag, mflag; ! 78: dev_t dev; ! 79: ino_t ino; ! 80: daddr_t b; ! 81: ! 82: u.u_cdirn = 0; ! 83: u.u_cdiri = NULL; ! 84: u.u_pdiri = NULL; ! 85: if ((c=ftoic(np++)) != '/') ! 86: cip = u.u_cdir; ! 87: else { ! 88: c = ftoic(np++); ! 89: cip = u.u_rdir; ! 90: } ! 91: while (c == '/') ! 92: c = ftoic(np++); ! 93: ilock(cip); ! 94: cip->i_refc++; ! 95: if (c == '\0') { ! 96: if (t == 'r') { ! 97: u.u_cdiri = cip; ! 98: return (u.u_error); ! 99: } ! 100: u.u_error = ENOENT; ! 101: idetach(cip); ! 102: return (u.u_error); ! 103: } ! 104: for (;;) { ! 105: cp = u.u_direct.d_name; ! 106: while (c!='/' && c!='\0') { ! 107: if (cp < &u.u_direct.d_name[DIRSIZ]) ! 108: *cp++ = c; ! 109: c = ftoic(np++); ! 110: } ! 111: while (c == '/') ! 112: c = ftoic(np++); ! 113: while (cp < &u.u_direct.d_name[DIRSIZ]) ! 114: *cp++ = '\0'; ! 115: if ((cip->i_mode&IFMT) != IFDIR) ! 116: u.u_error = ENOTDIR; ! 117: else ! 118: iaccess(cip, IPE); ! 119: if (u.u_error) { ! 120: idetach(cip); ! 121: return (u.u_error); ! 122: } ! 123: cp = u.u_direct.d_name; ! 124: if (cip->i_ino==ROOTIN && cip->i_dev!=rootdev) ! 125: if (*cp++=='.' && *cp++=='.' && *cp++=='\0') ! 126: cip = ftoim(cip); ! 127: b = 0; ! 128: fflag = 0; ! 129: mflag = 0; ! 130: cseek = 0; ! 131: s = cip->i_size; ! 132: while (s > 0) { ! 133: if ((bp=vread(cip, b++)) == NULL) { ! 134: idetach(cip); ! 135: return (u.u_error); ! 136: } ! 137: dp = FP_OFF(bp->b_faddr); ! 138: while (dp < FP_OFF(bp->b_faddr)+BSIZE) { ! 139: if ((s-=sizeof(*dp)) < 0) ! 140: break; ! 141: if ((ino=dp->d_ino) == 0) { ! 142: if (fflag == 0) { ! 143: fflag++; ! 144: fseek = cseek; ! 145: } ! 146: } else { ! 147: if (direq(dp)) { ! 148: canino(ino); ! 149: mflag = 1; ! 150: s = 0; ! 151: break; ! 152: } ! 153: } ! 154: cseek += sizeof(*dp); ! 155: dp++; ! 156: } ! 157: brelease(bp); ! 158: } ! 159: dev = cip->i_dev; ! 160: if (fflag == 0) ! 161: fseek = cseek; ! 162: if (mflag == 0) { ! 163: if (c=='\0' && t=='c') { ! 164: u.u_pdiri = cip; ! 165: u.u_io.io_seek = fseek; ! 166: } else { ! 167: u.u_error = ENOENT; ! 168: idetach(cip); ! 169: } ! 170: return (u.u_error); ! 171: } ! 172: if (c == '\0') { ! 173: if (t == 'u') { ! 174: u.u_cdirn = ino; ! 175: u.u_pdiri = cip; ! 176: u.u_io.io_seek = cseek; ! 177: return (u.u_error); ! 178: } ! 179: idetach(cip); ! 180: u.u_cdiri = iattach(dev, ino); ! 181: return (u.u_error); ! 182: } ! 183: idetach(cip); ! 184: if ((cip=iattach(dev, ino)) == NULL) ! 185: return (u.u_error); ! 186: } ! 187: } ! 188: ! 189: /* ! 190: * Given an inode which is the root of a file system, return the inode ! 191: * on which the file system was mounted. ! 192: */ ! 193: INODE * ! 194: ftoim(ip) ! 195: register INODE *ip; ! 196: { ! 197: register MOUNT *mp; ! 198: ! 199: for (mp=mountp; mp!=NULL; mp=mp->m_next) { ! 200: if (mp->m_dev == ip->i_dev) { ! 201: idetach(ip); ! 202: ip = mp->m_ip; ! 203: ilock(ip); ! 204: ip->i_refc++; ! 205: break; ! 206: } ! 207: } ! 208: return (ip); ! 209: } ! 210: ! 211: /* ! 212: * Compare the string in `u.u_direct.d_name' with the name in the ! 213: * given directory pointer. ! 214: */ ! 215: direq(dp) ! 216: struct direct *dp; ! 217: { ! 218: register char *cp1, *cp2; ! 219: register unsigned n; ! 220: ! 221: if (dp->d_ino == 0) ! 222: return (0); ! 223: cp1 = dp->d_name; ! 224: cp2 = u.u_direct.d_name; ! 225: n = DIRSIZ; ! 226: do { ! 227: if (*cp1++ != *cp2++) ! 228: return (0); ! 229: } while (--n); ! 230: return (1); ! 231: } ! 232: ! 233: /* ! 234: * Make an inode of the given mode and device. The parent directory, ! 235: * name and such stuff is set by ftoi. ! 236: */ ! 237: INODE * ! 238: imake(mode, rdev) ! 239: unsigned mode; ! 240: dev_t rdev; ! 241: { ! 242: register INODE *ip; ! 243: ! 244: ip = NULL; ! 245: mode &= ~u.u_umask; ! 246: if ((mode&ISVTXT)!=0 && super()==0) ! 247: goto det; ! 248: if (iaccess(u.u_pdiri, IPW) == 0) ! 249: goto det; ! 250: if ((ip=ialloc(u.u_pdiri->i_dev, mode)) == NULL) ! 251: goto det; ! 252: ip->i_nlink = 1; ! 253: ip->i_a.i_rdev = rdev; ! 254: idirent(ip->i_ino); ! 255: iamc(ip); /* creat/mknod - atime/mtime/ctime */ ! 256: det: ! 257: idetach(u.u_pdiri); ! 258: return (ip); ! 259: } ! 260: ! 261: /* ! 262: * Write a directory entry out. Everything necessary has been conveniently ! 263: * set by `ftoi', except the new inode number of this directory entry. ! 264: */ ! 265: idirent(ino) ! 266: { ! 267: u.u_direct.d_ino = ino; ! 268: canino(u.u_direct.d_ino); ! 269: u.u_io.io_ioc = sizeof (struct direct); ! 270: u.u_io.io_base = &u.u_direct; ! 271: u.u_io.io_seg = IOSYS; ! 272: u.u_io.io_flag = 0; ! 273: iwrite(u.u_pdiri, &u.u_io); ! 274: } ! 275: ! 276: /* ! 277: * Return a pointer to a locked inode in core containing the given ! 278: * inode number and device. ! 279: */ ! 280: INODE * ! 281: iattach(dev, ino) ! 282: { ! 283: register INODE *ip; ! 284: register INODE *fip; ! 285: register unsigned lrt; ! 286: register MOUNT *mp; ! 287: ! 288: for (;;) { ! 289: fip = NULL; ! 290: for (ip=&inodep[NINODE-1]; ip>=inodep; --ip) { ! 291: if (ip->i_ino==ino && ip->i_dev==dev) ! 292: break; ! 293: if (ip->i_refc == 0) { ! 294: if (fip==NULL || ip->i_lrt<lrt) { ! 295: fip = ip; ! 296: lrt = ip->i_lrt; ! 297: } ! 298: } ! 299: } ! 300: if (ip < inodep) { ! 301: if ((ip=fip) == NULL) { ! 302: devmsg(dev, "Inode table overflow"); ! 303: /*DEBUG*/ ! 304: { char cmd[11];int i; ! 305: for(i=0;i<10&&u.u_comm[i];i++) ! 306: cmd[i]=u.u_comm[i]; ! 307: cmd[i]='\0'; ! 308: printf("cmd=%s time=%lu\n",cmd, u.u_btime); ! 309: } ! 310: u.u_error = ENFILE; ! 311: return (NULL); ! 312: } ! 313: ilock(ip); ! 314: if (ip->i_refc != 0) { ! 315: iunlock(ip); ! 316: continue; ! 317: } ! 318: ip->i_dev = dev; ! 319: ip->i_ino = ino; ! 320: ip->i_refc = 1; ! 321: ip->i_lrt = timer.t_time; ! 322: if (icopydm(ip) == 0) { ! 323: ip->i_ino = 0; ! 324: ip->i_refc = 0; ! 325: iunlock(ip); ! 326: return (NULL); ! 327: } ! 328: return (ip); ! 329: } ! 330: if ((ip->i_flag&IFMNT) != 0) { ! 331: for (mp=mountp; mp!=NULL; mp=mp->m_next) { ! 332: if (mp->m_ip == ip) { ! 333: ino = ROOTIN; ! 334: dev = mp->m_dev; ! 335: break; ! 336: } ! 337: } ! 338: continue; ! 339: } ! 340: ilock(ip); ! 341: if (ip->i_ino!=ino || ip->i_dev!=dev) { ! 342: iunlock(ip); ! 343: continue; ! 344: } ! 345: if (ip->i_refc < 0) ! 346: panic("ialloc(%p), ip"); ! 347: ip->i_refc++; ! 348: ip->i_lrt = timer.t_time; ! 349: return (ip); ! 350: } ! 351: } ! 352: ! 353: /* ! 354: * Given a locked inode, deaccess it. ! 355: */ ! 356: idetach(ip) ! 357: register INODE *ip; ! 358: { ! 359: if (ilocked(ip)==0 || ip->i_refc<=0) ! 360: panic("idetach(%p)", ip); ! 361: if (--ip->i_refc == 0) { ! 362: if ((ip->i_flag&(IFACC|IFMOD|IFCRT)) != 0 ! 363: || ip->i_nlink == 0) ! 364: icopymd(ip); ! 365: } ! 366: iunlock(ip); ! 367: } ! 368: ! 369: /* ! 370: * Given a inode which isn't locked, lock it and then deaccess. ! 371: */ ! 372: ldetach(ip) ! 373: register INODE *ip; ! 374: { ! 375: ilock(ip); ! 376: idetach(ip); ! 377: } ! 378: ! 379: /* ! 380: * A specialized routine for finding whether the given inode may be unlinked. ! 381: * Quite simple you say, but we already have an inode locked and could run ! 382: * into gating problems if we were to lock another. So we look through the ! 383: * cache to see if the inode is there. If it is, we can easily tell. If it ! 384: * isn't, `icopydm' is called with a static. This routine is only used by ! 385: * `uunlink'. ! 386: */ ! 387: iucheck(dev, ino) ! 388: register dev_t dev; ! 389: register ino_t ino; ! 390: { ! 391: register INODE *ip; ! 392: INODE inode; ! 393: ! 394: for (ip=&inodep[NINODE-1]; ip>=inodep; --ip) { ! 395: if (ip->i_ino==ino && ip->i_dev==dev) ! 396: break; ! 397: } ! 398: if (ip < inodep) { ! 399: ip = &inode; ! 400: ip->i_dev = dev; ! 401: ip->i_ino = ino; ! 402: if (icopydm(ip) == 0) ! 403: return (0); ! 404: } ! 405: if ((ip->i_mode&IFMT) == IFDIR) { ! 406: if (super() == 0) ! 407: return (0); ! 408: } ! 409: return (1); ! 410: } ! 411: ! 412: /* ! 413: * Copy an inode from disk to memory performing canonization. ! 414: */ ! 415: icopydm(ip) ! 416: register INODE *ip; ! 417: { ! 418: register struct dinode *dip; ! 419: register BUF *bp; ! 420: register ino_t ino; ! 421: struct dinode dinode; ! 422: vaddr_t v; ! 423: ! 424: ip->i_flag = 0; ! 425: ino = ip->i_ino; ! 426: ! 427: if ((bp=bread(ip->i_dev, (daddr_t)iblockn(ino), 1)) == NULL) ! 428: return (0); ! 429: ! 430: dip = &dinode; ! 431: v = (char *)((struct dinode *)FP_OFF(bp->b_faddr) + iblocko(ino)); ! 432: kkcopy( v, dip, sizeof(dinode)); ! 433: brelease(bp); ! 434: ip->i_mode = dip->di_mode; ! 435: canshort(ip->i_mode); ! 436: ip->i_nlink = dip->di_nlink; ! 437: canshort(ip->i_nlink); ! 438: ip->i_uid = dip->di_uid; ! 439: canshort(ip->i_uid); ! 440: ip->i_gid = dip->di_gid; ! 441: canshort(ip->i_gid); ! 442: ip->i_size = dip->di_size; ! 443: cansize(ip->i_size); ! 444: ! 445: switch (ip->i_mode&IFMT) { ! 446: case IFBLK: ! 447: case IFCHR: ! 448: ip->i_a.i_rdev = dip->di_a.di_rdev; ! 449: candev(ip->i_a.i_rdev); ! 450: break; ! 451: case IFREG: ! 452: case IFDIR: ! 453: l3tol(ip->i_a.i_addr, dip->di_a.di_addb, NADDR); ! 454: break; ! 455: case IFPIPE: ! 456: l3tol(ip->i_pipe, dip->di_addp, ND); ! 457: ip->i_pnc = dip->di_pnc; ! 458: canint(ip->i_pnc); ! 459: ip->i_prx = dip->di_prx; ! 460: canint(ip->i_prx); ! 461: ip->i_pwx = dip->di_pwx; ! 462: canint(ip->i_pwx); ! 463: break; ! 464: default: ! 465: kclear(&ip->i_a, sizeof(ip->i_a)); ! 466: break; ! 467: } ! 468: ! 469: ip->i_atime = dip->di_atime; ! 470: cantime(ip->i_atime); ! 471: ip->i_mtime = dip->di_mtime; ! 472: cantime(ip->i_mtime); ! 473: ip->i_ctime = dip->di_ctime; ! 474: cantime(ip->i_ctime); ! 475: return (1); ! 476: } ! 477: ! 478: /* ! 479: * Copy an inode from memory back on to disk performing canonization. ! 480: */ ! 481: icopymd(ip) ! 482: register INODE *ip; ! 483: { ! 484: register struct dinode *dip; ! 485: register BUF *bp; ! 486: register ino_t ino; ! 487: struct dinode dinode; ! 488: vaddr_t v; ! 489: ! 490: if (getment(ip->i_dev, 0) == NULL) ! 491: return; ! 492: ! 493: ino = ip->i_ino; ! 494: if (ip->i_refc==0 && ip->i_nlink==0 && ino!=BADFIN && ino!=ROOTIN) { ! 495: iclear(ip); ! 496: ip->i_lrt = 0; ! 497: ip->i_mode = 0; ! 498: ifree(ip->i_dev, ino); ! 499: } ! 500: ! 501: dip = &dinode; ! 502: dip->di_mode = ip->i_mode; ! 503: canshort(dip->di_mode); ! 504: dip->di_nlink = ip->i_nlink; ! 505: canshort(dip->di_nlink); ! 506: dip->di_uid = ip->i_uid; ! 507: canshort(dip->di_uid); ! 508: dip->di_gid = ip->i_gid; ! 509: canshort(dip->di_gid); ! 510: dip->di_size = ip->i_size; ! 511: cansize(dip->di_size); ! 512: ! 513: switch (ip->i_mode&IFMT) { ! 514: case IFBLK: ! 515: case IFCHR: ! 516: dip->di_a.di_rdev = ip->i_a.i_rdev; ! 517: candev(dip->di_a.di_rdev); ! 518: break; ! 519: case IFREG: ! 520: case IFDIR: ! 521: ltol3(dip->di_addr, ip->i_a.i_addr, NADDR); ! 522: break; ! 523: case IFPIPE: ! 524: ltol3(dip->di_addp, ip->i_pipe, ND); ! 525: dip->di_pnc = ip->i_pnc; ! 526: canshort(dip->di_pnc); ! 527: dip->di_prx = ip->i_prx; ! 528: canshort(dip->di_prx); ! 529: dip->di_pwx = ip->i_pwx; ! 530: canshort(dip->di_pwx); ! 531: break; ! 532: default: ! 533: kclear(&dip->di_a, sizeof(dip->di_a)); ! 534: break; ! 535: } ! 536: ! 537: dip->di_atime = ip->i_atime; ! 538: cantime(dip->di_atime); ! 539: dip->di_mtime = ip->i_mtime; ! 540: cantime(dip->di_mtime); ! 541: dip->di_ctime = ip->i_ctime; ! 542: cantime(dip->di_ctime); ! 543: ! 544: if ((bp=bread(ip->i_dev, (daddr_t)iblockn(ino), 1)) == NULL) ! 545: return; ! 546: ! 547: v = (char *)((struct dinode *)FP_OFF(bp->b_faddr) + iblocko(ino)); ! 548: kkcopy(dip, v, sizeof(dinode)); ! 549: bp->b_flag |= BFMOD; ! 550: brelease(bp); ! 551: ip->i_flag &= ~(IFACC|IFMOD|IFCRT); ! 552: } ! 553: ! 554: /* ! 555: * Copy all relevant inodes out on device `dev'. ! 556: */ ! 557: isync(dev) ! 558: register dev_t dev; ! 559: { ! 560: register INODE *ip; ! 561: ! 562: for (ip=&inodep[NINODE-1]; ip>=inodep; --ip) { ! 563: if (ip->i_refc == 0) ! 564: continue; ! 565: if (ip->i_dev != dev) ! 566: continue; ! 567: if ( (ip->i_mode & IFMT) == IFCHR ) ! 568: continue; ! 569: if ((ip->i_flag&(IFACC|IFMOD|IFCRT)) == 0) ! 570: continue; ! 571: icopymd(ip); ! 572: } ! 573: } ! 574: ! 575: /* ! 576: * Clear the given inode and all space associated with it. ! 577: */ ! 578: iclear(ip) ! 579: register INODE *ip; ! 580: { ! 581: register int n; ! 582: register daddr_t b; ! 583: ! 584: switch (ip->i_mode&IFMT) { ! 585: case IFPIPE: ! 586: ip->i_pnc = 0; ! 587: ip->i_prx = 0; ! 588: ip->i_pwx = 0; ! 589: n = ND; ! 590: break; ! 591: case IFDIR: ! 592: case IFREG: ! 593: n = NADDR; ! 594: break; ! 595: default: ! 596: return; ! 597: } ! 598: while (n > ND) { ! 599: if ((b=ip->i_a.i_addr[--n]) != 0) ! 600: indfree(ip->i_dev, b, 1+n-ND); ! 601: } ! 602: while (n > 0) { ! 603: if ((b=ip->i_a.i_addr[--n]) != 0) ! 604: bfree(ip->i_dev, b); ! 605: } ! 606: ip->i_size = 0; ! 607: kclear(ip->i_a.i_addr, sizeof(ip->i_a.i_addr)); ! 608: iamc(ip); /* creat/pipe - atime/mtime/ctime */ ! 609: } ! 610: ! 611: /* ! 612: * Copy the appropriate information from the inode to the stat buffer. ! 613: */ ! 614: istat(ip, sbp) ! 615: register INODE *ip; ! 616: register struct stat *sbp; ! 617: { ! 618: sbp->st_dev = ip->i_dev; ! 619: sbp->st_ino = ip->i_ino; ! 620: sbp->st_mode = ip->i_mode; ! 621: sbp->st_nlink = ip->i_nlink; ! 622: sbp->st_uid = ip->i_uid; ! 623: sbp->st_gid = ip->i_gid; ! 624: sbp->st_rdev = NODEV; ! 625: sbp->st_size = ip->i_size; ! 626: sbp->st_atime = ip->i_atime; ! 627: sbp->st_mtime = ip->i_mtime; ! 628: sbp->st_ctime = ip->i_ctime; ! 629: switch (ip->i_mode&IFMT) { ! 630: case IFBLK: ! 631: case IFCHR: ! 632: sbp->st_rdev = ip->i_a.i_rdev; ! 633: sbp->st_size = 0; ! 634: break; ! 635: case IFPIPE: ! 636: sbp->st_size = ip->i_pnc; ! 637: break; ! 638: } ! 639: } ! 640: ! 641: /* ! 642: * See if it is possible to access the given inode with the bits in ! 643: * the given mode. ! 644: * If the mode includes writing, and i_refc is > 1, then check for ! 645: * shared text problems. ! 646: */ ! 647: iaccess(ip, mode) ! 648: register INODE *ip; ! 649: register int mode; ! 650: { ! 651: if ((imode(ip, u.u_uid, u.u_gid)&mode) != mode) { ! 652: u.u_error = EACCES; ! 653: return (0); ! 654: } ! 655: if ((mode&IPW) != 0 && ip->i_refc > 1 && sbusy(ip)) { ! 656: u.u_error = ETXTBSY; ! 657: return (0); ! 658: } ! 659: return (1); ! 660: } ! 661: ! 662: /* ! 663: * Get the maximum allowable mode on a file. ! 664: */ ! 665: imode(ip, uid, gid) ! 666: register INODE *ip; ! 667: { ! 668: if (uid == 0) ! 669: return (IPR|IPW|IPE); ! 670: if (uid == ip->i_uid) ! 671: return ((ip->i_mode>>6)&07); ! 672: if (gid == ip->i_gid) ! 673: return ((ip->i_mode>>3)&07); ! 674: return (ip->i_mode&07); ! 675: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.