summaryrefslogtreecommitdiff
path: root/ext2ed/dir_com.c
blob: b023e7a35b4950d495788ab235ed4d0571106c1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
/*

/usr/src/ext2ed/dir_com.c

A part of the extended file system 2 disk editor.

--------------------
Handles directories.
--------------------

This file contains the codes which allows the user to handle directories.

Most of the functions use the global variable file_info (along with the special directory fields there) to save
information and pass it between them.

Since a directory is just a big file which is composed of directory entries, you will find that
the functions here are a superset of those in the file_com.c source.

We assume that the user reached here using the dir command of the inode type and not by using settype dir, so
that init_dir_info is indeed called to gather the required information.

type_data is not changed! It still contains the inode of the file - We handle the directory in our own
variables, so that settype ext2_inode will "go back" to the inode of this directory.

First written on: April 28 1995

Copyright (C) 1995 Gadi Oxman

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ext2ed.h"

char name_search [80];
long entry_num_search;

int init_dir_info (struct struct_file_info *info_ptr)

/*

This function is called by the inode of the directory when the user issues the dir command from the inode.
It is used to gather information about the inode and to reset some variables which we need in order to handle
directories.

*/

{
	struct ext2_inode *ptr;

	ptr=&type_data.u.t_ext2_inode;					/* type_data contains the inode */

	info_ptr->inode_ptr=ptr;
	info_ptr->inode_offset=device_offset;				/* device offset contains the inode's offset */

									/* Reset the current position to the start */

	info_ptr->global_block_num=ptr->i_block [0];
	info_ptr->global_block_offset=ptr->i_block [0]*file_system_info.block_size;
	info_ptr->block_num=0;
	info_ptr->file_offset=0;
									/* Set the size of the directory */

	info_ptr->blocks_count=(ptr->i_size+file_system_info.block_size-1)/file_system_info.block_size;
	info_ptr->file_length=ptr->i_size;

	info_ptr->level=0;						/* We start using direct blocks */
	info_ptr->display=HEX;						/* This is not actually used */

	info_ptr->dir_entry_num=0;info_ptr->dir_entries_count=0;	/* We'll start at the first directory entry */
	info_ptr->dir_entry_offset=0;

	/* Find dir_entries_count */

	info_ptr->dir_entries_count=count_dir_entries (); 		/* Set the total number of entries */

	return (1);
}

struct struct_file_info search_dir_entries (int (*action) (struct struct_file_info *info),int *status)

/*
	This is the main function in this source file. Various actions are implemented using this basic function.

	This routine runs on all directory entries in the current directory.
	For each entry, action is called. We'll act according to the return code of action:

		ABORT		-	Current dir entry is returned.
		CONTINUE	-	Continue searching.
		FOUND		-	Current dir entry is returned.

	If the last entry is reached, it is returned, along with an ABORT status.

	status is updated to the returned code of action.
*/

{
	struct struct_file_info info;						/* Temporary variables used to */
	struct ext2_dir_entry_2 *dir_entry_ptr;					/* contain the current search entries */
	int return_code, next;

	info=first_file_info;							/* Start from the first entry - Read it */
	low_read (info.buffer,file_system_info.block_size,info.global_block_offset);
	dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);

	while (info.file_offset < info.file_length) {				/* While we haven't reached the end */

		*status=return_code=action (&info);				/* Call the client function to test */
										/* the current entry */
		if (return_code==ABORT || return_code==FOUND)
			return (info);						/* Stop, if so asked */

										/* Pass to the next entry */

		dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);

		info.dir_entry_num++;
		next = dir_entry_ptr->rec_len;
		if (!next)
			next = file_system_info.block_size - info.dir_entry_offset;
		info.dir_entry_offset += next;
		info.file_offset += next;

		if (info.file_offset >= info.file_length) break;

		if (info.dir_entry_offset >= file_system_info.block_size) {	/* We crossed a block boundary */
										/* Find the next block, */
			info.block_num++;
			info.global_block_num=file_block_to_global_block (info.block_num,&info);
			info.global_block_offset=info.global_block_num*file_system_info.block_size;
			info.file_offset=info.block_num*file_system_info.block_size;
			info.dir_entry_offset=0;
										/* read it and update the pointer */

			low_read (info.buffer,file_system_info.block_size,info.global_block_offset);
			dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);

		}

	}

	*status=ABORT;return (info);						/* There was no match */
}

long count_dir_entries (void)

/*

This function counts the number of entries in the directory. We just call search_dir_entries till the end.
The client function is action_count, which just tell search_dir_entries to continue.

*/

{
	int status;

	return (search_dir_entries (&action_count,&status).dir_entry_num);
}

int action_count (struct struct_file_info *info)

/*

Used by count_dir_entries above - This function is called by search_dir_entries, and it tells it to continue
searching, until we get to the last entry.

*/

{
	return (CONTINUE);							/* Just continue searching */
}

void type_dir___cd (char *command_line)

/*
	Changes to a directory, relative to the current directory.

	This is a complicated operation, so I would repeat here the explanation from the design and
	implementation document.

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
	calling directly type_ext2___cd.

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
	1 and into 2/3/4.

3.	It is the first part of the path that we need to search for in the current directory. We search for it using
	search_dir_entries, which accepts the action_name function as the client function.

4.	search_dir_entries will scan the entire entries and will call our action_name function for each entry.
	In action_name, the required name will be checked against the name of the current entry, and FOUND will be
	returned when a match occurs.

5.	If the required entry is found, we dispatch a remember command to insert the current inode (remember that
	type_data is still intact and contains the inode of the current directory) into the object memory.
	This is required to easily support symbolic links - If we find later that the inode pointed by the entry is
	actually a symbolic link, we'll need to return to this point, and the above inode doesn't have (and can't have,
	because of hard links) the information necessary to "move back".

6.	We then dispatch a followinode command to reach the inode pointed by the required entry. This command will
	automatically change the type to ext2_inode - We are now at an inode, and all the inode commands are available.

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",
	and recursively call ourself (The type is dir again) by dispatching a cd command, with the rest of the path
	as an argument.

8.	If the inode's type is a symbolic link (only fast symbolic link were meanwhile implemented. I guess this is
	typically the case.), we note the path it is pointing at, the saved inode is recalled, we dispatch dir to
	get back to the original directory, and we call ourself again with the link path/rest of the path argument.

9.	In any other case, we just stop at the resulting inode.

*/

{
	int status;
	char *ptr,full_dir_name [500],dir_name [500],temp [500],temp2 [500];
	struct struct_file_info info;
	struct ext2_dir_entry_2 *dir_entry_ptr;

	dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);

	ptr=parse_word (command_line,dir_name);

	if (*ptr==0) {						/* cd alone will enter the highlighted directory */
		strncpy (full_dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len);
		full_dir_name [dir_entry_ptr->name_len]=0;
	}
	else
		ptr=parse_word (ptr,full_dir_name);

	ptr=strchr (full_dir_name,'/');

	if (ptr==full_dir_name) {				/* Pathname is from root - Let the general cd do the job */
		sprintf (temp,"cd %s",full_dir_name);type_ext2___cd (temp);return;
	}

	if (ptr==NULL) {
		strcpy (dir_name,full_dir_name);
		full_dir_name [0]=0;
	}

	else {
		strncpy (dir_name,full_dir_name,ptr-full_dir_name);
		dir_name [ptr-full_dir_name]=0;
		strcpy (full_dir_name,++ptr);
	}
								/* dir_name contains the current entry, while */
								/* full_dir_name contains the rest */

	strcpy (name_search,dir_name);				/* name_search is used to hold the required entry name */

	if (dir_entry_ptr->name_len != strlen (dir_name) ||
	    strncmp (dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len)!=0)
		info=search_dir_entries (&action_name,&status);	/* Search for the entry. Answer in info. */
	else {
		status=FOUND;info=file_info;
	}

	if (status==FOUND) {					/* If found */
		file_info=info;					/* Switch to it, by setting the global file_info */
		dispatch ("remember internal_variable");	/* Move the inode into the objects memory */

		dispatch ("followinode");			/* Go to the inode pointed by this directory entry */

		if (S_ISLNK (type_data.u.t_ext2_inode.i_mode)) {/* Symbolic link ? */

			if (type_data.u.t_ext2_inode.i_size > 60) {	/* I'm lazy, I guess :-) */
				wprintw (command_win,"Error - Sorry, Only fast symbolic link following is currently supported\n");
				refresh_command_win ();
				return;
			}
								/* Get the pointed name and append the previous path */

			strcpy (temp2,(unsigned char *) &type_data.u.t_ext2_inode.i_block);
			strcat (temp2,"/");
			strcat (temp2,full_dir_name);

			dispatch ("recall internal_variable");	/* Return to the original inode */
			dispatch ("dir");			/* and to the directory */

			sprintf (temp,"cd %s",temp2);		/* And continue from there by dispatching a cd command */
			dispatch (temp);			/* (which can call ourself or the general cd) */

			return;
		}

		if (S_ISDIR (type_data.u.t_ext2_inode.i_mode)) { /* Is it an inode of a directory ? */

			dispatch ("dir");			/* Yes - Pass to the pointed directory */

			if (full_dir_name [0] != 0) {		/* And call ourself with the rest of the pathname */
				sprintf (temp,"cd %s",full_dir_name);
				dispatch (temp);
			}

			return;
		}

		else {						/* If we can't continue from here, we'll just stop */
			wprintw (command_win,"Can\'t continue - Stopping at last inode\n");refresh_command_win ();
			return;
		}
	}

	wprintw (command_win,"Error - Directory entry %s not found.\n",dir_name);	/* Hmm, an invalid path somewhere */
	refresh_command_win ();
}

int action_name (struct struct_file_info *info)

/*

Compares the current search entry name (somewhere inside info) with the required name (in name_search).
Returns FOUND if found, or CONTINUE if not found.

*/

{
	struct ext2_dir_entry_2 *dir_entry_ptr;

	dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset);

	if (dir_entry_ptr->name_len != strlen (name_search))
		return (CONTINUE);

	if (strncmp (dir_entry_ptr->name,name_search,dir_entry_ptr->name_len)==0)
		return (FOUND);

	return (CONTINUE);
}

void type_dir___entry (char *command_line)

/*

Selects a directory entry according to its number.
search_dir_entries is used along with action_entry_num, in the same fashion as the previous usage of search_dir_entries.

*/

{
	int status;
	struct struct_file_info info;
	char *ptr,buffer [80];

	ptr=parse_word (command_line,buffer);
	if (*ptr==0) {
		wprintw (command_win,"Error - Argument_not_specified\n");wrefresh (command_win);
		return;
	}
	ptr=parse_word (ptr,buffer);
	entry_num_search=atol (buffer);

	if (entry_num_search < 0 || entry_num_search >= file_info.dir_entries_count) {
		wprintw (command_win,"Error - Entry number out of range\n");wrefresh (command_win);
		return;
	}

	info=search_dir_entries (&action_entry_num,&status);
	if (status==FOUND) {
		file_info=info;
		dispatch ("show");
		return;
	}
#ifdef DEBUG
	internal_error ("dir_com","type_dir___entry","According to our gathered data, we should have found this entry");
#endif
}

int action_entry_num (struct struct_file_info *info)

/*

Used by the above function. Just compares the current number (in info) with the required one.

*/

{
	if (info->dir_entry_num == entry_num_search)
		return (FOUND);

	return (CONTINUE);
}

void type_dir___followinode (char *command_line)

/*

Here we pass to the inode pointed by the current entry.
It involves computing the device offset of the inode and using directly the setoffset and settype commands.

*/
{
	long inode_offset;
	char buffer [80];

	struct ext2_dir_entry_2 *dir_entry_ptr;

	low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
	dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);

	inode_offset=inode_num_to_inode_offset (dir_entry_ptr->inode);			/* Compute the inode's offset */
	sprintf (buffer,"setoffset %ld",inode_offset);dispatch (buffer);		/* Move to it */
	sprintf (buffer,"settype ext2_inode");dispatch (buffer);			/* and set the type to an inode */
}

void type_dir___inode (char *command_line)

/*

Returns to the parent inode of the current directory.
This is trivial, as we type_data is still intact and contains the parent inode !

*/

{
	dispatch ("settype ext2_inode");
}


void type_dir___show (char *command_line)

/*

We use search_dir_entries to run on all the entries. Each time, action_show will be called to show one entry.

*/

{
	int status;

	wmove (show_pad,0,0);
	show_pad_info.max_line=-1;

	search_dir_entries (&action_show,&status);
	show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2;
	refresh_show_pad ();
	show_dir_status ();
}

int action_show (struct struct_file_info *info)

/*

Show the current search entry (info) in one line. If the entry happens to be the current edited entry, it is highlighted.

*/

{
	unsigned char temp [80];
	struct ext2_dir_entry_2 *dir_entry_ptr;

	dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset);

	if (info->dir_entry_num == file_info.dir_entry_num)				/* Highlight the current entry */
		wattrset (show_pad,A_REVERSE);

	strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len);			/* The name is not terminated */
	temp [dir_entry_ptr->name_len]=0;
	if (dir_entry_ptr->name_len > (COLS - 55) && COLS > 55)
		temp [COLS-55]=0;
	wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n",	/* Display the various fields */
		 dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);

	show_pad_info.max_line++;

	if (info->dir_entry_num == file_info.dir_entry_num)
		wattrset (show_pad,A_NORMAL);

	return (CONTINUE);								/* And pass to the next */
}

void type_dir___next (char *command_line)

/*

This function moves to the next directory entry. It just uses the current information and the entry command.

*/

{
	int offset=1;
	char *ptr,buffer [80];

	ptr=parse_word (command_line,buffer);

	if (*ptr!=0) {
		ptr=parse_word (ptr,buffer);
		offset*=atol (buffer);
	}

	sprintf (buffer,"entry %ld",file_info.dir_entry_num+offset);dispatch (buffer);

}

void type_dir___prev (char *command_line)

{
	int offset=1;
	char *ptr,buffer [80];

	ptr=parse_word (command_line,buffer);

	if (*ptr!=0) {
		ptr=parse_word (ptr,buffer);
		offset*=atol (buffer);
	}

	sprintf (buffer,"entry %ld",file_info.dir_entry_num-offset);dispatch (buffer);
}

void show_dir_status (void)

/*

Various statistics about the directory.

*/

{
	long inode_num;

	wmove (show_win,0,0);
	wprintw (show_win,"Directory listing. Block %ld. ",file_info.global_block_num);
	wprintw (show_win,"Directory entry %ld of %ld.\n",file_info.dir_entry_num,file_info.dir_entries_count-1);
	wprintw (show_win,"Directory Offset %ld of %ld. ",file_info.file_offset,file_info.file_length-1);

	inode_num=inode_offset_to_inode_num (file_info.inode_offset);
	wprintw (show_win,"File inode %ld. Indirection level %ld.\n",inode_num,file_info.level);

	refresh_show_win ();
}

void type_dir___remember (char *command_line)

/*

This is overrided here because we don't remember a directory - It is too complicated. Instead, we remember the
inode of the current directory.

*/

{
	int found=0;
	long entry_num;
	char *ptr,buffer [80];
	struct struct_descriptor *descriptor_ptr;

	ptr=parse_word (command_line,buffer);

	if (*ptr==0) {
		wprintw (command_win,"Error - Argument not specified\n");wrefresh (command_win);
		return;
	}

	ptr=parse_word (ptr,buffer);

	entry_num=remember_lifo.entries_count++;
	if (entry_num>REMEMBER_COUNT-1) {
		entry_num=0;
		remember_lifo.entries_count--;
	}

	descriptor_ptr=first_type;
	while (descriptor_ptr!=NULL && !found) {
		if (strcmp (descriptor_ptr->name,"ext2_inode")==0)
			found=1;
		else
			descriptor_ptr=descriptor_ptr->next;
	}


	remember_lifo.offset [entry_num]=device_offset;
	remember_lifo.type [entry_num]=descriptor_ptr;
	strcpy (remember_lifo.name [entry_num],buffer);

	wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",descriptor_ptr->name,device_offset,buffer);
	wrefresh (command_win);
}

void type_dir___set (char *command_line)

/*

Since the dir object doesn't have variables, we provide the impression that it has here. ext2_dir_entry was not used
because it is of variable length.

*/

{
	int found=0;
	unsigned char *ptr,buffer [80],variable [80],value [80],temp [80];
	struct ext2_dir_entry_2 *dir_entry_ptr;

	dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);

	ptr=parse_word (command_line,buffer);
	if (*ptr==0) {
		wprintw (command_win,"Error - Missing arguments\n");refresh_command_win ();
		return;
	}
	parse_word (ptr,buffer);
	ptr=strchr (buffer,'=');
	if (ptr==NULL) {
		wprintw (command_win,"Error - Bad syntax\n");refresh_command_win ();return;
	}
	strncpy (variable,buffer,ptr-buffer);variable [ptr-buffer]=0;
	strcpy (value,++ptr);

	if (strcasecmp ("inode",variable)==0) {
		found=1;
		dir_entry_ptr->inode=atol (value);
		wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->inode);refresh_command_win ();

	}

	if (strcasecmp ("rec_len",variable)==0) {
		found=1;
		dir_entry_ptr->rec_len=(unsigned int) atol (value);
		wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->rec_len);refresh_command_win ();

	}

	if (strcasecmp ("name_len",variable)==0) {
		found=1;
		dir_entry_ptr->name_len=(unsigned int) atol (value);
		wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->name_len);refresh_command_win ();

	}

	if (strcasecmp ("name",variable)==0) {
		found=1;
		if (strlen (value) > dir_entry_ptr->name_len) {
			wprintw (command_win,"Error - Length of name greater then name_len\n");
			refresh_command_win ();return;
		}
		strncpy (dir_entry_ptr->name,value,strlen (value));
		wprintw (command_win,"Variable %s set to %s\n",variable,value);refresh_command_win ();

	}

	if (found) {
		wattrset (show_pad,A_REVERSE);
		strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len);
		temp [dir_entry_ptr->name_len]=0;
		wmove (show_pad,file_info.dir_entry_num,0);
		wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n",
			 dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);
		wattrset (show_pad,A_NORMAL);
		show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2;
		refresh_show_pad ();
		show_dir_status ();
	}

	else {
		wprintw (command_win,"Error - Variable %s not found\n",variable);
		refresh_command_win ();
	}

}

void type_dir___writedata (char *command_line)

/*

We need to override this since the data is not in type_data. Instead, we have to write the buffer which corresponds
to the current block.

*/

{
	low_write (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
	return;
}