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 
init_dir_info(struct struct_file_info * info_ptr)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 
search_dir_entries(int (* action)(struct struct_file_info * info),int * status)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_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 
count_dir_entries(void)147 long count_dir_entries (void)
148 
149 /*
150 
151 This function counts the number of entries in the directory. We just call search_dir_entries till the end.
152 The 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 
action_count(struct struct_file_info * info)162 int action_count (struct struct_file_info *info)
163 
164 /*
165 
166 Used by count_dir_entries above - This function is called by search_dir_entries, and it tells it to continue
167 searching, until we get to the last entry.
168 
169 */
170 
171 {
172 	return (CONTINUE);							/* Just continue searching */
173 }
174 
type_dir___cd(char * command_line)175 void 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 
183 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
184 	calling directly type_ext2___cd.
185 
186 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
187 	1 and into 2/3/4.
188 
189 3.	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 
192 4.	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 
196 5.	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 
202 6.	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 
205 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",
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 
209 8.	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 
213 9.	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 
action_name(struct struct_file_info * info)312 int action_name (struct struct_file_info *info)
313 
314 /*
315 
316 Compares the current search entry name (somewhere inside info) with the required name (in name_search).
317 Returns 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 
type_dir___entry(char * command_line)335 void type_dir___entry (char *command_line)
336 
337 /*
338 
339 Selects a directory entry according to its number.
340 search_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 
action_entry_num(struct struct_file_info * info)373 int action_entry_num (struct struct_file_info *info)
374 
375 /*
376 
377 Used 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 
type_dir___followinode(char * command_line)388 void type_dir___followinode (char *command_line)
389 
390 /*
391 
392 Here we pass to the inode pointed by the current entry.
393 It 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 
type_dir___inode(char * command_line)410 void type_dir___inode (char *command_line)
411 
412 /*
413 
414 Returns to the parent inode of the current directory.
415 This 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 
type_dir___show(char * command_line)424 void type_dir___show (char *command_line)
425 
426 /*
427 
428 We 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 
action_show(struct struct_file_info * info)444 int action_show (struct struct_file_info *info)
445 
446 /*
447 
448 Show 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 
type_dir___next(char * command_line)476 void type_dir___next (char *command_line)
477 
478 /*
479 
480 This 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 
type_dir___prev(char * command_line)499 void 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 
show_dir_status(void)515 void show_dir_status (void)
516 
517 /*
518 
519 Various 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 
type_dir___remember(char * command_line)537 void type_dir___remember (char *command_line)
538 
539 /*
540 
541 This is overrided here because we don't remember a directory - It is too complicated. Instead, we remember the
542 inode 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 
type_dir___set(char * command_line)584 void type_dir___set (char *command_line)
585 
586 /*
587 
588 Since the dir object doesn't have variables, we provide the impression that it has here. ext2_dir_entry was not used
589 because 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 
type_dir___writedata(char * command_line)665 void type_dir___writedata (char *command_line)
666 
667 /*
668 
669 We need to override this since the data is not in type_data. Instead, we have to write the buffer which corresponds
670 to 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