1 /* This file contains the procedures that look up path names in the directory 2 * system and determine the inode number that goes with a given path name. 3 * 4 * The entry points into this file are 5 * eat_path: the 'main' routine of the path-to-inode conversion mechanism 6 * last_dir: find the final directory on a given path 7 * advance: parse one component of a path name 8 * search_dir: search a directory for a string and return its inode number 9 * 10 * Created (MFS based): 11 * February 2010 (Evgeniy Ivanov) 12 */ 13 14 #include "fs.h" 15 #include <assert.h> 16 #include <string.h> 17 #include <minix/endpoint.h> 18 #include <sys/stat.h> 19 #include <sys/param.h> 20 #include <sys/types.h> 21 #include "buf.h" 22 #include "inode.h" 23 #include "super.h" 24 #include <minix/vfsif.h> 25 #include <minix/libminixfs.h> 26 27 char dot1[2] = "."; /* used for search_dir to bypass the access */ 28 char dot2[3] = ".."; /* permissions for . and .. */ 29 30 static char *get_name(char *name, char string[NAME_MAX+1]); 31 static int ltraverse(struct inode *rip, char *suffix); 32 static int parse_path(ino_t dir_ino, ino_t root_ino, int flags, struct 33 inode **res_inop, size_t *offsetp, int *symlinkp); 34 35 /*===========================================================================* 36 * fs_lookup * 37 *===========================================================================*/ 38 int fs_lookup() 39 { 40 cp_grant_id_t grant; 41 int r, r1, flags, symlinks; 42 unsigned int len; 43 size_t offset = 0, path_size; 44 ino_t dir_ino, root_ino; 45 struct inode *rip; 46 47 grant = fs_m_in.m_vfs_fs_lookup.grant_path; 48 path_size = fs_m_in.m_vfs_fs_lookup.path_size; /* Size of the buffer */ 49 len = fs_m_in.m_vfs_fs_lookup.path_len; /* including terminating nul */ 50 dir_ino = fs_m_in.m_vfs_fs_lookup.dir_ino; 51 root_ino = fs_m_in.m_vfs_fs_lookup.root_ino; 52 flags = fs_m_in.m_vfs_fs_lookup.flags; 53 54 /* Check length. */ 55 if(len > sizeof(user_path)) return(E2BIG); /* too big for buffer */ 56 if(len == 0) return(EINVAL); /* too small */ 57 58 /* Copy the pathname and set up caller's user and group id */ 59 r = sys_safecopyfrom(VFS_PROC_NR, grant, /*offset*/ 0, 60 (vir_bytes) user_path, (size_t) len); 61 if(r != OK) return(r); 62 63 /* Verify this is a null-terminated path. */ 64 if(user_path[len - 1] != '\0') return(EINVAL); 65 66 memset(&credentials, 0, sizeof(credentials)); 67 if(!(flags & PATH_GET_UCRED)) { /* Do we have to copy uid/gid credentials? */ 68 caller_uid = fs_m_in.m_vfs_fs_lookup.uid; 69 caller_gid = fs_m_in.m_vfs_fs_lookup.gid; 70 } else { 71 if((r=fs_lookup_credentials(&credentials, 72 &caller_uid, &caller_gid, fs_m_in.m_vfs_fs_lookup.grant_ucred, 73 fs_m_in.m_vfs_fs_lookup.ucred_size)) != OK) 74 return r; 75 } 76 77 /* Lookup inode */ 78 rip = NULL; 79 r = parse_path(dir_ino, root_ino, flags, &rip, &offset, &symlinks); 80 81 if(symlinks != 0 && (r == ELEAVEMOUNT || r == EENTERMOUNT || r == ESYMLINK)){ 82 len = strlen(user_path)+1; 83 if(len > path_size) return(ENAMETOOLONG); 84 85 r1 = sys_safecopyto(VFS_PROC_NR, grant, (vir_bytes) 0, 86 (vir_bytes) user_path, (size_t) len); 87 if (r1 != OK) return(r1); 88 } 89 90 if(r == ELEAVEMOUNT || r == ESYMLINK) { 91 /* Report offset and the error */ 92 fs_m_out.m_fs_vfs_lookup.offset = offset; 93 fs_m_out.m_fs_vfs_lookup.symloop = symlinks; 94 95 return(r); 96 } 97 98 if (r != OK && r != EENTERMOUNT) return(r); 99 100 fs_m_out.m_fs_vfs_lookup.inode = rip->i_num; 101 fs_m_out.m_fs_vfs_lookup.mode = rip->i_mode; 102 fs_m_out.m_fs_vfs_lookup.file_size = rip->i_size; 103 fs_m_out.m_fs_vfs_lookup.symloop = symlinks; 104 fs_m_out.m_fs_vfs_lookup.uid = rip->i_uid; 105 fs_m_out.m_fs_vfs_lookup.gid = rip->i_gid; 106 107 /* This is only valid for block and character specials. But it doesn't 108 * cause any harm to always set the device field. */ 109 fs_m_out.m_fs_vfs_lookup.device = (dev_t) rip->i_block[0]; 110 111 if(r == EENTERMOUNT) { 112 fs_m_out.m_fs_vfs_lookup.offset = offset; 113 put_inode(rip); /* Only return a reference to the final object */ 114 } 115 116 return(r); 117 } 118 119 120 /*===========================================================================* 121 * parse_path * 122 *===========================================================================*/ 123 static int parse_path(dir_ino, root_ino, flags, res_inop, offsetp, symlinkp) 124 ino_t dir_ino; 125 ino_t root_ino; 126 int flags; 127 struct inode **res_inop; 128 size_t *offsetp; 129 int *symlinkp; 130 { 131 /* Parse the path in user_path, starting at dir_ino. If the path is the empty 132 * string, just return dir_ino. It is upto the caller to treat an empty 133 * path in a special way. Otherwise, if the path consists of just one or 134 * more slash ('/') characters, the path is replaced with ".". Otherwise, 135 * just look up the first (or only) component in path after skipping any 136 * leading slashes. 137 */ 138 int r, leaving_mount; 139 struct inode *rip, *dir_ip; 140 char *cp, *next_cp; /* component and next component */ 141 char component[NAME_MAX+1]; 142 143 /* Start parsing path at the first component in user_path */ 144 cp = user_path; 145 146 /* No symlinks encountered yet */ 147 *symlinkp = 0; 148 149 /* Find starting inode inode according to the request message */ 150 if((rip = find_inode(fs_dev, dir_ino)) == NULL) 151 return(ENOENT); 152 153 /* If dir has been removed return ENOENT. */ 154 if (rip->i_links_count == NO_LINK) return(ENOENT); 155 156 dup_inode(rip); 157 158 /* If the given start inode is a mountpoint, we must be here because the file 159 * system mounted on top returned an ELEAVEMOUNT error. In this case, we must 160 * only accept ".." as the first path component. 161 */ 162 leaving_mount = rip->i_mountpoint; /* True iff rip is a mountpoint */ 163 164 /* Scan the path component by component. */ 165 while (TRUE) { 166 if(cp[0] == '\0') { 167 /* We're done; either the path was empty or we've parsed all 168 components of the path */ 169 170 *res_inop = rip; 171 *offsetp += cp - user_path; 172 173 /* Return EENTERMOUNT if we are at a mount point */ 174 if (rip->i_mountpoint) return(EENTERMOUNT); 175 176 return(OK); 177 } 178 179 while(cp[0] == '/') cp++; 180 next_cp = get_name(cp, component); 181 if (next_cp == NULL) { 182 put_inode(rip); 183 return(err_code); 184 } 185 186 /* Special code for '..'. A process is not allowed to leave a chrooted 187 * environment. A lookup of '..' at the root of a mounted filesystem 188 * has to return ELEAVEMOUNT. In both cases, the caller needs search 189 * permission for the current inode, as it is used as directory. 190 */ 191 if(strcmp(component, "..") == 0) { 192 /* 'rip' is now accessed as directory */ 193 if ((r = forbidden(rip, X_BIT)) != OK) { 194 put_inode(rip); 195 return(r); 196 } 197 198 if (rip->i_num == root_ino) { 199 cp = next_cp; 200 continue; /* Ignore the '..' at a process' root 201 and move on to the next component */ 202 } 203 204 if (rip->i_num == ROOT_INODE && !rip->i_sp->s_is_root) { 205 /* Climbing up to parent FS */ 206 207 put_inode(rip); 208 *offsetp += cp - user_path; 209 return(ELEAVEMOUNT); 210 } 211 } 212 213 /* Only check for a mount point if we are not coming from one. */ 214 if (!leaving_mount && rip->i_mountpoint) { 215 /* Going to enter a child FS */ 216 217 *res_inop = rip; 218 *offsetp += cp - user_path; 219 return(EENTERMOUNT); 220 } 221 222 /* There is more path. Keep parsing. 223 * If we're leaving a mountpoint, skip directory permission checks. 224 */ 225 dir_ip = rip; 226 rip = advance(dir_ip, leaving_mount ? dot2 : component, CHK_PERM); 227 if(err_code == ELEAVEMOUNT || err_code == EENTERMOUNT) 228 err_code = OK; 229 230 if (err_code != OK) { 231 put_inode(dir_ip); 232 return(err_code); 233 } 234 235 leaving_mount = 0; 236 237 /* The call to advance() succeeded. Fetch next component. */ 238 if (S_ISLNK(rip->i_mode)) { 239 240 if (next_cp[0] == '\0' && (flags & PATH_RET_SYMLINK)) { 241 put_inode(dir_ip); 242 *res_inop = rip; 243 *offsetp += next_cp - user_path; 244 return(OK); 245 } 246 247 /* Extract path name from the symlink file */ 248 r = ltraverse(rip, next_cp); 249 next_cp = user_path; 250 *offsetp = 0; 251 252 /* Symloop limit reached? */ 253 if (++(*symlinkp) > _POSIX_SYMLOOP_MAX) 254 r = ELOOP; 255 256 if (r != OK) { 257 put_inode(dir_ip); 258 put_inode(rip); 259 return(r); 260 } 261 262 if (next_cp[0] == '/') { 263 put_inode(dir_ip); 264 put_inode(rip); 265 return(ESYMLINK); 266 } 267 268 put_inode(rip); 269 dup_inode(dir_ip); 270 rip = dir_ip; 271 } 272 273 put_inode(dir_ip); 274 cp = next_cp; /* Process subsequent component in next round */ 275 } 276 277 } 278 279 280 /*===========================================================================* 281 * ltraverse * 282 *===========================================================================*/ 283 static int ltraverse(rip, suffix) 284 register struct inode *rip; /* symbolic link */ 285 char *suffix; /* current remaining path. Has to point in the 286 * user_path buffer 287 */ 288 { 289 /* Traverse a symbolic link. Copy the link text from the inode and insert 290 * the text into the path. Return error code or report success. Base 291 * directory has to be determined according to the first character of the 292 * new pathname. 293 */ 294 295 size_t llen; /* length of link */ 296 size_t slen; /* length of suffix */ 297 struct buf *bp; /* buffer containing link text */ 298 const char *sp; /* start of link text */ 299 300 llen = (size_t) rip->i_size; 301 302 if (llen >= MAX_FAST_SYMLINK_LENGTH) { 303 /* normal symlink */ 304 if(!(bp = get_block_map(rip, 0))) 305 return(EIO); 306 sp = b_data(bp); 307 } else { 308 /* fast symlink, stored in inode */ 309 sp = (const char*) rip->i_block; 310 } 311 312 slen = strlen(suffix); 313 314 /* The path we're parsing looks like this: 315 * /already/processed/path/<link> or 316 * /already/processed/path/<link>/not/yet/processed/path 317 * After expanding the <link>, the path will look like 318 * <expandedlink> or 319 * <expandedlink>/not/yet/processed 320 * In both cases user_path must have enough room to hold <expandedlink>. 321 * However, in the latter case we have to move /not/yet/processed to the 322 * right place first, before we expand <link>. When strlen(<expandedlink>) is 323 * smaller than strlen(/already/processes/path), we move the suffix to the 324 * left. Is strlen(<expandedlink>) greater then we move it to the right. Else 325 * we do nothing. 326 */ 327 328 if (slen > 0) { /* Do we have path after the link? */ 329 /* For simplicity we require that suffix starts with a slash */ 330 if (suffix[0] != '/') { 331 panic("ltraverse: suffix does not start with a slash"); 332 } 333 334 /* To be able to expand the <link>, we have to move the 'suffix' 335 * to the right place. 336 */ 337 if (slen + llen + 1 > sizeof(user_path)) 338 return(ENAMETOOLONG);/* <expandedlink>+suffix+\0 does not fit*/ 339 if ((unsigned)(suffix - user_path) != llen) { 340 /* Move suffix left or right if needed */ 341 memmove(&user_path[llen], suffix, slen+1); 342 } 343 } else { 344 if (llen + 1 > sizeof(user_path)) 345 return(ENAMETOOLONG); /* <expandedlink> + \0 does not fit */ 346 347 /* Set terminating nul */ 348 user_path[llen]= '\0'; 349 } 350 351 /* Everything is set, now copy the expanded link to user_path */ 352 memmove(user_path, sp, llen); 353 354 if (llen >= MAX_FAST_SYMLINK_LENGTH) 355 put_block(bp, DIRECTORY_BLOCK); 356 357 return(OK); 358 } 359 360 361 /*===========================================================================* 362 * advance * 363 *===========================================================================*/ 364 struct inode *advance(dirp, string, chk_perm) 365 struct inode *dirp; /* inode for directory to be searched */ 366 char string[NAME_MAX + 1]; /* component name to look for */ 367 int chk_perm; /* check permissions when string is looked up*/ 368 { 369 /* Given a directory and a component of a path, look up the component in 370 * the directory, find the inode, open it, and return a pointer to its inode 371 * slot. 372 */ 373 ino_t numb; 374 struct inode *rip; 375 376 /* If 'string' is empty, return an error. */ 377 if (string[0] == '\0') { 378 err_code = ENOENT; 379 return(NULL); 380 } 381 382 /* Check for NULL. */ 383 if (dirp == NULL) return(NULL); 384 385 /* If 'string' is not present in the directory, signal error. */ 386 if ( (err_code = search_dir(dirp, string, &numb, LOOK_UP, 387 chk_perm, 0)) != OK) { 388 return(NULL); 389 } 390 391 /* The component has been found in the directory. Get inode. */ 392 if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NULL) { 393 return(NULL); 394 } 395 396 /* The following test is for "mountpoint/.." where mountpoint is a 397 * mountpoint. ".." will refer to the root of the mounted filesystem, 398 * but has to become a reference to the parent of the 'mountpoint' 399 * directory. 400 * 401 * This case is recognized by the looked up name pointing to a 402 * root inode, and the directory in which it is held being a 403 * root inode, _and_ the name[1] being '.'. (This is a test for '..' 404 * and excludes '.'.) 405 */ 406 if (rip->i_num == ROOT_INODE) { 407 if (dirp->i_num == ROOT_INODE) { 408 if (string[1] == '.') { 409 if (!rip->i_sp->s_is_root) { 410 /* Climbing up mountpoint */ 411 err_code = ELEAVEMOUNT; 412 } 413 } 414 } 415 } 416 417 /* See if the inode is mounted on. If so, switch to root directory of the 418 * mounted file system. The super_block provides the linkage between the 419 * inode mounted on and the root directory of the mounted file system. 420 */ 421 if (rip->i_mountpoint) { 422 /* Mountpoint encountered, report it */ 423 err_code = EENTERMOUNT; 424 } 425 426 return(rip); 427 } 428 429 430 /*===========================================================================* 431 * get_name * 432 *===========================================================================*/ 433 static char *get_name(path_name, string) 434 char *path_name; /* path name to parse */ 435 char string[NAME_MAX+1]; /* component extracted from 'old_name' */ 436 { 437 /* Given a pointer to a path name in fs space, 'path_name', copy the first 438 * component to 'string' (truncated if necessary, always nul terminated). 439 * A pointer to the string after the first component of the name as yet 440 * unparsed is returned. Roughly speaking, 441 * 'get_name' = 'path_name' - 'string'. 442 * 443 * This routine follows the standard convention that /usr/ast, /usr//ast, 444 * //usr///ast and /usr/ast/ are all equivalent. 445 * 446 * If len of component is greater, than allowed, then return 0. 447 */ 448 size_t len; 449 char *cp, *ep; 450 451 cp = path_name; 452 453 /* Skip leading slashes */ 454 while (cp[0] == '/') cp++; 455 456 /* Find the end of the first component */ 457 ep = cp; 458 while(ep[0] != '\0' && ep[0] != '/') 459 ep++; 460 461 len = (size_t) (ep - cp); 462 463 if (len > NAME_MAX || len > EXT2_NAME_MAX) { 464 err_code = ENAMETOOLONG; 465 return(NULL); 466 } 467 468 /* Special case of the string at cp is empty */ 469 if (len == 0) 470 strlcpy(string, ".", NAME_MAX + 1); /* Return "." */ 471 else { 472 memcpy(string, cp, len); 473 string[len]= '\0'; 474 } 475 476 return(ep); 477 } 478 479 480 /*===========================================================================* 481 * search_dir * 482 *===========================================================================*/ 483 int search_dir(ldir_ptr, string, numb, flag, check_permissions, ftype) 484 register struct inode *ldir_ptr; /* ptr to inode for dir to search */ 485 const char string[NAME_MAX + 1]; /* component to search for */ 486 ino_t *numb; /* pointer to inode number */ 487 int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */ 488 int check_permissions; /* check permissions when flag is !IS_EMPTY */ 489 int ftype; /* used when ENTER and 490 * INCOMPAT_FILETYPE */ 491 { 492 /* This function searches the directory whose inode is pointed to by 'ldip': 493 * if (flag == ENTER) enter 'string' in the directory with inode # '*numb'; 494 * if (flag == DELETE) delete 'string' from the directory; 495 * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb'; 496 * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY; 497 * 498 * if 'string' is dot1 or dot2, no access permissions are checked. 499 */ 500 501 register struct ext2_disk_dir_desc *dp = NULL; 502 register struct ext2_disk_dir_desc *prev_dp = NULL; 503 register struct buf *bp = NULL; 504 int i, r, e_hit, t, match; 505 mode_t bits; 506 off_t pos; 507 unsigned new_slots; 508 int extended = 0; 509 int required_space = 0; 510 int string_len = 0; 511 512 /* If 'ldir_ptr' is not a pointer to a dir inode, error. */ 513 if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) { 514 return(ENOTDIR); 515 } 516 517 r = OK; 518 519 if (flag != IS_EMPTY) { 520 bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT); 521 522 if (string == dot1 || string == dot2) { 523 if (flag != LOOK_UP) r = read_only(ldir_ptr); 524 /* only a writable device is required. */ 525 } else if(check_permissions) { 526 r = forbidden(ldir_ptr, bits); /* check access permissions */ 527 } 528 } 529 if (r != OK) return(r); 530 531 new_slots = 0; 532 e_hit = FALSE; 533 match = 0; /* set when a string match occurs */ 534 pos = 0; 535 536 if (flag == ENTER) { 537 string_len = strlen(string); 538 required_space = MIN_DIR_ENTRY_SIZE + string_len; 539 required_space += (required_space & 0x03) == 0 ? 0 : 540 (DIR_ENTRY_ALIGN - (required_space & 0x03) ); 541 542 if (ldir_ptr->i_last_dpos < ldir_ptr->i_size && 543 ldir_ptr->i_last_dentry_size <= required_space) 544 pos = ldir_ptr->i_last_dpos; 545 } 546 547 for (; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) { 548 /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */ 549 if(!(bp = get_block_map(ldir_ptr, 550 rounddown(pos, ldir_ptr->i_sp->s_block_size)))) 551 panic("get_block returned NO_BLOCK"); 552 553 prev_dp = NULL; /* New block - new first dentry, so no prev. */ 554 555 /* Search a directory block. 556 * Note, we set prev_dp at the end of the loop. 557 */ 558 for (dp = (struct ext2_disk_dir_desc*) &b_data(bp); 559 CUR_DISC_DIR_POS(dp, &b_data(bp)) < ldir_ptr->i_sp->s_block_size; 560 dp = NEXT_DISC_DIR_DESC(dp) ) { 561 /* Match occurs if string found. */ 562 if (flag != ENTER && dp->d_ino != NO_ENTRY) { 563 if (flag == IS_EMPTY) { 564 /* If this test succeeds, dir is not empty. */ 565 if (ansi_strcmp(dp->d_name, ".", dp->d_name_len) != 0 && 566 ansi_strcmp(dp->d_name, "..", dp->d_name_len) != 0) match = 1; 567 } else { 568 if (ansi_strcmp(dp->d_name, string, dp->d_name_len) == 0){ 569 match = 1; 570 } 571 } 572 } 573 574 if (match) { 575 /* LOOK_UP or DELETE found what it wanted. */ 576 r = OK; 577 if (flag == IS_EMPTY) r = ENOTEMPTY; 578 else if (flag == DELETE) { 579 if (dp->d_name_len >= sizeof(ino_t)) { 580 /* Save d_ino for recovery. */ 581 t = dp->d_name_len - sizeof(ino_t); 582 *((ino_t *) &dp->d_name[t])= dp->d_ino; 583 } 584 dp->d_ino = NO_ENTRY; /* erase entry */ 585 lmfs_markdirty(bp); 586 587 /* If we don't support HTree (directory index), 588 * which is fully compatible ext2 feature, 589 * we should reset EXT2_INDEX_FL, when modify 590 * linked directory structure. 591 * 592 * @TODO: actually we could just reset it for 593 * each directory, but I added if() to not 594 * forget about it later, when add HTree 595 * support. 596 */ 597 if (!HAS_COMPAT_FEATURE(ldir_ptr->i_sp, 598 COMPAT_DIR_INDEX)) 599 ldir_ptr->i_flags &= ~EXT2_INDEX_FL; 600 if (pos < ldir_ptr->i_last_dpos) { 601 ldir_ptr->i_last_dpos = pos; 602 ldir_ptr->i_last_dentry_size = 603 conv2(le_CPU, dp->d_rec_len); 604 } 605 ldir_ptr->i_update |= CTIME | MTIME; 606 ldir_ptr->i_dirt = IN_DIRTY; 607 /* Now we have cleared dentry, if it's not 608 * the first one, merge it with previous one. 609 * Since we assume, that existing dentry must be 610 * correct, there is no way to spann a data block. 611 */ 612 if (prev_dp) { 613 u16_t temp = conv2(le_CPU, 614 prev_dp->d_rec_len); 615 temp += conv2(le_CPU, 616 dp->d_rec_len); 617 prev_dp->d_rec_len = conv2(le_CPU, 618 temp); 619 } 620 } else { 621 /* 'flag' is LOOK_UP */ 622 *numb = (ino_t) conv4(le_CPU, dp->d_ino); 623 } 624 assert(lmfs_dev(bp) != NO_DEV); 625 put_block(bp, DIRECTORY_BLOCK); 626 return(r); 627 } 628 629 /* Check for free slot for the benefit of ENTER. */ 630 if (flag == ENTER && dp->d_ino == NO_ENTRY) { 631 /* we found a free slot, check if it has enough space */ 632 if (required_space <= conv2(le_CPU, dp->d_rec_len)) { 633 e_hit = TRUE; /* we found a free slot */ 634 break; 635 } 636 } 637 /* Can we shrink dentry? */ 638 if (flag == ENTER && required_space <= DIR_ENTRY_SHRINK(dp)) { 639 /* Shrink directory and create empty slot, now 640 * dp->d_rec_len = DIR_ENTRY_ACTUAL_SIZE + DIR_ENTRY_SHRINK. 641 */ 642 int new_slot_size = conv2(le_CPU, dp->d_rec_len); 643 int actual_size = DIR_ENTRY_ACTUAL_SIZE(dp); 644 new_slot_size -= actual_size; 645 dp->d_rec_len = conv2(le_CPU, actual_size); 646 dp = NEXT_DISC_DIR_DESC(dp); 647 dp->d_rec_len = conv2(le_CPU, new_slot_size); 648 /* if we fail before writing real ino */ 649 dp->d_ino = NO_ENTRY; 650 lmfs_markdirty(bp); 651 e_hit = TRUE; /* we found a free slot */ 652 break; 653 } 654 655 prev_dp = dp; 656 } 657 658 /* The whole block has been searched or ENTER has a free slot. */ 659 assert(lmfs_dev(bp) != NO_DEV); 660 if (e_hit) break; /* e_hit set if ENTER can be performed now */ 661 put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */ 662 } 663 664 /* The whole directory has now been searched. */ 665 if (flag != ENTER) { 666 return(flag == IS_EMPTY ? OK : ENOENT); 667 } 668 669 /* When ENTER next time, start searching for free slot from 670 * i_last_dpos. It gives solid performance improvement. 671 */ 672 ldir_ptr->i_last_dpos = pos; 673 ldir_ptr->i_last_dentry_size = required_space; 674 675 /* This call is for ENTER. If no free slot has been found so far, try to 676 * extend directory. 677 */ 678 if (e_hit == FALSE) { /* directory is full and no room left in last block */ 679 new_slots++; /* increase directory size by 1 entry */ 680 if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NULL) 681 return(err_code); 682 dp = (struct ext2_disk_dir_desc*) &b_data(bp); 683 dp->d_rec_len = conv2(le_CPU, ldir_ptr->i_sp->s_block_size); 684 dp->d_name_len = DIR_ENTRY_MAX_NAME_LEN(dp); /* for failure */ 685 extended = 1; 686 } 687 688 /* 'bp' now points to a directory block with space. 'dp' points to slot. */ 689 dp->d_name_len = string_len; 690 for (i = 0; i < NAME_MAX && i < dp->d_name_len && string[i]; i++) 691 dp->d_name[i] = string[i]; 692 dp->d_ino = (int) conv4(le_CPU, *numb); 693 if (HAS_INCOMPAT_FEATURE(ldir_ptr->i_sp, INCOMPAT_FILETYPE)) { 694 /* Convert ftype (from inode.i_mode) to dp->d_file_type */ 695 if (ftype == I_REGULAR) 696 dp->d_file_type = EXT2_FT_REG_FILE; 697 else if (ftype == I_DIRECTORY) 698 dp->d_file_type = EXT2_FT_DIR; 699 else if (ftype == I_SYMBOLIC_LINK) 700 dp->d_file_type = EXT2_FT_SYMLINK; 701 else if (ftype == I_BLOCK_SPECIAL) 702 dp->d_file_type = EXT2_FT_BLKDEV; 703 else if (ftype == I_CHAR_SPECIAL) 704 dp->d_file_type = EXT2_FT_CHRDEV; 705 else if (ftype == I_NAMED_PIPE) 706 dp->d_file_type = EXT2_FT_FIFO; 707 else 708 dp->d_file_type = EXT2_FT_UNKNOWN; 709 } 710 lmfs_markdirty(bp); 711 put_block(bp, DIRECTORY_BLOCK); 712 ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */ 713 ldir_ptr->i_dirt = IN_DIRTY; 714 715 if (new_slots == 1) { 716 ldir_ptr->i_size += (off_t) conv2(le_CPU, dp->d_rec_len); 717 /* Send the change to disk if the directory is extended. */ 718 if (extended) rw_inode(ldir_ptr, WRITING); 719 } 720 return(OK); 721 722 } 723