|
|
1.1 ! root 1: /* ! 2: * linux/fs/ext/namei.c ! 3: * ! 4: * (C) 1992 Remy Card ([email protected]) ! 5: * ! 6: * from ! 7: * ! 8: * linux/fs/minix/namei.c ! 9: * ! 10: * (C) 1991 Linus Torvalds ! 11: */ ! 12: ! 13: #include <linux/sched.h> ! 14: #include <linux/ext_fs.h> ! 15: #include <linux/kernel.h> ! 16: #include <linux/string.h> ! 17: #include <linux/stat.h> ! 18: #include <linux/fcntl.h> ! 19: #include <asm/segment.h> ! 20: ! 21: #include <errno.h> ! 22: #include <const.h> ! 23: ! 24: /* ! 25: * comment out this line if you want names > EXT_NAME_LEN chars to be ! 26: * truncated. Else they will be disallowed. ! 27: */ ! 28: /* #define NO_TRUNCATE */ ! 29: ! 30: /* ! 31: * EXT_DIR_PAD defines the directory entries boundaries ! 32: * ! 33: * NOTE: It must be a power of 2 and must be greater or equal than 8 ! 34: * because a directory entry needs 8 bytes for its fixed part ! 35: * (4 bytes for the inode, 2 bytes for the entry length and 2 bytes ! 36: * for the name length) ! 37: */ ! 38: #define EXT_DIR_PAD 8 ! 39: ! 40: /* ! 41: * ! 42: * EXT_DIR_MIN_SIZE is the minimal size of a directory entry ! 43: * ! 44: * During allocations, a directory entry is split into 2 ones ! 45: * *ONLY* if the size of the unused part is greater than or ! 46: * equal to EXT_DIR_MIN_SIZE ! 47: */ ! 48: #define EXT_DIR_MIN_SIZE 12 ! 49: ! 50: /* ! 51: * ok, we cannot use strncmp, as the name is not in our data space. ! 52: * Thus we'll have to use ext_match. No big problem. Match also makes ! 53: * some sanity tests. ! 54: * ! 55: * NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure. ! 56: */ ! 57: static int ext_match(int len,const char * name,struct ext_dir_entry * de) ! 58: { ! 59: register int same __asm__("ax"); ! 60: ! 61: if (!de || !de->inode || len > EXT_NAME_LEN) ! 62: return 0; ! 63: /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ ! 64: if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) ! 65: return 1; ! 66: /* if (len < EXT_NAME_LEN && de->name[len]) ! 67: return 0; */ ! 68: if (len < EXT_NAME_LEN && len != de->name_len) ! 69: return 0; ! 70: __asm__("cld\n\t" ! 71: "fs ; repe ; cmpsb\n\t" ! 72: "setz %%al" ! 73: :"=a" (same) ! 74: :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) ! 75: :"cx","di","si"); ! 76: return same; ! 77: } ! 78: ! 79: /* ! 80: * ext_find_entry() ! 81: * ! 82: * finds an entry in the specified directory with the wanted name. It ! 83: * returns the cache buffer in which the entry was found, and the entry ! 84: * itself (as a parameter - res_dir). It does NOT read the inode of the ! 85: * entry - you'll have to do that yourself if you want to. ! 86: * ! 87: * addition for the ext file system : this function returns the previous ! 88: * and next directory entries in the parameters prev_dir and next_dir ! 89: */ ! 90: static struct buffer_head * ext_find_entry(struct inode * dir, ! 91: const char * name, int namelen, struct ext_dir_entry ** res_dir, ! 92: struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir) ! 93: { ! 94: /* int entries; */ ! 95: int block /* ,i */; ! 96: long offset; ! 97: struct buffer_head * bh; ! 98: struct ext_dir_entry * de; ! 99: ! 100: *res_dir = NULL; ! 101: if (!dir) ! 102: return NULL; ! 103: #ifdef NO_TRUNCATE ! 104: if (namelen > EXT_NAME_LEN) ! 105: return NULL; ! 106: #else ! 107: if (namelen > EXT_NAME_LEN) ! 108: namelen = EXT_NAME_LEN; ! 109: #endif ! 110: /* entries = dir->i_size / (sizeof (struct ext_dir_entry)); */ ! 111: if (!(block = dir->i_data[0])) ! 112: return NULL; ! 113: if (!(bh = bread(dir->i_dev,block))) ! 114: return NULL; ! 115: if (prev_dir) ! 116: *prev_dir = NULL; ! 117: if (next_dir) ! 118: *next_dir = NULL; ! 119: /* i = 0; */ ! 120: offset = 0; ! 121: de = (struct ext_dir_entry *) bh->b_data; ! 122: while (offset < dir->i_size) { ! 123: if ((char *)de >= BLOCK_SIZE+bh->b_data) { ! 124: brelse(bh); ! 125: bh = NULL; ! 126: if (!(block = ext_bmap(dir,offset>>BLOCK_SIZE_BITS)) || ! 127: !(bh = bread(dir->i_dev,block))) { ! 128: /* i += EXT_DIR_ENTRIES_PER_BLOCK; */ ! 129: /* offset += BLOCK_SIZE; */ ! 130: continue; ! 131: } ! 132: de = (struct ext_dir_entry *) bh->b_data; ! 133: if (prev_dir) ! 134: *prev_dir = NULL; ! 135: } ! 136: if (ext_match(namelen,name,de)) { ! 137: *res_dir = de; ! 138: if (next_dir) ! 139: if (offset + de->rec_len < dir->i_size) ! 140: *next_dir = (struct ext_dir_entry *) ! 141: ((char *) de + de->rec_len); ! 142: else ! 143: *next_dir = NULL; ! 144: return bh; ! 145: } ! 146: offset += de->rec_len; ! 147: if (prev_dir) ! 148: *prev_dir = de; ! 149: de = (struct ext_dir_entry *) ((char *) de + de->rec_len); ! 150: /* i++; */ ! 151: } ! 152: brelse(bh); ! 153: return NULL; ! 154: } ! 155: ! 156: int ext_lookup(struct inode * dir,const char * name, int len, ! 157: struct inode ** result) ! 158: { ! 159: int ino; ! 160: struct ext_dir_entry * de; ! 161: struct buffer_head * bh; ! 162: ! 163: *result = NULL; ! 164: if (!dir) ! 165: return -ENOENT; ! 166: if (!S_ISDIR(dir->i_mode)) { ! 167: iput(dir); ! 168: return -ENOENT; ! 169: } ! 170: if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) { ! 171: iput(dir); ! 172: return -ENOENT; ! 173: } ! 174: ino = de->inode; ! 175: brelse(bh); ! 176: if (!(*result = iget(dir->i_dev,ino))) { ! 177: iput(dir); ! 178: return -EACCES; ! 179: } ! 180: iput(dir); ! 181: return 0; ! 182: } ! 183: ! 184: /* ! 185: * ext_add_entry() ! 186: * ! 187: * adds a file entry to the specified directory, using the same ! 188: * semantics as ext_find_entry(). It returns NULL if it failed. ! 189: * ! 190: * NOTE!! The inode part of 'de' is left at 0 - which means you ! 191: * may not sleep between calling this and putting something into ! 192: * the entry, as someone else might have used it while you slept. ! 193: */ ! 194: static struct buffer_head * ext_add_entry(struct inode * dir, ! 195: const char * name, int namelen, struct ext_dir_entry ** res_dir) ! 196: { ! 197: int block,i; ! 198: long offset; ! 199: unsigned short rec_len; ! 200: struct buffer_head * bh; ! 201: struct ext_dir_entry * de, * de1; ! 202: ! 203: *res_dir = NULL; ! 204: if (!dir) ! 205: return NULL; ! 206: #ifdef NO_TRUNCATE ! 207: if (namelen > EXT_NAME_LEN) ! 208: return NULL; ! 209: #else ! 210: if (namelen > EXT_NAME_LEN) ! 211: namelen = EXT_NAME_LEN; ! 212: #endif ! 213: if (!namelen) ! 214: return NULL; ! 215: if (!(block = dir->i_data[0])) ! 216: return NULL; ! 217: if (!(bh = bread(dir->i_dev,block))) ! 218: return NULL; ! 219: rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD; ! 220: /* i = 0; */ ! 221: offset = 0; ! 222: de = (struct ext_dir_entry *) bh->b_data; ! 223: while (1) { ! 224: if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) { ! 225: #ifdef EXTFS_DEBUG ! 226: printk ("ext_add_entry: skipping to next block\n"); ! 227: #endif ! 228: brelse(bh); ! 229: bh = NULL; ! 230: block = ext_create_block(dir,offset>>BLOCK_SIZE_BITS); ! 231: if (!block) ! 232: return NULL; ! 233: if (!(bh = bread(dir->i_dev,block))) { ! 234: /* i += EXT_DIR_ENTRIES_PER_BLOCK; */ ! 235: offset += BLOCK_SIZE; ! 236: continue; ! 237: } ! 238: de = (struct ext_dir_entry *) bh->b_data; ! 239: } ! 240: if (offset >= dir->i_size) { ! 241: /* Check that the directory entry fits in the block */ ! 242: if (offset % BLOCK_SIZE == 0 ! 243: || (BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) { ! 244: if ((offset % BLOCK_SIZE) != 0) { ! 245: /* If the entry does not fit in the ! 246: block, the remainder of the block ! 247: becomes an unused entry */ ! 248: de->inode = 0; ! 249: de->rec_len = BLOCK_SIZE ! 250: - (offset & (BLOCK_SIZE - 1)); ! 251: de->name_len = 0; ! 252: offset += de->rec_len; ! 253: dir->i_size += de->rec_len; ! 254: dir->i_dirt = 1; ! 255: dir->i_ctime = CURRENT_TIME; ! 256: bh->b_dirt = 1; ! 257: } ! 258: brelse (bh); ! 259: bh = NULL; ! 260: block = ext_create_block (dir,offset>>BLOCK_SIZE_BITS); ! 261: #ifdef EXTFS_DEBUG ! 262: printk ("ext_add_entry : creating next block\n"); ! 263: #endif ! 264: if (!block) ! 265: return NULL; ! 266: if (!(bh = bread(dir->i_dev,block))) ! 267: return NULL; /* Other thing to do ??? */ ! 268: de = (struct ext_dir_entry *) bh->b_data; ! 269: } ! 270: /* Allocate the entry */ ! 271: de->inode=0; ! 272: de->rec_len = rec_len; ! 273: /* dir->i_size = (i+1)*sizeof(struct ext_dir_entry); */ ! 274: dir->i_size += de->rec_len; ! 275: dir->i_dirt = 1; ! 276: dir->i_ctime = CURRENT_TIME; ! 277: } ! 278: if (!de->inode && de->rec_len >= rec_len) { ! 279: if (de->rec_len > rec_len ! 280: && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) { ! 281: /* The found entry is too big : it is split ! 282: into 2 ones : ! 283: - the 1st one will be used to hold the name, ! 284: - the 2nd one is unused */ ! 285: de1 = (struct ext_dir_entry *) ((char *) de + rec_len); ! 286: de1->inode = 0; ! 287: de1->rec_len = de->rec_len - rec_len; ! 288: de1->name_len = 0; ! 289: de->rec_len = rec_len; ! 290: } ! 291: dir->i_mtime = CURRENT_TIME; ! 292: de->name_len = namelen; ! 293: for (i=0; i < namelen ; i++) ! 294: de->name[i]=/*(i<namelen)?*/get_fs_byte(name+i)/*:0*/; ! 295: bh->b_dirt = 1; ! 296: *res_dir = de; ! 297: return bh; ! 298: } ! 299: offset += de->rec_len; ! 300: de = (struct ext_dir_entry *) ((char *) de + de->rec_len); ! 301: } ! 302: brelse(bh); ! 303: return NULL; ! 304: } ! 305: ! 306: int ext_create(struct inode * dir,const char * name, int len, int mode, ! 307: struct inode ** result) ! 308: { ! 309: struct inode * inode; ! 310: struct buffer_head * bh; ! 311: struct ext_dir_entry * de; ! 312: ! 313: *result = NULL; ! 314: if (!dir) ! 315: return -ENOENT; ! 316: inode = ext_new_inode(dir->i_dev); ! 317: if (!inode) { ! 318: iput(dir); ! 319: return -ENOSPC; ! 320: } ! 321: inode->i_op = &ext_file_inode_operations; ! 322: inode->i_mode = mode; ! 323: inode->i_dirt = 1; ! 324: bh = ext_add_entry(dir,name,len,&de); ! 325: if (!bh) { ! 326: inode->i_nlink--; ! 327: inode->i_dirt = 1; ! 328: iput(inode); ! 329: iput(dir); ! 330: return -ENOSPC; ! 331: } ! 332: de->inode = inode->i_ino; ! 333: bh->b_dirt = 1; ! 334: brelse(bh); ! 335: iput(dir); ! 336: *result = inode; ! 337: return 0; ! 338: } ! 339: ! 340: int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) ! 341: { ! 342: struct inode * inode; ! 343: struct buffer_head * bh; ! 344: struct ext_dir_entry * de; ! 345: ! 346: if (!dir) ! 347: return -ENOENT; ! 348: bh = ext_find_entry(dir,name,len,&de,NULL,NULL); ! 349: if (bh) { ! 350: brelse(bh); ! 351: iput(dir); ! 352: return -EEXIST; ! 353: } ! 354: inode = ext_new_inode(dir->i_dev); ! 355: if (!inode) { ! 356: iput(dir); ! 357: return -ENOSPC; ! 358: } ! 359: inode->i_uid = current->euid; ! 360: inode->i_mode = mode; ! 361: inode->i_op = NULL; ! 362: if (S_ISREG(inode->i_mode)) ! 363: inode->i_op = &ext_file_inode_operations; ! 364: else if (S_ISDIR(inode->i_mode)) ! 365: inode->i_op = &ext_dir_inode_operations; ! 366: else if (S_ISLNK(inode->i_mode)) ! 367: inode->i_op = &ext_symlink_inode_operations; ! 368: else if (S_ISCHR(inode->i_mode)) ! 369: inode->i_op = &ext_chrdev_inode_operations; ! 370: else if (S_ISBLK(inode->i_mode)) ! 371: inode->i_op = &ext_blkdev_inode_operations; ! 372: else if (S_ISFIFO(inode->i_mode)) { ! 373: inode->i_op = &ext_fifo_inode_operations; ! 374: inode->i_size = 0; ! 375: inode->i_pipe = 1; ! 376: PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; ! 377: PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; ! 378: } ! 379: if (S_ISBLK(mode) || S_ISCHR(mode)) ! 380: inode->i_rdev = rdev; ! 381: inode->i_mtime = inode->i_atime = CURRENT_TIME; ! 382: inode->i_dirt = 1; ! 383: bh = ext_add_entry(dir,name,len,&de); ! 384: if (!bh) { ! 385: inode->i_nlink--; ! 386: inode->i_dirt = 1; ! 387: iput(inode); ! 388: iput(dir); ! 389: return -ENOSPC; ! 390: } ! 391: de->inode = inode->i_ino; ! 392: bh->b_dirt = 1; ! 393: brelse(bh); ! 394: iput(dir); ! 395: iput(inode); ! 396: return 0; ! 397: } ! 398: ! 399: int ext_mkdir(struct inode * dir, const char * name, int len, int mode) ! 400: { ! 401: struct inode * inode; ! 402: struct buffer_head * bh, *dir_block; ! 403: struct ext_dir_entry * de; ! 404: ! 405: bh = ext_find_entry(dir,name,len,&de,NULL,NULL); ! 406: if (bh) { ! 407: brelse(bh); ! 408: iput(dir); ! 409: return -EEXIST; ! 410: } ! 411: inode = ext_new_inode(dir->i_dev); ! 412: if (!inode) { ! 413: iput(dir); ! 414: return -ENOSPC; ! 415: } ! 416: inode->i_op = &ext_dir_inode_operations; ! 417: inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".." ! 418: - 4 bytes for the inode number, ! 419: - 2 bytes for the record length ! 420: - 2 bytes for the name length ! 421: - 8 bytes for the name */ ! 422: inode->i_mtime = inode->i_atime = CURRENT_TIME; ! 423: if (!(inode->i_data[0] = ext_new_block(inode->i_dev))) { ! 424: iput(dir); ! 425: inode->i_nlink--; ! 426: inode->i_dirt = 1; ! 427: iput(inode); ! 428: return -ENOSPC; ! 429: } ! 430: inode->i_dirt = 1; ! 431: if (!(dir_block = bread(inode->i_dev,inode->i_data[0]))) { ! 432: iput(dir); ! 433: inode->i_nlink--; ! 434: inode->i_dirt = 1; ! 435: iput(inode); ! 436: return -EIO; ! 437: } ! 438: de = (struct ext_dir_entry *) dir_block->b_data; ! 439: de->inode=inode->i_ino; ! 440: de->rec_len=16; ! 441: de->name_len=1; ! 442: strcpy(de->name,"."); ! 443: /* de++; */ ! 444: de = (struct ext_dir_entry *) ((char *) de + de->rec_len); ! 445: de->inode = dir->i_ino; ! 446: de->rec_len=16; ! 447: de->name_len=2; ! 448: strcpy(de->name,".."); ! 449: inode->i_nlink = 2; ! 450: dir_block->b_dirt = 1; ! 451: brelse(dir_block); ! 452: inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); ! 453: inode->i_dirt = 1; ! 454: bh = ext_add_entry(dir,name,len,&de); ! 455: if (!bh) { ! 456: iput(dir); ! 457: inode->i_nlink=0; ! 458: iput(inode); ! 459: return -ENOSPC; ! 460: } ! 461: de->inode = inode->i_ino; ! 462: bh->b_dirt = 1; ! 463: dir->i_nlink++; ! 464: dir->i_dirt = 1; ! 465: iput(dir); ! 466: iput(inode); ! 467: brelse(bh); ! 468: return 0; ! 469: } ! 470: ! 471: /* ! 472: * routine to check that the specified directory is empty (for rmdir) ! 473: */ ! 474: static int empty_dir(struct inode * inode) ! 475: { ! 476: int /* nr, */ block; ! 477: /* int len; */ ! 478: unsigned long offset; ! 479: struct buffer_head * bh; ! 480: struct ext_dir_entry * de, * de1; ! 481: ! 482: /* len = inode->i_size / sizeof (struct ext_dir_entry); */ ! 483: if (inode->i_size < 2 * 12 || !inode->i_data[0] || ! 484: !(bh=bread(inode->i_dev,inode->i_data[0]))) { ! 485: printk("warning - bad directory on dev %04x\n",inode->i_dev); ! 486: return 0; ! 487: } ! 488: de = (struct ext_dir_entry *) bh->b_data; ! 489: de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len); ! 490: if (de->inode != inode->i_ino || !de1->inode || ! 491: strcmp(".",de->name) || strcmp("..",de1->name)) { ! 492: printk("warning - bad directory on dev %04x\n",inode->i_dev); ! 493: return 0; ! 494: } ! 495: /* nr = 2; */ ! 496: offset = de->rec_len + de1->rec_len; ! 497: de = (struct ext_dir_entry *) ((char *) de1 + de1->rec_len); ! 498: while (offset < inode->i_size ) { ! 499: if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { ! 500: brelse(bh); ! 501: block = ext_bmap(inode, offset >> BLOCK_SIZE_BITS); ! 502: if (!block) { ! 503: offset += BLOCK_SIZE; ! 504: continue; ! 505: } ! 506: if (!(bh=bread(inode->i_dev,block))) ! 507: return 0; ! 508: de = (struct ext_dir_entry *) bh->b_data; ! 509: } ! 510: if (de->inode) { ! 511: brelse(bh); ! 512: return 0; ! 513: } ! 514: offset += de->rec_len; ! 515: de = (struct ext_dir_entry *) ((char *) de + de->rec_len); ! 516: } ! 517: brelse(bh); ! 518: return 1; ! 519: } ! 520: ! 521: static inline void ext_merge_entries (struct ext_dir_entry * de, ! 522: struct ext_dir_entry * pde, struct ext_dir_entry * nde) ! 523: { ! 524: if (! nde->inode) ! 525: de->rec_len += nde->rec_len; ! 526: if (! pde->inode) ! 527: pde->rec_len += de->rec_len; ! 528: } ! 529: ! 530: int ext_rmdir(struct inode * dir, const char * name, int len) ! 531: { ! 532: int retval; ! 533: struct inode * inode; ! 534: struct buffer_head * bh; ! 535: struct ext_dir_entry * de, * pde, * nde; ! 536: ! 537: inode = NULL; ! 538: bh = ext_find_entry(dir,name,len,&de,&pde,&nde); ! 539: retval = -ENOENT; ! 540: if (!bh) ! 541: goto end_rmdir; ! 542: retval = -EPERM; ! 543: if (!(inode = iget(dir->i_dev, de->inode))) ! 544: goto end_rmdir; ! 545: if ((dir->i_mode & S_ISVTX) && current->euid && ! 546: inode->i_uid != current->euid) ! 547: goto end_rmdir; ! 548: if (inode->i_dev != dir->i_dev) ! 549: goto end_rmdir; ! 550: if (inode == dir) /* we may not delete ".", but "../dir" is ok */ ! 551: goto end_rmdir; ! 552: if (!S_ISDIR(inode->i_mode)) { ! 553: retval = -ENOTDIR; ! 554: goto end_rmdir; ! 555: } ! 556: if (!empty_dir(inode)) { ! 557: retval = -ENOTEMPTY; ! 558: goto end_rmdir; ! 559: } ! 560: if (inode->i_count > 1) { ! 561: retval = -EBUSY; ! 562: goto end_rmdir; ! 563: } ! 564: if (inode->i_nlink != 2) ! 565: printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink); ! 566: de->inode = 0; ! 567: de->name_len = 0; ! 568: ext_merge_entries (de, pde, nde); ! 569: bh->b_dirt = 1; ! 570: inode->i_nlink=0; ! 571: inode->i_dirt=1; ! 572: dir->i_nlink--; ! 573: dir->i_ctime = dir->i_mtime = CURRENT_TIME; ! 574: dir->i_dirt=1; ! 575: retval = 0; ! 576: end_rmdir: ! 577: iput(dir); ! 578: iput(inode); ! 579: brelse(bh); ! 580: return retval; ! 581: } ! 582: ! 583: int ext_unlink(struct inode * dir, const char * name, int len) ! 584: { ! 585: int retval; ! 586: struct inode * inode; ! 587: struct buffer_head * bh; ! 588: struct ext_dir_entry * de, * pde, * nde; ! 589: ! 590: retval = -ENOENT; ! 591: inode = NULL; ! 592: bh = ext_find_entry(dir,name,len,&de,&pde,&nde); ! 593: if (!bh) ! 594: goto end_unlink; ! 595: if (!(inode = iget(dir->i_dev, de->inode))) ! 596: goto end_unlink; ! 597: retval = -EPERM; ! 598: if ((dir->i_mode & S_ISVTX) && !suser() && ! 599: current->euid != inode->i_uid && ! 600: current->euid != dir->i_uid) ! 601: goto end_unlink; ! 602: if (S_ISDIR(inode->i_mode)) ! 603: goto end_unlink; ! 604: if (!inode->i_nlink) { ! 605: printk("Deleting nonexistent file (%04x:%d), %d\n", ! 606: inode->i_dev,inode->i_ino,inode->i_nlink); ! 607: inode->i_nlink=1; ! 608: } ! 609: de->inode = 0; ! 610: de->name_len = 0; ! 611: ext_merge_entries (de, pde, nde); ! 612: bh->b_dirt = 1; ! 613: inode->i_nlink--; ! 614: inode->i_dirt = 1; ! 615: inode->i_ctime = CURRENT_TIME; ! 616: retval = 0; ! 617: end_unlink: ! 618: brelse(bh); ! 619: iput(inode); ! 620: iput(dir); ! 621: return retval; ! 622: } ! 623: ! 624: int ext_symlink(struct inode * dir, const char * name, int len, const char * symname) ! 625: { ! 626: struct ext_dir_entry * de; ! 627: struct inode * inode = NULL; ! 628: struct buffer_head * bh = NULL, * name_block = NULL; ! 629: int i; ! 630: char c; ! 631: ! 632: if (!(inode = ext_new_inode(dir->i_dev))) { ! 633: iput(dir); ! 634: return -ENOSPC; ! 635: } ! 636: inode->i_mode = S_IFLNK | 0777; ! 637: inode->i_op = &ext_symlink_inode_operations; ! 638: if (!(inode->i_data[0] = ext_new_block(inode->i_dev))) { ! 639: iput(dir); ! 640: inode->i_nlink--; ! 641: inode->i_dirt = 1; ! 642: iput(inode); ! 643: return -ENOSPC; ! 644: } ! 645: inode->i_dirt = 1; ! 646: if (!(name_block = bread(inode->i_dev,inode->i_data[0]))) { ! 647: iput(dir); ! 648: inode->i_nlink--; ! 649: inode->i_dirt = 1; ! 650: iput(inode); ! 651: return -EIO; ! 652: } ! 653: i = 0; ! 654: while (i < 1023 && (c=get_fs_byte(symname++))) ! 655: name_block->b_data[i++] = c; ! 656: name_block->b_data[i] = 0; ! 657: name_block->b_dirt = 1; ! 658: brelse(name_block); ! 659: inode->i_size = i; ! 660: inode->i_dirt = 1; ! 661: bh = ext_find_entry(dir,name,len,&de,NULL,NULL); ! 662: if (bh) { ! 663: inode->i_nlink--; ! 664: inode->i_dirt = 1; ! 665: iput(inode); ! 666: brelse(bh); ! 667: iput(dir); ! 668: return -EEXIST; ! 669: } ! 670: bh = ext_add_entry(dir,name,len,&de); ! 671: if (!bh) { ! 672: inode->i_nlink--; ! 673: inode->i_dirt = 1; ! 674: iput(inode); ! 675: iput(dir); ! 676: return -ENOSPC; ! 677: } ! 678: de->inode = inode->i_ino; ! 679: bh->b_dirt = 1; ! 680: brelse(bh); ! 681: iput(dir); ! 682: iput(inode); ! 683: return 0; ! 684: } ! 685: ! 686: int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len) ! 687: { ! 688: struct ext_dir_entry * de; ! 689: struct buffer_head * bh; ! 690: ! 691: if (S_ISDIR(oldinode->i_mode)) { ! 692: iput(oldinode); ! 693: iput(dir); ! 694: return -EPERM; ! 695: } ! 696: bh = ext_find_entry(dir,name,len,&de,NULL,NULL); ! 697: if (bh) { ! 698: brelse(bh); ! 699: iput(dir); ! 700: iput(oldinode); ! 701: return -EEXIST; ! 702: } ! 703: bh = ext_add_entry(dir,name,len,&de); ! 704: if (!bh) { ! 705: iput(dir); ! 706: iput(oldinode); ! 707: return -ENOSPC; ! 708: } ! 709: de->inode = oldinode->i_ino; ! 710: bh->b_dirt = 1; ! 711: brelse(bh); ! 712: iput(dir); ! 713: oldinode->i_nlink++; ! 714: oldinode->i_ctime = CURRENT_TIME; ! 715: oldinode->i_dirt = 1; ! 716: iput(oldinode); ! 717: return 0; ! 718: } ! 719: ! 720: static int subdir(struct inode * new, struct inode * old) ! 721: { ! 722: unsigned short fs; ! 723: int ino; ! 724: int result; ! 725: ! 726: __asm__("mov %%fs,%0":"=r" (fs)); ! 727: __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); ! 728: new->i_count++; ! 729: result = 0; ! 730: for (;;) { ! 731: if (new == old) { ! 732: result = 1; ! 733: break; ! 734: } ! 735: if (new->i_dev != old->i_dev) ! 736: break; ! 737: ino = new->i_ino; ! 738: if (ext_lookup(new,"..",2,&new)) ! 739: break; ! 740: if (new->i_ino == ino) ! 741: break; ! 742: } ! 743: iput(new); ! 744: __asm__("mov %0,%%fs"::"r" (fs)); ! 745: return result; ! 746: } ! 747: ! 748: #define PARENT_INO(buffer) \ ! 749: ((struct ext_dir_entry *) ((char *) buffer + \ ! 750: ((struct ext_dir_entry *) buffer)->rec_len))->inode ! 751: /* (((struct ext_dir_entry *) (buffer))[1].inode) */ ! 752: ! 753: #define PARENT_NAME(buffer) \ ! 754: ((struct ext_dir_entry *) ((char *) buffer + \ ! 755: ((struct ext_dir_entry *) buffer)->rec_len))->name ! 756: /* (((struct ext_dir_entry *) (buffer))[1].name) */ ! 757: ! 758: /* ! 759: * rename uses retrying to avoid race-conditions: at least they should be minimal. ! 760: * it tries to allocate all the blocks, then sanity-checks, and if the sanity- ! 761: * checks fail, it tries to restart itself again. Very practical - no changes ! 762: * are done until we know everything works ok.. and then all the changes can be ! 763: * done in one fell swoop when we have claimed all the buffers needed. ! 764: * ! 765: * Anybody can rename anything with this: the permission checks are left to the ! 766: * higher-level routines. ! 767: */ ! 768: static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_len, ! 769: struct inode * new_dir, const char * new_name, int new_len) ! 770: { ! 771: struct inode * old_inode, * new_inode; ! 772: struct buffer_head * old_bh, * new_bh, * dir_bh; ! 773: struct ext_dir_entry * old_de, * new_de, * pde, * nde; ! 774: int retval; ! 775: ! 776: goto start_up; ! 777: try_again: ! 778: brelse(old_bh); ! 779: brelse(new_bh); ! 780: brelse(dir_bh); ! 781: iput(old_inode); ! 782: iput(new_inode); ! 783: current->counter = 0; ! 784: schedule(); ! 785: start_up: ! 786: old_inode = new_inode = NULL; ! 787: old_bh = new_bh = dir_bh = NULL; ! 788: old_bh = ext_find_entry(old_dir,old_name,old_len,&old_de,&pde,&nde); ! 789: retval = -ENOENT; ! 790: if (!old_bh) ! 791: goto end_rename; ! 792: old_inode = iget(old_dir->i_dev, old_de->inode); ! 793: if (!old_inode) ! 794: goto end_rename; ! 795: if ((old_dir->i_mode & S_ISVTX) && ! 796: current->euid != old_inode->i_uid && ! 797: current->euid != old_dir->i_uid && !suser()) ! 798: goto end_rename; ! 799: new_bh = ext_find_entry(new_dir,new_name,new_len,&new_de,NULL,NULL); ! 800: if (new_bh) { ! 801: new_inode = iget(new_dir->i_dev, new_de->inode); ! 802: if (!new_inode) { ! 803: brelse(new_bh); ! 804: new_bh = NULL; ! 805: } ! 806: } ! 807: if (new_inode == old_inode) { ! 808: retval = 0; ! 809: goto end_rename; ! 810: } ! 811: if (S_ISDIR(old_inode->i_mode)) { ! 812: retval = -EEXIST; ! 813: if (new_bh) ! 814: goto end_rename; ! 815: retval = -EACCES; ! 816: if (!permission(old_inode, MAY_WRITE)) ! 817: goto end_rename; ! 818: retval = -EINVAL; ! 819: if (subdir(new_dir, old_inode)) ! 820: goto end_rename; ! 821: retval = -EIO; ! 822: if (!old_inode->i_data[0]) ! 823: goto end_rename; ! 824: if (!(dir_bh = bread(old_inode->i_dev, old_inode->i_data[0]))) ! 825: goto end_rename; ! 826: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) ! 827: goto end_rename; ! 828: } ! 829: if (!new_bh) ! 830: new_bh = ext_add_entry(new_dir,new_name,new_len,&new_de); ! 831: retval = -ENOSPC; ! 832: if (!new_bh) ! 833: goto end_rename; ! 834: /* sanity checking before doing the rename - avoid races */ ! 835: if (new_inode && (new_de->inode != new_inode->i_ino)) ! 836: goto try_again; ! 837: if (new_de->inode && !new_inode) ! 838: goto try_again; ! 839: if (old_de->inode != old_inode->i_ino) ! 840: goto try_again; ! 841: /* ok, that's it */ ! 842: old_de->inode = 0; ! 843: old_de->name_len = 0; ! 844: ext_merge_entries (old_de, pde, nde); ! 845: new_de->inode = old_inode->i_ino; ! 846: if (new_inode) { ! 847: new_inode->i_nlink--; ! 848: new_inode->i_dirt = 1; ! 849: } ! 850: old_bh->b_dirt = 1; ! 851: new_bh->b_dirt = 1; ! 852: if (dir_bh) { ! 853: PARENT_INO(dir_bh->b_data) = new_dir->i_ino; ! 854: dir_bh->b_dirt = 1; ! 855: old_dir->i_nlink--; ! 856: new_dir->i_nlink++; ! 857: old_dir->i_dirt = 1; ! 858: new_dir->i_dirt = 1; ! 859: } ! 860: retval = 0; ! 861: end_rename: ! 862: brelse(dir_bh); ! 863: brelse(old_bh); ! 864: brelse(new_bh); ! 865: iput(old_inode); ! 866: iput(new_inode); ! 867: iput(old_dir); ! 868: iput(new_dir); ! 869: return retval; ! 870: } ! 871: ! 872: /* ! 873: * Ok, rename also locks out other renames, as they can change the parent of ! 874: * a directory, and we don't want any races. Other races are checked for by ! 875: * "do_rename()", which restarts if there are inconsistencies. ! 876: * ! 877: * Note that there is no race between different filesystems: it's only within ! 878: * the same device that races occur: many renames can happen at once, as long ! 879: * as they are on different partitions. ! 880: */ ! 881: int ext_rename(struct inode * old_dir, const char * old_name, int old_len, ! 882: struct inode * new_dir, const char * new_name, int new_len) ! 883: { ! 884: static struct task_struct * wait = NULL; ! 885: static int lock = 0; ! 886: int result; ! 887: ! 888: while (lock) ! 889: sleep_on(&wait); ! 890: lock = 1; ! 891: result = do_ext_rename(old_dir, old_name, old_len, ! 892: new_dir, new_name, new_len); ! 893: lock = 0; ! 894: wake_up(&wait); ! 895: return result; ! 896: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.