|
|
1.1 ! root 1: #include <linux/sched.h> ! 2: #include <linux/kernel.h> ! 3: #include <asm/segment.h> ! 4: ! 5: #include <string.h> ! 6: #include <fcntl.h> ! 7: #include <errno.h> ! 8: #include <const.h> ! 9: #include <sys/stat.h> ! 10: ! 11: #define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE]) ! 12: ! 13: /* ! 14: * comment out this line if you want names > NAME_LEN chars to be ! 15: * truncated. Else they will be disallowed. ! 16: */ ! 17: /* #define NO_TRUNCATE */ ! 18: ! 19: #define MAY_EXEC 1 ! 20: #define MAY_WRITE 2 ! 21: #define MAY_READ 4 ! 22: ! 23: /* ! 24: * permission() ! 25: * ! 26: * is used to check for read/write/execute permissions on a file. ! 27: * I don't know if we should look at just the euid or both euid and ! 28: * uid, but that should be easily changed. ! 29: */ ! 30: static int permission(struct m_inode * inode,int mask) ! 31: { ! 32: int mode = inode->i_mode; ! 33: ! 34: /* special case: not even root can read/write a deleted file */ ! 35: if (inode->i_dev && !inode->i_nlinks) ! 36: return 0; ! 37: if (!(current->uid && current->euid)) ! 38: mode=0777; ! 39: else if (current->uid==inode->i_uid || current->euid==inode->i_uid) ! 40: mode >>= 6; ! 41: else if (current->gid==inode->i_gid || current->egid==inode->i_gid) ! 42: mode >>= 3; ! 43: return mode & mask & 0007; ! 44: } ! 45: ! 46: /* ! 47: * ok, we cannot use strncmp, as the name is not in our data space. ! 48: * Thus we'll have to use match. No big problem. Match also makes ! 49: * some sanity tests. ! 50: * ! 51: * NOTE! unlike strncmp, match returns 1 for success, 0 for failure. ! 52: */ ! 53: static int match(int len,const char * name,struct dir_entry * de) ! 54: { ! 55: register int same __asm__("ax"); ! 56: ! 57: if (!de || !de->inode || len > NAME_LEN) ! 58: return 0; ! 59: if (len < NAME_LEN && de->name[len]) ! 60: return 0; ! 61: __asm__("cld\n\t" ! 62: "fs ; repe ; cmpsb\n\t" ! 63: "setz %%al" ! 64: :"=a" (same) ! 65: :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) ! 66: :"cx","di","si"); ! 67: return same; ! 68: } ! 69: ! 70: /* ! 71: * find_entry() ! 72: * ! 73: * finds and entry in the specified directory with the wanted name. It ! 74: * returns the cache buffer in which the entry was found, and the entry ! 75: * itself (as a parameter - res_dir). It does NOT read the inode of the ! 76: * entry - you'll have to do that yourself if you want to. ! 77: */ ! 78: static struct buffer_head * find_entry(struct m_inode * dir, ! 79: const char * name, int namelen, struct dir_entry ** res_dir) ! 80: { ! 81: int entries; ! 82: int block,i; ! 83: struct buffer_head * bh; ! 84: struct dir_entry * de; ! 85: ! 86: #ifdef NO_TRUNCATE ! 87: if (namelen > NAME_LEN) ! 88: return NULL; ! 89: #else ! 90: if (namelen > NAME_LEN) ! 91: namelen = NAME_LEN; ! 92: #endif ! 93: entries = dir->i_size / (sizeof (struct dir_entry)); ! 94: *res_dir = NULL; ! 95: if (!namelen) ! 96: return NULL; ! 97: if (!(block = dir->i_zone[0])) ! 98: return NULL; ! 99: if (!(bh = bread(dir->i_dev,block))) ! 100: return NULL; ! 101: i = 0; ! 102: de = (struct dir_entry *) bh->b_data; ! 103: while (i < entries) { ! 104: if ((char *)de >= BLOCK_SIZE+bh->b_data) { ! 105: brelse(bh); ! 106: bh = NULL; ! 107: if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) || ! 108: !(bh = bread(dir->i_dev,block))) { ! 109: i += DIR_ENTRIES_PER_BLOCK; ! 110: continue; ! 111: } ! 112: de = (struct dir_entry *) bh->b_data; ! 113: } ! 114: if (match(namelen,name,de)) { ! 115: *res_dir = de; ! 116: return bh; ! 117: } ! 118: de++; ! 119: i++; ! 120: } ! 121: brelse(bh); ! 122: return NULL; ! 123: } ! 124: ! 125: /* ! 126: * add_entry() ! 127: * ! 128: * adds a file entry to the specified directory, using the same ! 129: * semantics as find_entry(). It returns NULL if it failed. ! 130: * ! 131: * NOTE!! The inode part of 'de' is left at 0 - which means you ! 132: * may not sleep between calling this and putting something into ! 133: * the entry, as someone else might have used it while you slept. ! 134: */ ! 135: static struct buffer_head * add_entry(struct m_inode * dir, ! 136: const char * name, int namelen, struct dir_entry ** res_dir) ! 137: { ! 138: int block,i; ! 139: struct buffer_head * bh; ! 140: struct dir_entry * de; ! 141: ! 142: *res_dir = NULL; ! 143: #ifdef NO_TRUNCATE ! 144: if (namelen > NAME_LEN) ! 145: return NULL; ! 146: #else ! 147: if (namelen > NAME_LEN) ! 148: namelen = NAME_LEN; ! 149: #endif ! 150: if (!namelen) ! 151: return NULL; ! 152: if (!(block = dir->i_zone[0])) ! 153: return NULL; ! 154: if (!(bh = bread(dir->i_dev,block))) ! 155: return NULL; ! 156: i = 0; ! 157: de = (struct dir_entry *) bh->b_data; ! 158: while (1) { ! 159: if ((char *)de >= BLOCK_SIZE+bh->b_data) { ! 160: brelse(bh); ! 161: bh = NULL; ! 162: block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); ! 163: if (!block) ! 164: return NULL; ! 165: if (!(bh = bread(dir->i_dev,block))) { ! 166: i += DIR_ENTRIES_PER_BLOCK; ! 167: continue; ! 168: } ! 169: de = (struct dir_entry *) bh->b_data; ! 170: } ! 171: if (i*sizeof(struct dir_entry) >= dir->i_size) { ! 172: de->inode=0; ! 173: dir->i_size = (i+1)*sizeof(struct dir_entry); ! 174: dir->i_dirt = 1; ! 175: dir->i_ctime = CURRENT_TIME; ! 176: } ! 177: if (!de->inode) { ! 178: dir->i_mtime = CURRENT_TIME; ! 179: for (i=0; i < NAME_LEN ; i++) ! 180: de->name[i]=(i<namelen)?get_fs_byte(name+i):0; ! 181: bh->b_dirt = 1; ! 182: *res_dir = de; ! 183: return bh; ! 184: } ! 185: de++; ! 186: i++; ! 187: } ! 188: brelse(bh); ! 189: return NULL; ! 190: } ! 191: ! 192: /* ! 193: * get_dir() ! 194: * ! 195: * Getdir traverses the pathname until it hits the topmost directory. ! 196: * It returns NULL on failure. ! 197: */ ! 198: static struct m_inode * get_dir(const char * pathname) ! 199: { ! 200: char c; ! 201: const char * thisname; ! 202: struct m_inode * inode; ! 203: struct buffer_head * bh; ! 204: int namelen,inr,idev; ! 205: struct dir_entry * de; ! 206: ! 207: if (!current->root || !current->root->i_count) ! 208: panic("No root inode"); ! 209: if (!current->pwd || !current->pwd->i_count) ! 210: panic("No cwd inode"); ! 211: if ((c=get_fs_byte(pathname))=='/') { ! 212: inode = current->root; ! 213: pathname++; ! 214: } else if (c) ! 215: inode = current->pwd; ! 216: else ! 217: return NULL; /* empty name is bad */ ! 218: inode->i_count++; ! 219: while (1) { ! 220: thisname = pathname; ! 221: if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { ! 222: iput(inode); ! 223: return NULL; ! 224: } ! 225: for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) ! 226: /* nothing */ ; ! 227: if (!c) ! 228: return inode; ! 229: if (!(bh = find_entry(inode,thisname,namelen,&de))) { ! 230: iput(inode); ! 231: return NULL; ! 232: } ! 233: inr = de->inode; ! 234: idev = inode->i_dev; ! 235: brelse(bh); ! 236: iput(inode); ! 237: if (!(inode = iget(idev,inr))) ! 238: return NULL; ! 239: } ! 240: } ! 241: ! 242: /* ! 243: * dir_namei() ! 244: * ! 245: * dir_namei() returns the inode of the directory of the ! 246: * specified name, and the name within that directory. ! 247: */ ! 248: static struct m_inode * dir_namei(const char * pathname, ! 249: int * namelen, const char ** name) ! 250: { ! 251: char c; ! 252: const char * basename; ! 253: struct m_inode * dir; ! 254: ! 255: if (!(dir = get_dir(pathname))) ! 256: return NULL; ! 257: basename = pathname; ! 258: while (c=get_fs_byte(pathname++)) ! 259: if (c=='/') ! 260: basename=pathname; ! 261: *namelen = pathname-basename-1; ! 262: *name = basename; ! 263: return dir; ! 264: } ! 265: ! 266: /* ! 267: * namei() ! 268: * ! 269: * is used by most simple commands to get the inode of a specified name. ! 270: * Open, link etc use their own routines, but this is enough for things ! 271: * like 'chmod' etc. ! 272: */ ! 273: struct m_inode * namei(const char * pathname) ! 274: { ! 275: const char * basename; ! 276: int inr,dev,namelen; ! 277: struct m_inode * dir; ! 278: struct buffer_head * bh; ! 279: struct dir_entry * de; ! 280: ! 281: if (!(dir = dir_namei(pathname,&namelen,&basename))) ! 282: return NULL; ! 283: if (!namelen) /* special case: '/usr/' etc */ ! 284: return dir; ! 285: bh = find_entry(dir,basename,namelen,&de); ! 286: if (!bh) { ! 287: iput(dir); ! 288: return NULL; ! 289: } ! 290: inr = de->inode; ! 291: dev = dir->i_dev; ! 292: brelse(bh); ! 293: iput(dir); ! 294: dir=iget(dev,inr); ! 295: if (dir) { ! 296: dir->i_atime=CURRENT_TIME; ! 297: dir->i_dirt=1; ! 298: } ! 299: return dir; ! 300: } ! 301: ! 302: /* ! 303: * open_namei() ! 304: * ! 305: * namei for open - this is in fact almost the whole open-routine. ! 306: */ ! 307: int open_namei(const char * pathname, int flag, int mode, ! 308: struct m_inode ** res_inode) ! 309: { ! 310: const char * basename; ! 311: int inr,dev,namelen; ! 312: struct m_inode * dir, *inode; ! 313: struct buffer_head * bh; ! 314: struct dir_entry * de; ! 315: ! 316: if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) ! 317: flag |= O_WRONLY; ! 318: mode &= 0777 & ~current->umask; ! 319: mode |= I_REGULAR; ! 320: if (!(dir = dir_namei(pathname,&namelen,&basename))) ! 321: return -ENOENT; ! 322: if (!namelen) { /* special case: '/usr/' etc */ ! 323: if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { ! 324: *res_inode=dir; ! 325: return 0; ! 326: } ! 327: iput(dir); ! 328: return -EISDIR; ! 329: } ! 330: bh = find_entry(dir,basename,namelen,&de); ! 331: if (!bh) { ! 332: if (!(flag & O_CREAT)) { ! 333: iput(dir); ! 334: return -ENOENT; ! 335: } ! 336: if (!permission(dir,MAY_WRITE)) { ! 337: iput(dir); ! 338: return -EACCES; ! 339: } ! 340: inode = new_inode(dir->i_dev); ! 341: if (!inode) { ! 342: iput(dir); ! 343: return -ENOSPC; ! 344: } ! 345: inode->i_mode = mode; ! 346: inode->i_dirt = 1; ! 347: bh = add_entry(dir,basename,namelen,&de); ! 348: if (!bh) { ! 349: inode->i_nlinks--; ! 350: iput(inode); ! 351: iput(dir); ! 352: return -ENOSPC; ! 353: } ! 354: de->inode = inode->i_num; ! 355: bh->b_dirt = 1; ! 356: brelse(bh); ! 357: iput(dir); ! 358: *res_inode = inode; ! 359: return 0; ! 360: } ! 361: inr = de->inode; ! 362: dev = dir->i_dev; ! 363: brelse(bh); ! 364: iput(dir); ! 365: if (flag & O_EXCL) ! 366: return -EEXIST; ! 367: if (!(inode=iget(dev,inr))) ! 368: return -EACCES; ! 369: if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || ! 370: permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) { ! 371: iput(inode); ! 372: return -EPERM; ! 373: } ! 374: inode->i_atime = CURRENT_TIME; ! 375: if (flag & O_TRUNC) ! 376: truncate(inode); ! 377: *res_inode = inode; ! 378: return 0; ! 379: } ! 380: ! 381: int sys_mkdir(const char * pathname, int mode) ! 382: { ! 383: const char * basename; ! 384: int namelen; ! 385: struct m_inode * dir, * inode; ! 386: struct buffer_head * bh, *dir_block; ! 387: struct dir_entry * de; ! 388: ! 389: if (current->euid && current->uid) ! 390: return -EPERM; ! 391: if (!(dir = dir_namei(pathname,&namelen,&basename))) ! 392: return -ENOENT; ! 393: if (!namelen) { ! 394: iput(dir); ! 395: return -ENOENT; ! 396: } ! 397: if (!permission(dir,MAY_WRITE)) { ! 398: iput(dir); ! 399: return -EPERM; ! 400: } ! 401: bh = find_entry(dir,basename,namelen,&de); ! 402: if (bh) { ! 403: brelse(bh); ! 404: iput(dir); ! 405: return -EEXIST; ! 406: } ! 407: inode = new_inode(dir->i_dev); ! 408: if (!inode) { ! 409: iput(dir); ! 410: return -ENOSPC; ! 411: } ! 412: inode->i_size = 32; ! 413: inode->i_dirt = 1; ! 414: inode->i_mtime = inode->i_atime = CURRENT_TIME; ! 415: if (!(inode->i_zone[0]=new_block(inode->i_dev))) { ! 416: iput(dir); ! 417: inode->i_nlinks--; ! 418: iput(inode); ! 419: return -ENOSPC; ! 420: } ! 421: inode->i_dirt = 1; ! 422: if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { ! 423: iput(dir); ! 424: free_block(inode->i_dev,inode->i_zone[0]); ! 425: inode->i_nlinks--; ! 426: iput(inode); ! 427: return -ERROR; ! 428: } ! 429: de = (struct dir_entry *) dir_block->b_data; ! 430: de->inode=inode->i_num; ! 431: strcpy(de->name,"."); ! 432: de++; ! 433: de->inode = dir->i_num; ! 434: strcpy(de->name,".."); ! 435: inode->i_nlinks = 2; ! 436: dir_block->b_dirt = 1; ! 437: brelse(dir_block); ! 438: inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); ! 439: inode->i_dirt = 1; ! 440: bh = add_entry(dir,basename,namelen,&de); ! 441: if (!bh) { ! 442: iput(dir); ! 443: free_block(inode->i_dev,inode->i_zone[0]); ! 444: inode->i_nlinks=0; ! 445: iput(inode); ! 446: return -ENOSPC; ! 447: } ! 448: de->inode = inode->i_num; ! 449: bh->b_dirt = 1; ! 450: dir->i_nlinks++; ! 451: dir->i_dirt = 1; ! 452: iput(dir); ! 453: iput(inode); ! 454: brelse(bh); ! 455: return 0; ! 456: } ! 457: ! 458: /* ! 459: * routine to check that the specified directory is empty (for rmdir) ! 460: */ ! 461: static int empty_dir(struct m_inode * inode) ! 462: { ! 463: int nr,block; ! 464: int len; ! 465: struct buffer_head * bh; ! 466: struct dir_entry * de; ! 467: ! 468: len = inode->i_size / sizeof (struct dir_entry); ! 469: if (len<2 || !inode->i_zone[0] || ! 470: !(bh=bread(inode->i_dev,inode->i_zone[0]))) { ! 471: printk("warning - bad directory on dev %04x\n",inode->i_dev); ! 472: return 0; ! 473: } ! 474: de = (struct dir_entry *) bh->b_data; ! 475: if (de[0].inode != inode->i_num || !de[1].inode || ! 476: strcmp(".",de[0].name) || strcmp("..",de[1].name)) { ! 477: printk("warning - bad directory on dev %04x\n",inode->i_dev); ! 478: return 0; ! 479: } ! 480: nr = 2; ! 481: de += 2; ! 482: while (nr<len) { ! 483: if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { ! 484: brelse(bh); ! 485: block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); ! 486: if (!block) { ! 487: nr += DIR_ENTRIES_PER_BLOCK; ! 488: continue; ! 489: } ! 490: if (!(bh=bread(inode->i_dev,block))) ! 491: return 0; ! 492: de = (struct dir_entry *) bh->b_data; ! 493: } ! 494: if (de->inode) { ! 495: brelse(bh); ! 496: return 0; ! 497: } ! 498: de++; ! 499: nr++; ! 500: } ! 501: brelse(bh); ! 502: return 1; ! 503: } ! 504: ! 505: int sys_rmdir(const char * name) ! 506: { ! 507: const char * basename; ! 508: int namelen; ! 509: struct m_inode * dir, * inode; ! 510: struct buffer_head * bh; ! 511: struct dir_entry * de; ! 512: ! 513: if (current->euid && current->uid) ! 514: return -EPERM; ! 515: if (!(dir = dir_namei(name,&namelen,&basename))) ! 516: return -ENOENT; ! 517: if (!namelen) { ! 518: iput(dir); ! 519: return -ENOENT; ! 520: } ! 521: bh = find_entry(dir,basename,namelen,&de); ! 522: if (!bh) { ! 523: iput(dir); ! 524: return -ENOENT; ! 525: } ! 526: if (!permission(dir,MAY_WRITE)) { ! 527: iput(dir); ! 528: brelse(bh); ! 529: return -EPERM; ! 530: } ! 531: if (!(inode = iget(dir->i_dev, de->inode))) { ! 532: iput(dir); ! 533: brelse(bh); ! 534: return -EPERM; ! 535: } ! 536: if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ ! 537: iput(inode); ! 538: iput(dir); ! 539: brelse(bh); ! 540: return -EPERM; ! 541: } ! 542: if (!S_ISDIR(inode->i_mode)) { ! 543: iput(inode); ! 544: iput(dir); ! 545: brelse(bh); ! 546: return -ENOTDIR; ! 547: } ! 548: if (!empty_dir(inode)) { ! 549: iput(inode); ! 550: iput(dir); ! 551: brelse(bh); ! 552: return -ENOTEMPTY; ! 553: } ! 554: if (inode->i_nlinks != 2) ! 555: printk("empty directory has nlink!=2 (%d)",inode->i_nlinks); ! 556: de->inode = 0; ! 557: bh->b_dirt = 1; ! 558: brelse(bh); ! 559: inode->i_nlinks=0; ! 560: inode->i_dirt=1; ! 561: dir->i_nlinks--; ! 562: dir->i_ctime = dir->i_mtime = CURRENT_TIME; ! 563: dir->i_dirt=1; ! 564: iput(dir); ! 565: iput(inode); ! 566: return 0; ! 567: } ! 568: ! 569: int sys_unlink(const char * name) ! 570: { ! 571: const char * basename; ! 572: int namelen; ! 573: struct m_inode * dir, * inode; ! 574: struct buffer_head * bh; ! 575: struct dir_entry * de; ! 576: ! 577: if (!(dir = dir_namei(name,&namelen,&basename))) ! 578: return -ENOENT; ! 579: if (!namelen) { ! 580: iput(dir); ! 581: return -ENOENT; ! 582: } ! 583: if (!permission(dir,MAY_WRITE)) { ! 584: iput(dir); ! 585: return -EPERM; ! 586: } ! 587: bh = find_entry(dir,basename,namelen,&de); ! 588: if (!bh) { ! 589: iput(dir); ! 590: return -ENOENT; ! 591: } ! 592: inode = iget(dir->i_dev, de->inode); ! 593: if (!inode) { ! 594: printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode); ! 595: iput(dir); ! 596: brelse(bh); ! 597: return -ENOENT; ! 598: } ! 599: if (!S_ISREG(inode->i_mode)) { ! 600: iput(inode); ! 601: iput(dir); ! 602: brelse(bh); ! 603: return -EPERM; ! 604: } ! 605: if (!inode->i_nlinks) { ! 606: printk("Deleting nonexistent file (%04x:%d), %d\n", ! 607: inode->i_dev,inode->i_num,inode->i_nlinks); ! 608: inode->i_nlinks=1; ! 609: } ! 610: de->inode = 0; ! 611: bh->b_dirt = 1; ! 612: brelse(bh); ! 613: inode->i_nlinks--; ! 614: inode->i_dirt = 1; ! 615: inode->i_ctime = CURRENT_TIME; ! 616: iput(inode); ! 617: iput(dir); ! 618: return 0; ! 619: } ! 620: ! 621: int sys_link(const char * oldname, const char * newname) ! 622: { ! 623: struct dir_entry * de; ! 624: struct m_inode * oldinode, * dir; ! 625: struct buffer_head * bh; ! 626: const char * basename; ! 627: int namelen; ! 628: ! 629: oldinode=namei(oldname); ! 630: if (!oldinode) ! 631: return -ENOENT; ! 632: if (!S_ISREG(oldinode->i_mode)) { ! 633: iput(oldinode); ! 634: return -EPERM; ! 635: } ! 636: dir = dir_namei(newname,&namelen,&basename); ! 637: if (!dir) { ! 638: iput(oldinode); ! 639: return -EACCES; ! 640: } ! 641: if (!namelen) { ! 642: iput(oldinode); ! 643: iput(dir); ! 644: return -EPERM; ! 645: } ! 646: if (dir->i_dev != oldinode->i_dev) { ! 647: iput(dir); ! 648: iput(oldinode); ! 649: return -EXDEV; ! 650: } ! 651: if (!permission(dir,MAY_WRITE)) { ! 652: iput(dir); ! 653: iput(oldinode); ! 654: return -EACCES; ! 655: } ! 656: bh = find_entry(dir,basename,namelen,&de); ! 657: if (bh) { ! 658: brelse(bh); ! 659: iput(dir); ! 660: iput(oldinode); ! 661: return -EEXIST; ! 662: } ! 663: bh = add_entry(dir,basename,namelen,&de); ! 664: if (!bh) { ! 665: iput(dir); ! 666: iput(oldinode); ! 667: return -ENOSPC; ! 668: } ! 669: de->inode = oldinode->i_num; ! 670: bh->b_dirt = 1; ! 671: brelse(bh); ! 672: iput(dir); ! 673: oldinode->i_nlinks++; ! 674: oldinode->i_ctime = CURRENT_TIME; ! 675: oldinode->i_dirt = 1; ! 676: iput(oldinode); ! 677: return 0; ! 678: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.