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 
init_dir_info(struct struct_file_info * info_ptr)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 
search_dir_entries(int (* action)(struct struct_file_info * info),int * status)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 
count_dir_entries(void)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 
action_count(struct struct_file_info * info)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 
type_dir___cd(char * command_line)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 
action_name(struct struct_file_info * info)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 
type_dir___entry(char * command_line)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 
action_entry_num(struct struct_file_info * info)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 
type_dir___followinode(char * command_line)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 
type_dir___inode(char * command_line)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 
type_dir___show(char * command_line)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 
action_show(struct struct_file_info * info)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 
type_dir___next(char * command_line)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 
type_dir___prev(char * command_line)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 
show_dir_status(void)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 
type_dir___remember(char * command_line)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 
type_dir___set(char * command_line)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 
type_dir___writedata(char * command_line)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 }
679