Whamcloud - gitweb
e2fsck: mark that we don't care about the return value of e2fsck_lookup()
[tools/e2fsprogs.git] / ext2ed / dir_com.c
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 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
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 }