1/*
2
3/usr/src/ext2ed/dir_com.c
4
5A part of the extended file system 2 disk editor.
6
7--------------------
8Handles directories.
9--------------------
10
11This file contains the codes which allows the user to handle directories.
12
13Most of the functions use the global variable file_info (along with the special directory fields there) to save
14information and pass it between them.
15
16Since a directory is just a big file which is composed of directory entries, you will find that
17the functions here are a superset of those in the file_com.c source.
18
19We assume that the user reached here using the dir command of the inode type and not by using settype dir, so
20that init_dir_info is indeed called to gather the required information.
21
22type_data is not changed! It still contains the inode of the file - We handle the directory in our own
23variables, so that settype ext2_inode will "go back" to the inode of this directory.
24
25First written on: April 28 1995
26
27Copyright (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
37char name_search [80];
38long entry_num_search;
39
40int init_dir_info (struct struct_file_info *info_ptr)
41
42/*
43
44This function is called by the inode of the directory when the user issues the dir command from the inode.
45It is used to gather information about the inode and to reset some variables which we need in order to handle
46directories.
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
82struct 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
147long count_dir_entries (void)
148
149/*
150
151This function counts the number of entries in the directory. We just call search_dir_entries till the end.
152The 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
162int action_count (struct struct_file_info *info)
163
164/*
165
166Used by count_dir_entries above - This function is called by search_dir_entries, and it tells it to continue
167searching, until we get to the last entry.
168
169*/
170
171{
172	return (CONTINUE);							/* Just continue searching */
173}
174
175void 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
1831.	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
1862.	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
1893.	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
1924.	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
1965.	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
2026.	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
2057.	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
2098.	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
2139.	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
312int action_name (struct struct_file_info *info)
313
314/*
315
316Compares the current search entry name (somewhere inside info) with the required name (in name_search).
317Returns 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
335void type_dir___entry (char *command_line)
336
337/*
338
339Selects a directory entry according to its number.
340search_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
373int action_entry_num (struct struct_file_info *info)
374
375/*
376
377Used 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
388void type_dir___followinode (char *command_line)
389
390/*
391
392Here we pass to the inode pointed by the current entry.
393It 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
410void type_dir___inode (char *command_line)
411
412/*
413
414Returns to the parent inode of the current directory.
415This 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
424void type_dir___show (char *command_line)
425
426/*
427
428We 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
444int action_show (struct struct_file_info *info)
445
446/*
447
448Show 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
476void type_dir___next (char *command_line)
477
478/*
479
480This 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
499void 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
515void show_dir_status (void)
516
517/*
518
519Various 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
537void type_dir___remember (char *command_line)
538
539/*
540
541This is overrided here because we don't remember a directory - It is too complicated. Instead, we remember the
542inode 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
584void type_dir___set (char *command_line)
585
586/*
587
588Since the dir object doesn't have variables, we provide the impression that it has here. ext2_dir_entry was not used
589because 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
665void type_dir___writedata (char *command_line)
666
667/*
668
669We need to override this since the data is not in type_data. Instead, we have to write the buffer which corresponds
670to 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