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