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