1 /* 2 3 /usr/src/ext2ed/dir_com.c 4 5 A part of the extended file system 2 disk editor. 6 7 -------------------- 8 Handles directories. 9 -------------------- 10 11 This file contains the codes which allows the user to handle directories. 12 13 Most of the functions use the global variable file_info (along with the special directory fields there) to save 14 information and pass it between them. 15 16 Since a directory is just a big file which is composed of directory entries, you will find that 17 the functions here are a superset of those in the file_com.c source. 18 19 We assume that the user reached here using the dir command of the inode type and not by using settype dir, so 20 that init_dir_info is indeed called to gather the required information. 21 22 type_data is not changed! It still contains the inode of the file - We handle the directory in our own 23 variables, so that settype ext2_inode will "go back" to the inode of this directory. 24 25 First written on: April 28 1995 26 27 Copyright (C) 1995 Gadi Oxman 28 29 */ 30 31 #include "config.h" 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 36 #include "ext2ed.h" 37 38 char name_search [80]; 39 long entry_num_search; 40 41 int init_dir_info (struct struct_file_info *info_ptr) 42 43 /* 44 45 This function is called by the inode of the directory when the user issues the dir command from the inode. 46 It is used to gather information about the inode and to reset some variables which we need in order to handle 47 directories. 48 49 */ 50 51 { 52 struct ext2_inode *ptr; 53 54 ptr=&type_data.u.t_ext2_inode; /* type_data contains the inode */ 55 56 info_ptr->inode_ptr=ptr; 57 info_ptr->inode_offset=device_offset; /* device offset contains the inode's offset */ 58 59 /* Reset the current position to the start */ 60 61 info_ptr->global_block_num=ptr->i_block [0]; 62 info_ptr->global_block_offset=ptr->i_block [0]*file_system_info.block_size; 63 info_ptr->block_num=0; 64 info_ptr->file_offset=0; 65 /* Set the size of the directory */ 66 67 info_ptr->blocks_count=(ptr->i_size+file_system_info.block_size-1)/file_system_info.block_size; 68 info_ptr->file_length=ptr->i_size; 69 70 info_ptr->level=0; /* We start using direct blocks */ 71 info_ptr->display=HEX; /* This is not actually used */ 72 73 info_ptr->dir_entry_num=0;info_ptr->dir_entries_count=0; /* We'll start at the first directory entry */ 74 info_ptr->dir_entry_offset=0; 75 76 /* Find dir_entries_count */ 77 78 info_ptr->dir_entries_count=count_dir_entries (); /* Set the total number of entries */ 79 80 return (1); 81 } 82 83 struct struct_file_info search_dir_entries (int (*action) (struct struct_file_info *info),int *status) 84 85 /* 86 This is the main function in this source file. Various actions are implemented using this basic function. 87 88 This routine runs on all directory entries in the current directory. 89 For each entry, action is called. We'll act according to the return code of action: 90 91 ABORT - Current dir entry is returned. 92 CONTINUE - Continue searching. 93 FOUND - Current dir entry is returned. 94 95 If the last entry is reached, it is returned, along with an ABORT status. 96 97 status is updated to the returned code of action. 98 */ 99 100 { 101 struct struct_file_info info; /* Temporary variables used to */ 102 struct ext2_dir_entry_2 *dir_entry_ptr; /* contain the current search entries */ 103 int return_code, next; 104 105 info=first_file_info; /* Start from the first entry - Read it */ 106 low_read (info.buffer,file_system_info.block_size,info.global_block_offset); 107 dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset); 108 109 while (info.file_offset < info.file_length) { /* While we haven't reached the end */ 110 111 *status=return_code=action (&info); /* Call the client function to test */ 112 /* the current entry */ 113 if (return_code==ABORT || return_code==FOUND) 114 return (info); /* Stop, if so asked */ 115 116 /* Pass to the next entry */ 117 118 dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset); 119 120 info.dir_entry_num++; 121 next = dir_entry_ptr->rec_len; 122 if (!next) 123 next = file_system_info.block_size - info.dir_entry_offset; 124 info.dir_entry_offset += next; 125 info.file_offset += next; 126 127 if (info.file_offset >= info.file_length) break; 128 129 if (info.dir_entry_offset >= file_system_info.block_size) { /* We crossed a block boundary */ 130 /* Find the next block, */ 131 info.block_num++; 132 info.global_block_num=file_block_to_global_block (info.block_num,&info); 133 info.global_block_offset=info.global_block_num*file_system_info.block_size; 134 info.file_offset=info.block_num*file_system_info.block_size; 135 info.dir_entry_offset=0; 136 /* read it and update the pointer */ 137 138 low_read (info.buffer,file_system_info.block_size,info.global_block_offset); 139 dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset); 140 141 } 142 143 } 144 145 *status=ABORT;return (info); /* There was no match */ 146 } 147 148 long count_dir_entries (void) 149 150 /* 151 152 This function counts the number of entries in the directory. We just call search_dir_entries till the end. 153 The client function is action_count, which just tell search_dir_entries to continue. 154 155 */ 156 157 { 158 int status; 159 160 return (search_dir_entries (&action_count,&status).dir_entry_num); 161 } 162 163 int action_count (struct struct_file_info *info) 164 165 /* 166 167 Used by count_dir_entries above - This function is called by search_dir_entries, and it tells it to continue 168 searching, until we get to the last entry. 169 170 */ 171 172 { 173 return (CONTINUE); /* Just continue searching */ 174 } 175 176 void type_dir___cd (char *command_line) 177 178 /* 179 Changes to a directory, relative to the current directory. 180 181 This is a complicated operation, so I would repeat here the explanation from the design and 182 implementation document. 183 184 1. The path is checked that it is not an absolute path (from /). If it is, we let the general cd to do the job by 185 calling directly type_ext2___cd. 186 187 2. The path is divided into the nearest path and the rest of the path. For example, cd 1/2/3/4 is divided into 188 1 and into 2/3/4. 189 190 3. It is the first part of the path that we need to search for in the current directory. We search for it using 191 search_dir_entries, which accepts the action_name function as the client function. 192 193 4. search_dir_entries will scan the entire entries and will call our action_name function for each entry. 194 In action_name, the required name will be checked against the name of the current entry, and FOUND will be 195 returned when a match occurs. 196 197 5. If the required entry is found, we dispatch a remember command to insert the current inode (remember that 198 type_data is still intact and contains the inode of the current directory) into the object memory. 199 This is required to easily support symbolic links - If we find later that the inode pointed by the entry is 200 actually a symbolic link, we'll need to return to this point, and the above inode doesn't have (and can't have, 201 because of hard links) the information necessary to "move back". 202 203 6. We then dispatch a followinode command to reach the inode pointed by the required entry. This command will 204 automatically change the type to ext2_inode - We are now at an inode, and all the inode commands are available. 205 206 7. We check the inode's type to see if it is a directory. If it is, we dispatch a dir command to "enter the directory", 207 and recursively call ourself (The type is dir again) by dispatching a cd command, with the rest of the path 208 as an argument. 209 210 8. If the inode's type is a symbolic link (only fast symbolic link were meanwhile implemented. I guess this is 211 typically the case.), we note the path it is pointing at, the saved inode is recalled, we dispatch dir to 212 get back to the original directory, and we call ourself again with the link path/rest of the path argument. 213 214 9. In any other case, we just stop at the resulting inode. 215 216 */ 217 218 { 219 int status; 220 char *ptr,full_dir_name [500],dir_name [500],temp [500],temp2 [500]; 221 struct struct_file_info info; 222 struct ext2_dir_entry_2 *dir_entry_ptr; 223 224 dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset); 225 226 ptr=parse_word (command_line,dir_name); 227 228 if (*ptr==0) { /* cd alone will enter the highlighted directory */ 229 strncpy (full_dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len); 230 full_dir_name [dir_entry_ptr->name_len]=0; 231 } 232 else 233 ptr=parse_word (ptr,full_dir_name); 234 235 ptr=strchr (full_dir_name,'/'); 236 237 if (ptr==full_dir_name) { /* Pathname is from root - Let the general cd do the job */ 238 sprintf (temp,"cd %s",full_dir_name);type_ext2___cd (temp);return; 239 } 240 241 if (ptr==NULL) { 242 strcpy (dir_name,full_dir_name); 243 full_dir_name [0]=0; 244 } 245 246 else { 247 strncpy (dir_name,full_dir_name,ptr-full_dir_name); 248 dir_name [ptr-full_dir_name]=0; 249 strcpy (full_dir_name,++ptr); 250 } 251 /* dir_name contains the current entry, while */ 252 /* full_dir_name contains the rest */ 253 254 strcpy (name_search,dir_name); /* name_search is used to hold the required entry name */ 255 256 if (dir_entry_ptr->name_len != strlen (dir_name) || 257 strncmp (dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len)!=0) 258 info=search_dir_entries (&action_name,&status); /* Search for the entry. Answer in info. */ 259 else { 260 status=FOUND;info=file_info; 261 } 262 263 if (status==FOUND) { /* If found */ 264 file_info=info; /* Switch to it, by setting the global file_info */ 265 dispatch ("remember internal_variable"); /* Move the inode into the objects memory */ 266 267 dispatch ("followinode"); /* Go to the inode pointed by this directory entry */ 268 269 if (S_ISLNK (type_data.u.t_ext2_inode.i_mode)) {/* Symbolic link ? */ 270 271 if (type_data.u.t_ext2_inode.i_size > 60) { /* I'm lazy, I guess :-) */ 272 wprintw (command_win,"Error - Sorry, Only fast symbolic link following is currently supported\n"); 273 refresh_command_win (); 274 return; 275 } 276 /* Get the pointed name and append the previous path */ 277 278 strcpy (temp2,(unsigned char *) &type_data.u.t_ext2_inode.i_block); 279 strcat (temp2,"/"); 280 strcat (temp2,full_dir_name); 281 282 dispatch ("recall internal_variable"); /* Return to the original inode */ 283 dispatch ("dir"); /* and to the directory */ 284 285 sprintf (temp,"cd %s",temp2); /* And continue from there by dispatching a cd command */ 286 dispatch (temp); /* (which can call ourself or the general cd) */ 287 288 return; 289 } 290 291 if (S_ISDIR (type_data.u.t_ext2_inode.i_mode)) { /* Is it an inode of a directory ? */ 292 293 dispatch ("dir"); /* Yes - Pass to the pointed directory */ 294 295 if (full_dir_name [0] != 0) { /* And call ourself with the rest of the pathname */ 296 sprintf (temp,"cd %s",full_dir_name); 297 dispatch (temp); 298 } 299 300 return; 301 } 302 303 else { /* If we can't continue from here, we'll just stop */ 304 wprintw (command_win,"Can\'t continue - Stopping at last inode\n");refresh_command_win (); 305 return; 306 } 307 } 308 309 wprintw (command_win,"Error - Directory entry %s not found.\n",dir_name); /* Hmm, an invalid path somewhere */ 310 refresh_command_win (); 311 } 312 313 int action_name (struct struct_file_info *info) 314 315 /* 316 317 Compares the current search entry name (somewhere inside info) with the required name (in name_search). 318 Returns FOUND if found, or CONTINUE if not found. 319 320 */ 321 322 { 323 struct ext2_dir_entry_2 *dir_entry_ptr; 324 325 dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset); 326 327 if (dir_entry_ptr->name_len != strlen (name_search)) 328 return (CONTINUE); 329 330 if (strncmp (dir_entry_ptr->name,name_search,dir_entry_ptr->name_len)==0) 331 return (FOUND); 332 333 return (CONTINUE); 334 } 335 336 void type_dir___entry (char *command_line) 337 338 /* 339 340 Selects a directory entry according to its number. 341 search_dir_entries is used along with action_entry_num, in the same fashion as the previous usage of search_dir_entries. 342 343 */ 344 345 { 346 int status; 347 struct struct_file_info info; 348 char *ptr,buffer [80]; 349 350 ptr=parse_word (command_line,buffer); 351 if (*ptr==0) { 352 wprintw (command_win,"Error - Argument_not_specified\n");wrefresh (command_win); 353 return; 354 } 355 ptr=parse_word (ptr,buffer); 356 entry_num_search=atol (buffer); 357 358 if (entry_num_search < 0 || entry_num_search >= file_info.dir_entries_count) { 359 wprintw (command_win,"Error - Entry number out of range\n");wrefresh (command_win); 360 return; 361 } 362 363 info=search_dir_entries (&action_entry_num,&status); 364 if (status==FOUND) { 365 file_info=info; 366 dispatch ("show"); 367 return; 368 } 369 #ifdef DEBUG 370 internal_error ("dir_com","type_dir___entry","According to our gathered data, we should have found this entry"); 371 #endif 372 } 373 374 int action_entry_num (struct struct_file_info *info) 375 376 /* 377 378 Used by the above function. Just compares the current number (in info) with the required one. 379 380 */ 381 382 { 383 if (info->dir_entry_num == entry_num_search) 384 return (FOUND); 385 386 return (CONTINUE); 387 } 388 389 void type_dir___followinode (char *command_line) 390 391 /* 392 393 Here we pass to the inode pointed by the current entry. 394 It involves computing the device offset of the inode and using directly the setoffset and settype commands. 395 396 */ 397 { 398 long inode_offset; 399 char buffer [80]; 400 401 struct ext2_dir_entry_2 *dir_entry_ptr; 402 403 low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset); 404 dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset); 405 406 inode_offset=inode_num_to_inode_offset (dir_entry_ptr->inode); /* Compute the inode's offset */ 407 sprintf (buffer,"setoffset %ld",inode_offset);dispatch (buffer); /* Move to it */ 408 sprintf (buffer,"settype ext2_inode");dispatch (buffer); /* and set the type to an inode */ 409 } 410 411 void type_dir___inode (char *command_line) 412 413 /* 414 415 Returns to the parent inode of the current directory. 416 This is trivial, as we type_data is still intact and contains the parent inode ! 417 418 */ 419 420 { 421 dispatch ("settype ext2_inode"); 422 } 423 424 425 void type_dir___show (char *command_line) 426 427 /* 428 429 We use search_dir_entries to run on all the entries. Each time, action_show will be called to show one entry. 430 431 */ 432 433 { 434 int status; 435 436 wmove (show_pad,0,0); 437 show_pad_info.max_line=-1; 438 439 search_dir_entries (&action_show,&status); 440 show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2; 441 refresh_show_pad (); 442 show_dir_status (); 443 } 444 445 int action_show (struct struct_file_info *info) 446 447 /* 448 449 Show the current search entry (info) in one line. If the entry happens to be the current edited entry, it is highlighted. 450 451 */ 452 453 { 454 unsigned char temp [80]; 455 struct ext2_dir_entry_2 *dir_entry_ptr; 456 457 dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset); 458 459 if (info->dir_entry_num == file_info.dir_entry_num) /* Highlight the current entry */ 460 wattrset (show_pad,A_REVERSE); 461 462 strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len); /* The name is not terminated */ 463 temp [dir_entry_ptr->name_len]=0; 464 if (dir_entry_ptr->name_len > (COLS - 55) && COLS > 55) 465 temp [COLS-55]=0; 466 wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n", /* Display the various fields */ 467 dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp); 468 469 show_pad_info.max_line++; 470 471 if (info->dir_entry_num == file_info.dir_entry_num) 472 wattrset (show_pad,A_NORMAL); 473 474 return (CONTINUE); /* And pass to the next */ 475 } 476 477 void type_dir___next (char *command_line) 478 479 /* 480 481 This function moves to the next directory entry. It just uses the current information and the entry command. 482 483 */ 484 485 { 486 int offset=1; 487 char *ptr,buffer [80]; 488 489 ptr=parse_word (command_line,buffer); 490 491 if (*ptr!=0) { 492 ptr=parse_word (ptr,buffer); 493 offset*=atol (buffer); 494 } 495 496 sprintf (buffer,"entry %ld",file_info.dir_entry_num+offset);dispatch (buffer); 497 498 } 499 500 void type_dir___prev (char *command_line) 501 502 { 503 int offset=1; 504 char *ptr,buffer [80]; 505 506 ptr=parse_word (command_line,buffer); 507 508 if (*ptr!=0) { 509 ptr=parse_word (ptr,buffer); 510 offset*=atol (buffer); 511 } 512 513 sprintf (buffer,"entry %ld",file_info.dir_entry_num-offset);dispatch (buffer); 514 } 515 516 void show_dir_status (void) 517 518 /* 519 520 Various statistics about the directory. 521 522 */ 523 524 { 525 long inode_num; 526 527 wmove (show_win,0,0); 528 wprintw (show_win,"Directory listing. Block %ld. ",file_info.global_block_num); 529 wprintw (show_win,"Directory entry %ld of %ld.\n",file_info.dir_entry_num,file_info.dir_entries_count-1); 530 wprintw (show_win,"Directory Offset %ld of %ld. ",file_info.file_offset,file_info.file_length-1); 531 532 inode_num=inode_offset_to_inode_num (file_info.inode_offset); 533 wprintw (show_win,"File inode %ld. Indirection level %ld.\n",inode_num,file_info.level); 534 535 refresh_show_win (); 536 } 537 538 void type_dir___remember (char *command_line) 539 540 /* 541 542 This is overrided here because we don't remember a directory - It is too complicated. Instead, we remember the 543 inode of the current directory. 544 545 */ 546 547 { 548 int found=0; 549 long entry_num; 550 char *ptr,buffer [80]; 551 struct struct_descriptor *descriptor_ptr; 552 553 ptr=parse_word (command_line,buffer); 554 555 if (*ptr==0) { 556 wprintw (command_win,"Error - Argument not specified\n");wrefresh (command_win); 557 return; 558 } 559 560 ptr=parse_word (ptr,buffer); 561 562 entry_num=remember_lifo.entries_count++; 563 if (entry_num>REMEMBER_COUNT-1) { 564 entry_num=0; 565 remember_lifo.entries_count--; 566 } 567 568 descriptor_ptr=first_type; 569 while (descriptor_ptr!=NULL && !found) { 570 if (strcmp (descriptor_ptr->name,"ext2_inode")==0) 571 found=1; 572 else 573 descriptor_ptr=descriptor_ptr->next; 574 } 575 576 577 remember_lifo.offset [entry_num]=device_offset; 578 remember_lifo.type [entry_num]=descriptor_ptr; 579 strcpy (remember_lifo.name [entry_num],buffer); 580 581 wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",descriptor_ptr->name,device_offset,buffer); 582 wrefresh (command_win); 583 } 584 585 void type_dir___set (char *command_line) 586 587 /* 588 589 Since the dir object doesn't have variables, we provide the impression that it has here. ext2_dir_entry was not used 590 because it is of variable length. 591 592 */ 593 594 { 595 int found=0; 596 unsigned char *ptr,buffer [80],variable [80],value [80],temp [80]; 597 struct ext2_dir_entry_2 *dir_entry_ptr; 598 599 dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset); 600 601 ptr=parse_word (command_line,buffer); 602 if (*ptr==0) { 603 wprintw (command_win,"Error - Missing arguments\n");refresh_command_win (); 604 return; 605 } 606 parse_word (ptr,buffer); 607 ptr=strchr (buffer,'='); 608 if (ptr==NULL) { 609 wprintw (command_win,"Error - Bad syntax\n");refresh_command_win ();return; 610 } 611 strncpy (variable,buffer,ptr-buffer);variable [ptr-buffer]=0; 612 strcpy (value,++ptr); 613 614 if (strcasecmp ("inode",variable)==0) { 615 found=1; 616 dir_entry_ptr->inode=atol (value); 617 wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->inode);refresh_command_win (); 618 619 } 620 621 if (strcasecmp ("rec_len",variable)==0) { 622 found=1; 623 dir_entry_ptr->rec_len=(unsigned int) atol (value); 624 wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->rec_len);refresh_command_win (); 625 626 } 627 628 if (strcasecmp ("name_len",variable)==0) { 629 found=1; 630 dir_entry_ptr->name_len=(unsigned int) atol (value); 631 wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->name_len);refresh_command_win (); 632 633 } 634 635 if (strcasecmp ("name",variable)==0) { 636 found=1; 637 if (strlen (value) > dir_entry_ptr->name_len) { 638 wprintw (command_win,"Error - Length of name greater then name_len\n"); 639 refresh_command_win ();return; 640 } 641 strncpy (dir_entry_ptr->name,value,strlen (value)); 642 wprintw (command_win,"Variable %s set to %s\n",variable,value);refresh_command_win (); 643 644 } 645 646 if (found) { 647 wattrset (show_pad,A_REVERSE); 648 strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len); 649 temp [dir_entry_ptr->name_len]=0; 650 wmove (show_pad,file_info.dir_entry_num,0); 651 wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n", 652 dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp); 653 wattrset (show_pad,A_NORMAL); 654 show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2; 655 refresh_show_pad (); 656 show_dir_status (); 657 } 658 659 else { 660 wprintw (command_win,"Error - Variable %s not found\n",variable); 661 refresh_command_win (); 662 } 663 664 } 665 666 void type_dir___writedata (char *command_line) 667 668 /* 669 670 We need to override this since the data is not in type_data. Instead, we have to write the buffer which corresponds 671 to the current block. 672 673 */ 674 675 { 676 low_write (file_info.buffer,file_system_info.block_size,file_info.global_block_offset); 677 return; 678 } 679