1 /****************************************************************/
5 /* Main loop of the "Disk editor". */
7 /****************************************************************/
8 /* origination 1989-Jan-15 Terrence W. Holm */
9 /****************************************************************/
12 #include <minix/config.h>
13 #include <sys/types.h>
19 #undef ERROR /* arrgghh, errno.h has this pollution */
28 #include <minix/const.h>
29 #include <minix/type.h>
30 #include "../../servers/mfs/const.h"
31 #include "../../servers/mfs/type.h"
32 #include "../../servers/mfs/inode.h"
36 static char copyright
[] = "de (c) Terrence W. Holm 1989";
39 _PROTOTYPE(void Push
, (de_state
*s
));
40 _PROTOTYPE(int Get_Base
, (int *base
));
41 _PROTOTYPE(int Get_Filename
, (de_state
*s
));
42 _PROTOTYPE(int Get_Count
, (char *units
, unsigned long *result
));
43 _PROTOTYPE(void Exec_Shell
, (void));
44 _PROTOTYPE(void Sigint
, (int));
48 /****************************************************************/
52 /* Initialize. Handle the "-r" recovery option if */
53 /* specified, else enter the main processing loop. */
55 /****************************************************************/
58 void main( argc
, argv
)
63 static de_state s
; /* it is safer not to put it on the stack
64 * and some things probably now rely on zero
67 char *command_name
= argv
[0];
71 s
.device_mode
= O_RDONLY
;
76 if ( argc
== 3 && strcmp( argv
[1], "-r" ) == 0 )
82 else if ( argc
== 3 && strcmp( argv
[1], "-w" ) == 0 )
84 s
.device_mode
= O_RDWR
;
89 if ( argc
!= 2 || *argv
[1] == '-' )
91 fprintf( stderr
, "Usage: %s [-w] /dev/device\n", command_name
);
92 fprintf( stderr
, " %s -r lost_file_name\n", command_name
);
97 /* Set the effective id to the real id. This eliminates */
98 /* any increase in privilege done by a set-uid bit on the */
99 /* executable file. We want to be "root" for recovering */
100 /* files, because we must be able to read the device. */
101 /* However, in normal usage, de(1) should not let just */
102 /* anyone look at a file system, thus we drop the privilege. */
104 /* NOTE: There is a security hole when using "-r" with a */
105 /* set-uid de(1). Do not use set-uid root if there is any */
106 /* way to externally access your Minix system. */
115 /* Set terminal characteristics, and ^C interrupt handler */
119 if ( signal( SIGINT
, SIG_IGN
) != SIG_IGN
)
121 signal( SIGINT
, Sigint
);
122 signal( SIGQUIT
, Sigint
);
127 if ( ! Init_Termcap() )
128 Error( "Requires a termcap entry" );
132 /* Get the device file name. If recovering, also open an output file. */
138 struct stat device_stat
;
139 struct stat tmp_stat
;
141 /* Split the path name into a directory and a file name. */
143 if ( strlen(argv
[1]) > MAX_STRING
)
144 Error( "Path name too long" );
146 if ( ! Path_Dir_File( argv
[1], &dir_name
, &file_name
) )
147 Error( "Recover aborted" );
149 /* Find the device holding the directory. */
151 if ( (s
.device_name
= File_Device( dir_name
)) == NULL
)
152 Error( "Recover aborted" );
155 /* The output file will be in /tmp with the same file name. */
157 strcpy( s
.file_name
, TMP
);
158 strcat( s
.file_name
, "/" );
159 strcat( s
.file_name
, file_name
);
162 /* Make sure /tmp is not on the same device as the file we */
163 /* are trying to recover (we don't want to use up the free */
164 /* i-node and blocks before we get a chance to recover them). */
166 if ( stat( s
.device_name
, &device_stat
) == -1 )
167 Error( "Can not stat(2) device %s", s
.device_name
);
169 if ( stat( TMP
, &tmp_stat
) == -1 )
170 Error( "Can not stat(2) directory %s", TMP
);
172 if ( device_stat
.st_rdev
== tmp_stat
.st_dev
)
173 Error( "Will not recover files on the same device as %s", TMP
);
175 if ( access( s
.file_name
, F_OK
) == 0 )
176 Error( "Will not overwrite file %s", s
.file_name
);
179 /* Open the output file. */
181 if ( (s
.file_f
= fopen( s
.file_name
, "w" )) == NULL
)
182 Error( "Can not open file %s", s
.file_name
);
184 /* Don't let anyone else look at the recovered file */
186 chmod( s
.file_name
, 0700 );
188 /* If running as root then change the owner of the */
189 /* restored file. If not running as root then the */
190 /* chown(2) will fail. */
192 chown( s
.file_name
, getuid(), getgid() );
196 s
.device_name
= argv
[1];
197 s
.file_name
[ 0 ] = '\0';
201 /* Open the device file. */
204 struct stat device_stat
;
207 if ( stat( s
.device_name
, &device_stat
) == -1 )
208 Error( "Can not find file %s", s
.device_name
);
210 if ( (device_stat
.st_mode
& S_IFMT
) != S_IFBLK
&&
211 (device_stat
.st_mode
& S_IFMT
) != S_IFREG
)
212 Error( "Can only edit block special or regular files" );
215 if ( (s
.device_d
= open( s
.device_name
, s
.device_mode
)) == -1 )
216 Error( "Can not open %s", s
.device_name
);
218 if ( (size
= lseek( s
.device_d
, 0L, SEEK_END
)) == -1 )
219 Error( "Error seeking %s", s
.device_name
);
223 Warning( "Device size is not a multiple of 1024" );
224 Warning( "The (partial) last block will not be accessible" );
229 /* Initialize the rest of the state record */
233 s
.search_string
[ 0 ] = '\0';
238 for ( i
= 0; i
< MAX_PREV
; ++i
)
240 s
.prev_addr
[ i
] = 0L;
241 s
.prev_mode
[ i
] = WORD
;
248 Read_Super_Block( &s
);
256 /* Recover mode basically performs an 'x' and an 'X' */
260 ino_t inode
= Find_Deleted_Entry( &s
, argv
[1] );
265 unlink( s
.file_name
);
266 Error( "Recover aborted" );
269 s
.address
= ( (long) s
.first_data
- s
.inode_blocks
) * K
270 + (long) (inode
- 1) * s
.inode_size
;
272 Read_Block( &s
, s
.buffer
);
275 /* Have found the lost i-node, now extract the blocks. */
277 if ( (size
= Recover_Blocks( &s
)) == -1L )
279 unlink( s
.file_name
);
280 Error( "Recover aborted" );
285 printf( "Recovered %ld bytes, written to file %s\n", size
, s
.file_name
);
291 /* Enter the main loop, first time redraw the screen */
300 Read_Block( &s
, s
.buffer
);
302 s
.last_addr
= s
.address
;
306 else if ( rc
== REDRAW_POINTERS
)
308 s
.offset
= (unsigned) (s
.address
& ~ K_MASK
);
312 else if ( rc
== ERROR
)
317 } while ( (rc
= Process( &s
, Arrow_Esc(Get_Char()) )) != EOF
);
321 /* If there is an open output file that was never written to */
322 /* then remove its directory entry. This occurs when no 'w' */
323 /* or 'W' command occurred between a 'c' command and exiting */
326 if ( s
.file_name
[0] != '\0' && ! s
.file_written
)
327 unlink( s
.file_name
);
330 Reset_Term(); /* Restore terminal characteristics */
337 /****************************************************************/
339 /* Get_Base( base ) */
341 /* Get a new base value. */
342 /* Returns REDRAW or ERROR. */
344 /****************************************************************/
351 switch ( Get_Char() )
353 case 'h' : *base
= 16;
356 case 'd' : *base
= 10;
359 case 'o' : *base
= 8;
362 case 'b' : *base
= 2;
365 default : return( ERROR
);
373 /****************************************************************/
375 /* Process( state, input_char ) */
377 /* Determine the function requested by the */
378 /* input character. Returns OK, REDRAW, */
379 /* REDRAW_POINTERS, ERROR or EOF. */
381 /****************************************************************/
391 case 'b' : /* Back up one block */
394 if ( s
->address
== 0 )
397 s
->address
= (s
->address
- K
) & K_MASK
;
402 case 'B' : /* Back up to home */
405 if ( s
->address
== 0 )
415 case 'c' : /* Change file name */
418 int rc
= Get_Filename( s
);
420 return( rc
== OK
? REDRAW
: rc
);
424 case 'd' : /* Down */
428 s
->last_addr
= s
->address
;
432 case WORD
: s
->address
+= 2;
434 if ( (s
->address
& PAGE_MASK
) == 0 )
437 return( REDRAW_POINTERS
);
439 case BLOCK
: s
->address
+= 64;
441 if ( (s
->last_addr
& K_MASK
) !=
442 (s
->address
& K_MASK
) )
445 return( REDRAW_POINTERS
);
447 case MAP
: s
->address
+= 256;
451 default : Error( "Internal fault (mode)" );
456 case 'f' : /* Forward one block */
460 if ( s
->block
== s
->device_size
- 1 )
463 s
->address
= (s
->address
+ K
) & K_MASK
;
468 case 'F' : /* Forward to end */
472 off_t last_block
= ( (long) s
->device_size
- 1 ) * K
;
474 if ( s
->address
== last_block
)
479 s
->address
= last_block
;
485 case 'g' : /* Goto block */
490 if ( Get_Count( "Block?", &block
) )
492 if ( block
>= s
->zones
)
494 Warning( "Block number too large" );
500 s
->address
= (off_t
) block
* K
;
509 case 'G' : /* Goto block indirect */
512 unsigned block
= *( (word_t
*) &s
->buffer
[ s
->offset
] );
514 if ( s
->mode
!= WORD
)
516 Warning( "Must be in visual mode \"word\"" );
520 if ( block
>= s
->zones
)
522 Warning( "Block number too large" );
529 s
->address
= (long) block
* K
;
535 case 'h' : /* Help */
538 Draw_Help_Screen( s
);
545 case 'i' : /* Goto i-node */
550 if ( Get_Count( "I-node?", &inode
) )
552 if ( inode
< 1 || inode
> s
->inodes
)
554 Warning( "Illegal i-node number" );
561 s
->address
= (off_t
) (s
->first_data
- s
->inode_blocks
) * K
562 + (off_t
) (inode
- 1) * s
->inode_size
;
571 case 'I' : /* Filename to i-node */
577 Draw_Prompt( "File name?" );
579 filename
= Get_Line();
581 if ( filename
== NULL
|| filename
[0] == '\0' )
584 inode
= Find_Inode( s
, filename
);
591 s
->address
= ( (long) s
->first_data
- s
->inode_blocks
) * K
592 + (long) (inode
- 1) * s
->inode_size
;
599 case 'l' : /* Left */
603 s
->last_addr
= s
->address
;
607 case WORD
: s
->address
= s
->address
- 32;
611 case BLOCK
: s
->address
-= 1;
613 if ( (s
->last_addr
& K_MASK
) !=
614 (s
->address
& K_MASK
) )
617 return( REDRAW_POINTERS
);
619 case MAP
: s
->address
-= 4;
621 if ( (s
->last_addr
& ~ MAP_MASK
) !=
622 (s
->address
& ~ MAP_MASK
) )
625 return( REDRAW_POINTERS
);
627 default : Error( "Internal fault (mode)" );
632 case 'm' : /* Invoke a Minix shell */
643 case 'n' : /* Search for next */
648 if ( s
->search_string
[0] == '\0' )
650 Warning( "No search string defined" );
654 Draw_Prompt( "Searching..." );
656 if ( (addr
= Search( s
, s
->search_string
)) == -1L )
658 Warning( "Search string not found" );
672 case 'o' : /* Set output base */
674 Draw_Prompt( "Output base?" );
676 return( Get_Base( &s
->output_base
) );
679 case 'p' : /* Previous address */
684 s
->address
= s
->prev_addr
[ 0 ];
685 s
->mode
= s
->prev_mode
[ 0 ];
687 for ( i
= 0; i
< MAX_PREV
- 1; ++i
)
689 s
->prev_addr
[ i
] = s
->prev_addr
[ i
+ 1 ];
690 s
->prev_mode
[ i
] = s
->prev_mode
[ i
+ 1 ];
697 case 'q' : /* Quit */
704 case 'r' : /* Right */
708 s
->last_addr
= s
->address
;
712 case WORD
: s
->address
+= 32;
716 case BLOCK
: s
->address
+= 1;
718 if ( (s
->last_addr
& K_MASK
) !=
719 (s
->address
& K_MASK
) )
722 return( REDRAW_POINTERS
);
724 case MAP
: s
->address
+= 4;
726 if ( (s
->last_addr
& ~ MAP_MASK
) !=
727 (s
->address
& ~ MAP_MASK
) )
730 return( REDRAW_POINTERS
);
732 default : Error( "Internal fault (mode)" );
736 case 's' : /* Store word */
741 if ( s
->mode
!= WORD
)
743 Warning( "Must be in visual mode \"word\"" );
747 if ( s
->device_mode
== O_RDONLY
)
749 Warning( "Use -w option to open device for writing" );
753 if ( Get_Count( "Store word?", &word
) )
755 if ( word
!= (word_t
) word
)
757 Warning( "Word is more than 16 bits" );
760 Write_Word( s
, (word_t
) word
);
773 s
->last_addr
= s
->address
;
777 case WORD
: s
->address
-= 2;
779 if ( (s
->last_addr
& PAGE_MASK
) == 0 )
782 return( REDRAW_POINTERS
);
784 case BLOCK
: s
->address
-= 64;
786 if ( (s
->last_addr
& K_MASK
) !=
787 (s
->address
& K_MASK
) )
790 return( REDRAW_POINTERS
);
792 case MAP
: s
->address
-= 256;
796 default : Error( "Internal fault (mode)" );
801 case 'v' : /* Visual mode */
803 Draw_Prompt( "Visual mode?" );
805 switch ( Get_Char() )
807 case 'w' : s
->mode
= WORD
;
810 case 'b' : s
->mode
= BLOCK
;
814 /* Assume user knows if map mode is possible
815 char *tty = ttyname( 0 );
818 strcmp( tty, "/dev/tty0" ) != 0 )
819 Warning( "Must be at console" );
827 default : return( ERROR
);
833 case 'w' : /* Write ASCII block */
835 if ( s
->file_name
[0] == '\0' )
837 int rc
= Get_Filename( s
);
843 /* We have a successfully opened file */
845 /* Eliminate non-ASCII characters */
849 char *from
= s
->buffer
;
852 for ( i
= 0; i
< K
; ++i
, ++from
)
856 if ( *to
!= '\0' && *to
!= '\177' )
860 if ( fwrite( buf
, 1, (int)(to
- buf
), s
->file_f
) != to
- buf
)
861 Warning( "Problem writing out buffer" );
869 case 'W' : /* Write block exactly */
871 if ( s
->file_name
[0] == '\0' )
873 int rc
= Get_Filename( s
);
879 /* We have a successfully opened file */
881 if ( fwrite( s
->buffer
, 1, K
, s
->file_f
) != K
)
882 Warning( "Problem writing out buffer" );
889 case 'x' : /* eXtract lost entry */
895 Draw_Prompt( "Lost file name?" );
897 filename
= Get_Line();
899 if ( filename
== NULL
|| filename
[0] == '\0' )
902 inode
= Find_Deleted_Entry( s
, filename
);
909 s
->address
= ( (long) s
->first_data
- s
->inode_blocks
) * K
910 + (long) (inode
- 1) * s
->inode_size
;
917 case 'X' : /* eXtract lost blocks */
922 if ( s
->mode
!= WORD
)
924 Warning( "Must be in visual mode \"word\"" );
929 /* Force a new output file name. */
931 if ( (rc
= Get_Filename( s
)) != OK
)
938 Draw_Prompt( "Recovering..." );
940 if ( Recover_Blocks( s
) == -1L )
941 unlink( s
->file_name
);
943 /* Force closure of output file. */
946 s
->file_name
[ 0 ] = '\0';
952 case '/' : /* Search */
959 Draw_Prompt( "Search string?" );
963 if ( string
== NULL
)
966 if ( string
[0] != '\0' )
968 strcpy( s
->search_string
, string
);
972 else if ( s
->search_string
[0] == '\0' )
974 Warning( "No search string defined" );
979 Draw_Prompt( "Searching..." );
981 if ( (addr
= Search( s
, s
->search_string
)) == -1L )
983 Warning( "Search string not found" );
1009 /****************************************************************/
1013 /* Push current address and mode, used by the */
1014 /* commands B, F, g, G, i, I, n, x and /. This */
1015 /* information is popped by the 'p' command. */
1017 /****************************************************************/
1026 for ( i
= MAX_PREV
- 1; i
> 0; --i
)
1028 s
->prev_addr
[ i
] = s
->prev_addr
[ i
- 1 ];
1029 s
->prev_mode
[ i
] = s
->prev_mode
[ i
- 1 ];
1032 s
->prev_addr
[ 0 ] = s
->address
;
1033 s
->prev_mode
[ 0 ] = s
->mode
;
1041 /****************************************************************/
1043 /* Get_Filename( state ) */
1045 /* Read and check a filename. */
1047 /****************************************************************/
1050 int Get_Filename( s
)
1058 Draw_Prompt( "File name?" );
1060 filename
= Get_Line();
1062 if ( filename
== NULL
|| filename
[0] == '\0' )
1066 for ( name
= filename
; *name
!= '\0'; ++name
)
1067 if ( ! isgraph( *name
) )
1069 Warning( "File name contains non-graphic characters" );
1074 if ( access( filename
, F_OK
) == 0 )
1076 Warning( "Will not overwrite file %s", filename
);
1080 if ( (f
= fopen( filename
, "w" )) == NULL
)
1082 Warning( "Can not open file %s", filename
);
1086 /* If there is already an open output file then */
1087 /* close it. If it was never written to then */
1088 /* remove its directory entry. */
1090 if ( s
->file_name
[0] != '\0' )
1092 if ( ! s
->file_written
)
1093 unlink( s
->file_name
);
1095 fclose( s
->file_f
);
1098 strcpy( s
->file_name
, filename
);
1100 s
->file_written
= 0;
1110 /****************************************************************/
1114 /* Read and check a number. Returns non-zero */
1115 /* if successful. */
1117 /****************************************************************/
1120 int Get_Count( units
, result
)
1122 unsigned long *result
;
1127 Draw_Prompt( units
);
1129 number
= Get_Line();
1131 if ( number
== NULL
|| number
[0] == '\0' )
1135 *result
= strtoul( number
, (char **) NULL
, 0 );
1136 return( errno
== 0 );
1144 /****************************************************************/
1146 /* In_Use( bit, map ) */
1148 /* Is the bit set in the map? */
1150 /****************************************************************/
1153 int In_Use( bit
, map
)
1158 return( map
[ (int) (bit
/ (CHAR_BIT
* sizeof (bitchunk_t
))) ] &
1159 (1 << ((unsigned) bit
% (CHAR_BIT
* sizeof (bitchunk_t
)))) );
1167 /****************************************************************/
1169 /* Find_Inode( state, filename ) */
1171 /* Find the i-node for the given file name. */
1173 /****************************************************************/
1176 ino_t
Find_Inode( s
, filename
)
1181 struct stat device_stat
;
1182 struct stat file_stat
;
1186 if ( fstat( s
->device_d
, &device_stat
) == -1 )
1187 Error( "Can not fstat(2) file system device" );
1190 if ( lstat( filename
, &file_stat
) == -1 )
1192 if ( stat( filename
, &file_stat
) == -1 )
1195 Warning( "Can not find file %s", filename
);
1199 if ( device_stat
.st_rdev
!= file_stat
.st_dev
)
1201 Warning( "File is not on device %s", s
->device_name
);
1206 inode
= file_stat
.st_ino
;
1208 if ( inode
< 1 || inode
> s
->inodes
)
1210 Warning( "Illegal i-node number" );
1222 /****************************************************************/
1226 /* Fork off a sub-process to exec() the shell. */
1228 /****************************************************************/
1242 /* The child process */
1244 extern char **environ
;
1245 char *shell
= getenv( "SHELL" );
1247 if ( shell
== NULL
)
1250 execle( shell
, shell
, (char *) 0, environ
);
1257 /* The parent process: ignore signals, wait for sub-process */
1259 signal( SIGINT
, SIG_IGN
);
1260 signal( SIGQUIT
, SIG_IGN
);
1266 while ( (w
=wait(&status
)) != pid
&& w
!= -1 );
1269 signal( SIGINT
, Sigint
);
1270 signal( SIGQUIT
, Sigint
);
1280 /****************************************************************/
1284 /* Terminate the program on an interrupt (^C) */
1285 /* or quit (^\) signal. */
1287 /****************************************************************/
1293 Reset_Term(); /* Restore terminal characteristics */
1305 /****************************************************************/
1307 /* Error( text, ... ) */
1309 /* Print an error message on stderr. */
1311 /****************************************************************/
1315 void Error( const char *text
, ... )
1326 fprintf( stderr
, "\nde: " );
1327 va_start( argp
, text
);
1328 vfprintf( stderr
, text
, argp
);
1331 fprintf( stderr
, ": %s", strerror( errno
) );
1332 fprintf( stderr
, "\n" );