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
init_dir_info(struct struct_file_info * info_ptr)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
search_dir_entries(int (* action)(struct struct_file_info * info),int * status)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
count_dir_entries(void)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
action_count(struct struct_file_info * info)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
type_dir___cd(char * command_line)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
action_name(struct struct_file_info * info)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
type_dir___entry(char * command_line)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
action_entry_num(struct struct_file_info * info)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
type_dir___followinode(char * command_line)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
type_dir___inode(char * command_line)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
type_dir___show(char * command_line)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
action_show(struct struct_file_info * info)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
type_dir___next(char * command_line)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
type_dir___prev(char * command_line)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
show_dir_status(void)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
type_dir___remember(char * command_line)538 void type_dir___remember (char *command_line)
539
540 /*
541
542 This is overridden 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
type_dir___set(char * command_line)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
type_dir___writedata(char * command_line)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